@cartbase/storefront 0.20.0 → 0.20.1

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 (99) hide show
  1. package/LICENSE +21 -21
  2. package/package.json +258 -258
  3. package/src/api/auth.ts +108 -108
  4. package/src/api/carts.ts +523 -523
  5. package/src/api/categories.ts +184 -184
  6. package/src/api/checkout.ts +526 -526
  7. package/src/api/collections.ts +130 -130
  8. package/src/api/consent.ts +75 -75
  9. package/src/api/content.ts +125 -125
  10. package/src/api/customers.ts +303 -303
  11. package/src/api/gift-cards.ts +112 -112
  12. package/src/api/http.ts +180 -180
  13. package/src/api/index.ts +30 -30
  14. package/src/api/menus.ts +77 -77
  15. package/src/api/metaobjects.ts +136 -136
  16. package/src/api/orders.ts +290 -290
  17. package/src/api/redirects.ts +37 -37
  18. package/src/api/regions.ts +200 -200
  19. package/src/api/search.ts +163 -163
  20. package/src/api/store.ts +35 -35
  21. package/src/api/types.ts +91 -91
  22. package/src/cart-drawer/context.tsx +778 -778
  23. package/src/cart-drawer/cross-sell-carousel.tsx +211 -211
  24. package/src/cart-drawer/cross-sell-sidebar.tsx +158 -158
  25. package/src/cart-drawer/gift-wrap.tsx +82 -82
  26. package/src/cart-drawer/item/index.tsx +162 -162
  27. package/src/cart-drawer/item/upsell.tsx +110 -110
  28. package/src/cart-drawer/labels.ts +123 -123
  29. package/src/cart-drawer/mutation-queue.ts +78 -78
  30. package/src/cart-drawer/notes.tsx +131 -131
  31. package/src/cart-drawer/sticky-footer.tsx +73 -73
  32. package/src/cart-drawer/summary-breakdown.tsx +197 -197
  33. package/src/checkout/address-error-copy.ts +117 -117
  34. package/src/checkout/boxnow-locker-selector.tsx +410 -410
  35. package/src/checkout/compare-addresses.ts +40 -40
  36. package/src/checkout/discount-section.tsx +218 -218
  37. package/src/checkout/error-copy-codes.ts +63 -63
  38. package/src/checkout/geocode.ts +154 -154
  39. package/src/checkout/gift-card-section.tsx +224 -224
  40. package/src/checkout/index.ts +72 -72
  41. package/src/checkout/labels.ts +495 -495
  42. package/src/checkout/payment-button.tsx +372 -372
  43. package/src/checkout/payment-error-copy.ts +154 -154
  44. package/src/checkout/promotion-error-copy.ts +91 -91
  45. package/src/common/country-flag.tsx +52 -52
  46. package/src/common/country-select.tsx +11 -11
  47. package/src/common/index.ts +20 -20
  48. package/src/common/market-select.tsx +57 -57
  49. package/src/index.ts +12 -12
  50. package/src/lib/country-name.ts +59 -59
  51. package/src/lib/get-product-price.ts +133 -133
  52. package/src/lib/media-image.tsx +39 -39
  53. package/src/lib/payment-constants.ts +53 -53
  54. package/src/lib/platform.ts +13 -13
  55. package/src/lib/price.tsx +39 -39
  56. package/src/lib/store-api-error.ts +36 -36
  57. package/src/lib/variant-caption.ts +32 -32
  58. package/src/locales/context.ts +37 -37
  59. package/src/locales/en.ts +26 -26
  60. package/src/locales/index.ts +19 -19
  61. package/src/locales/provider.tsx +59 -59
  62. package/src/locales/types.ts +77 -77
  63. package/src/order/index.ts +62 -62
  64. package/src/order/labels.ts +79 -79
  65. package/src/order/order-totals.tsx +250 -250
  66. package/src/primitives/select-field.tsx +93 -93
  67. package/src/products/image-gallery.tsx +43 -43
  68. package/src/products/option-select.tsx +67 -67
  69. package/src/products/product-info.tsx +5 -5
  70. package/src/products/product-price.tsx +69 -69
  71. package/src/products/product-promises.tsx +61 -61
  72. package/src/products/product-specs.tsx +65 -65
  73. package/src/products/product-tabs.tsx +123 -123
  74. package/src/products/purchase-options.tsx +130 -130
  75. package/src/products/sets.ts +110 -110
  76. package/src/products/variant-matching.ts +71 -71
  77. package/src/products/variant-url.ts +74 -74
  78. package/src/reviews-ui/lightbox-state.ts +46 -46
  79. package/src/reviews-ui/review-header.tsx +166 -166
  80. package/src/reviews-ui/review-lightbox.tsx +271 -271
  81. package/src/reviews-ui/review-list.tsx +193 -193
  82. package/src/reviews-ui/review-widget.tsx +219 -218
  83. package/src/reviews-ui/widget-options.ts +55 -55
  84. package/src/store/category-template.tsx +136 -136
  85. package/src/store/index.ts +40 -40
  86. package/src/tracking/chatgpt-pixel.tsx +99 -99
  87. package/src/tracking/consent-init.tsx +62 -62
  88. package/src/tracking/events.ts +348 -348
  89. package/src/tracking/ga4.tsx +93 -93
  90. package/src/tracking/google-ads.ts +84 -84
  91. package/src/tracking/gtm.tsx +60 -60
  92. package/src/tracking/inline-script.ts +49 -49
  93. package/src/tracking/oaiq.ts +206 -206
  94. package/src/tracking/tiktok-pixel.tsx +91 -91
  95. package/src/tracking/track-init.tsx +56 -56
  96. package/src/tracking/track-order-purchase.tsx +122 -122
  97. package/src/tracking/ttq.ts +180 -180
  98. package/src/tracking/use-tracking-config.ts +54 -54
  99. package/theme/index.css +25 -25
