@kotao/storefront 0.2.5 → 0.2.6

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.
Files changed (69) hide show
  1. package/README.md +50 -2
  2. package/dist/bridge-ZKbKqgFd.d.ts +239 -0
  3. package/dist/cart-documents-BLavRDiu.d.ts +89 -0
  4. package/dist/cart-documents.d.ts +1 -2
  5. package/dist/cart-documents.js +1 -1
  6. package/dist/{chunk-M7BPQ54D.js → chunk-2MRB6SMP.js} +4 -3
  7. package/dist/chunk-2MRB6SMP.js.map +1 -0
  8. package/dist/chunk-CKGI3Y7L.js +19 -0
  9. package/dist/chunk-CKGI3Y7L.js.map +1 -0
  10. package/dist/{chunk-GGOZELAA.js → chunk-EG34HLSX.js} +209 -162
  11. package/dist/chunk-EG34HLSX.js.map +1 -0
  12. package/dist/chunk-O6YZEY7X.js +1382 -0
  13. package/dist/chunk-O6YZEY7X.js.map +1 -0
  14. package/dist/{chunk-MCIFKJQK.js → chunk-R4N565GX.js} +5 -2
  15. package/dist/chunk-R4N565GX.js.map +1 -0
  16. package/dist/{chunk-XMTWFJCZ.js → chunk-RFM27BKZ.js} +31 -7
  17. package/dist/chunk-RFM27BKZ.js.map +1 -0
  18. package/dist/{chunk-MYDKIJRH.js → chunk-SVMYFYIA.js} +2 -2
  19. package/dist/{chunk-MYDKIJRH.js.map → chunk-SVMYFYIA.js.map} +1 -1
  20. package/dist/{chunk-H3D7BAOL.js → chunk-VOTBNOWH.js} +21 -3
  21. package/dist/chunk-VOTBNOWH.js.map +1 -0
  22. package/dist/{chunk-PXVDVUFX.js → chunk-W74FGUZH.js} +10 -3
  23. package/dist/chunk-W74FGUZH.js.map +1 -0
  24. package/dist/{chunk-2IWNUDSZ.js → chunk-WNXI3MPM.js} +2 -2
  25. package/dist/chunk-WNXI3MPM.js.map +1 -0
  26. package/dist/chunk-XXNCFWXT.js +2118 -0
  27. package/dist/chunk-XXNCFWXT.js.map +1 -0
  28. package/dist/{client-bsiNPHBi.d.ts → client-CwOPPpFF.d.ts} +11 -1
  29. package/dist/{createCartHandler-C4RrPwSG.d.ts → createCartHandler-7DdKB-GF.d.ts} +1 -1
  30. package/dist/custom-css-Bl7Ur3-H.d.ts +6 -0
  31. package/dist/editor.d.ts +57 -104
  32. package/dist/editor.js +270 -1
  33. package/dist/editor.js.map +1 -1
  34. package/dist/events-react.d.ts +1 -1
  35. package/dist/events-react.js +1 -1
  36. package/dist/events.d.ts +3 -3
  37. package/dist/events.js +1 -1
  38. package/dist/{fetch-M3Y-fQrW.d.ts → fetch-Bq_cc8_G.d.ts} +5 -0
  39. package/dist/hydrogen-Bu7k0kE4.d.ts +259 -0
  40. package/dist/index.d.ts +74 -51
  41. package/dist/index.js +19 -15
  42. package/dist/index.js.map +1 -1
  43. package/dist/react-router.d.ts +30 -9
  44. package/dist/react-router.js +16 -7
  45. package/dist/react-router.js.map +1 -1
  46. package/dist/react.d.ts +79 -11
  47. package/dist/react.js +279 -10
  48. package/dist/react.js.map +1 -1
  49. package/dist/{routing-sEonP93J.d.ts → routing-Bz3Cg0BW.d.ts} +36 -6
  50. package/dist/seo.d.ts +7 -4
  51. package/dist/seo.js +2 -2
  52. package/dist/settings.d.ts +14 -4
  53. package/dist/settings.js +2 -2
  54. package/dist/{shop-BJSvtmnV.d.ts → shop-CnrnSAJw.d.ts} +33 -6
  55. package/dist/{store-CtCWHI8H.d.ts → store-DO08fzJi.d.ts} +1 -1
  56. package/dist/storefront-api-types.d.ts +12 -0
  57. package/dist/{types-BDhHWZsP.d.ts → types-BkY0yET-.d.ts} +3 -3
  58. package/dist/{types-bcbQzx87.d.ts → types-DZX8ScsJ.d.ts} +2 -0
  59. package/package.json +4 -4
  60. package/dist/cart-documents-CwwRTvm8.d.ts +0 -21
  61. package/dist/chunk-2IWNUDSZ.js.map +0 -1
  62. package/dist/chunk-GGOZELAA.js.map +0 -1
  63. package/dist/chunk-H3D7BAOL.js.map +0 -1
  64. package/dist/chunk-M7BPQ54D.js.map +0 -1
  65. package/dist/chunk-MCIFKJQK.js.map +0 -1
  66. package/dist/chunk-PXVDVUFX.js.map +0 -1
  67. package/dist/chunk-PXZ737N3.js +0 -439
  68. package/dist/chunk-PXZ737N3.js.map +0 -1
  69. package/dist/chunk-XMTWFJCZ.js.map +0 -1
