@thorprovider/blocks 0.1.0 → 0.1.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.
Files changed (57) hide show
  1. package/.turbo/turbo-build.log +15 -15
  2. package/.turbo/turbo-test.log +32 -5
  3. package/AGENTS.md +1 -1
  4. package/README.md +52 -0
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/contracts.d.mts +1111 -90
  7. package/dist/contracts.d.ts +1111 -90
  8. package/dist/contracts.js +189 -6
  9. package/dist/contracts.js.map +1 -1
  10. package/dist/contracts.mjs +173 -7
  11. package/dist/contracts.mjs.map +1 -1
  12. package/dist/index.d.mts +467 -89
  13. package/dist/index.d.ts +467 -89
  14. package/dist/index.js +2967 -2250
  15. package/dist/index.js.map +1 -1
  16. package/dist/index.mjs +2989 -2282
  17. package/dist/index.mjs.map +1 -1
  18. package/docs/standards/theming.md +32 -5
  19. package/package.json +4 -3
  20. package/src/blocks/e-commerce/category-filters/category-filter-2-filter-panel.tsx +136 -93
  21. package/src/blocks/e-commerce/category-filters/category-filter-2.test.tsx +513 -0
  22. package/src/blocks/e-commerce/category-filters/category-filter-2.tsx +728 -148
  23. package/src/blocks/e-commerce/checkout-forms/checkout-confirmation-fulfilment-progress.tsx +14 -8
  24. package/src/blocks/e-commerce/checkout-forms/checkout-confirmation.test.tsx +136 -0
  25. package/src/blocks/e-commerce/checkout-forms/checkout-confirmation.tsx +145 -99
  26. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-order-summary.tsx +215 -102
  27. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-payment-step.tsx +6 -2
  28. package/src/blocks/e-commerce/checkout-forms/checkout-form-1-shipping-step.tsx +51 -2
  29. package/src/blocks/e-commerce/checkout-forms/checkout-form-1.test.tsx +216 -0
  30. package/src/blocks/e-commerce/checkout-forms/checkout-form-1.tsx +114 -11
  31. package/src/blocks/e-commerce/navigation/mobile-category-drawer.test.tsx +42 -2
  32. package/src/blocks/e-commerce/navigation/mobile-category-drawer.tsx +10 -8
  33. package/src/blocks/e-commerce/product-categories/product-category-2.test.tsx +51 -0
  34. package/src/blocks/e-commerce/product-categories/product-category-2.tsx +56 -36
  35. package/src/blocks/e-commerce/product-overview/product-overview-1.test.tsx +182 -0
  36. package/src/blocks/e-commerce/product-overview/product-overview-1.tsx +298 -129
  37. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.test.tsx +137 -0
  38. package/src/blocks/e-commerce/shopping-carts/shopping-cart-4.tsx +231 -89
  39. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.test.tsx +65 -0
  40. package/src/blocks/e-commerce/storefront-hero/storefront-hero-1.tsx +19 -0
  41. package/src/blocks/marketing/footers/footer-section-1.test.tsx +117 -0
  42. package/src/blocks/marketing/footers/footer-section-1.tsx +46 -9
  43. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.test.tsx +54 -0
  44. package/src/blocks/marketing/navbars/navbar-4-cart-sheet.tsx +43 -13
  45. package/src/blocks/marketing/navbars/navbar-4.test.tsx +112 -0
  46. package/src/blocks/marketing/navbars/navbar-4.tsx +101 -28
  47. package/src/index.ts +38 -0
  48. package/src/lib/cart.test.ts +15 -0
  49. package/src/lib/cart.ts +11 -0
  50. package/src/lib/contracts-curated.test.ts +207 -0
  51. package/src/lib/contracts-server-boundary.test.ts +52 -0
  52. package/src/lib/contracts.ts +182 -4
  53. package/src/lib/money.test.ts +64 -0
  54. package/src/lib/money.ts +65 -0
  55. package/styles.css +1 -0
  56. package/tsup.config.ts +0 -1
  57. package/vitest.config.ts +9 -1