package/src/api/menus.ts CHANGED
@@ -1,77 +1,77 @@
1
- /**
2
- * @cartbase/storefront/api/menus — backend-owned navigation
3
- * (content-navigation card).
4
- *
5
- * Ground truth: src/app/api/store/menus/[handle]/route.ts +
6
- * src/lib/content/menus.ts.
7
- *
8
- * Shopify Storefront `Menu` shape verbatim. Item `url`s are COMPUTED AT
9
- * READ TIME from live resource handles (src/lib/content/paths.ts) — a
10
- * handle rename never breaks a menu. Items whose referenced resource is
11
- * deleted or not storefront-visible (draft page/post/product, inactive or
12
- * internal category) are SKIPPED, subtree included — the payload only ever
13
- * contains renderable links. 3-level nesting max.
14
- *
15
- * ## ETag / caching semantics
16
- * The route serves `ETag` (sha1 of the payload) +
17
- * `Cache-Control: public, s-maxage=60, stale-while-revalidate=3600`, and
18
- * answers `If-None-Match` with `304` (empty body). Browser `fetch` handles
19
- * this transparently; server-side callers on Next.js should instead lean on
20
- * the RSC cache (`next: { revalidate }`) via `RequestOptions`. Note the SDK
21
- * itself does NOT replay ETags — a 304 only occurs when the caller's fetch
22
- * layer sends `If-None-Match`, in which case the platform serves the cached
23
- * body before the SDK sees it. The `menu.updated` event fires on every menu
24
- * mutation for future revalidation hooks.
25
- */
26
-
27
- import type { StorefrontClient } from "./http"
28
-
29
- /** One navigation node — recursive, ≤ 3 levels total. */
30
- export interface MenuItem {
31
- title: string
32
- /** What the item points at. */
33
- type:
34
- | "frontpage"
35
- | "collection"
36
- | "product"
37
- | "category"
38
- | "page"
39
- | "blog_post"
40
- | "external"
41
- /**
42
- * Computed at read time: `/` for frontpage, the shared path convention
43
- * (`/collections/<handle>`, `/products/<handle>`, `/categories/<handle>`,
44
- * `/pages/<handle>`, `/blogs/<blog>/<post>`) for resources, and the
45
- * stored URL verbatim for `external`.
46
- */
47
- url: string
48
- /** Referenced resource id (`pcol_…`, `prod_…`); null for frontpage/external. */
49
- resourceId: string | null
50
- items: MenuItem[]
51
- }
52
-
53
- export interface Menu {
54
- handle: string
55
- title: string
56
- items: MenuItem[]
57
- }
58
-
59
- export interface MenuResponse {
60
- menu: Menu
61
- }
62
-
63
- /**
64
- * GET /api/store/menus/:handle — one menu, renderable links only.
65
- *
66
- * Auth: anon (x-client-id).
67
- * Errors: 404 not_found (unknown/deleted handle — a store with zero menus
68
- * 404s every handle; render no nav, never crash).
69
- * Settings: menus are authored in admin → Content → Navigation; deleting a
70
- * referenced resource silently drops its item (subtree included) here.
71
- */
72
- export async function getMenu(
73
- client: StorefrontClient,
74
- handle: string
75
- ): Promise<MenuResponse> {
76
- return client.get(`/api/store/menus/${encodeURIComponent(handle)}`)
77
- }
1
+ /**
2
+ * @cartbase/storefront/api/menus — backend-owned navigation
3
+ * (content-navigation card).
4
+ *
5
+ * Ground truth: src/app/api/store/menus/[handle]/route.ts +
6
+ * src/lib/content/menus.ts.
7
+ *
8
+ * Shopify Storefront `Menu` shape verbatim. Item `url`s are COMPUTED AT
9
+ * READ TIME from live resource handles (src/lib/content/paths.ts) — a
10
+ * handle rename never breaks a menu. Items whose referenced resource is
11
+ * deleted or not storefront-visible (draft page/post/product, inactive or
12
+ * internal category) are SKIPPED, subtree included — the payload only ever
13
+ * contains renderable links. 3-level nesting max.
14
+ *
15
+ * ## ETag / caching semantics
16
+ * The route serves `ETag` (sha1 of the payload) +
17
+ * `Cache-Control: public, s-maxage=60, stale-while-revalidate=3600`, and
18
+ * answers `If-None-Match` with `304` (empty body). Browser `fetch` handles
19
+ * this transparently; server-side callers on Next.js should instead lean on
20
+ * the RSC cache (`next: { revalidate }`) via `RequestOptions`. Note the SDK
21
+ * itself does NOT replay ETags — a 304 only occurs when the caller's fetch
22
+ * layer sends `If-None-Match`, in which case the platform serves the cached
23
+ * body before the SDK sees it. The `menu.updated` event fires on every menu
24
+ * mutation for future revalidation hooks.
25
+ */
26
+
27
+ import type { StorefrontClient } from "./http"
28
+
29
+ /** One navigation node — recursive, ≤ 3 levels total. */
30
+ export interface MenuItem {
31
+ title: string
32
+ /** What the item points at. */
33
+ type:
34
+ | "frontpage"
35
+ | "collection"
36
+ | "product"
37
+ | "category"
38
+ | "page"
39
+ | "blog_post"
40
+ | "external"
41
+ /**
42
+ * Computed at read time: `/` for frontpage, the shared path convention
43
+ * (`/collections/<handle>`, `/products/<handle>`, `/categories/<handle>`,
44
+ * `/pages/<handle>`, `/blogs/<blog>/<post>`) for resources, and the
45
+ * stored URL verbatim for `external`.
46
+ */
47
+ url: string
48
+ /** Referenced resource id (`pcol_…`, `prod_…`); null for frontpage/external. */
49
+ resourceId: string | null
50
+ items: MenuItem[]
51
+ }
52
+
53
+ export interface Menu {
54
+ handle: string
55
+ title: string
56
+ items: MenuItem[]
57
+ }
58
+
59
+ export interface MenuResponse {
60
+ menu: Menu
61
+ }
62
+
63
+ /**
64
+ * GET /api/store/menus/:handle — one menu, renderable links only.
65
+ *
66
+ * Auth: anon (x-client-id).
67
+ * Errors: 404 not_found (unknown/deleted handle — a store with zero menus
68
+ * 404s every handle; render no nav, never crash).
69
+ * Settings: menus are authored in admin → Content → Navigation; deleting a
70
+ * referenced resource silently drops its item (subtree included) here.
71
+ */
72
+ export async function getMenu(
73
+ client: StorefrontClient,
74
+ handle: string
75
+ ): Promise<MenuResponse> {
76
+ return client.get(`/api/store/menus/${encodeURIComponent(handle)}`)
77
+ }
@@ -1,136 +1,136 @@
1
- /**
2
- * @cartbase/storefront/api/metaobjects — merchant-defined content types
3
- * (metaobjects card): size charts, brand profiles, FAQ blocks…
4
- *
5
- * Ground truth: src/app/api/store/metaobjects/** +
6
- * src/lib/metaobjects/store.ts.
7
- *
8
- * ACTIVE entries only — drafts 404 (lib filter AND anon RLS). Capabilities
9
- * per definition: `renderable` → `seo` present; `online_store` → `url`
10
- * present (`/metaobjects/<type>/<handle>`; the storefront prepends its
11
- * origin). References resolve at READ time: a target the storefront cannot
12
- * see yields `reference: null` — never dropped, never a 500.
13
- *
14
- * `by-metafield` is a RESERVED type slug (the static route shadows /:type).
15
- */
16
-
17
- import type { StorefrontClient } from "./http"
18
- import type { IsoDateString, PaginationQuery } from "./types"
19
-
20
- /** One field of a metaobject entry. */
21
- export interface MetaobjectField {
22
- key: string
23
- /** Field kind from the definition (text, rich_text, reference kinds…). */
24
- kind: string
25
- /** Raw stored value (shape depends on `kind`). */
26
- value: unknown
27
- /**
28
- * Reference kinds only (absent otherwise). Resolved target, or null when
29
- * the target is deleted / not storefront-visible. NOT resolved on LIST
30
- * payloads — only the by-handle read and by-metafield resolve references.
31
- */
32
- reference?: { id: string; title: string; handle: string; type?: string } | null
33
- }
34
-
35
- export interface Metaobject {
36
- id: string
37
- /** The definition's type slug. */
38
- type: string
39
- handle: string
40
- displayName: string
41
- fields: MetaobjectField[]
42
- /** Present only when the definition has the `renderable` capability.
43
- * title falls back to displayName; description to null. */
44
- seo?: { title: string; description: string | null }
45
- /** Present only with the `online_store` capability:
46
- * `/metaobjects/<type>/<handle>`. */
47
- url?: string
48
- updatedAt: IsoDateString
49
- }
50
-
51
- export type ListMetaobjectsQuery = PaginationQuery
52
-
53
- export interface MetaobjectListResponse {
54
- metaobjects: Metaobject[]
55
- count: number
56
- offset: number
57
- limit: number
58
- }
59
-
60
- /**
61
- * GET /api/store/metaobjects/:type — paginated ACTIVE entries of one type,
62
- * newest-updated first. `limit` clamped to 1–100 (default 20; out-of-range
63
- * values are clamped, not rejected). List payloads carry RAW field values —
64
- * `reference` is NOT resolved here (use getMetaobject for the resolve path).
65
- *
66
- * Auth: anon (x-client-id).
67
- * Errors: 404 not_found (unknown type).
68
- */
69
- export async function listMetaobjects(
70
- client: StorefrontClient,
71
- type: string,
72
- query?: ListMetaobjectsQuery
73
- ): Promise<MetaobjectListResponse> {
74
- return client.get(`/api/store/metaobjects/${encodeURIComponent(type)}`, {
75
- query: { ...query },
76
- })
77
- }
78
-
79
- export interface MetaobjectResponse {
80
- metaobject: Metaobject
81
- }
82
-
83
- /**
84
- * GET /api/store/metaobjects/:type/:handle — one ACTIVE entry, references
85
- * RESOLVED (invisible targets → `reference: null`).
86
- *
87
- * Auth: anon (x-client-id).
88
- * Errors: 404 not_found (unknown type, unknown handle, or draft entry).
89
- */
90
- export async function getMetaobject(
91
- client: StorefrontClient,
92
- type: string,
93
- handle: string
94
- ): Promise<MetaobjectResponse> {
95
- return client.get(
96
- `/api/store/metaobjects/${encodeURIComponent(type)}/${encodeURIComponent(handle)}`
97
- )
98
- }
99
-
100
- export interface ByMetafieldQuery {
101
- /** Owning entity type, e.g. `product`. */
102
- entity_type: string
103
- /** Owning entity id, e.g. `prod_…`. */
104
- entity_id: string
105
- /** The metafield key — its definition MUST be `metaobject_reference`. */
106
- key: string
107
- }
108
-
109
- /**
110
- * Response of the by-metafield chain. `metaobjects` is always present in
111
- * stored order. `metaobject` (first entry or null) is present ONLY when the
112
- * metafield definition is single-valued — LIST definitions return
113
- * `{metaobjects}` alone.
114
- */
115
- export interface ByMetafieldResponse {
116
- metaobject?: Metaobject | null
117
- metaobjects: Metaobject[]
118
- }
119
-
120
- /**
121
- * GET /api/store/metaobjects/by-metafield — the product→size-chart fetch
122
- * chain: resolve an entity's `metaobject_reference` METAFIELD into full
123
- * metaobject payload(s), references resolved. Deliberate leak boundary:
124
- * only `metaobject_reference` definitions resolve here (any other key →
125
- * 404), and only ACTIVE entries return.
126
- *
127
- * Auth: anon (x-client-id).
128
- * Errors: 400 invalid_data (missing entity_type/entity_id/key) ·
129
- * 404 not_found (no such definition, wrong field_type, or no stored value).
130
- */
131
- export async function getMetaobjectsByMetafield(
132
- client: StorefrontClient,
133
- query: ByMetafieldQuery
134
- ): Promise<ByMetafieldResponse> {
135
- return client.get("/api/store/metaobjects/by-metafield", { query: { ...query } })
136
- }
1
+ /**
2
+ * @cartbase/storefront/api/metaobjects — merchant-defined content types
3
+ * (metaobjects card): size charts, brand profiles, FAQ blocks…
4
+ *
5
+ * Ground truth: src/app/api/store/metaobjects/** +
6
+ * src/lib/metaobjects/store.ts.
7
+ *
8
+ * ACTIVE entries only — drafts 404 (lib filter AND anon RLS). Capabilities
9
+ * per definition: `renderable` → `seo` present; `online_store` → `url`
10
+ * present (`/metaobjects/<type>/<handle>`; the storefront prepends its
11
+ * origin). References resolve at READ time: a target the storefront cannot
12
+ * see yields `reference: null` — never dropped, never a 500.
13
+ *
14
+ * `by-metafield` is a RESERVED type slug (the static route shadows /:type).
15
+ */
16
+
17
+ import type { StorefrontClient } from "./http"
18
+ import type { IsoDateString, PaginationQuery } from "./types"
19
+
20
+ /** One field of a metaobject entry. */
21
+ export interface MetaobjectField {
22
+ key: string
23
+ /** Field kind from the definition (text, rich_text, reference kinds…). */
24
+ kind: string
25
+ /** Raw stored value (shape depends on `kind`). */
26
+ value: unknown
27
+ /**
28
+ * Reference kinds only (absent otherwise). Resolved target, or null when
29
+ * the target is deleted / not storefront-visible. NOT resolved on LIST
30
+ * payloads — only the by-handle read and by-metafield resolve references.
31
+ */
32
+ reference?: { id: string; title: string; handle: string; type?: string } | null
33
+ }
34
+
35
+ export interface Metaobject {
36
+ id: string
37
+ /** The definition's type slug. */
38
+ type: string
39
+ handle: string
40
+ displayName: string
41
+ fields: MetaobjectField[]
42
+ /** Present only when the definition has the `renderable` capability.
43
+ * title falls back to displayName; description to null. */
44
+ seo?: { title: string; description: string | null }
45
+ /** Present only with the `online_store` capability:
46
+ * `/metaobjects/<type>/<handle>`. */
47
+ url?: string
48
+ updatedAt: IsoDateString
49
+ }
50
+
51
+ export type ListMetaobjectsQuery = PaginationQuery
52
+
53
+ export interface MetaobjectListResponse {
54
+ metaobjects: Metaobject[]
55
+ count: number
56
+ offset: number
57
+ limit: number
58
+ }
59
+
60
+ /**
61
+ * GET /api/store/metaobjects/:type — paginated ACTIVE entries of one type,
62
+ * newest-updated first. `limit` clamped to 1–100 (default 20; out-of-range
63
+ * values are clamped, not rejected). List payloads carry RAW field values —
64
+ * `reference` is NOT resolved here (use getMetaobject for the resolve path).
65
+ *
66
+ * Auth: anon (x-client-id).
67
+ * Errors: 404 not_found (unknown type).
68
+ */
69
+ export async function listMetaobjects(
70
+ client: StorefrontClient,
71
+ type: string,
72
+ query?: ListMetaobjectsQuery
73
+ ): Promise<MetaobjectListResponse> {
74
+ return client.get(`/api/store/metaobjects/${encodeURIComponent(type)}`, {
75
+ query: { ...query },
76
+ })
77
+ }
78
+
79
+ export interface MetaobjectResponse {
80
+ metaobject: Metaobject
81
+ }
82
+
83
+ /**
84
+ * GET /api/store/metaobjects/:type/:handle — one ACTIVE entry, references
85
+ * RESOLVED (invisible targets → `reference: null`).
86
+ *
87
+ * Auth: anon (x-client-id).
88
+ * Errors: 404 not_found (unknown type, unknown handle, or draft entry).
89
+ */
90
+ export async function getMetaobject(
91
+ client: StorefrontClient,
92
+ type: string,
93
+ handle: string
94
+ ): Promise<MetaobjectResponse> {
95
+ return client.get(
96
+ `/api/store/metaobjects/${encodeURIComponent(type)}/${encodeURIComponent(handle)}`
97
+ )
98
+ }
99
+
100
+ export interface ByMetafieldQuery {
101
+ /** Owning entity type, e.g. `product`. */
102
+ entity_type: string
103
+ /** Owning entity id, e.g. `prod_…`. */
104
+ entity_id: string
105
+ /** The metafield key — its definition MUST be `metaobject_reference`. */
106
+ key: string
107
+ }
108
+
109
+ /**
110
+ * Response of the by-metafield chain. `metaobjects` is always present in
111
+ * stored order. `metaobject` (first entry or null) is present ONLY when the
112
+ * metafield definition is single-valued — LIST definitions return
113
+ * `{metaobjects}` alone.
114
+ */
115
+ export interface ByMetafieldResponse {
116
+ metaobject?: Metaobject | null
117
+ metaobjects: Metaobject[]
118
+ }
119
+
120
+ /**
121
+ * GET /api/store/metaobjects/by-metafield — the product→size-chart fetch
122
+ * chain: resolve an entity's `metaobject_reference` METAFIELD into full
123
+ * metaobject payload(s), references resolved. Deliberate leak boundary:
124
+ * only `metaobject_reference` definitions resolve here (any other key →
125
+ * 404), and only ACTIVE entries return.
126
+ *
127
+ * Auth: anon (x-client-id).
128
+ * Errors: 400 invalid_data (missing entity_type/entity_id/key) ·
129
+ * 404 not_found (no such definition, wrong field_type, or no stored value).
130
+ */
131
+ export async function getMetaobjectsByMetafield(
132
+ client: StorefrontClient,
133
+ query: ByMetafieldQuery
134
+ ): Promise<ByMetafieldResponse> {
135
+ return client.get("/api/store/metaobjects/by-metafield", { query: { ...query } })
136
+ }