@@ -1,8 +1,8 @@
1
- import { S as StoredThemeSettings } from './fetch-M3Y-fQrW.js';
1
+ import { S as StoredThemeSettings } from './fetch-Bq_cc8_G.js';
2
2
  import { C as CacheStrategy, K as KotaoCache, S as StorefrontClient, b as StorefrontCacheInput } from './client-D51WdvYR.js';
3
- import { C as CartHandler } from './createCartHandler-C4RrPwSG.js';
4
- import { E as EventsClient } from './client-bsiNPHBi.js';
5
- import { H as HydrogenSession } from './types-bcbQzx87.js';
3
+ import { C as CartHandler } from './createCartHandler-7DdKB-GF.js';
4
+ import { E as EventsClient } from './client-CwOPPpFF.js';
5
+ import { H as HydrogenSession } from './types-DZX8ScsJ.js';
6
6
 
7
7
  /**
8
8
  * Typed customer-account shapes for the `@kotao/storefront` SDK surface (#91). The names/signatures
@@ -123,7 +123,7 @@ interface CustomerAccountClient {
123
123
  requestLoginCode(input: RequestLoginCodeInput): Promise<RequestLoginCodeResult>;
124
124
  /**
125
125
  * POST to the proxy `/account/verify` endpoint with the OTP code. Returns the raw proxy `Response`
126
- * so its `Set-Cookie` and 302 redirect headers propagate to the theme's RR7 action unchanged —
126
+ * so its `Set-Cookie` and 302 redirect headers propagate to the theme's RR8 action unchanged —
127
127
  * do NOT swallow or re-create this Response.
128
128
  */
129
129
  verifyLoginCode(input: VerifyLoginCodeInput): Promise<Response>;
@@ -217,12 +217,22 @@ interface KotaoSession extends HydrogenSession {
217
217
  }
218
218
  interface KotaoContext {
219
219
  readonly storefront: StorefrontClient;
220
+ /**
221
+ * Authenticated fetch for REST resources below `STOREFRONT_API_ENDPOINT`.
222
+ *
223
+ * This is deliberately not a general-purpose fetch: theme code is untrusted, so requests outside
224
+ * the configured storefront API origin + base path fail closed before Gateway-signed shop headers
225
+ * are attached.
226
+ */
227
+ readonly fetch: typeof fetch;
220
228
  readonly events: EventsClient;
221
229
  readonly cart: CartHandler;
222
230
  readonly customerAccount: CustomerAccountClient;
223
231
  readonly geo: Geo | null;
224
232
  readonly withCache: WithCache;
225
233
  readonly locale: string | null;
234
+ /** Gateway-selected Market context. Informational in theme code; API authorization uses HMAC. */
235
+ readonly market?: StorefrontMarketContext | null;
226
236
  /** Cookie-presence hint. `isLoggedIn` is true when the session cookie is present in the request.
227
237
  * The authoritative check (HMAC verification) happens server-side in the proxy. */
228
238
  readonly session: KotaoSession;
@@ -236,8 +246,26 @@ interface KotaoContext {
236
246
  */
237
247
  readonly loadThemeSettings: (opts?: {
238
248
  readonly fetch?: typeof fetch;
249
+ readonly templateHandle?: string;
239
250
  }) => Promise<StoredThemeSettings>;
240
251
  }
252
+ interface StorefrontMarketContext {
253
+ readonly id: string;
254
+ readonly pathPrefix: string | null;
255
+ readonly defaultLocale: string;
256
+ readonly alternateLocales: readonly string[];
257
+ readonly rootUrls: readonly {
258
+ readonly locale: string;
259
+ readonly url: string;
260
+ }[];
261
+ readonly canonicalUrl: string;
262
+ readonly themeId: string | null;
263
+ readonly settingsEnvironmentHandle: string | null;
264
+ }
265
+
266
+ declare function marketContextFromHeaders(headers: {
267
+ get(name: string): string | null;
268
+ }): StorefrontMarketContext | null;
241
269
  declare function trustedShopOriginFromHeaders(headers: Headers): string | undefined;
242
270
  /**
243
271
  * Framework-agnostic per-request context, mirroring Hydrogen's `createHydrogenContext`:
@@ -256,6 +284,8 @@ interface StrippedLocalePath {
256
284
  }
257
285
  interface LocalizePathOptions {
258
286
  readonly primaryLocale?: string | null;
287
+ /** Trusted Market subfolder prefix that must survive locale switching. */
288
+ readonly marketPathPrefix?: string | null;
259
289
  }
260
290
  declare function isLocalePathSegment(segment: string): boolean;
261
291
  declare function localeFromAcceptLanguage(header: string | null | undefined, availableLocales?: readonly string[]): string | null;
