@kotao/storefront
v0.2.9
Published
Kotao Storefronts theme SDK — framework-agnostic server runtime, React Router 8 binding, and authenticated source-authoritative React/Hydrogen editor bridge (ADR-0003/ADR-0047). Leaf package (ADR-0006).
Readme
@kotao/storefront
Framework-agnostic theme SDK runtime for Kotao Storefronts (ADR-0003). It mirrors Hydrogen's
API so a Hydrogen → Kotao migration is largely mechanical, and papers over the four Cloudflare
Workers-for-Platforms untrusted-mode realities (no request.cf, no caches.default, no eval,
frozen intra-request clock).
API mapping
| Hydrogen | Kotao | Notes |
| ---------------------------------------- | -------------------------------------------------------- | ----------------------------------------------------------------- |
| createStorefrontClient | createStorefrontClient | typed GraphQL; SWR-cached query, uncached mutate |
| createWithCache | createWithCache | SWR caching for third-party data |
| CacheShort / CacheLong / CacheNone | CacheShort / CacheLong / CacheNone / CacheCustom | plus StorefrontDefaultCache (fresh 1s / serve ~1 day) |
| createHydrogenContext | createKotaoContext | storefront + geo + cache + locale + session seam |
| Oxygen request.cf | context.geo | backed by signed x-kotao-* headers; null if unsigned/tampered |
| (Oxygen-managed) | @kotao/storefront/react-router | RR8 getKotaoLoadContext |
The one real migration diff is request.cf → context.geo.
Entry points
@kotao/storefront— framework-agnostic core (no React Router import).@kotao/storefront/editor— Liquid settings bridge plus the authenticated, bounded Hydrogen component-instance protocol and preview store.@kotao/storefront/react— Hydrogen-shaped React components and editor-only component boundaries/registry helpers.@kotao/storefront/react-router— the React Router 8 binding (getKotaoLoadContext).
Event approval offers
EventApprovalOffer from @kotao/storefront/events/react accepts an optional formatDateTime
callback for the visible event start and offer expiry. Format these in the event's locale and a
fixed time zone so server and browser output agree. The original timestamps remain unchanged in
the time elements and the offer lifecycle.
Hydrogen editor preview
createHydrogenEditorPreviewRuntime projects manifest-authorized operations into an in-memory
instance tree. The source manifest and Git base remain authoritative; this store never writes source
or becomes public configuration. Each message is bound to the exact admin/preview origin, expected
window, session, base SHA, manifest hash, channel nonce, short-lived preview token, protocol version,
message ID, and monotonic sequence. Payloads fail closed above 128 KiB/depth 32, and at most 100
authenticated messages are buffered before Ready.
React themes mount HydrogenEditorProvider only in authenticated editor previews, mark extracted
instances with HydrogenEditorBoundary, and use HydrogenEditorSlot for declared insertion
boundaries. Per-instance subscriptions let prop/style changes reconcile only the affected subtree;
route, loader, Suspense, and HMR reconciliation retains draft operations and reports whether the
selection is active or out of route. Ordinary public storefront responses must not mount these
overlays or receive editor credentials.
Preview servers turn the opaque token into browser-safe runtime configuration with
createHydrogenEditorPreviewBootstrap. The expected binding must come from the trusted edit
session, never from request parameters:
const editorBootstrap = await createHydrogenEditorPreviewBootstrap({
token: requestToken,
secret: env.HYDROGEN_EDITOR_PREVIEW_GRANT_SECRET,
expected: trustedEditSession.previewBinding,
now: Date.now(),
})An invalid, missing, expired, or incorrectly bound token returns null. Only a non-null result may
be serialized into an editor-preview response; the signing secret is never part of that result.
Public responses omit both the bootstrap and the editor entry point. In the browser, dynamically
import @kotao/storefront/editor only when the server supplied a bootstrap, then call
createHydrogenEditorPreviewRuntimeFromBootstrap with currentOrigin: location.origin. The
factory refuses to start unless the current preview origin exactly matches the signed binding and
targets every runtime message at the signed Workspaces origin.
On editor-only preview hosts, the Gateway's Hydrogen canvas controller measures those stable DOM
boundaries and emits same-document interaction intents for move, inline content, bounded layout,
and insertion requests. HydrogenEditorProvider is the only consumer: it translates each intent
through dispatchCanvasIntent, validates it against the live manifest/slot/capability contract,
and only then applies and forwards the preview operation over the authenticated bridge. The DOM
controller has no source-write path and cannot bypass unsupported or read-only boundaries.
Signed geo contract
signGeo (used by the Gateway Worker, #81) and verifyGeo (used here) live together so the
x-kotao-geo / x-kotao-geo-signature HMAC-SHA256 contract can never drift. The signing key is
env.STOREFRONT_GEO_SIGNING_KEY. Any missing/invalid signature yields null — unsigned data is
never trusted.
Status: #79 ships the core runtime. The Storefront API GraphQL schema + typed codegen, cart, SEO, analytics, and customer-account surfaces are tracked as separate issues.