package/dist/contracts.js CHANGED
@@ -1,4 +1,3 @@
1
- 'use client';
2
1
  "use strict";
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -23,15 +22,31 @@ var contracts_exports = {};
23
22
  __export(contracts_exports, {
24
23
  PRODUCT_IMAGE_FALLBACK: () => PRODUCT_IMAGE_FALLBACK,
25
24
  accountMenuItemSchema: () => accountMenuItemSchema,
25
+ brandAccentSchema: () => brandAccentSchema,
26
26
  buttonPropsSchema: () => buttonPropsSchema,
27
27
  cardProductSchema: () => cardProductSchema,
28
28
  cartItemSchema: () => cartItemSchema,
29
+ cartTotalsSchema: () => cartTotalsSchema,
30
+ ctaSchema: () => ctaSchema,
31
+ ctaSection1PropsSchema: () => ctaSection1PropsSchema,
32
+ faqBlock1PropsSchema: () => faqBlock1PropsSchema,
33
+ faqItemSchema: () => faqItemSchema,
34
+ formatMoney: () => formatMoney,
35
+ heroImageSchema: () => heroImageSchema,
36
+ isAvailableMoney: () => isAvailableMoney,
37
+ isCompleteFiniteDecimal: () => isCompleteFiniteDecimal,
38
+ logoAccentSchema: () => logoAccentSchema,
39
+ moneySchema: () => moneySchema,
29
40
  navCartItemSchema: () => navCartItemSchema,
30
41
  navbar4PropsSchema: () => navbar4PropsSchema,
31
42
  paymentMethodSchema: () => paymentMethodSchema,
32
43
  productCard1PropsSchema: () => productCard1PropsSchema,
44
+ productDetailProductSchema: () => productDetailProductSchema,
45
+ productDetailVariantSchema: () => productDetailVariantSchema,
46
+ productOverview1PropsSchema: () => productOverview1PropsSchema,
33
47
  promotionSchema: () => promotionSchema,
34
48
  recentlyViewedItemSchema: () => recentlyViewedItemSchema,
49
+ resolveAbsoluteQuantity: () => resolveAbsoluteQuantity,
35
50
  shoppingCart4PropsSchema: () => shoppingCart4PropsSchema,
36
51
  storeCategorySchema: () => storeCategorySchema,
37
52
  storeContainer1PropsSchema: () => storeContainer1PropsSchema,
@@ -40,9 +55,91 @@ __export(contracts_exports, {
40
55
  });
41
56
  module.exports = __toCommonJS(contracts_exports);
42
57
  var import_zod = require("zod");
58
+
59
+ // src/lib/money.ts
60
+ var COMPLETE_FINITE_DECIMAL = /^-?(?:\d+)(?:\.\d+)?$/;
61
+ function isCompleteFiniteDecimal(value) {
62
+ if (typeof value !== "string") return false;
63
+ const trimmed = value.trim();
64
+ if (trimmed === "" || !COMPLETE_FINITE_DECIMAL.test(trimmed)) return false;
65
+ return Number.isFinite(Number(trimmed));
66
+ }
67
+ function isAvailableMoney(money) {
68
+ return !!money && typeof money.currencyCode === "string" && money.currencyCode.trim() !== "" && isCompleteFiniteDecimal(money.amount);
69
+ }
70
+ function formatMoney(money, formatter) {
71
+ if (!isAvailableMoney(money)) return null;
72
+ if (formatter) return formatter(money);
73
+ return new Intl.NumberFormat(void 0, {
74
+ style: "currency",
75
+ currency: money.currencyCode
76
+ }).format(Number(money.amount));
77
+ }
78
+
79
+ // src/lib/cart.ts
80
+ function resolveAbsoluteQuantity(currentQuantity, delta) {
81
+ return Math.max(1, currentQuantity + delta);
82
+ }
83
+
84
+ // src/lib/contracts.ts
43
85
  var PRODUCT_IMAGE_FALLBACK = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(
44
86
  `<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><rect width='100%' height='100%' fill='#e2e8f0'/><g fill='none' stroke='#94a3b8' stroke-width='12' stroke-linecap='round'><path d='M150 300h300M300 150v300'/><path d='M220 230h50M220 300h50M220 370h50'/><path d='M330 230h50M330 300h50M330 370h50'/></g></svg>`
45
87
  );
88
+ var moneySchema = import_zod.z.object({
89
+ amount: import_zod.z.string(),
90
+ currencyCode: import_zod.z.string()
91
+ });
92
+ var imageSchema = import_zod.z.object({
93
+ id: import_zod.z.string().optional(),
94
+ url: import_zod.z.string(),
95
+ altText: import_zod.z.string(),
96
+ width: import_zod.z.number().optional(),
97
+ height: import_zod.z.number().optional()
98
+ });
99
+ var selectedOptionSchema = import_zod.z.object({
100
+ name: import_zod.z.string(),
101
+ value: import_zod.z.string()
102
+ });
103
+ var productDetailVariantSchema = import_zod.z.object({
104
+ id: import_zod.z.string(),
105
+ title: import_zod.z.string(),
106
+ sku: import_zod.z.string().optional(),
107
+ availableForSale: import_zod.z.boolean(),
108
+ selectedOptions: import_zod.z.array(selectedOptionSchema),
109
+ /** Omit or null when the backend has no resolved price (purchase disabled). */
110
+ price: moneySchema.nullish(),
111
+ compareAtPrice: moneySchema.optional(),
112
+ image: imageSchema.optional()
113
+ });
114
+ var productDetailProductSchema = import_zod.z.object({
115
+ id: import_zod.z.string(),
116
+ handle: import_zod.z.string().optional(),
117
+ title: import_zod.z.string(),
118
+ description: import_zod.z.string().optional(),
119
+ vendor: import_zod.z.string().optional(),
120
+ options: import_zod.z.array(import_zod.z.object({ id: import_zod.z.string(), name: import_zod.z.string(), values: import_zod.z.array(import_zod.z.string()) })),
121
+ variants: import_zod.z.array(productDetailVariantSchema),
122
+ images: import_zod.z.array(imageSchema),
123
+ featuredImage: imageSchema.optional()
124
+ });
125
+ var productOverview1PropsSchema = import_zod.z.object({
126
+ product: productDetailProductSchema,
127
+ selectedVariantId: import_zod.z.string().nullable().optional(),
128
+ quantity: import_zod.z.number().optional(),
129
+ rating: import_zod.z.object({ value: import_zod.z.number(), count: import_zod.z.number() }).optional(),
130
+ deliveryEstimate: import_zod.z.string().optional(),
131
+ shipping: moneySchema.optional(),
132
+ sizesLabel: import_zod.z.string().optional(),
133
+ variantLabel: import_zod.z.string().optional(),
134
+ reviewsLabel: import_zod.z.string().optional(),
135
+ addToCartLabel: import_zod.z.string().optional(),
136
+ outOfStockLabel: import_zod.z.string().optional(),
137
+ unavailablePriceLabel: import_zod.z.string().optional(),
138
+ selectVariantLabel: import_zod.z.string().optional(),
139
+ quantityLabel: import_zod.z.string().optional(),
140
+ deliveryEstimateLabel: import_zod.z.string().optional(),
141
+ shippingLabel: import_zod.z.string().optional()
142
+ });
46
143
  var cardProductSchema = import_zod.z.object({
47
144
  id: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]),
48
145
  title: import_zod.z.string(),
@@ -56,8 +153,20 @@ var cartItemSchema = import_zod.z.object({
56
153
  name: import_zod.z.string(),
57
154
  price: import_zod.z.number(),
58
155
  quantity: import_zod.z.number().int().min(0),
59
- image: import_zod.z.string(),
60
- variant: import_zod.z.string().optional()
156
+ /** Optional; absent images are omitted, never serialized as `""`. */
157
+ image: import_zod.z.string().optional(),
158
+ variant: import_zod.z.string().optional(),
159
+ /** Canonical backend per-unit price, preferred for display when present. */
160
+ unitPrice: moneySchema.optional(),
161
+ /** Canonical backend line total, preferred over `price * quantity`. */
162
+ lineTotal: moneySchema.optional()
163
+ });
164
+ var cartTotalsSchema = import_zod.z.object({
165
+ subtotal: moneySchema,
166
+ discount: moneySchema.optional(),
167
+ tax: moneySchema.optional(),
168
+ shipping: moneySchema.optional(),
169
+ total: moneySchema
61
170
  });
62
171
  var recentlyViewedItemSchema = import_zod.z.object({
63
172
  id: import_zod.z.string(),
@@ -88,10 +197,11 @@ var utilityLinkSchema = import_zod.z.object({
88
197
  var navCartItemSchema = import_zod.z.object({
89
198
  id: import_zod.z.string(),
90
199
  name: import_zod.z.string(),
91
- variant: import_zod.z.string(),
200
+ variant: import_zod.z.string().optional(),
92
201
  price: import_zod.z.number(),
93
202
  qty: import_zod.z.number().int().min(0),
94
- image: import_zod.z.string()
203
+ /** Optional; absent images are omitted, never serialized as `""`. */
204
+ image: import_zod.z.string().optional()
95
205
  });
96
206
  var storeUserSchema = import_zod.z.object({
97
207
  name: import_zod.z.string(),
@@ -151,13 +261,22 @@ var shoppingCart4PropsSchema = import_zod.z.object({
151
261
  items: import_zod.z.array(cartItemSchema),
152
262
  promotions: import_zod.z.array(promotionSchema).default([]),
153
263
  recentlyViewed: import_zod.z.array(recentlyViewedItemSchema).optional(),
264
+ /** Backend-authoritative totals; rendered verbatim, never computed locally. */
265
+ totals: cartTotalsSchema.optional(),
266
+ totalsState: import_zod.z.enum(["idle", "pending", "error"]).optional(),
267
+ totalsErrorLabel: import_zod.z.string().optional(),
268
+ totalsUnavailableLabel: import_zod.z.string().optional(),
269
+ amountUnavailableLabel: import_zod.z.string().optional(),
270
+ retryLabel: import_zod.z.string().optional(),
154
271
  taxRate: import_zod.z.number().optional(),
155
272
  titleLabel: import_zod.z.string().optional(),
156
273
  subtotalLabel: import_zod.z.string().optional(),
274
+ discountLabel: import_zod.z.string().optional(),
157
275
  taxLabel: import_zod.z.string().optional(),
158
276
  shippingLabel: import_zod.z.string().optional(),
159
277
  shippingFreeLabel: import_zod.z.string().optional(),
160
278
  totalLabel: import_zod.z.string().optional(),
279
+ totalsPendingLabel: import_zod.z.string().optional(),
161
280
  awayFromLabel: import_zod.z.string().optional(),
162
281
  activatedLabel: import_zod.z.string().optional(),
163
282
  itemsLabel: import_zod.z.string().optional(),
@@ -171,7 +290,23 @@ var shoppingCart4PropsSchema = import_zod.z.object({
171
290
  expandLabel: import_zod.z.string().optional(),
172
291
  checkoutLabel: import_zod.z.string().optional(),
173
292
  quickCheckoutLabel: import_zod.z.string().optional(),
174
- checkoutNowLabel: import_zod.z.string().optional()
293
+ checkoutNowLabel: import_zod.z.string().optional(),
294
+ emptyLabel: import_zod.z.string().optional(),
295
+ removeLabel: import_zod.z.string().optional(),
296
+ decreaseLabel: import_zod.z.string().optional(),
297
+ increaseLabel: import_zod.z.string().optional()
298
+ });
299
+ var logoAccentSchema = import_zod.z.object({
300
+ text: import_zod.z.string(),
301
+ className: import_zod.z.string().optional()
302
+ });
303
+ var brandAccentSchema = import_zod.z.object({
304
+ text: import_zod.z.string(),
305
+ className: import_zod.z.string().optional()
306
+ });
307
+ var heroImageSchema = import_zod.z.object({
308
+ src: import_zod.z.string(),
309
+ alt: import_zod.z.string()
175
310
  });
176
311
  var navbar4PropsSchema = import_zod.z.object({
177
312
  storeCategories: import_zod.z.array(storeCategorySchema),
@@ -179,25 +314,73 @@ var navbar4PropsSchema = import_zod.z.object({
179
314
  utilityLinks: import_zod.z.array(utilityLinkSchema),
180
315
  logoText: import_zod.z.string(),
181
316
  logoHref: import_zod.z.string().optional(),
317
+ /** Optional accent for a trailing segment of `logoText`; omitted → unchanged. */
318
+ logoAccent: logoAccentSchema.optional(),
319
+ /**
320
+ * Consumer-supplied classes merged onto the block's outer surface as the
321
+ * final argument (e.g. a semantic surface token). Omitted → unchanged.
322
+ */
323
+ className: import_zod.z.string().optional(),
182
324
  accountHref: import_zod.z.string().optional(),
183
325
  searchQuery: import_zod.z.string().optional(),
184
326
  items: import_zod.z.array(navCartItemSchema).optional(),
327
+ /** Authoritative backend subtotal for the mini-cart. */
328
+ subtotal: moneySchema.optional(),
329
+ subtotalUnavailableLabel: import_zod.z.string().optional(),
185
330
  user: storeUserSchema.optional(),
186
331
  accountMenuItems: import_zod.z.array(accountMenuItemSchema).optional()
187
332
  });
333
+ var ctaSchema = import_zod.z.object({
334
+ label: import_zod.z.string(),
335
+ href: import_zod.z.string().optional()
336
+ });
337
+ var faqItemSchema = import_zod.z.object({
338
+ value: import_zod.z.string(),
339
+ question: import_zod.z.string(),
340
+ answer: import_zod.z.string()
341
+ });
342
+ var faqBlock1PropsSchema = import_zod.z.object({
343
+ items: import_zod.z.array(faqItemSchema),
344
+ title: import_zod.z.string().optional(),
345
+ description: import_zod.z.string().optional(),
346
+ accordionType: import_zod.z.enum(["single", "multiple"]).optional()
347
+ });
348
+ var ctaSection1PropsSchema = import_zod.z.object({
349
+ badgeText: import_zod.z.string().optional(),
350
+ title: import_zod.z.string().optional(),
351
+ description: import_zod.z.string().optional(),
352
+ primaryCta: ctaSchema.optional(),
353
+ secondaryCta: ctaSchema.optional()
354
+ });
188
355
  // Annotate the CommonJS export names for ESM import in node:
189
356
  0 && (module.exports = {
190
357
  PRODUCT_IMAGE_FALLBACK,
191
358
  accountMenuItemSchema,
359
+ brandAccentSchema,
192
360
  buttonPropsSchema,
193
361
  cardProductSchema,
194
362
  cartItemSchema,
363
+ cartTotalsSchema,
364
+ ctaSchema,
365
+ ctaSection1PropsSchema,
366
+ faqBlock1PropsSchema,
367
+ faqItemSchema,
368
+ formatMoney,
369
+ heroImageSchema,
370
+ isAvailableMoney,
371
+ isCompleteFiniteDecimal,
372
+ logoAccentSchema,
373
+ moneySchema,
195
374
  navCartItemSchema,
196
375
  navbar4PropsSchema,
197
376
  paymentMethodSchema,
198
377
  productCard1PropsSchema,
378
+ productDetailProductSchema,
379
+ productDetailVariantSchema,
380
+ productOverview1PropsSchema,
199
381
  promotionSchema,
200
382
  recentlyViewedItemSchema,
383
+ resolveAbsoluteQuantity,
201
384
  shoppingCart4PropsSchema,
202
385
  storeCategorySchema,
203
386
  storeContainer1PropsSchema,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lib/contracts.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { Money, PaymentMethod, ProductPreview } from '@thorprovider/types'\n\n/**\n * Serialization contracts for spec-driven storefronts.\n *\n * These Zod schemas are the *serializable subset* of what a spec may write for\n * a block. The component keeps its rich React interface; the catalog/registry\n * is the adapter that maps these props to real component props and binds\n * callbacks (see storefronts-on-blocks design D3). `z.infer` is the typed\n * boundary between spec and catalog — a prop outside the schema is a compile\n * error, not a runtime surprise.\n *\n * Data shapes are projections of the @thorprovider/types canon so that\n * @thorprovider/adapters output validates without conflict:\n * - Prices are plain numbers here while the canon `Money` is a decimal string\n * ({ amount: \"45.00\", currencyCode }). The projection happens at the\n * loader boundary.\n * - Blocks use `title` while the canon `ProductPreview` uses `name`. Reconcile\n * via the canonical aliases below or the catalog/loader adapter.\n */\nexport type CanonMoney = Money\nexport type CanonProduct = ProductPreview\n\n/**\n * Fallback data-URI for required image fields when the data source has no\n * image. Image fields are non-optional `z.string()` in these contracts, so the\n * boundary must never emit `\"\"` (React warns about `src=\"\"`); it must emit a\n * real, renderable value. Hosts may override with their own branded fallback\n * (e.g. electrostore's `CATEGORY_IMAGE_FALLBACK`), but blocks ship a neutral\n * one so direct consumers are safe by default.\n */\nexport const PRODUCT_IMAGE_FALLBACK =\n 'data:image/svg+xml;charset=utf-8,' +\n encodeURIComponent(\n `<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><rect width='100%' height='100%' fill='#e2e8f0'/><g fill='none' stroke='#94a3b8' stroke-width='12' stroke-linecap='round'><path d='M150 300h300M300 150v300'/><path d='M220 230h50M220 300h50M220 370h50'/><path d='M330 230h50M330 300h50M330 370h50'/></g></svg>`,\n )\n\n// ---------------------------------------------------------------------------\n// Data contracts\n// ---------------------------------------------------------------------------\n\nexport const cardProductSchema = z.object({\n id: z.union([z.string(), z.number()]),\n title: z.string(),\n price: z.number(),\n originalPrice: z.number().optional(),\n image: z.string(),\n badge: z.string().optional(),\n})\nexport type CardProduct = z.infer<typeof cardProductSchema>\n\nexport const cartItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n price: z.number(),\n quantity: z.number().int().min(0),\n image: z.string(),\n variant: z.string().optional(),\n})\nexport type CartItem = z.infer<typeof cartItemSchema>\n\nexport const recentlyViewedItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n price: z.number(),\n image: z.string(),\n})\nexport type RecentlyViewedItem = z.infer<typeof recentlyViewedItemSchema>\n\nexport const promotionSchema = z.object({\n threshold: z.number(),\n reward: z.string(),\n icon: z.string().default('gift'),\n})\nexport type Promotion = z.infer<typeof promotionSchema>\n\nconst storeCategoryBase = {\n label: z.string(),\n href: z.string(),\n emphasis: z.boolean().optional(),\n}\n\nexport const storeCategorySchema: z.ZodType<StoreCategory> = z.lazy(() =>\n z.object({\n ...storeCategoryBase,\n children: z.array(storeCategorySchema).optional(),\n }),\n)\nexport type StoreCategory = {\n label: string\n href: string\n children?: StoreCategory[]\n emphasis?: boolean\n}\n\nexport const utilityLinkSchema = z.object({\n label: z.string(),\n href: z.string(),\n})\nexport type UtilityLink = z.infer<typeof utilityLinkSchema>\n\nexport const navCartItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n variant: z.string(),\n price: z.number(),\n qty: z.number().int().min(0),\n image: z.string(),\n})\nexport type NavCartItem = z.infer<typeof navCartItemSchema>\n\nexport const storeUserSchema = z.object({\n name: z.string(),\n email: z.string(),\n avatar: z.string(),\n initials: z.string(),\n})\nexport type StoreUser = z.infer<typeof storeUserSchema>\n\nexport const accountMenuItemSchema = z.object({\n label: z.string(),\n href: z.string(),\n description: z.string().optional(),\n icon: z.enum(['orders', 'wishlist', 'addresses', 'settings']).optional(),\n})\nexport type AccountMenuItem = z.infer<typeof accountMenuItemSchema>\n\n/**\n * Serialization contract for a payment method offered at checkout.\n *\n * A projection of `@thorprovider/types` `PaymentMethod` — the storefront-facing\n * shape produced by the adapters transform. Checkout blocks render the method\n * list from this data instead of hardcoding providers.\n */\nexport const paymentMethodSchema: z.ZodType<PaymentMethod> = z.object({\n id: z.string(),\n name: z.string(),\n type: z.enum([\n 'card',\n 'wallet',\n 'bank_transfer',\n 'buy_now_pay_later',\n 'cash_on_delivery',\n 'crypto',\n 'other',\n ]),\n isEnabled: z.boolean(),\n requiresSetup: z.boolean(),\n description: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n features: z\n .object({\n saveForLater: z.boolean().optional(),\n requires3DS: z.boolean().optional(),\n supportsRefunds: z.boolean().optional(),\n minAmount: z.number().optional(),\n maxAmount: z.number().optional(),\n supportedCurrencies: z.array(z.string()).optional(),\n requiresVerification: z.boolean().optional(),\n })\n .optional(),\n logo: z.string().optional(),\n})\n\n// ---------------------------------------------------------------------------\n// Block prop schemas\n// ---------------------------------------------------------------------------\n\nexport const buttonPropsSchema = z.object({\n variant: z\n .enum(['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'])\n .optional(),\n size: z\n .enum(['default', 'xs', 'sm', 'lg', 'icon', 'icon-xs', 'icon-sm', 'icon-lg'])\n .optional(),\n disabled: z.boolean().optional(),\n className: z.string().optional(),\n})\nexport type ButtonSpecProps = z.infer<typeof buttonPropsSchema>\n\nexport const storeContainer1PropsSchema = z.object({\n size: z.enum(['default', 'narrow', 'wide', 'full']).optional(),\n className: z.string().optional(),\n})\nexport type StoreContainer1SpecProps = z.infer<typeof storeContainer1PropsSchema>\n\nexport const productCard1PropsSchema = z.object({\n products: z.array(cardProductSchema),\n heading: z.string().optional(),\n maxItems: z.number().int().positive().optional(),\n})\nexport type ProductCard1SpecProps = z.infer<typeof productCard1PropsSchema>\n\nexport const shoppingCart4PropsSchema = z.object({\n items: z.array(cartItemSchema),\n promotions: z.array(promotionSchema).default([]),\n recentlyViewed: z.array(recentlyViewedItemSchema).optional(),\n taxRate: z.number().optional(),\n titleLabel: z.string().optional(),\n subtotalLabel: z.string().optional(),\n taxLabel: z.string().optional(),\n shippingLabel: z.string().optional(),\n shippingFreeLabel: z.string().optional(),\n totalLabel: z.string().optional(),\n awayFromLabel: z.string().optional(),\n activatedLabel: z.string().optional(),\n itemsLabel: z.string().optional(),\n expressCheckoutLabel: z.string().optional(),\n expressLabel: z.string().optional(),\n standardLabel: z.string().optional(),\n estimatedDeliveryLabel: z.string().optional(),\n secureCheckoutLabel: z.string().optional(),\n recentlyViewedLabel: z.string().optional(),\n collapseLabel: z.string().optional(),\n expandLabel: z.string().optional(),\n checkoutLabel: z.string().optional(),\n quickCheckoutLabel: z.string().optional(),\n checkoutNowLabel: z.string().optional(),\n})\nexport type ShoppingCart4SpecProps = z.infer<typeof shoppingCart4PropsSchema>\n\nexport const navbar4PropsSchema = z.object({\n storeCategories: z.array(storeCategorySchema),\n shippingNote: z.string(),\n utilityLinks: z.array(utilityLinkSchema),\n logoText: z.string(),\n logoHref: z.string().optional(),\n accountHref: z.string().optional(),\n searchQuery: z.string().optional(),\n items: z.array(navCartItemSchema).optional(),\n user: storeUserSchema.optional(),\n accountMenuItems: z.array(accountMenuItemSchema).optional(),\n})\nexport type Navbar4SpecProps = z.infer<typeof navbar4PropsSchema>"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/lib/contracts.ts","../src/lib/money.ts","../src/lib/cart.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { Money, PaymentMethod, ProductPreview } from '@thorprovider/types'\n\n/**\n * Serialization contracts for spec-driven storefronts.\n *\n * These Zod schemas are the *serializable subset* of what a spec may write for\n * a block. The component keeps its rich React interface; the catalog/registry\n * is the adapter that maps these props to real component props and binds\n * callbacks (see storefronts-on-blocks design D3). `z.infer` is the typed\n * boundary between spec and catalog — a prop outside the schema is a compile\n * error, not a runtime surprise.\n *\n * Data shapes are projections of the @thorprovider/types canon so that\n * @thorprovider/adapters output validates without conflict:\n * - Prices are plain numbers here while the canon `Money` is a decimal string\n * ({ amount: \"45.00\", currencyCode }). The projection happens at the\n * loader boundary.\n * - Blocks use `title` while the canon `ProductPreview` uses `name`. Reconcile\n * via the canonical aliases below or the catalog/loader adapter.\n */\nexport type CanonMoney = Money\nexport type CanonProduct = ProductPreview\n\n/**\n * Fallback data-URI for required image fields when the data source has no\n * image. Image fields are non-optional `z.string()` in these contracts, so the\n * boundary must never emit `\"\"` (React warns about `src=\"\"`); it must emit a\n * real, renderable value. Hosts may override with their own branded fallback\n * (e.g. electrostore's `CATEGORY_IMAGE_FALLBACK`), but blocks ship a neutral\n * one so direct consumers are safe by default.\n */\nexport const PRODUCT_IMAGE_FALLBACK =\n 'data:image/svg+xml;charset=utf-8,' +\n encodeURIComponent(\n `<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><rect width='100%' height='100%' fill='#e2e8f0'/><g fill='none' stroke='#94a3b8' stroke-width='12' stroke-linecap='round'><path d='M150 300h300M300 150v300'/><path d='M220 230h50M220 300h50M220 370h50'/><path d='M330 230h50M330 300h50M330 370h50'/></g></svg>`,\n )\n\n// ---------------------------------------------------------------------------\n// Data contracts\n// ---------------------------------------------------------------------------\n\n/**\n * Canonical `Money` (`@thorprovider/types`): a decimal string in major units\n * (`\"69.99\"`), never cents. A missing price is represented by omitting the\n * field (or `null` on a nullable field), not by an empty string.\n *\n * The amount stays a permissive `z.string()` on purpose: an unresolved backend\n * price may arrive as the canonical sentinel `\"\"`, and tightening the schema\n * would reject the whole view instead of letting the presentation layer render\n * an unavailable state. Validity is enforced at render time by\n * `isAvailableMoney`/`formatMoney` (a `\"\"`/malformed amount never becomes `$0`).\n */\nexport const moneySchema: z.ZodType<Money> = z.object({\n amount: z.string(),\n currencyCode: z.string(),\n})\n\nconst imageSchema = z.object({\n id: z.string().optional(),\n url: z.string(),\n altText: z.string(),\n width: z.number().optional(),\n height: z.number().optional(),\n})\n\nconst selectedOptionSchema = z.object({\n name: z.string(),\n value: z.string(),\n})\n\nexport const productDetailVariantSchema = z.object({\n id: z.string(),\n title: z.string(),\n sku: z.string().optional(),\n availableForSale: z.boolean(),\n selectedOptions: z.array(selectedOptionSchema),\n /** Omit or null when the backend has no resolved price (purchase disabled). */\n price: moneySchema.nullish(),\n compareAtPrice: moneySchema.optional(),\n image: imageSchema.optional(),\n})\nexport type ProductDetailVariantSpec = z.infer<typeof productDetailVariantSchema>\n\nexport const productDetailProductSchema = z.object({\n id: z.string(),\n handle: z.string().optional(),\n title: z.string(),\n description: z.string().optional(),\n vendor: z.string().optional(),\n options: z.array(z.object({ id: z.string(), name: z.string(), values: z.array(z.string()) })),\n variants: z.array(productDetailVariantSchema),\n images: z.array(imageSchema),\n featuredImage: imageSchema.optional(),\n})\nexport type ProductDetailProductSpec = z.infer<typeof productDetailProductSchema>\n\nexport const productOverview1PropsSchema = z.object({\n product: productDetailProductSchema,\n selectedVariantId: z.string().nullable().optional(),\n quantity: z.number().optional(),\n rating: z.object({ value: z.number(), count: z.number() }).optional(),\n deliveryEstimate: z.string().optional(),\n shipping: moneySchema.optional(),\n sizesLabel: z.string().optional(),\n variantLabel: z.string().optional(),\n reviewsLabel: z.string().optional(),\n addToCartLabel: z.string().optional(),\n outOfStockLabel: z.string().optional(),\n unavailablePriceLabel: z.string().optional(),\n selectVariantLabel: z.string().optional(),\n quantityLabel: z.string().optional(),\n deliveryEstimateLabel: z.string().optional(),\n shippingLabel: z.string().optional(),\n})\nexport type ProductOverview1SpecProps = z.infer<typeof productOverview1PropsSchema>\n\nexport const cardProductSchema = z.object({\n id: z.union([z.string(), z.number()]),\n title: z.string(),\n price: z.number(),\n originalPrice: z.number().optional(),\n image: z.string(),\n badge: z.string().optional(),\n})\nexport type CardProduct = z.infer<typeof cardProductSchema>\n\nexport const cartItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n price: z.number(),\n quantity: z.number().int().min(0),\n /** Optional; absent images are omitted, never serialized as `\"\"`. */\n image: z.string().optional(),\n variant: z.string().optional(),\n /** Canonical backend per-unit price, preferred for display when present. */\n unitPrice: moneySchema.optional(),\n /** Canonical backend line total, preferred over `price * quantity`. */\n lineTotal: moneySchema.optional(),\n})\nexport type CartItem = z.infer<typeof cartItemSchema>\n\nexport const cartTotalsSchema = z.object({\n subtotal: moneySchema,\n discount: moneySchema.optional(),\n tax: moneySchema.optional(),\n shipping: moneySchema.optional(),\n total: moneySchema,\n})\nexport type CartTotals = z.infer<typeof cartTotalsSchema>\n\nexport const recentlyViewedItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n price: z.number(),\n image: z.string(),\n})\nexport type RecentlyViewedItem = z.infer<typeof recentlyViewedItemSchema>\n\nexport const promotionSchema = z.object({\n threshold: z.number(),\n reward: z.string(),\n icon: z.string().default('gift'),\n})\nexport type Promotion = z.infer<typeof promotionSchema>\n\nconst storeCategoryBase = {\n label: z.string(),\n href: z.string(),\n emphasis: z.boolean().optional(),\n}\n\nexport const storeCategorySchema: z.ZodType<StoreCategory> = z.lazy(() =>\n z.object({\n ...storeCategoryBase,\n children: z.array(storeCategorySchema).optional(),\n }),\n)\nexport type StoreCategory = {\n label: string\n href: string\n children?: StoreCategory[]\n emphasis?: boolean\n}\n\nexport const utilityLinkSchema = z.object({\n label: z.string(),\n href: z.string(),\n})\nexport type UtilityLink = z.infer<typeof utilityLinkSchema>\n\nexport const navCartItemSchema = z.object({\n id: z.string(),\n name: z.string(),\n variant: z.string().optional(),\n price: z.number(),\n qty: z.number().int().min(0),\n /** Optional; absent images are omitted, never serialized as `\"\"`. */\n image: z.string().optional(),\n})\nexport type NavCartItem = z.infer<typeof navCartItemSchema>\n\nexport const storeUserSchema = z.object({\n name: z.string(),\n email: z.string(),\n avatar: z.string(),\n initials: z.string(),\n})\nexport type StoreUser = z.infer<typeof storeUserSchema>\n\nexport const accountMenuItemSchema = z.object({\n label: z.string(),\n href: z.string(),\n description: z.string().optional(),\n icon: z.enum(['orders', 'wishlist', 'addresses', 'settings']).optional(),\n})\nexport type AccountMenuItem = z.infer<typeof accountMenuItemSchema>\n\n/**\n * Serialization contract for a payment method offered at checkout.\n *\n * A projection of `@thorprovider/types` `PaymentMethod` — the storefront-facing\n * shape produced by the adapters transform. Checkout blocks render the method\n * list from this data instead of hardcoding providers.\n */\nexport const paymentMethodSchema: z.ZodType<PaymentMethod> = z.object({\n id: z.string(),\n name: z.string(),\n type: z.enum([\n 'card',\n 'wallet',\n 'bank_transfer',\n 'buy_now_pay_later',\n 'cash_on_delivery',\n 'crypto',\n 'other',\n ]),\n isEnabled: z.boolean(),\n requiresSetup: z.boolean(),\n description: z.string().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n features: z\n .object({\n saveForLater: z.boolean().optional(),\n requires3DS: z.boolean().optional(),\n supportsRefunds: z.boolean().optional(),\n minAmount: z.number().optional(),\n maxAmount: z.number().optional(),\n supportedCurrencies: z.array(z.string()).optional(),\n requiresVerification: z.boolean().optional(),\n })\n .optional(),\n logo: z.string().optional(),\n})\n\n// ---------------------------------------------------------------------------\n// Block prop schemas\n// ---------------------------------------------------------------------------\n\nexport const buttonPropsSchema = z.object({\n variant: z\n .enum(['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'])\n .optional(),\n size: z\n .enum(['default', 'xs', 'sm', 'lg', 'icon', 'icon-xs', 'icon-sm', 'icon-lg'])\n .optional(),\n disabled: z.boolean().optional(),\n className: z.string().optional(),\n})\nexport type ButtonSpecProps = z.infer<typeof buttonPropsSchema>\n\nexport const storeContainer1PropsSchema = z.object({\n size: z.enum(['default', 'narrow', 'wide', 'full']).optional(),\n className: z.string().optional(),\n})\nexport type StoreContainer1SpecProps = z.infer<typeof storeContainer1PropsSchema>\n\nexport const productCard1PropsSchema = z.object({\n products: z.array(cardProductSchema),\n heading: z.string().optional(),\n maxItems: z.number().int().positive().optional(),\n})\nexport type ProductCard1SpecProps = z.infer<typeof productCard1PropsSchema>\n\nexport const shoppingCart4PropsSchema = z.object({\n items: z.array(cartItemSchema),\n promotions: z.array(promotionSchema).default([]),\n recentlyViewed: z.array(recentlyViewedItemSchema).optional(),\n /** Backend-authoritative totals; rendered verbatim, never computed locally. */\n totals: cartTotalsSchema.optional(),\n totalsState: z.enum(['idle', 'pending', 'error']).optional(),\n totalsErrorLabel: z.string().optional(),\n totalsUnavailableLabel: z.string().optional(),\n amountUnavailableLabel: z.string().optional(),\n retryLabel: z.string().optional(),\n taxRate: z.number().optional(),\n titleLabel: z.string().optional(),\n subtotalLabel: z.string().optional(),\n discountLabel: z.string().optional(),\n taxLabel: z.string().optional(),\n shippingLabel: z.string().optional(),\n shippingFreeLabel: z.string().optional(),\n totalLabel: z.string().optional(),\n totalsPendingLabel: z.string().optional(),\n awayFromLabel: z.string().optional(),\n activatedLabel: z.string().optional(),\n itemsLabel: z.string().optional(),\n expressCheckoutLabel: z.string().optional(),\n expressLabel: z.string().optional(),\n standardLabel: z.string().optional(),\n estimatedDeliveryLabel: z.string().optional(),\n secureCheckoutLabel: z.string().optional(),\n recentlyViewedLabel: z.string().optional(),\n collapseLabel: z.string().optional(),\n expandLabel: z.string().optional(),\n checkoutLabel: z.string().optional(),\n quickCheckoutLabel: z.string().optional(),\n checkoutNowLabel: z.string().optional(),\n emptyLabel: z.string().optional(),\n removeLabel: z.string().optional(),\n decreaseLabel: z.string().optional(),\n increaseLabel: z.string().optional(),\n})\nexport type ShoppingCart4SpecProps = z.infer<typeof shoppingCart4PropsSchema>\n\n/**\n * Optional brand seams shared by the existing blocks. These mirror the\n * components' optional props as a serializable subset (no React): an accent\n * marks a trailing segment of the wordmark/brand name, and an editorial image\n * is a native `<img>` source. Omitting each keeps the block's current output.\n */\nexport const logoAccentSchema = z.object({\n text: z.string(),\n className: z.string().optional(),\n})\nexport type LogoAccent = z.infer<typeof logoAccentSchema>\n\nexport const brandAccentSchema = z.object({\n text: z.string(),\n className: z.string().optional(),\n})\nexport type BrandAccent = z.infer<typeof brandAccentSchema>\n\nexport const heroImageSchema = z.object({\n src: z.string(),\n alt: z.string(),\n})\nexport type HeroImage = z.infer<typeof heroImageSchema>\n\nexport const navbar4PropsSchema = z.object({\n storeCategories: z.array(storeCategorySchema),\n shippingNote: z.string(),\n utilityLinks: z.array(utilityLinkSchema),\n logoText: z.string(),\n logoHref: z.string().optional(),\n /** Optional accent for a trailing segment of `logoText`; omitted → unchanged. */\n logoAccent: logoAccentSchema.optional(),\n /**\n * Consumer-supplied classes merged onto the block's outer surface as the\n * final argument (e.g. a semantic surface token). Omitted → unchanged.\n */\n className: z.string().optional(),\n accountHref: z.string().optional(),\n searchQuery: z.string().optional(),\n items: z.array(navCartItemSchema).optional(),\n /** Authoritative backend subtotal for the mini-cart. */\n subtotal: moneySchema.optional(),\n subtotalUnavailableLabel: z.string().optional(),\n user: storeUserSchema.optional(),\n accountMenuItems: z.array(accountMenuItemSchema).optional(),\n})\nexport type Navbar4SpecProps = z.infer<typeof navbar4PropsSchema>\n\n/**\n * Serializable contracts for the two bounded editorial sections (`faq`, `cta`).\n * These are the serializable subset of `FaqBlock1Props`/`CtaSection1Props`: the\n * React-only callbacks (`onPrimaryClick`/`onSecondaryClick`) are intentionally\n * excluded — the catalog adapter binds them (see design D3).\n */\nexport const ctaSchema = z.object({\n label: z.string(),\n href: z.string().optional(),\n})\nexport type CtaSpec = z.infer<typeof ctaSchema>\n\nexport const faqItemSchema = z.object({\n value: z.string(),\n question: z.string(),\n answer: z.string(),\n})\nexport type FaqItemSpec = z.infer<typeof faqItemSchema>\n\nexport const faqBlock1PropsSchema = z.object({\n items: z.array(faqItemSchema),\n title: z.string().optional(),\n description: z.string().optional(),\n accordionType: z.enum(['single', 'multiple']).optional(),\n})\nexport type FaqBlock1SpecProps = z.infer<typeof faqBlock1PropsSchema>\n\nexport const ctaSection1PropsSchema = z.object({\n badgeText: z.string().optional(),\n title: z.string().optional(),\n description: z.string().optional(),\n primaryCta: ctaSchema.optional(),\n secondaryCta: ctaSchema.optional(),\n})\nexport type CtaSection1SpecProps = z.infer<typeof ctaSection1PropsSchema>\n\n// Pure monetary helpers, re-exported from the server-safe contracts entry so a\n// host boundary can validate/format `Money` without importing the React entry.\nexport { isCompleteFiniteDecimal, isAvailableMoney, formatMoney } from './money'\nexport { resolveAbsoluteQuantity } from './cart'","import type { Money } from '@thorprovider/types'\n\n/**\n * Pure monetary helpers shared by the curated commerce blocks.\n *\n * Canonical `Money` (`@thorprovider/types`) carries a decimal string in major\n * units (e.g. `\"69.99\"`) — never cents. A missing, invalid, or unresolved\n * amount is *unavailable*, which is a different state from a legitimate `\"0\"`.\n * These helpers keep that distinction in the presentation layer: they validate\n * a complete finite decimal before any display conversion, and they never\n * invent a currency.\n */\n\nconst COMPLETE_FINITE_DECIMAL = /^-?(?:\\d+)(?:\\.\\d+)?$/\n\n/**\n * True only for a complete, finite decimal string (`\"0\"`, `\"69.99\"`, `\"-3.5\"`).\n * Rejects `\"\"`, `\" \"`, `\"1.\"`, `\".5\"`, `\"1e3\"`, `\"abc\"`, and non-strings.\n */\nexport function isCompleteFiniteDecimal(value: unknown): value is string {\n if (typeof value !== 'string') return false\n const trimmed = value.trim()\n if (trimmed === '' || !COMPLETE_FINITE_DECIMAL.test(trimmed)) return false\n return Number.isFinite(Number(trimmed))\n}\n\n/**\n * True when a `Money` value is safe to display and price a purchase with: a\n * complete finite decimal amount and a non-empty currency code. A valid zero\n * (`{ amount: '0', currencyCode: 'USD' }`) is available — zero alone does not\n * make a price unavailable.\n */\nexport function isAvailableMoney(money: Money | null | undefined): money is Money {\n return (\n !!money &&\n typeof money.currencyCode === 'string' &&\n money.currencyCode.trim() !== '' &&\n isCompleteFiniteDecimal(money.amount)\n )\n}\n\n/**\n * Formats a `Money` for display, or `null` when the value is unavailable.\n *\n * This is the safety gate the whole curated surface relies on: an absent or\n * malformed amount (`\"\"`, `\" \"`, `\"abc\"`, a non-string, a missing currency)\n * MUST NOT reach `Intl.NumberFormat`, because `Number(\"\")` is `0` and would\n * render a fake `$0.00` for missing data. A backend-resolved valid zero\n * (`{ amount: \"0\", currencyCode: \"USD\" }`) is available and formats as zero.\n *\n * When no `formatter` is supplied the currency comes from\n * `money.currencyCode`, so no currency is hardcoded. Callers render their own\n * unavailable/pending/recovery state when this returns `null`.\n */\nexport function formatMoney(\n money: Money | null | undefined,\n formatter?: (money: Money) => string,\n): string | null {\n if (!isAvailableMoney(money)) return null\n if (formatter) return formatter(money)\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: money.currencyCode,\n }).format(Number(money.amount))\n}\n","/**\n * Cart line quantity normalization shared by the curated cart surfaces.\n *\n * The Navbar mini-cart expresses a quantity DELTA while the cart page reports\n * an ABSOLUTE quantity. The boundary normalizes the delta against the\n * authoritative current line quantity (from the backend cart) before mutating;\n * it must never reinterpret one as the other.\n */\nexport function resolveAbsoluteQuantity(currentQuantity: number, delta: number): number {\n return Math.max(1, currentQuantity + delta)\n}\n"],"mappings":"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","names":[]}
@@ -1,10 +1,90 @@
1
- 'use client';
2
-
3
1
  // src/lib/contracts.ts
4
2
  import { z } from "zod";
3
+
4
+ // src/lib/money.ts
5
+ var COMPLETE_FINITE_DECIMAL = /^-?(?:\d+)(?:\.\d+)?$/;
6
+ function isCompleteFiniteDecimal(value) {
7
+ if (typeof value !== "string") return false;
8
+ const trimmed = value.trim();
9
+ if (trimmed === "" || !COMPLETE_FINITE_DECIMAL.test(trimmed)) return false;
10
+ return Number.isFinite(Number(trimmed));
11
+ }
12
+ function isAvailableMoney(money) {
13
+ return !!money && typeof money.currencyCode === "string" && money.currencyCode.trim() !== "" && isCompleteFiniteDecimal(money.amount);
14
+ }
15
+ function formatMoney(money, formatter) {
16
+ if (!isAvailableMoney(money)) return null;
17
+ if (formatter) return formatter(money);
18
+ return new Intl.NumberFormat(void 0, {
19
+ style: "currency",
20
+ currency: money.currencyCode
21
+ }).format(Number(money.amount));
22
+ }
23
+
24
+ // src/lib/cart.ts
25
+ function resolveAbsoluteQuantity(currentQuantity, delta) {
26
+ return Math.max(1, currentQuantity + delta);
27
+ }
28
+
29
+ // src/lib/contracts.ts
5
30
  var PRODUCT_IMAGE_FALLBACK = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(
6
31
  `<svg xmlns='http://www.w3.org/2000/svg' width='600' height='600'><rect width='100%' height='100%' fill='#e2e8f0'/><g fill='none' stroke='#94a3b8' stroke-width='12' stroke-linecap='round'><path d='M150 300h300M300 150v300'/><path d='M220 230h50M220 300h50M220 370h50'/><path d='M330 230h50M330 300h50M330 370h50'/></g></svg>`
7
32
  );
33
+ var moneySchema = z.object({
34
+ amount: z.string(),
35
+ currencyCode: z.string()
36
+ });
37
+ var imageSchema = z.object({
38
+ id: z.string().optional(),
39
+ url: z.string(),
40
+ altText: z.string(),
41
+ width: z.number().optional(),
42
+ height: z.number().optional()
43
+ });
44
+ var selectedOptionSchema = z.object({
45
+ name: z.string(),
46
+ value: z.string()
47
+ });
48
+ var productDetailVariantSchema = z.object({
49
+ id: z.string(),
50
+ title: z.string(),
51
+ sku: z.string().optional(),
52
+ availableForSale: z.boolean(),
53
+ selectedOptions: z.array(selectedOptionSchema),
54
+ /** Omit or null when the backend has no resolved price (purchase disabled). */
55
+ price: moneySchema.nullish(),
56
+ compareAtPrice: moneySchema.optional(),
57
+ image: imageSchema.optional()
58
+ });
59
+ var productDetailProductSchema = z.object({
60
+ id: z.string(),
61
+ handle: z.string().optional(),
62
+ title: z.string(),
63
+ description: z.string().optional(),
64
+ vendor: z.string().optional(),
65
+ options: z.array(z.object({ id: z.string(), name: z.string(), values: z.array(z.string()) })),
66
+ variants: z.array(productDetailVariantSchema),
67
+ images: z.array(imageSchema),
68
+ featuredImage: imageSchema.optional()
69
+ });
70
+ var productOverview1PropsSchema = z.object({
71
+ product: productDetailProductSchema,
72
+ selectedVariantId: z.string().nullable().optional(),
73
+ quantity: z.number().optional(),
74
+ rating: z.object({ value: z.number(), count: z.number() }).optional(),
75
+ deliveryEstimate: z.string().optional(),
76
+ shipping: moneySchema.optional(),
77
+ sizesLabel: z.string().optional(),
78
+ variantLabel: z.string().optional(),
79
+ reviewsLabel: z.string().optional(),
80
+ addToCartLabel: z.string().optional(),
81
+ outOfStockLabel: z.string().optional(),
82
+ unavailablePriceLabel: z.string().optional(),
83
+ selectVariantLabel: z.string().optional(),
84
+ quantityLabel: z.string().optional(),
85
+ deliveryEstimateLabel: z.string().optional(),
86
+ shippingLabel: z.string().optional()
87
+ });
8
88
  var cardProductSchema = z.object({
9
89
  id: z.union([z.string(), z.number()]),
10
90
  title: z.string(),
@@ -18,8 +98,20 @@ var cartItemSchema = z.object({
18
98
  name: z.string(),
19
99
  price: z.number(),
20
100
  quantity: z.number().int().min(0),
21
- image: z.string(),
22
- variant: z.string().optional()
101
+ /** Optional; absent images are omitted, never serialized as `""`. */
102
+ image: z.string().optional(),
103
+ variant: z.string().optional(),
104
+ /** Canonical backend per-unit price, preferred for display when present. */
105
+ unitPrice: moneySchema.optional(),
106
+ /** Canonical backend line total, preferred over `price * quantity`. */
107
+ lineTotal: moneySchema.optional()
108
+ });
109
+ var cartTotalsSchema = z.object({
110
+ subtotal: moneySchema,
111
+ discount: moneySchema.optional(),
112
+ tax: moneySchema.optional(),
113
+ shipping: moneySchema.optional(),
114
+ total: moneySchema
23
115
  });
24
116
  var recentlyViewedItemSchema = z.object({
25
117
  id: z.string(),
@@ -50,10 +142,11 @@ var utilityLinkSchema = z.object({
50
142
  var navCartItemSchema = z.object({
51
143
  id: z.string(),
52
144
  name: z.string(),
53
- variant: z.string(),
145
+ variant: z.string().optional(),
54
146
  price: z.number(),
55
147
  qty: z.number().int().min(0),
56
- image: z.string()
148
+ /** Optional; absent images are omitted, never serialized as `""`. */
149
+ image: z.string().optional()
57
150
  });
58
151
  var storeUserSchema = z.object({
59
152
  name: z.string(),
@@ -113,13 +206,22 @@ var shoppingCart4PropsSchema = z.object({
113
206
  items: z.array(cartItemSchema),
114
207
  promotions: z.array(promotionSchema).default([]),
115
208
  recentlyViewed: z.array(recentlyViewedItemSchema).optional(),
209
+ /** Backend-authoritative totals; rendered verbatim, never computed locally. */
210
+ totals: cartTotalsSchema.optional(),
211
+ totalsState: z.enum(["idle", "pending", "error"]).optional(),
212
+ totalsErrorLabel: z.string().optional(),
213
+ totalsUnavailableLabel: z.string().optional(),
214
+ amountUnavailableLabel: z.string().optional(),
215
+ retryLabel: z.string().optional(),
116
216
  taxRate: z.number().optional(),
117
217
  titleLabel: z.string().optional(),
118
218
  subtotalLabel: z.string().optional(),
219
+ discountLabel: z.string().optional(),
119
220
  taxLabel: z.string().optional(),
120
221
  shippingLabel: z.string().optional(),
121
222
  shippingFreeLabel: z.string().optional(),
122
223
  totalLabel: z.string().optional(),
224
+ totalsPendingLabel: z.string().optional(),
123
225
  awayFromLabel: z.string().optional(),
124
226
  activatedLabel: z.string().optional(),
125
227
  itemsLabel: z.string().optional(),
@@ -133,7 +235,23 @@ var shoppingCart4PropsSchema = z.object({
133
235
  expandLabel: z.string().optional(),
134
236
  checkoutLabel: z.string().optional(),
135
237
  quickCheckoutLabel: z.string().optional(),
136
- checkoutNowLabel: z.string().optional()
238
+ checkoutNowLabel: z.string().optional(),
239
+ emptyLabel: z.string().optional(),
240
+ removeLabel: z.string().optional(),
241
+ decreaseLabel: z.string().optional(),
242
+ increaseLabel: z.string().optional()
243
+ });
244
+ var logoAccentSchema = z.object({
245
+ text: z.string(),
246
+ className: z.string().optional()
247
+ });
248
+ var brandAccentSchema = z.object({
249
+ text: z.string(),
250
+ className: z.string().optional()
251
+ });
252
+ var heroImageSchema = z.object({
253
+ src: z.string(),
254
+ alt: z.string()
137
255
  });
138
256
  var navbar4PropsSchema = z.object({
139
257
  storeCategories: z.array(storeCategorySchema),
@@ -141,24 +259,72 @@ var navbar4PropsSchema = z.object({
141
259
  utilityLinks: z.array(utilityLinkSchema),
142
260
  logoText: z.string(),
143
261
  logoHref: z.string().optional(),
262
+ /** Optional accent for a trailing segment of `logoText`; omitted → unchanged. */
263
+ logoAccent: logoAccentSchema.optional(),
264
+ /**
265
+ * Consumer-supplied classes merged onto the block's outer surface as the
266
+ * final argument (e.g. a semantic surface token). Omitted → unchanged.
267
+ */
268
+ className: z.string().optional(),
144
269
  accountHref: z.string().optional(),
145
270
  searchQuery: z.string().optional(),
146
271
  items: z.array(navCartItemSchema).optional(),
272
+ /** Authoritative backend subtotal for the mini-cart. */
273
+ subtotal: moneySchema.optional(),
274
+ subtotalUnavailableLabel: z.string().optional(),
147
275
  user: storeUserSchema.optional(),
148
276
  accountMenuItems: z.array(accountMenuItemSchema).optional()
149
277
  });
278
+ var ctaSchema = z.object({
279
+ label: z.string(),
280
+ href: z.string().optional()
281
+ });
282
+ var faqItemSchema = z.object({
283
+ value: z.string(),
284
+ question: z.string(),
285
+ answer: z.string()
286
+ });
287
+ var faqBlock1PropsSchema = z.object({
288
+ items: z.array(faqItemSchema),
289
+ title: z.string().optional(),
290
+ description: z.string().optional(),
291
+ accordionType: z.enum(["single", "multiple"]).optional()
292
+ });
293
+ var ctaSection1PropsSchema = z.object({
294
+ badgeText: z.string().optional(),
295
+ title: z.string().optional(),
296
+ description: z.string().optional(),
297
+ primaryCta: ctaSchema.optional(),
298
+ secondaryCta: ctaSchema.optional()
299
+ });
150
300
  export {
151
301
  PRODUCT_IMAGE_FALLBACK,
152
302
  accountMenuItemSchema,
303
+ brandAccentSchema,
153
304
  buttonPropsSchema,
154
305
  cardProductSchema,
155
306
  cartItemSchema,
307
+ cartTotalsSchema,
308
+ ctaSchema,
309
+ ctaSection1PropsSchema,
310
+ faqBlock1PropsSchema,
311
+ faqItemSchema,
312
+ formatMoney,
313
+ heroImageSchema,
314
+ isAvailableMoney,
315
+ isCompleteFiniteDecimal,
316
+ logoAccentSchema,
317
+ moneySchema,
156
318
  navCartItemSchema,
157
319
  navbar4PropsSchema,
158
320
  paymentMethodSchema,
159
321
  productCard1PropsSchema,
322
+ productDetailProductSchema,
323
+ productDetailVariantSchema,
324
+ productOverview1PropsSchema,
160
325
  promotionSchema,
161
326
  recentlyViewedItemSchema,
327
+ resolveAbsoluteQuantity,
162
328
  shoppingCart4PropsSchema,
163
329
  storeCategorySchema,
164
330
  storeContainer1PropsSchema,