@kotao/storefront 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-U5677AEA.js → chunk-7XYWHWT4.js} +16 -6
- package/dist/chunk-7XYWHWT4.js.map +1 -0
- package/dist/{chunk-GCQI6OKH.js → chunk-H3D7BAOL.js} +3 -26
- package/dist/chunk-H3D7BAOL.js.map +1 -0
- package/dist/chunk-M7BPQ54D.js +26 -0
- package/dist/chunk-M7BPQ54D.js.map +1 -0
- package/dist/{chunk-ISAFEOR2.js → chunk-PXVDVUFX.js} +3 -2
- package/dist/chunk-PXVDVUFX.js.map +1 -0
- package/dist/{client-D9YYA2w6.d.ts → client-DiPdlvPu.d.ts} +1 -1
- package/dist/fetch-B6TIc6lI.d.ts +47 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/react-router.d.ts +5 -3
- package/dist/react-router.js +3 -2
- package/dist/react-router.js.map +1 -1
- package/dist/react.js +285 -146
- package/dist/react.js.map +1 -1
- package/dist/{routing-BfUp49o8.d.ts → routing-BxRlep_-.d.ts} +11 -1
- package/dist/seo.d.ts +3 -1
- package/dist/seo.js +1 -1
- package/dist/settings.d.ts +3 -37
- package/dist/settings.js +2 -1
- package/package.json +1 -1
- package/dist/chunk-GCQI6OKH.js.map +0 -1
- package/dist/chunk-ISAFEOR2.js.map +0 -1
- package/dist/chunk-U5677AEA.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { S as
|
|
1
|
+
import { S as StoredThemeSettings } from './fetch-B6TIc6lI.js';
|
|
2
|
+
import { a as StorefrontClient, C as CacheStrategy, K as KotaoCache } from './client-DiPdlvPu.js';
|
|
2
3
|
import { C as CartFormAction, a as Cart, b as CartLineInput, c as CartAttribute, d as CartBuyerIdentityInput, e as CartMutationResult, f as CartLineUpdateInput, g as CartMetafield, h as CartMetafieldIdentifier, i as CartDeliveryAddress, j as CartSelectedDeliveryOption, H as HydrogenSession } from './types-Df3bGbKU.js';
|
|
3
4
|
|
|
4
5
|
interface CartCreateInput {
|
|
@@ -271,6 +272,15 @@ interface KotaoContext {
|
|
|
271
272
|
readonly session: KotaoSession;
|
|
272
273
|
readonly env: any;
|
|
273
274
|
readonly waitUntil: (p: Promise<unknown>) => void;
|
|
275
|
+
/**
|
|
276
|
+
* Fetch this shop's stored theme settings (ordered section instances + global tokens) for SSR.
|
|
277
|
+
* The Gateway-signed shop context from the inbound request is forwarded automatically and the call
|
|
278
|
+
* goes cross-origin to the API — mirroring how `storefront` authenticates, because a Worker
|
|
279
|
+
* subrequest to its own zone bypasses the Gateway carve-out.
|
|
280
|
+
*/
|
|
281
|
+
readonly loadThemeSettings: (opts?: {
|
|
282
|
+
readonly fetch?: typeof fetch;
|
|
283
|
+
}) => Promise<StoredThemeSettings>;
|
|
274
284
|
}
|
|
275
285
|
/**
|
|
276
286
|
* Framework-agnostic per-request context, mirroring Hydrogen's `createHydrogenContext`:
|
package/dist/seo.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { S as SeoConfig, b as SeoDescriptor, J as JsonLd } from './types-BDhHWZsP.js';
|
|
2
2
|
export { A as Alternate, O as OpenGraph, a as OpenGraphImage, T as TwitterCard } from './types-BDhHWZsP.js';
|
|
3
|
-
import {
|
|
3
|
+
import { a as StorefrontClient } from './client-DiPdlvPu.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Merge SEO config across levels and emit a flat, deduplicated descriptor list (RR7 `meta`-compatible):
|
|
@@ -119,6 +119,8 @@ declare function sitemapIndexResponse(sitemaps: readonly SitemapIndexEntry[]): R
|
|
|
119
119
|
interface RobotsOptions {
|
|
120
120
|
readonly production: boolean;
|
|
121
121
|
readonly sitemapUrl?: string;
|
|
122
|
+
/** Path prefixes to Disallow in production (e.g. /cart, /checkout — Shopify parity). */
|
|
123
|
+
readonly disallow?: readonly string[];
|
|
122
124
|
}
|
|
123
125
|
declare function buildRobotsTxt(options: RobotsOptions): string;
|
|
124
126
|
declare function robotsResponse(options: RobotsOptions): Response;
|
package/dist/seo.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './chunk-
|
|
1
|
+
export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './chunk-PXVDVUFX.js';
|
|
2
2
|
export { getSeoMeta, serializeJsonLd } from './chunk-MYDKIJRH.js';
|
|
3
3
|
//# sourceMappingURL=seo.js.map
|
|
4
4
|
//# sourceMappingURL=seo.js.map
|
package/dist/settings.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { SectionSchema, Preset, ValuesDocument, SettingInput, SectionInstance } from '@kotao/storefront-contracts/theme';
|
|
2
2
|
import { a as ResolvedSection, b as ResolvedSettings, T as ThemeSettingsSnapshot } from './store-DQK1nhob.js';
|
|
3
3
|
export { R as ResolvedBlock, c as ThemeSettingsStore, d as createThemeSettingsStore } from './store-DQK1nhob.js';
|
|
4
|
-
|
|
4
|
+
export { F as FetchThemeSettingsOptions, S as StoredThemeSettings, f as fetchThemeSettings } from './fetch-B6TIc6lI.js';
|
|
5
|
+
import './client-DiPdlvPu.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The settings-as-data merge engine (ADR-0003 §"Settings-as-data customizer", #93). Pure: it resolves
|
|
@@ -49,39 +50,4 @@ interface ResolveThemeSettingsArgs {
|
|
|
49
50
|
/** Compose the merge over an entire values document → the snapshot a theme renders from. */
|
|
50
51
|
declare function resolveThemeSettings(args: ResolveThemeSettingsArgs): ThemeSettingsSnapshot;
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
* The stored settings a theme hydrates from (#93): the layout document's ordered section instances +
|
|
54
|
-
* the global theme tokens. Pass to {@link resolveThemeSettings} together with the theme's bundled
|
|
55
|
-
* section schemas + presets to produce the merged {@link ThemeSettingsSnapshot}.
|
|
56
|
-
*/
|
|
57
|
-
interface StoredThemeSettings {
|
|
58
|
-
readonly shopId: string;
|
|
59
|
-
readonly sections: readonly SectionInstance[];
|
|
60
|
-
readonly global: Record<string, unknown>;
|
|
61
|
-
readonly templates?: Readonly<Partial<Record<string, ValuesDocument>>>;
|
|
62
|
-
readonly sectionGroups?: Readonly<Partial<Record<string, ValuesDocument>>>;
|
|
63
|
-
}
|
|
64
|
-
interface FetchThemeSettingsOptions {
|
|
65
|
-
/** Injectable for tests; defaults to the global `fetch`. */
|
|
66
|
-
readonly fetch?: typeof fetch;
|
|
67
|
-
/** Same-origin settings endpoint (default `/api/storefront/settings`). */
|
|
68
|
-
readonly endpoint?: string;
|
|
69
|
-
/**
|
|
70
|
-
* Explicit local-preview escape hatch for server-side theme workers that call the Kotao API directly.
|
|
71
|
-
* Production themes should keep the default same-origin path.
|
|
72
|
-
*/
|
|
73
|
-
readonly allowCrossOrigin?: boolean;
|
|
74
|
-
/** Optional server-side auth headers for local previews. Never pass browser-visible tokens here. */
|
|
75
|
-
readonly auth?: {
|
|
76
|
-
readonly token: string;
|
|
77
|
-
readonly tokenHeader?: StorefrontAuthHeader;
|
|
78
|
-
readonly siteId?: string;
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Fetch the shop's stored settings document via the same-origin `/api/storefront/settings` carve-out.
|
|
83
|
-
* The shop is resolved server-side from the Gateway-signed context — the theme sends no id and no token.
|
|
84
|
-
*/
|
|
85
|
-
declare function fetchThemeSettings(options?: FetchThemeSettingsOptions): Promise<StoredThemeSettings>;
|
|
86
|
-
|
|
87
|
-
export { type FetchThemeSettingsOptions, type ResolveThemeSettingsArgs, ResolvedSection, ResolvedSettings, type StoredThemeSettings, ThemeSettingsSnapshot, fetchThemeSettings, mergeSection, mergeSettings, resolveThemeSettings };
|
|
53
|
+
export { type ResolveThemeSettingsArgs, ResolvedSection, ResolvedSettings, ThemeSettingsSnapshot, mergeSection, mergeSettings, resolveThemeSettings };
|
package/dist/settings.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { createThemeSettingsStore,
|
|
1
|
+
export { createThemeSettingsStore, mergeSection, mergeSettings, resolveThemeSettings } from './chunk-H3D7BAOL.js';
|
|
2
|
+
export { fetchThemeSettings } from './chunk-M7BPQ54D.js';
|
|
2
3
|
//# sourceMappingURL=settings.js.map
|
|
3
4
|
//# sourceMappingURL=settings.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kotao/storefront",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Kotao Storefronts theme SDK — framework-agnostic server runtime (storefront client, SWR isolated cache, signed geo) + React Router 7 binding. Mirrors Hydrogen's API (ADR-0003). Leaf package (ADR-0006).",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/settings/merge.ts","../src/settings/store.ts","../src/settings/fetch.ts"],"names":["section","block"],"mappings":"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","file":"chunk-GCQI6OKH.js","sourcesContent":["/**\n * The settings-as-data merge engine (ADR-0003 §\"Settings-as-data customizer\", #93). Pure: it resolves\n * a section's effective settings with the universal three-layer order\n *\n * stored value → preset value → input `defaultValue`\n *\n * and composes that over a whole values document (`resolveThemeSettings`). It lives in the SDK (not a\n * commerce module) because it consumes `@kotao/storefront-contracts` types — the storefront layer that\n * may import them (turbo boundaries forbids a module from doing so). The per-shop stored values come from\n * the web module's settings store, read server-side and handed to the SDK as a snapshot.\n */\nimport type {\n Preset,\n SectionInstance,\n SectionSchema,\n SettingInput,\n ValuesDocument,\n} from '@kotao/storefront-contracts/theme'\n\nimport type {\n ResolvedSection,\n ResolvedSettings,\n ResolvedValuesDocument,\n ThemeSettingsSnapshot,\n} from './types'\n\nexport type {\n ResolvedBlock,\n ResolvedSection,\n ResolvedSettings,\n ResolvedValuesDocument,\n ThemeSettingsSnapshot,\n} from './types'\n\n/**\n * Resolve one settings layer: for every input the schema declares, take the stored value, else the\n * preset value, else the input's `defaultValue`. Only declared inputs appear (stray stored keys for\n * removed settings are dropped).\n */\nexport function mergeSettings(\n inputs: readonly SettingInput[],\n stored: Readonly<Record<string, unknown>> | undefined,\n preset: Readonly<Record<string, unknown>> | undefined,\n): ResolvedSettings {\n const out: ResolvedSettings = {}\n for (const input of inputs) {\n const storedValue = stored?.[input.name]\n const presetValue = preset?.[input.name]\n out[input.name] =\n storedValue !== undefined\n ? storedValue\n : presetValue !== undefined\n ? presetValue\n : input.defaultValue\n }\n return out\n}\n\n/**\n * Resolve a full section instance: its settings plus each contained block's settings, all three-layer\n * merged against the section's schema (block instances come from the stored values).\n */\nexport function mergeSection(args: {\n readonly schema: SectionSchema\n readonly preset?: Preset\n readonly instance?: SectionInstance\n}): ResolvedSection {\n const settings = mergeSettings(\n args.schema.settings,\n args.instance?.settings,\n args.preset?.settings,\n )\n if (args.instance?.settings.disabled === true) settings.disabled = true\n const blockDefs = new Map((args.schema.blocks ?? []).map((b) => [b.type, b] as const))\n const blocks = (args.instance?.blocks ?? []).map((b) => ({\n ...(b.id !== undefined ? { id: b.id } : {}),\n type: b.type,\n settings: mergeSettings(blockDefs.get(b.type)?.settings ?? [], b.settings, undefined),\n }))\n return { id: args.instance?.id ?? '', type: args.schema.type, settings, blocks }\n}\n\nexport interface ResolveThemeSettingsArgs {\n /** Section schemas keyed by section `type` (from the deployment manifest). */\n readonly schemas: Readonly<Record<string, SectionSchema>>\n /** Optional default preset per section `type`. */\n readonly presets?: Readonly<Record<string, Preset>>\n /** The per-shop stored values document (from the web module's settings store). */\n readonly values: ValuesDocument\n /** Template-specific values documents, keyed by handles like `index`, `product`, or `cart`. */\n readonly templates?: Partial<Record<string, ValuesDocument>>\n /** Layout section-group values documents, keyed by handles like `header-group`. */\n readonly sectionGroups?: Partial<Record<string, ValuesDocument>>\n /** Global theme-token inputs (logo/colors/fonts), merged the same way. */\n readonly globalInputs?: readonly SettingInput[]\n readonly globalStored?: Readonly<Record<string, unknown>>\n readonly globalPreset?: Readonly<Record<string, unknown>>\n}\n\nfunction resolveValuesDocument(\n args: Pick<ResolveThemeSettingsArgs, 'presets' | 'schemas'>,\n values: ValuesDocument,\n): ResolvedValuesDocument {\n const sections = values.sections.map((instance): ResolvedSection => {\n const schema = args.schemas[instance.type]\n if (schema === undefined) {\n // Unknown section type (theme dropped it) — pass stored values through untouched; nothing to merge.\n return {\n id: instance.id,\n type: instance.type,\n settings: instance.settings,\n blocks: instance.blocks.map((b) => ({\n ...(b.id !== undefined ? { id: b.id } : {}),\n type: b.type,\n settings: b.settings,\n })),\n }\n }\n const preset = args.presets?.[instance.type]\n return mergeSection({ schema, instance, ...(preset !== undefined ? { preset } : {}) })\n })\n return { shopId: values.shopId, sections }\n}\n\nfunction resolveDocumentRecord(\n args: Pick<ResolveThemeSettingsArgs, 'presets' | 'schemas'>,\n documents: Partial<Record<string, ValuesDocument>> | undefined,\n): Readonly<Record<string, ResolvedValuesDocument>> | undefined {\n if (documents === undefined) return undefined\n const resolved: Record<string, ResolvedValuesDocument> = {}\n for (const [handle, document] of Object.entries(documents)) {\n if (document !== undefined) resolved[handle] = resolveValuesDocument(args, document)\n }\n return resolved\n}\n\n/** Compose the merge over an entire values document → the snapshot a theme renders from. */\nexport function resolveThemeSettings(args: ResolveThemeSettingsArgs): ThemeSettingsSnapshot {\n const root = resolveValuesDocument(args, args.values)\n const global = mergeSettings(args.globalInputs ?? [], args.globalStored, args.globalPreset)\n const templates = resolveDocumentRecord(args, args.templates)\n const sectionGroups = resolveDocumentRecord(args, args.sectionGroups)\n return {\n sections: root.sections,\n global,\n ...(templates !== undefined ? { templates } : {}),\n ...(sectionGroups !== undefined ? { sectionGroups } : {}),\n }\n}\n","/**\n * Framework-agnostic theme-settings store (#93). Holds the merged {@link ThemeSettingsSnapshot} and lets\n * subscribers react to live edits. In plain runtime the snapshot is the stable server-provided value and\n * never changes; in the visual editor, postMessage-driven setting patches call `patchSection`/`patchGlobal`\n * so the React binding (useSyncExternalStore) re-renders without a reload. `getSnapshot` returns a stable\n * reference between patches (a new one only on change), as useSyncExternalStore requires.\n */\nimport type {\n ResolvedBlock,\n ResolvedSection,\n ResolvedSettings,\n ResolvedValuesDocument,\n ThemeSettingsSnapshot,\n} from './types'\n\nexport interface ThemeSettingsStore {\n getSnapshot(): ThemeSettingsSnapshot\n subscribe(listener: () => void): () => void\n /** Replace a section instance's resolved settings (editor live-patch); no-op if the id is unknown. */\n patchSection(instanceId: string, settings: ResolvedSettings): void\n /** Insert a fully-resolved section instance (editor live-add); reserved ids are ignored. */\n insertSection(section: ResolvedSection, index?: number): void\n /** Remove one section instance (editor live-delete); no-op if unknown. */\n removeSection(instanceId: string): void\n /** Move one section instance to a new position (editor live-reorder); no-op if unknown. */\n moveSection(instanceId: string, index: number): void\n /** Replace the global theme tokens (editor live-patch). */\n patchGlobal(settings: ResolvedSettings): void\n /** Replace a block instance's resolved settings; no-op if section/block is unknown. */\n patchBlock(sectionId: string, blockId: string, settings: ResolvedSettings): void\n /** Insert a fully-resolved block instance into a section. */\n insertBlock(sectionId: string, block: ResolvedBlock, index?: number): void\n /** Remove one block instance from a section. */\n removeBlock(sectionId: string, blockId: string): void\n /** Move one block instance to a new position inside a section. */\n moveBlock(sectionId: string, blockId: string, index: number): void\n /** Replace all preview sections from a template values document. */\n replaceSections(sections: readonly ResolvedSection[]): void\n /** Replace one layout section group document, such as header or footer. */\n replaceSectionGroup(handle: string, document: ResolvedValuesDocument): void\n}\n\nexport function createThemeSettingsStore(initial: ThemeSettingsSnapshot): ThemeSettingsStore {\n let snapshot = initial\n const listeners = new Set<() => void>()\n const clampIndex = (index: number | undefined, length: number): number => {\n if (index === undefined || Number.isNaN(index)) return length\n return Math.max(0, Math.min(Math.trunc(index), length))\n }\n const emit = (): void => {\n for (const listener of listeners) listener()\n }\n const updateSection = (\n sectionId: string,\n update: (section: ResolvedSection) => ResolvedSection | null,\n ): boolean => {\n if (sectionId.startsWith('theme:')) return false\n let changed = false\n const sections = snapshot.sections.map((section) => {\n if (section.id !== sectionId) return section\n const next = update(section)\n if (next === null || next === section) return section\n changed = true\n return next\n })\n if (!changed) return false\n snapshot = { ...snapshot, sections }\n emit()\n return true\n }\n return {\n getSnapshot: () => snapshot,\n subscribe: (listener) => {\n listeners.add(listener)\n return () => {\n listeners.delete(listener)\n }\n },\n patchSection: (instanceId, settings) => {\n if (instanceId.startsWith('theme:')) return\n let changed = false\n const sections = snapshot.sections.map((section) => {\n if (section.id !== instanceId) return section\n changed = true\n return { ...section, settings }\n })\n if (!changed) return // unknown instance → no new snapshot, no notify\n snapshot = { ...snapshot, sections }\n emit()\n },\n insertSection: (section, index) => {\n if (section.id.startsWith('theme:')) return\n if (snapshot.sections.some((existing) => existing.id === section.id)) return\n const next = [...snapshot.sections]\n next.splice(clampIndex(index, next.length), 0, section)\n snapshot = { ...snapshot, sections: next }\n emit()\n },\n removeSection: (instanceId) => {\n if (instanceId.startsWith('theme:')) return\n const sections = snapshot.sections.filter((section) => section.id !== instanceId)\n if (sections.length === snapshot.sections.length) return\n snapshot = { ...snapshot, sections }\n emit()\n },\n moveSection: (instanceId, index) => {\n if (instanceId.startsWith('theme:')) return\n const fromIndex = snapshot.sections.findIndex((section) => section.id === instanceId)\n if (fromIndex === -1) return\n const next = [...snapshot.sections]\n const [section] = next.splice(fromIndex, 1)\n if (section === undefined) return\n const toIndex = clampIndex(index, next.length)\n if (fromIndex === toIndex) return\n next.splice(toIndex, 0, section)\n snapshot = { ...snapshot, sections: next }\n emit()\n },\n patchGlobal: (settings) => {\n if (settings === snapshot.global) return // same ref → no-op (symmetric with patchSection)\n snapshot = { ...snapshot, global: settings }\n emit()\n },\n patchBlock: (sectionId, blockId, settings) => {\n updateSection(sectionId, (section) => {\n let changed = false\n const blocks = section.blocks.map((block) => {\n if (block.id !== blockId) return block\n changed = true\n return { ...block, settings }\n })\n return changed ? { ...section, blocks } : null\n })\n },\n insertBlock: (sectionId, block, index) => {\n updateSection(sectionId, (section) => {\n if (block.id !== undefined && section.blocks.some((existing) => existing.id === block.id)) {\n return null\n }\n const blocks = [...section.blocks]\n blocks.splice(clampIndex(index, blocks.length), 0, block)\n return { ...section, blocks }\n })\n },\n removeBlock: (sectionId, blockId) => {\n updateSection(sectionId, (section) => {\n const blocks = section.blocks.filter((block) => block.id !== blockId)\n return blocks.length === section.blocks.length ? null : { ...section, blocks }\n })\n },\n moveBlock: (sectionId, blockId, index) => {\n updateSection(sectionId, (section) => {\n const fromIndex = section.blocks.findIndex((block) => block.id === blockId)\n if (fromIndex === -1) return null\n const blocks = [...section.blocks]\n const [block] = blocks.splice(fromIndex, 1)\n if (block === undefined) return null\n const toIndex = clampIndex(index, blocks.length)\n if (fromIndex === toIndex) return null\n blocks.splice(toIndex, 0, block)\n return { ...section, blocks }\n })\n },\n replaceSections: (sections) => {\n const next = sections.filter((section) => !section.id.startsWith('theme:'))\n if (\n next.length === snapshot.sections.length &&\n next.every((section, index) => section === snapshot.sections[index])\n ) {\n return\n }\n snapshot = { ...snapshot, sections: next }\n emit()\n },\n replaceSectionGroup: (handle, document) => {\n if (handle.trim() === '') return\n const sectionGroups = snapshot.sectionGroups ?? {}\n const nextDocument: ResolvedValuesDocument = {\n ...document,\n sections: document.sections.filter((section) => !section.id.startsWith('theme:')),\n }\n if (sectionGroups[handle] === nextDocument) return\n snapshot = {\n ...snapshot,\n sectionGroups: {\n ...sectionGroups,\n [handle]: nextDocument,\n },\n }\n emit()\n },\n }\n}\n","import type { SectionInstance, ValuesDocument } from '@kotao/storefront-contracts/theme'\n\nimport type { StorefrontAuthHeader } from '../storefront-client/client'\n\n/** A single leading slash, not protocol-relative (`//`/`\\`) — keeps the fetch same-origin (no exfil). */\nconst SAME_ORIGIN_PATH = /^\\/(?![/\\\\])/\nconst HTTPS_ENDPOINT = /^https:\\/\\//\nconst DEFAULT_ENDPOINT = '/api/storefront/settings'\n\n/**\n * The stored settings a theme hydrates from (#93): the layout document's ordered section instances +\n * the global theme tokens. Pass to {@link resolveThemeSettings} together with the theme's bundled\n * section schemas + presets to produce the merged {@link ThemeSettingsSnapshot}.\n */\nexport interface StoredThemeSettings {\n readonly shopId: string\n readonly sections: readonly SectionInstance[]\n readonly global: Record<string, unknown>\n readonly templates?: Readonly<Partial<Record<string, ValuesDocument>>>\n readonly sectionGroups?: Readonly<Partial<Record<string, ValuesDocument>>>\n}\n\nexport interface FetchThemeSettingsOptions {\n /** Injectable for tests; defaults to the global `fetch`. */\n readonly fetch?: typeof fetch\n /** Same-origin settings endpoint (default `/api/storefront/settings`). */\n readonly endpoint?: string\n /**\n * Explicit local-preview escape hatch for server-side theme workers that call the Kotao API directly.\n * Production themes should keep the default same-origin path.\n */\n readonly allowCrossOrigin?: boolean\n /** Optional server-side auth headers for local previews. Never pass browser-visible tokens here. */\n readonly auth?: {\n readonly token: string\n readonly tokenHeader?: StorefrontAuthHeader\n readonly siteId?: string\n }\n}\n\n/**\n * Fetch the shop's stored settings document via the same-origin `/api/storefront/settings` carve-out.\n * The shop is resolved server-side from the Gateway-signed context — the theme sends no id and no token.\n */\nexport async function fetchThemeSettings(\n options: FetchThemeSettingsOptions = {},\n): Promise<StoredThemeSettings> {\n const endpoint = options.endpoint ?? DEFAULT_ENDPOINT\n if (\n !SAME_ORIGIN_PATH.test(endpoint) &&\n !(options.allowCrossOrigin === true && HTTPS_ENDPOINT.test(endpoint))\n ) {\n throw new Error(`theme-settings endpoint must be a same-origin path, got '${endpoint}'`)\n }\n const doFetch = options.fetch ?? fetch\n const headers: Record<string, string> = {}\n if (options.auth !== undefined) {\n headers[options.auth.tokenHeader ?? 'x-kotao-storefront-token'] = options.auth.token\n if (options.auth.siteId !== undefined && options.auth.siteId.trim().length > 0) {\n headers['x-kotao-site-id'] = options.auth.siteId\n }\n }\n const init = Object.keys(headers).length > 0 ? { headers } : undefined\n const res = await doFetch(endpoint, init)\n if (!res.ok) throw new Error(`theme settings fetch failed: HTTP ${res.status}`)\n return (await res.json()) as StoredThemeSettings\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/seo/json-ld/common.ts","../src/seo/json-ld/product.ts","../src/seo/json-ld/local-business.ts","../src/seo/sitemap.ts","../src/seo/robots.ts","../src/seo/redirects.ts"],"names":[],"mappings":"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","file":"chunk-ISAFEOR2.js","sourcesContent":["import type { JsonLd } from '../types'\n\n/** Drop undefined / null / '' / empty-array fields so JSON-LD never renders empty or null values. */\nexport function compact(obj: Record<string, unknown>): JsonLd {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(obj)) {\n if (value === undefined || value === null || value === '') continue\n if (Array.isArray(value) && value.length === 0) continue\n out[key] = value\n }\n return out\n}\n\nconst SCHEMA = 'https://schema.org'\n\nexport interface BreadcrumbItem {\n readonly name: string\n readonly url: string\n}\n\nexport function breadcrumbListJsonLd(items: readonly BreadcrumbItem[]): JsonLd {\n return {\n '@context': SCHEMA,\n '@type': 'BreadcrumbList',\n itemListElement: items.map((item, index) => ({\n '@type': 'ListItem',\n position: index + 1,\n name: item.name,\n item: item.url,\n })),\n }\n}\n\nexport interface OrganizationInput {\n readonly name: string\n readonly url?: string\n readonly logo?: string\n readonly sameAs?: readonly string[]\n}\n\nexport function organizationJsonLd(input: OrganizationInput): JsonLd {\n return compact({\n '@context': SCHEMA,\n '@type': 'Organization',\n name: input.name,\n url: input.url,\n logo: input.logo,\n sameAs: input.sameAs,\n })\n}\n\nexport interface WebSiteInput {\n readonly name: string\n readonly url: string\n /** A URL template with `{search_term_string}` to enable the sitelinks search box. */\n readonly searchUrlTemplate?: string\n}\n\nexport function websiteJsonLd(input: WebSiteInput): JsonLd {\n return compact({\n '@context': SCHEMA,\n '@type': 'WebSite',\n name: input.name,\n url: input.url,\n potentialAction:\n input.searchUrlTemplate !== undefined\n ? {\n '@type': 'SearchAction',\n target: { '@type': 'EntryPoint', urlTemplate: input.searchUrlTemplate },\n 'query-input': 'required name=search_term_string',\n }\n : undefined,\n })\n}\n","import type { JsonLd } from '../types'\nimport { compact } from './common'\n\nexport interface OfferInput {\n readonly price: string | number\n readonly currency: string\n /** schema.org ItemAvailability short name, e.g. 'InStock' | 'OutOfStock' | 'PreOrder'. */\n readonly availability?: string\n readonly url?: string\n readonly sku?: string\n}\n\nexport interface ProductJsonLdInput {\n readonly name: string\n readonly description?: string\n readonly sku?: string\n readonly image?: string | readonly string[]\n readonly brand?: string\n readonly url?: string\n readonly offer?: OfferInput\n}\n\nfunction offerJsonLd(offer: OfferInput): JsonLd {\n return compact({\n '@type': 'Offer',\n price: String(offer.price),\n priceCurrency: offer.currency,\n availability:\n offer.availability !== undefined ? `https://schema.org/${offer.availability}` : undefined,\n url: offer.url,\n sku: offer.sku,\n })\n}\n\nexport function productJsonLd(input: ProductJsonLdInput): JsonLd {\n return compact({\n '@context': 'https://schema.org',\n '@type': 'Product',\n name: input.name,\n description: input.description,\n sku: input.sku,\n image: input.image,\n brand: input.brand !== undefined ? { '@type': 'Brand', name: input.brand } : undefined,\n url: input.url,\n offers: input.offer !== undefined ? offerJsonLd(input.offer) : undefined,\n })\n}\n","import type { JsonLd } from '../types'\nimport { compact } from './common'\n\nexport interface AddressInput {\n readonly streetAddress?: string\n readonly locality?: string\n readonly region?: string\n readonly postalCode?: string\n readonly country?: string\n}\n\nexport interface GeoInput {\n readonly latitude: number\n readonly longitude: number\n}\n\nexport interface LocalBusinessInput {\n readonly name: string\n /** schema.org type, e.g. 'LocalBusiness' | 'Restaurant' | 'Store'. Defaults to 'LocalBusiness'. */\n readonly type?: string\n readonly url?: string\n readonly telephone?: string\n readonly priceRange?: string\n readonly image?: string | readonly string[]\n readonly address?: AddressInput\n readonly geo?: GeoInput\n /** schema.org openingHours strings, e.g. ['Mo-Fr 09:00-17:00', 'Sa 10:00-14:00']. */\n readonly openingHours?: readonly string[]\n /** Restaurant fields (used when type is 'Restaurant'). */\n readonly servesCuisine?: string | readonly string[]\n readonly menu?: string\n readonly acceptsReservations?: boolean\n}\n\nfunction addressJsonLd(address: AddressInput): JsonLd {\n return compact({\n '@type': 'PostalAddress',\n streetAddress: address.streetAddress,\n addressLocality: address.locality,\n addressRegion: address.region,\n postalCode: address.postalCode,\n addressCountry: address.country,\n })\n}\n\n/**\n * LocalBusiness JSON-LD — the brick-and-mortar / restaurant differentiator beyond Shopify's defaults\n * (the SDK table calls this out). `restaurantJsonLd` is the same builder pinned to `Restaurant`.\n */\nexport function localBusinessJsonLd(input: LocalBusinessInput): JsonLd {\n return compact({\n '@context': 'https://schema.org',\n '@type': input.type ?? 'LocalBusiness',\n name: input.name,\n url: input.url,\n telephone: input.telephone,\n priceRange: input.priceRange,\n image: input.image,\n address: input.address !== undefined ? addressJsonLd(input.address) : undefined,\n geo:\n input.geo !== undefined\n ? {\n '@type': 'GeoCoordinates',\n latitude: input.geo.latitude,\n longitude: input.geo.longitude,\n }\n : undefined,\n openingHours: input.openingHours,\n servesCuisine: input.servesCuisine,\n hasMenu: input.menu,\n acceptsReservations: input.acceptsReservations,\n })\n}\n\nexport function restaurantJsonLd(input: Omit<LocalBusinessInput, 'type'>): JsonLd {\n return localBusinessJsonLd({ ...input, type: 'Restaurant' })\n}\n","/**\n * sitemap.xml + sitemap-index factories. The entry source is injected (the caller resolves it from the\n * Storefront API or a search index — an open question, NOT decided here): build the entry list, then call\n * these. The index shape exists so large catalogs split into child sitemaps rather than one document.\n */\n\nexport type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never'\n\nexport interface SitemapEntry {\n readonly loc: string\n readonly lastmod?: string\n readonly changefreq?: ChangeFreq\n /** 0.0–1.0 (clamped). */\n readonly priority?: number\n}\n\nexport interface SitemapIndexEntry {\n readonly loc: string\n readonly lastmod?: string\n}\n\nconst XML_CONTENT_TYPE = 'application/xml; charset=utf-8'\n\nfunction escapeXml(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\nexport function buildSitemapXml(entries: readonly SitemapEntry[]): string {\n const urls = entries\n .map((entry) => {\n const parts = [` <loc>${escapeXml(entry.loc)}</loc>`]\n if (entry.lastmod !== undefined)\n parts.push(` <lastmod>${escapeXml(entry.lastmod)}</lastmod>`)\n if (entry.changefreq !== undefined)\n parts.push(` <changefreq>${entry.changefreq}</changefreq>`)\n if (entry.priority !== undefined) {\n parts.push(\n ` <priority>${Math.max(0, Math.min(1, entry.priority)).toFixed(1)}</priority>`,\n )\n }\n return ` <url>\\n${parts.join('\\n')}\\n </url>`\n })\n .join('\\n')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${urls}\\n</urlset>\\n`\n}\n\nexport function buildSitemapIndexXml(sitemaps: readonly SitemapIndexEntry[]): string {\n const items = sitemaps\n .map((sitemap) => {\n const parts = [` <loc>${escapeXml(sitemap.loc)}</loc>`]\n if (sitemap.lastmod !== undefined)\n parts.push(` <lastmod>${escapeXml(sitemap.lastmod)}</lastmod>`)\n return ` <sitemap>\\n${parts.join('\\n')}\\n </sitemap>`\n })\n .join('\\n')\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\\n${items}\\n</sitemapindex>\\n`\n}\n\nexport function sitemapResponse(entries: readonly SitemapEntry[]): Response {\n return new Response(buildSitemapXml(entries), { headers: { 'content-type': XML_CONTENT_TYPE } })\n}\n\nexport function sitemapIndexResponse(sitemaps: readonly SitemapIndexEntry[]): Response {\n return new Response(buildSitemapIndexXml(sitemaps), {\n headers: { 'content-type': XML_CONTENT_TYPE },\n })\n}\n","/**\n * robots.txt factory. Non-production / preview deployments auto-disallow ALL crawling; production allows\n * and references the sitemap. `production` comes from the SDK's stage signal (context.env), NEVER the\n * request Host/URL (stage-pinned-config rule, AGENTS.md §Environments).\n */\nexport interface RobotsOptions {\n readonly production: boolean\n readonly sitemapUrl?: string\n}\n\nexport function buildRobotsTxt(options: RobotsOptions): string {\n if (!options.production) {\n return 'User-agent: *\\nDisallow: /\\n'\n }\n const lines = ['User-agent: *', 'Allow: /']\n if (options.sitemapUrl !== undefined) lines.push(`Sitemap: ${options.sitemapUrl}`)\n return `${lines.join('\\n')}\\n`\n}\n\nexport function robotsResponse(options: RobotsOptions): Response {\n return new Response(buildRobotsTxt(options), {\n headers: { 'content-type': 'text/plain; charset=utf-8' },\n })\n}\n","import type { StorefrontClient } from '../storefront-client/client'\n\n/**\n * Slug-change 301 handler. The redirect store is owned elsewhere (catalog/storefront-API work); this\n * owns only the request-handling shape. The lookup is injected: given the current path, it returns the\n * canonical target (or null). A self-redirect (target === path) returns null to avoid an immediate loop.\n */\nexport type RedirectLookup = (path: string) => Promise<string | null> | string | null\n\nexport interface RedirectOptions {\n /** 301 (permanent, default) or 302 (temporary). */\n readonly status?: 301 | 302\n}\n\nexport interface StorefrontRedirectOptions extends RedirectOptions {\n readonly request: Request\n readonly response: Response\n readonly storefront: StorefrontClient\n}\n\nexport async function resolveRedirect(\n lookup: RedirectLookup,\n path: string,\n options: RedirectOptions = {},\n): Promise<Response | null> {\n const target = await lookup(path)\n if (target === null || target === '' || target === path) return null\n return new Response(null, {\n status: options.status ?? 301,\n headers: { location: target },\n })\n}\n\nexport const URL_REDIRECT_QUERY = `#graphql\n query UrlRedirect($path: String!) {\n urlRedirect(path: $path) {\n target\n }\n }\n`\n\n/**\n * Look up `path` in the storefront-api's url_redirects and, if found, return a 301 Response.\n * Returns null when there is no redirect, a self-redirect, or the lookup query fails (so the caller\n * falls through to its normal 404 — a lookup failure must never become a 500).\n */\nexport async function resolveStorefrontRedirect(\n storefront: StorefrontClient,\n path: string,\n options: RedirectOptions = {},\n): Promise<Response | null> {\n let target: string | null = null\n try {\n const data = await storefront.query<{ urlRedirect: { target: string } | null }>(\n URL_REDIRECT_QUERY,\n { variables: { path } },\n )\n target = data.urlRedirect?.target ?? null\n } catch {\n return null\n }\n return resolveRedirect(() => target, path, options)\n}\n\n/**\n * Hydrogen-compatible 404 redirect helper. It preserves the original response when there is no\n * matching URL redirect, mirroring the server.ts pattern used by Hydrogen demo stores.\n */\nexport async function storefrontRedirect(options: StorefrontRedirectOptions): Promise<Response> {\n if (options.response.status !== 404) return options.response\n const path = new URL(options.request.url).pathname\n const redirectOptions = options.status === undefined ? {} : { status: options.status }\n return (\n (await resolveStorefrontRedirect(options.storefront, path, redirectOptions)) ?? options.response\n )\n}\n"]}
|