@@ -266,4 +296,4 @@ declare function stripLocalePrefixFromPathname(pathname: string): StrippedLocale
266
296
  declare function stripLocalePrefixFromRequest(request: Request): Request;
267
297
  declare function localizePath(path: string, locale: string | null | undefined, options?: LocalizePathOptions): string;
268
298
 
269
- export { type Address as A, trustedShopOriginFromHeaders as B, type CustomerAccountClient as C, verifyGeo as D, GEO_HEADER as G, type KotaoEnv as K, type LocalizePathOptions as L, type Money as M, type Order as O, ROUTING_LOCALE_HEADER as R, type StorefrontExecutionContext as S, type VerifyLoginCodeInput as V, type WithCache as W, type KotaoContext as a, type KotaoSession as b, type AddressConnection as c, type CustomerAccount as d, CustomerAccountError as e, type CustomerAccountErrorCode as f, GEO_SIGNATURE_HEADER as g, type Geo as h, type OrderConnection as i, type OrderPageInfo as j, type OrdersOptions as k, ROUTING_ORIGINAL_PATHNAME_HEADER as l, type RequestLoginCodeInput as m, type RequestLoginCodeResult as n, type StrippedLocalePath as o, createKotaoContext as p, createWithCache as q, isLocalePathSegment as r, stripLocalePrefixFromRequest as s, localeFromAcceptLanguage as t, localeFromPathname as u, localeFromRequest as v, localizePath as w, originalPathnameFromRequest as x, signGeo as y, stripLocalePrefixFromPathname as z };
299
+ export { type Address as A, signGeo as B, type CustomerAccountClient as C, stripLocalePrefixFromPathname as D, trustedShopOriginFromHeaders as E, verifyGeo as F, GEO_HEADER as G, type KotaoContext as K, type LocalizePathOptions as L, type Money as M, type Order as O, ROUTING_LOCALE_HEADER as R, type StorefrontExecutionContext as S, type VerifyLoginCodeInput as V, type WithCache as W, type KotaoEnv as a, type KotaoSession as b, type AddressConnection as c, type CustomerAccount as d, CustomerAccountError as e, type CustomerAccountErrorCode as f, GEO_SIGNATURE_HEADER as g, type Geo as h, type OrderConnection as i, type OrderPageInfo as j, type OrdersOptions as k, ROUTING_ORIGINAL_PATHNAME_HEADER as l, type RequestLoginCodeInput as m, type RequestLoginCodeResult as n, type StorefrontMarketContext as o, type StrippedLocalePath as p, createKotaoContext as q, createWithCache as r, stripLocalePrefixFromRequest as s, isLocalePathSegment as t, localeFromAcceptLanguage as u, localeFromPathname as v, localeFromRequest as w, localizePath as x, marketContextFromHeaders as y, originalPathnameFromRequest as z };
package/dist/seo.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { S as SeoConfig, b as SeoDescriptor, J as JsonLd } from './types-BDhHWZsP.js';
2
- export { A as Alternate, O as OpenGraph, a as OpenGraphImage, T as TwitterCard } from './types-BDhHWZsP.js';
1
+ import { S as SeoConfig, b as SeoDescriptor, J as JsonLd, A as Alternate } from './types-BkY0yET-.js';
2
+ export { O as OpenGraph, a as OpenGraphImage, T as TwitterCard } from './types-BkY0yET-.js';
3
3
  import { S as StorefrontClient } from './client-D51WdvYR.js';
4
4
  import './http-types-CGh8WGHB.js';
5
5
 
6
6
  /**
7
- * Merge SEO config across levels and emit a flat, deduplicated descriptor list (RR7 `meta`-compatible):
7
+ * Merge SEO config across levels and emit a flat, deduplicated descriptor list (RR8 `meta`-compatible):
8
8
  * title, description, canonical, robots, Open Graph, Twitter Card, hreflang alternates, and JSON-LD.
9
9
  * Open Graph / Twitter titles + descriptions fall back to the page title/description (Hydrogen-shaped).
10
10
  */
@@ -95,6 +95,7 @@ declare function restaurantJsonLd(input: Omit<LocalBusinessInput, 'type'>): Json
95
95
  * Storefront API or a search index — an open question, NOT decided here): build the entry list, then call
96
96
  * these. The index shape exists so large catalogs split into child sitemaps rather than one document.
97
97
  */
98
+
98
99
  type ChangeFreq = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
