@cartbase/storefront 0.19.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 (105) 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/reviews.ts +22 -1
  20. package/src/api/search.ts +163 -163
  21. package/src/api/store.ts +35 -35
  22. package/src/api/types.ts +91 -91
  23. package/src/cart-drawer/context.tsx +778 -778
  24. package/src/cart-drawer/cross-sell-carousel.tsx +211 -211
  25. package/src/cart-drawer/cross-sell-sidebar.tsx +158 -158
  26. package/src/cart-drawer/gift-wrap.tsx +82 -82
  27. package/src/cart-drawer/item/index.tsx +162 -162
  28. package/src/cart-drawer/item/upsell.tsx +110 -110
  29. package/src/cart-drawer/labels.ts +123 -123
  30. package/src/cart-drawer/mutation-queue.ts +78 -78
  31. package/src/cart-drawer/notes.tsx +131 -131
  32. package/src/cart-drawer/sticky-footer.tsx +73 -73
  33. package/src/cart-drawer/summary-breakdown.tsx +197 -197
  34. package/src/checkout/address-error-copy.ts +117 -117
  35. package/src/checkout/boxnow-locker-selector.tsx +410 -410
  36. package/src/checkout/compare-addresses.ts +40 -40
  37. package/src/checkout/discount-section.tsx +218 -218
  38. package/src/checkout/error-copy-codes.ts +63 -63
  39. package/src/checkout/geocode.ts +154 -154
  40. package/src/checkout/gift-card-section.tsx +224 -224
  41. package/src/checkout/index.ts +72 -72
  42. package/src/checkout/labels.ts +495 -495
  43. package/src/checkout/payment-button.tsx +372 -372
  44. package/src/checkout/payment-error-copy.ts +154 -154
  45. package/src/checkout/promotion-error-copy.ts +91 -91
  46. package/src/common/country-flag.tsx +52 -52
  47. package/src/common/country-select.tsx +11 -11
  48. package/src/common/index.ts +20 -20
  49. package/src/common/market-select.tsx +57 -57
  50. package/src/index.ts +12 -12
  51. package/src/lib/country-name.ts +59 -59
  52. package/src/lib/get-product-price.ts +133 -133
  53. package/src/lib/media-image.tsx +39 -39
  54. package/src/lib/payment-constants.ts +53 -53
  55. package/src/lib/platform.ts +13 -13
  56. package/src/lib/price.tsx +39 -39
  57. package/src/lib/store-api-error.ts +36 -36
  58. package/src/lib/variant-caption.ts +32 -32
  59. package/src/locales/bg.ts +3 -2
  60. package/src/locales/context.ts +37 -37
  61. package/src/locales/en.ts +26 -26
  62. package/src/locales/es.ts +3 -2
  63. package/src/locales/index.ts +19 -19
  64. package/src/locales/provider.tsx +59 -59
  65. package/src/locales/types.ts +77 -77
  66. package/src/order/index.ts +62 -62
  67. package/src/order/labels.ts +79 -79
  68. package/src/order/order-totals.tsx +250 -250
  69. package/src/primitives/select-field.tsx +93 -93
  70. package/src/products/image-gallery.tsx +43 -43
  71. package/src/products/option-select.tsx +67 -67
  72. package/src/products/product-info.tsx +5 -5
  73. package/src/products/product-price.tsx +69 -69
  74. package/src/products/product-promises.tsx +61 -61
  75. package/src/products/product-specs.tsx +65 -65
  76. package/src/products/product-tabs.tsx +123 -123
  77. package/src/products/purchase-options.tsx +130 -130
  78. package/src/products/sets.ts +110 -110
  79. package/src/products/variant-matching.ts +71 -71
  80. package/src/products/variant-url.ts +74 -74
  81. package/src/reviews-ui/index.ts +12 -0
  82. package/src/reviews-ui/labels.ts +5 -2
  83. package/src/reviews-ui/lightbox-state.ts +46 -46
  84. package/src/reviews-ui/review-header.tsx +166 -0
  85. package/src/reviews-ui/review-lightbox.tsx +271 -271
  86. package/src/reviews-ui/review-list.tsx +193 -191
  87. package/src/reviews-ui/review-widget.tsx +63 -71
  88. package/src/reviews-ui/star-badge.tsx +6 -4
  89. package/src/reviews-ui/widget-options.ts +55 -0
  90. package/src/store/category-template.tsx +136 -136
  91. package/src/store/index.ts +40 -40
  92. package/src/tracking/chatgpt-pixel.tsx +99 -99
  93. package/src/tracking/consent-init.tsx +62 -62
  94. package/src/tracking/events.ts +348 -348
  95. package/src/tracking/ga4.tsx +93 -93
  96. package/src/tracking/google-ads.ts +84 -84
  97. package/src/tracking/gtm.tsx +60 -60
  98. package/src/tracking/inline-script.ts +49 -49
  99. package/src/tracking/oaiq.ts +206 -206
  100. package/src/tracking/tiktok-pixel.tsx +91 -91
  101. package/src/tracking/track-init.tsx +56 -56
  102. package/src/tracking/track-order-purchase.tsx +122 -122
  103. package/src/tracking/ttq.ts +180 -180
  104. package/src/tracking/use-tracking-config.ts +54 -54
  105. 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
+ }