99
100
  interface SitemapEntry {
100
101
  readonly loc: string;
@@ -102,6 +103,8 @@ interface SitemapEntry {
102
103
  readonly changefreq?: ChangeFreq;
103
104
  /** 0.0–1.0 (clamped). */
104
105
  readonly priority?: number;
106
+ /** Reciprocal language variants for this URL, including x-default when applicable. */
107
+ readonly alternates?: readonly Alternate[];
105
108
  }
106
109
  interface SitemapIndexEntry {
107
110
  readonly loc: string;
@@ -155,4 +158,4 @@ declare function resolveStorefrontRedirect(storefront: StorefrontClient, path: s
155
158
  */
156
159
  declare function storefrontRedirect(options: StorefrontRedirectOptions): Promise<Response>;
157
160
 
158
- export { type AddressInput, type BreadcrumbItem, type ChangeFreq, type GeoInput, JsonLd, type LocalBusinessInput, type OfferInput, type OrganizationInput, type ProductJsonLdInput, type RedirectLookup, type RedirectOptions, type RobotsOptions, SeoConfig, SeoDescriptor, type SitemapEntry, type SitemapIndexEntry, type StorefrontRedirectOptions, URL_REDIRECT_QUERY, type WebSiteInput, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, getSeoMeta, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, serializeJsonLd, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd };
161
+ export { type AddressInput, Alternate, type BreadcrumbItem, type ChangeFreq, type GeoInput, JsonLd, type LocalBusinessInput, type OfferInput, type OrganizationInput, type ProductJsonLdInput, type RedirectLookup, type RedirectOptions, type RobotsOptions, SeoConfig, SeoDescriptor, type SitemapEntry, type SitemapIndexEntry, type StorefrontRedirectOptions, URL_REDIRECT_QUERY, type WebSiteInput, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, getSeoMeta, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, serializeJsonLd, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd };
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-PXVDVUFX.js';
2
- export { getSeoMeta, serializeJsonLd } from './chunk-MYDKIJRH.js';
1
+ export { URL_REDIRECT_QUERY, breadcrumbListJsonLd, buildRobotsTxt, buildSitemapIndexXml, buildSitemapXml, localBusinessJsonLd, organizationJsonLd, productJsonLd, resolveRedirect, resolveStorefrontRedirect, restaurantJsonLd, robotsResponse, sitemapIndexResponse, sitemapResponse, storefrontRedirect, websiteJsonLd } from './chunk-W74FGUZH.js';
2
+ export { getSeoMeta, serializeJsonLd } from './chunk-SVMYFYIA.js';
3
3
  //# sourceMappingURL=seo.js.map
4
4
  //# sourceMappingURL=seo.js.map
@@ -1,7 +1,7 @@
1
1
  import { SectionSchema, Preset, ValuesDocument, SettingInput, SectionInstance } from '@kotao/storefront-contracts/theme';
2
- import { c as ResolvedSection, R as ResolvedSettings, a as ThemeSettingsSnapshot } from './store-CtCWHI8H.js';
3
- export { b as ResolvedBlock, T as ThemeSettingsStore, d as createThemeSettingsStore } from './store-CtCWHI8H.js';
4
- export { F as FetchThemeSettingsOptions, S as StoredThemeSettings, f as fetchThemeSettings } from './fetch-M3Y-fQrW.js';
2
+ import { a as ResolvedSection, R as ResolvedSettings, c as ThemeSettingsSnapshot } from './store-DO08fzJi.js';
3
+ export { b as ResolvedBlock, T as ThemeSettingsStore, d as createThemeSettingsStore } from './store-DO08fzJi.js';
4
+ export { F as FetchThemeSettingsOptions, S as StoredThemeSettings, f as fetchThemeSettings } from './fetch-Bq_cc8_G.js';
5
5
  import './client-D51WdvYR.js';
6
6
  import './http-types-CGh8WGHB.js';
7
7
 
@@ -51,4 +51,14 @@ interface ResolveThemeSettingsArgs {
51
51
  /** Compose the merge over an entire values document → the snapshot a theme renders from. */
52
52
  declare function resolveThemeSettings(args: ResolveThemeSettingsArgs): ThemeSettingsSnapshot;
53
53
 
54
- export { type ResolveThemeSettingsArgs, ResolvedSection, ResolvedSettings, ThemeSettingsSnapshot, mergeSection, mergeSettings, resolveThemeSettings };
54
+ /**
55
+ * Reconcile a platform-derived layout group with previously published merchant values.
56
+ *
57
+ * Synthetic groups such as `layout-static` are rebuilt from layout/theme.liquid and are not
58
+ * merchant-editable section groups. The current document therefore owns membership and order.
59
+ * Stored values only enrich matching instances, so removed/guarded sections cannot return after a
60
+ * runtime rebuild while saved settings for drawers and other surviving furniture remain intact.
61
+ */
62
+ declare function reconcileSyntheticSectionGroup(current: ValuesDocument, stored: ValuesDocument | null | undefined): ValuesDocument;
63
+
64
+ export { type ResolveThemeSettingsArgs, ResolvedSection, ResolvedSettings, ThemeSettingsSnapshot, mergeSection, mergeSettings, reconcileSyntheticSectionGroup, resolveThemeSettings };
package/dist/settings.js CHANGED
@@ -1,4 +1,4 @@
1
- export { createThemeSettingsStore, mergeSection, mergeSettings, resolveThemeSettings } from './chunk-H3D7BAOL.js';
2
- export { fetchThemeSettings } from './chunk-M7BPQ54D.js';
1
+ export { createThemeSettingsStore, mergeSection, mergeSettings, reconcileSyntheticSectionGroup, resolveThemeSettings } from './chunk-VOTBNOWH.js';
2
+ export { fetchThemeSettings } from './chunk-2MRB6SMP.js';
3
3
  //# sourceMappingURL=settings.js.map
4
4
  //# sourceMappingURL=settings.js.map
@@ -1,4 +1,4 @@
1
- import { j as CartFormAction } from './types-bcbQzx87.js';
1
+ import { j as CartFormAction } from './types-DZX8ScsJ.js';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  interface CartFormProps {
@@ -79,18 +79,34 @@ declare const ANALYTICS_EVENTS: {
79
79
  readonly productAddedToCart: "product_added_to_cart";
80
80
  };
81
81
  type AnalyticsEventName = (typeof ANALYTICS_EVENTS)[keyof typeof ANALYTICS_EVENTS];
82
+ type AnalyticsDeviceClass = 'mobile' | 'tablet' | 'desktop';
82
83
  /** Ambient context the provider attaches to every event before it reaches the pixel. */
83
84
  interface AnalyticsContext {
84
85
  readonly shopId?: string;
85
86
  readonly url?: string;
86
87
  readonly geo?: AnalyticsGeo;
88
+ readonly referrer?: string;
89
+ readonly utmSource?: string;
90
+ readonly utmMedium?: string;
91
+ readonly utmCampaign?: string;
92
+ readonly deviceClass?: AnalyticsDeviceClass;
87
93
  }
88
94
  interface PageViewedPayload {
89
95
  readonly title?: string;
90
96
  }
91
97
  interface ProductViewedPayload {
92
98
  readonly productId: string;
99
+ /** Stable offer id; Google Merchant Center and Google ecommerce events use this as item_id. */
100
+ readonly variantId?: string;
93
101
  readonly handle?: string;
102
+ readonly title?: string;
103
+ readonly variantTitle?: string;
104
+ readonly vendor?: string;
105
+ readonly sku?: string;
106
+ /** Major-unit price, e.g. 24.95. */
107
+ readonly price?: number;
108
+ readonly currency?: string;
109
+ readonly quantity?: number;
94
110
  }
95
111
  interface CollectionViewedPayload {
96
112
  readonly collectionId: string;
@@ -107,6 +123,13 @@ interface ProductAddedToCartPayload {
107
123
  readonly productId: string;
108
124
  readonly variantId: string;
109
125
  readonly quantity: number;
126
+ readonly title?: string;
127
+ readonly variantTitle?: string;
128
+ readonly vendor?: string;
129
+ readonly sku?: string;
130
+ /** Major-unit price, e.g. 24.95. */
131
+ readonly price?: number;
132
+ readonly currency?: string;
110
133
  }
111
134
  /** Event name → payload (the typed pub/sub surface). */
112
135
  interface AnalyticsEventMap {
@@ -135,6 +158,8 @@ interface AnalyticsEmitter {
135
158
  }
136
159
  declare function createAnalyticsEmitter(): AnalyticsEmitter;
137
160
 
161
+ type AnonymousAnalyticsContext = Pick<AnalyticsContext, 'url'>;
162
+
138
163
  /** The actual network send. Injectable so tests don't touch the network. */
139
164
  type PixelSend = (endpoint: string, body: string) => void;
140
165
  interface PixelTransportOptions {
@@ -163,8 +188,10 @@ interface AnalyticsBusOptions {
163
188
  readonly transport: PixelTransport;
164
189
  /** Legacy consent seam retained for API compatibility while Storefront consent UI is disabled. */
165
190
  readonly getConsent: () => ConsentState;
166
- /** Ambient context (shopId, geo, url) attached to each pixel event. */
191
+ /** Ambient context (including attribution and device class) attached to consented events. */
167
192
  readonly getContext?: () => AnalyticsContext;
193
+ /** Sanitized same-origin page URL used only for the anonymous page-view fallback. */
194
+ readonly getAnonymousContext?: () => AnonymousAnalyticsContext;
168
195
  /** Clock seam for deterministic timestamps in tests. */
169
196
  readonly now?: () => string;
170
197
  }
@@ -174,9 +201,9 @@ interface AnalyticsBus {
174
201
  flush(): void;
175
202
  }
176
203
  /**
177
- * Wire the emitter to the pixel. Every published event reaches theme subscribers (via the emitter) and
178
- * is also recorded to the pixel. Consent gating is temporarily disabled with the UI; the consent seam
179
- * remains so a proper replacement can reinstate the policy without changing theme APIs.
204
+ * Wire the emitter to the pixel. Every published event reaches theme subscribers (via the emitter).
205
+ * First-party recording is consent-strict: denied visits get only an anonymous page_viewed row and
206
+ * never evaluate ambient context, which may read attribution or device-class signals.
180
207
  */
181
208
  declare function createAnalyticsBus(options: AnalyticsBusOptions): AnalyticsBus;
182
209
 
@@ -199,4 +226,4 @@ interface GetShopAnalyticsOptions {
199
226
  }
200
227
  declare function getShopAnalytics(options: GetShopAnalyticsOptions): ShopAnalytics;
201
228
 
202
- export { type AnalyticsContext as A, type BoundNonceProvider as B, type ConsentState as C, DENIED_CONSENT as D, type EventHandler as E, createAnalyticsEmitter as F, GRANTED_CONSENT as G, createPixelTransport as H, getShopAnalytics as I, isGranted as J, mergeConsent as K, parseConsent as L, readConsentCookie as M, serializeConsent as N, type PageViewedPayload as P, type ShopAnalytics as S, type AnalyticsEmitter as a, type AnalyticsBus as b, type ConsentBucket as c, ANALYTICS_EVENTS as d, type AnalyticsBusOptions as e, type AnalyticsEventMap as f, getCartFormInput as g, type AnalyticsEventName as h, type AnalyticsGeo as i, CONSENT_BUCKETS as j, CONSENT_COOKIE as k, CartForm as l, type CartFormProps as m, type CartViewedPayload as n, type CollectionViewedPayload as o, type GetShopAnalyticsOptions as p, type PixelEvent as q, type PixelSend as r, type PixelTransport as s, type PixelTransportOptions as t, useNonce as u, type ProductAddedToCartPayload as v, type ProductViewedPayload as w, type SearchViewedPayload as x, buildConsentCookie as y, createAnalyticsBus as z };
229
+ export { type AnalyticsContext as A, type BoundNonceProvider as B, type ConsentState as C, DENIED_CONSENT as D, type EventHandler as E, createAnalyticsBus as F, GRANTED_CONSENT as G, createAnalyticsEmitter as H, createPixelTransport as I, getShopAnalytics as J, isGranted as K, mergeConsent as L, parseConsent as M, readConsentCookie as N, serializeConsent as O, type PageViewedPayload as P, type ShopAnalytics as S, type AnalyticsEmitter as a, type AnalyticsBus as b, type ConsentBucket as c, ANALYTICS_EVENTS as d, type AnalyticsBusOptions as e, type AnalyticsDeviceClass as f, getCartFormInput as g, type AnalyticsEventMap as h, type AnalyticsEventName as i, type AnalyticsGeo as j, CONSENT_BUCKETS as k, CONSENT_COOKIE as l, CartForm as m, type CartFormProps as n, type CartViewedPayload as o, type CollectionViewedPayload as p, type GetShopAnalyticsOptions as q, type PixelEvent as r, type PixelSend as s, type PixelTransport as t, useNonce as u, type PixelTransportOptions as v, type ProductAddedToCartPayload as w, type ProductViewedPayload as x, type SearchViewedPayload as y, buildConsentCookie as z };
@@ -64,4 +64,4 @@ interface ThemeSettingsStore {
64
64
  }
65
65
  declare function createThemeSettingsStore(initial: ThemeSettingsSnapshot): ThemeSettingsStore;
66
66
 
67
- export { type ResolvedSettings as R, type ThemeSettingsStore as T, type ThemeSettingsSnapshot as a, type ResolvedBlock as b, type ResolvedSection as c, createThemeSettingsStore as d };
67
+ export { type ResolvedSettings as R, type ThemeSettingsStore as T, type ResolvedSection as a, type ResolvedBlock as b, type ThemeSettingsSnapshot as c, createThemeSettingsStore as d };
@@ -201,6 +201,17 @@ interface ProductVariant extends LooseObject {
201
201
  readonly id: string;
202
202
  readonly image?: Image | null;
203
203
  readonly price: MoneyV2;
204
+ readonly quantityRule?: {
205
+ readonly minimum: number;
206
+ readonly maximum: number | null;
207
+ readonly increment: number;
208
+ } | null;
209
+ readonly quantityPriceBreaks?: {
210
+ readonly nodes?: Array<{
211
+ readonly minimumQuantity: number;
212
+ readonly price: MoneyV2;
213
+ }>;
214
+ };
204
215
  readonly product: Pick<Product, 'id' | 'handle' | 'title'>;
205
216
  readonly selectedOptions: SelectedOption[];
206
217
  readonly sku?: string | null;
@@ -289,6 +300,7 @@ interface CartBuyerIdentityInput extends LooseObject {
289
300
  readonly customerAccessToken?: string | null;
290
301
  readonly email?: string | null;
291
302
  readonly phone?: string | null;
303
+ readonly companyLocationId?: string | null;
292
304
  }
293
305
  interface CartDiscountCode extends LooseObject {
294
306
  readonly applicable?: boolean;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * SEO surface types for `@kotao/storefront` (ADR-0003 §SDK), Hydrogen-shaped. Framework-agnostic:
3
- * `getSeoMeta` emits a flat descriptor list a theme can return from a React Router 7 `meta` export or
3
+ * `getSeoMeta` emits a flat descriptor list a theme can return from a React Router 8 `meta` export or
4
4
  * feed to the `<Seo/>` component.
5
5
  */
6
6
  interface OpenGraphImage {
@@ -27,7 +27,7 @@ interface TwitterCard {
27
27
  readonly description?: string;
28
28
  readonly image?: string;
29
29
  }
30
- /** An hreflang alternate. Design-only hook (no multi-language data ships) — emitted only when supplied. */
30
+ /** An hreflang alternate emitted only when supplied. */
31
31
  interface Alternate {
32
32
  readonly hreflang: string;
33
33
  readonly href: string;
@@ -56,7 +56,7 @@ interface SeoConfig {
56
56
  /** schema.org JSON-LD object(s); accumulated (not overridden) across levels. */
57
57
  readonly jsonLd?: JsonLd | readonly JsonLd[];
58
58
  }
59
- /** A head descriptor, shaped to be React Router 7 `meta`-export compatible. */
59
+ /** A head descriptor, shaped to be React Router 8 `meta`-export compatible. */
60
60
  type SeoDescriptor = {
61
61
  readonly title: string;
62
62
  } | {
@@ -35,6 +35,7 @@ interface CartBuyerIdentityInput {
35
35
  readonly phone?: string | null;
36
36
  readonly countryCode?: string | null;
37
37
  readonly customerAccessToken?: string | null;
38
+ readonly companyLocationId?: string | null;
38
39
  }
39
40
  interface CartMetafield {
40
41
  readonly namespace: string;
@@ -104,6 +105,7 @@ interface CartBuyerIdentity {
104
105
  readonly email: string | null;
105
106
  readonly phone: string | null;
106
107
  readonly countryCode: string | null;
108
+ readonly companyLocationId: string | null;
107
109
  }
108
110
  interface Cart {
109
111
  readonly id: string;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@kotao/storefront",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "type": "module",
5
5
  "license": "MIT",
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).",
6
+ "description": "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).",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/kotao-org/monorepo.git",
@@ -72,11 +72,11 @@
72
72
  "LICENSE"
73
73
  ],
74
74
  "dependencies": {
75
- "@kotao/storefront-contracts": "^0.2.2"
75
+ "@kotao/storefront-contracts": "^0.2.3"
76
76
  },
77
77
  "peerDependencies": {
78
78
  "react": "^18.2.0 || ^19.0.0",
79
- "react-router": "^7.16.0"
79
+ "react-router": "^8.3.0"
80
80
  },
81
81
  "peerDependenciesMeta": {
82
82
  "react": {
@@ -1,21 +0,0 @@
1
- import { CartAdapter } from '@kotao/smart-cart';
2
-
3
- /**
4
- * All fully-resolved cart GraphQL document strings from the react seam adapter.
5
- * Exported for schema-validation tests (apps/storefront-api/test/cart-graphql-documents.test.ts)
6
- * that submit each document to a real graphql-yoga instance and assert zero validation errors,
7
- * catching invalid field selections (e.g. subfields on a scalar) at test time rather than runtime.
8
- */
9
- declare const REACT_SEAM_CART_DOCUMENTS: readonly string[];
10
- /**
11
- * A CartAdapter that drives the storefront-api cart over the same-origin /api/storefront GraphQL proxy
12
- * (the proxy injects the read token + resolves the shop server-side). The theme supplies the cart id
13
- * (from its loader's cart cookie/session) and renders <CartDrawerOverlay adapter={this} />.
14
- */
15
- declare function createStorefrontApiCartAdapter(opts: {
16
- readonly cartId: string;
17
- readonly apiPath?: string;
18
- readonly fetchImpl?: typeof fetch;
19
- }): CartAdapter;
20
-
21
- export { REACT_SEAM_CART_DOCUMENTS as R, createStorefrontApiCartAdapter as c };
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/react/storefront-cart-adapter.ts"],"names":[],"mappings":"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","file":"chunk-2IWNUDSZ.js","sourcesContent":["import type { CartAdapter, SmartCartLine, SmartCartState } from '@kotao/smart-cart'\n\ninterface GqlMoney {\n amount: string\n currencyCode: string\n}\ninterface GqlCartLine {\n id: string\n quantity: number\n attributes: { key: string; value: string | null }[] | null\n cost: { totalAmount: GqlMoney }\n merchandise: {\n id: string\n title: string | null\n image: { url: string } | null\n product: { title: string }\n }\n}\ninterface GqlCart {\n checkoutUrl: string\n cost: { subtotalAmount: GqlMoney; discountTotal?: GqlMoney }\n discountCodes?: string[]\n lines: { nodes: GqlCartLine[] }\n}\n\n// GraphQL Money is a decimal string (\"216.00\"); SmartCartState money is integer minor units.\nconst toMinorUnits = (m: GqlMoney): number => Math.round(parseFloat(m.amount) * 100)\n\nconst CART_FIELDS = `\n checkoutUrl\n cost { subtotalAmount { amount currencyCode } discountTotal { amount currencyCode } }\n discountCodes\n lines(first: 250) { nodes {\n id quantity\n attributes { key value }\n cost { totalAmount { amount currencyCode } }\n merchandise { ... on ProductVariant { id title image { url } product { title } } }\n } }`\nconst CART_QUERY = `query KotaoSmartCart($cartId: ID!) { cart(id: $cartId) { ${CART_FIELDS} } }`\nconst LINES_ADD = `mutation KotaoSmartCartAdd($cartId: ID!, $lines: [CartLineInput!]!) { cartLinesAdd(cartId: $cartId, lines: $lines) { cart { ${CART_FIELDS} } } }`\nconst LINES_UPDATE = `mutation KotaoSmartCartUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) { cartLinesUpdate(cartId: $cartId, lines: $lines) { cart { ${CART_FIELDS} } } }`\nconst LINES_REMOVE = `mutation KotaoSmartCartRemove($cartId: ID!, $lineIds: [ID!]!) { cartLinesRemove(cartId: $cartId, lineIds: $lineIds) { cart { ${CART_FIELDS} } } }`\nconst DISCOUNT_CODES_UPDATE = `mutation KotaoSmartCartDiscountCodes($cartId: ID!, $discountCodes: [String!]!) { cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) { cart { ${CART_FIELDS} } } }`\n\n/**\n * All fully-resolved cart GraphQL document strings from the react seam adapter.\n * Exported for schema-validation tests (apps/storefront-api/test/cart-graphql-documents.test.ts)\n * that submit each document to a real graphql-yoga instance and assert zero validation errors,\n * catching invalid field selections (e.g. subfields on a scalar) at test time rather than runtime.\n */\nexport const REACT_SEAM_CART_DOCUMENTS: readonly string[] = [\n CART_QUERY,\n LINES_ADD,\n LINES_UPDATE,\n LINES_REMOVE,\n DISCOUNT_CODES_UPDATE,\n]\n\nfunction mapCart(c: GqlCart): SmartCartState {\n const currency = c.cost.subtotalAmount.currencyCode\n const lines: SmartCartLine[] = c.lines.nodes.map((l) => {\n const attributes: Record<string, string> = {}\n for (const a of l.attributes ?? []) {\n if (a.value !== null) attributes[a.key] = a.value\n }\n const hasAttributes = Object.keys(attributes).length > 0\n return {\n id: l.id,\n title: l.merchandise.product.title,\n variantTitle: l.merchandise.title,\n variantId: l.merchandise.id ?? null,\n quantity: l.quantity,\n imageUrl: l.merchandise.image?.url ?? null,\n linePrice: { amount: toMinorUnits(l.cost.totalAmount), currency },\n ...(hasAttributes && { attributes }),\n }\n })\n const discountMinorUnits = c.cost.discountTotal != null ? toMinorUnits(c.cost.discountTotal) : 0\n const appliedCodes = c.discountCodes?.filter((code) => code.length > 0)\n return {\n lines,\n subtotal: { amount: toMinorUnits(c.cost.subtotalAmount), currency },\n checkoutUrl: c.checkoutUrl,\n currency,\n ...(discountMinorUnits > 0 && {\n discountTotal: { amount: discountMinorUnits, currency },\n }),\n ...(appliedCodes != null && appliedCodes.length > 0 && { appliedCodes }),\n }\n}\n\n/**\n * A CartAdapter that drives the storefront-api cart over the same-origin /api/storefront GraphQL proxy\n * (the proxy injects the read token + resolves the shop server-side). The theme supplies the cart id\n * (from its loader's cart cookie/session) and renders <CartDrawerOverlay adapter={this} />.\n */\nexport function createStorefrontApiCartAdapter(opts: {\n readonly cartId: string\n readonly apiPath?: string\n readonly fetchImpl?: typeof fetch\n}): CartAdapter {\n const fetchImpl = opts.fetchImpl ?? fetch\n const apiPath = opts.apiPath ?? '/api/storefront'\n const run = async (\n query: string,\n variables: Record<string, unknown>,\n key: string,\n ): Promise<SmartCartState> => {\n const res = await fetchImpl(apiPath, {\n method: 'POST',\n headers: { 'content-type': 'application/json', accept: 'application/json' },\n body: JSON.stringify({ query, variables }),\n })\n if (!res.ok) {\n throw new Error(`storefront cart request failed: ${res.status} ${res.statusText}`)\n }\n const json = (await res.json()) as { data?: Record<string, unknown> }\n const payload = json.data?.[key] as GqlCart | { cart: GqlCart } | undefined\n const cart = payload && 'cart' in payload ? payload.cart : (payload as GqlCart)\n return mapCart(cart)\n }\n return {\n getCart: () => run(CART_QUERY, { cartId: opts.cartId }, 'cart'),\n addLine: (merchandiseId, quantity, attributes) =>\n run(\n LINES_ADD,\n {\n cartId: opts.cartId,\n lines: [\n {\n merchandiseId,\n quantity,\n ...(attributes !== undefined && {\n attributes: Object.entries(attributes).map(([key, value]) => ({ key, value })),\n }),\n },\n ],\n },\n 'cartLinesAdd',\n ),\n updateLine: (id, quantity) =>\n run(LINES_UPDATE, { cartId: opts.cartId, lines: [{ id, quantity }] }, 'cartLinesUpdate'),\n removeLine: (id) =>\n run(LINES_REMOVE, { cartId: opts.cartId, lineIds: [id] }, 'cartLinesRemove'),\n updateDiscountCodes: (codes) =>\n run(\n DISCOUNT_CODES_UPDATE,\n { cartId: opts.cartId, discountCodes: codes },\n 'cartDiscountCodesUpdate',\n ),\n }\n}\n"]}