@doswiftly/storefront-sdk 14.0.0 → 16.0.0

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 (42) hide show
  1. package/CHANGELOG.md +294 -0
  2. package/README.md +28 -0
  3. package/dist/core/auth/auth-client.d.ts +13 -1
  4. package/dist/core/auth/auth-client.d.ts.map +1 -1
  5. package/dist/core/auth/auth-client.js +16 -1
  6. package/dist/core/auth/types.d.ts +18 -52
  7. package/dist/core/auth/types.d.ts.map +1 -1
  8. package/dist/core/auth/types.js +0 -3
  9. package/dist/core/cart/cart-client.d.ts +50 -1
  10. package/dist/core/cart/cart-client.d.ts.map +1 -1
  11. package/dist/core/cart/cart-client.js +63 -1
  12. package/dist/core/cart/types.d.ts +75 -391
  13. package/dist/core/cart/types.d.ts.map +1 -1
  14. package/dist/core/cart/types.js +0 -8
  15. package/dist/core/format.d.ts +81 -46
  16. package/dist/core/format.d.ts.map +1 -1
  17. package/dist/core/format.js +116 -94
  18. package/dist/core/generated/operation-types.d.ts +4486 -0
  19. package/dist/core/generated/operation-types.d.ts.map +1 -0
  20. package/dist/core/generated/operation-types.js +4 -0
  21. package/dist/core/index.d.ts +3 -3
  22. package/dist/core/index.d.ts.map +1 -1
  23. package/dist/core/index.js +1 -1
  24. package/dist/core/operations/auth.d.ts +7 -0
  25. package/dist/core/operations/auth.d.ts.map +1 -1
  26. package/dist/core/operations/auth.js +68 -0
  27. package/dist/core/operations/cart.d.ts +30 -0
  28. package/dist/core/operations/cart.d.ts.map +1 -1
  29. package/dist/core/operations/cart.js +158 -0
  30. package/dist/react/components/Money.d.ts +22 -8
  31. package/dist/react/components/Money.d.ts.map +1 -1
  32. package/dist/react/components/Money.js +16 -9
  33. package/dist/react/hooks/use-cart.d.ts +78 -0
  34. package/dist/react/hooks/use-cart.d.ts.map +1 -0
  35. package/dist/react/hooks/use-cart.js +121 -0
  36. package/dist/react/hooks/use-format.d.ts +85 -0
  37. package/dist/react/hooks/use-format.d.ts.map +1 -0
  38. package/dist/react/hooks/use-format.js +141 -0
  39. package/dist/react/index.d.ts +2 -0
  40. package/dist/react/index.d.ts.map +1 -1
  41. package/dist/react/index.js +3 -0
  42. package/package.json +7 -1
@@ -12,12 +12,42 @@
12
12
  * Drift detection: PreToolUse hook validuje strings przeciwko storefront-operations/schema.graphql.
13
13
  */
14
14
  export declare const CART_QUERY: string;
15
+ /**
16
+ * cartAvailableShippingMethods — cart-aware shipping discovery for the
17
+ * checkout shipping step. Backend uses the cart's contents (subtotal,
18
+ * physical-item weight) and surfaces a `DIGITAL_ONLY_NO_SHIPPING` userError
19
+ * when the cart has no shippable items. Each method carries `deliveryType`
20
+ * so the storefront can render a pickup-point / locker picker without
21
+ * inferring the type from the method name.
22
+ */
23
+ export declare const CART_AVAILABLE_SHIPPING_METHODS_QUERY: string;
24
+ /**
25
+ * availablePaymentMethods — shop-level list of active payment methods
26
+ * (default + sorted by merchant position). Independent of cart contents
27
+ * today; storefront fetches once for the checkout payment step.
28
+ */
29
+ export declare const AVAILABLE_PAYMENT_METHODS_QUERY: string;
30
+ /**
31
+ * orderByToken — guest order lookup by opaque access token (returned by
32
+ * `cartComplete.order.accessToken`). Optional `email` adds defense-in-depth:
33
+ * if supplied it must match the order's buyer email case-insensitively, else
34
+ * the query returns `null` (same shape as an invalid token). Rate-limited by
35
+ * the backend (5/min per IP+shop); no cache (`Cache-Control: no-store`).
36
+ */
37
+ export declare const ORDER_BY_TOKEN_QUERY: string;
15
38
  export declare const CART_CREATE: string;
16
39
  export declare const CART_ADD_LINES: string;
17
40
  export declare const CART_UPDATE_LINES: string;
18
41
  export declare const CART_REMOVE_LINES: string;
19
42
  export declare const CART_DISCOUNT_CODES_UPDATE: string;
20
43
  export declare const CART_UPDATE_NOTE: string;
44
+ /**
45
+ * cartUpdateAttributes — replaces (NOT merges) the cart's custom `[{ key, value }]`
46
+ * attribute pairs. Free-form metadata visible to the merchant in admin: delivery
47
+ * instructions, gift-wrap flags, B2B PO numbers. Backend limit: 250 pairs / 255-char
48
+ * keys (rejected with `CART_ATTRIBUTES_LIMIT_EXCEEDED` userError).
49
+ */
50
+ export declare const CART_UPDATE_ATTRIBUTES: string;
21
51
  export declare const CART_UPDATE_BUYER_IDENTITY: string;
22
52
  export declare const CART_SET_SHIPPING_ADDRESS: string;
23
53
  export declare const CART_SET_BILLING_ADDRESS: string;
@@ -1 +1 @@
1
- {"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAsSH,eAAO,MAAM,UAAU,QAOrB,CAAC;AAMH,eAAO,MAAM,WAAW,QAWtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAW3B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAMH,eAAO,MAAM,yBAAyB,QAWpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH,eAAO,MAAM,2BAA2B,QAWtC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,oBAAoB,QAW/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAWhC,CAAC;AAEH,eAAO,MAAM,+BAA+B,QAW1C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAWxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QASzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,QAoBtC,CAAC"}
1
+ {"version":3,"file":"cart.d.ts","sourceRoot":"","sources":["../../../src/core/operations/cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqYH,eAAO,MAAM,UAAU,QAOrB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,qCAAqC,QAehD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,QAO1C,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAO/B,CAAC;AAMH,eAAO,MAAM,WAAW,QAWtB,CAAC;AAEH,eAAO,MAAM,cAAc,QAWzB,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,iBAAiB,QAW5B,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,gBAAgB,QAW3B,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAWjC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAMH,eAAO,MAAM,yBAAyB,QAWpC,CAAC;AAEH,eAAO,MAAM,wBAAwB,QAWnC,CAAC;AAEH,eAAO,MAAM,2BAA2B,QAWtC,CAAC;AAEH,eAAO,MAAM,0BAA0B,QAWrC,CAAC;AAEH,eAAO,MAAM,oBAAoB,QAW/B,CAAC;AAEH,eAAO,MAAM,qBAAqB,QAWhC,CAAC;AAEH,eAAO,MAAM,+BAA+B,QAW1C,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,QAWxB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QASzB,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,QAoBtC,CAAC"}
@@ -74,6 +74,8 @@ const CART_COST_FRAGMENT = `
74
74
  totalTax { ...Money }
75
75
  totalDuty { ...Money }
76
76
  checkoutCharge { ...Money }
77
+ totalDiscount { ...Money }
78
+ totalShipping { ...Money }
77
79
  }
78
80
  `;
79
81
  const CART_LINE_COST_FRAGMENT = `
@@ -138,6 +140,14 @@ const CART_DISCOUNT_ALLOCATION_FRAGMENT = `
138
140
  amount { ...Money }
139
141
  }
140
142
  `;
143
+ const PICKUP_POINT_FRAGMENT = `
144
+ fragment PickupPoint on PickupPoint {
145
+ provider
146
+ pointId
147
+ name
148
+ address
149
+ }
150
+ `;
141
151
  const MAILING_ADDRESS_FRAGMENT = `
142
152
  fragment MailingAddress on MailingAddress {
143
153
  id
@@ -155,7 +165,11 @@ const MAILING_ADDRESS_FRAGMENT = `
155
165
  postalCode
156
166
  phone
157
167
  isDefault
168
+ taxId
169
+ vatNumber
170
+ pickupPoint { ...PickupPoint }
158
171
  }
172
+ ${PICKUP_POINT_FRAGMENT}
159
173
  `;
160
174
  const CART_SHIPPING_METHOD_FRAGMENT = `
161
175
  fragment CartShippingMethod on CartShippingMethod {
@@ -166,6 +180,7 @@ const CART_SHIPPING_METHOD_FRAGMENT = `
166
180
  `;
167
181
  const CART_APPLIED_GIFT_CARD_FRAGMENT = `
168
182
  fragment CartAppliedGiftCard on CartAppliedGiftCard {
183
+ id
169
184
  maskedCode
170
185
  lastCharacters
171
186
  appliedAmount { ...Money }
@@ -211,6 +226,15 @@ const CART_FRAGMENT = `
211
226
  requiresShipping
212
227
  createdAt
213
228
  updatedAt
229
+ status
230
+ completedOrder {
231
+ id
232
+ orderNumber
233
+ accessToken
234
+ status
235
+ paymentStatus
236
+ fulfillmentStatus
237
+ }
214
238
  }
215
239
  ${CART_COST_FRAGMENT}
216
240
  ${CART_LINE_FRAGMENT}
@@ -263,6 +287,70 @@ const CART_WARNING_FRAGMENT = `
263
287
  target
264
288
  }
265
289
  `;
290
+ const SHIPPING_CARRIER_FRAGMENT = `
291
+ fragment ShippingCarrier on ShippingCarrier {
292
+ id
293
+ name
294
+ logoUrl
295
+ serviceCode
296
+ }
297
+ `;
298
+ const DELIVERY_ESTIMATE_FRAGMENT = `
299
+ fragment DeliveryEstimate on DeliveryEstimate {
300
+ minDays
301
+ maxDays
302
+ description
303
+ }
304
+ `;
305
+ const FREE_SHIPPING_PROGRESS_FRAGMENT = `
306
+ fragment FreeShippingProgress on FreeShippingProgress {
307
+ qualifies
308
+ currentAmount { ...Money }
309
+ threshold { ...Money }
310
+ remaining { ...Money }
311
+ progressPercent
312
+ message
313
+ }
314
+ ${MONEY_FRAGMENT}
315
+ `;
316
+ const AVAILABLE_SHIPPING_METHOD_FRAGMENT = `
317
+ fragment AvailableShippingMethod on AvailableShippingMethod {
318
+ id
319
+ name
320
+ description
321
+ deliveryType
322
+ carrier { ...ShippingCarrier }
323
+ price { ...Money }
324
+ isFree
325
+ estimatedDelivery { ...DeliveryEstimate }
326
+ freeShippingProgress { ...FreeShippingProgress }
327
+ sortOrder
328
+ }
329
+ ${SHIPPING_CARRIER_FRAGMENT}
330
+ ${MONEY_FRAGMENT}
331
+ ${DELIVERY_ESTIMATE_FRAGMENT}
332
+ ${FREE_SHIPPING_PROGRESS_FRAGMENT}
333
+ `;
334
+ const PAYMENT_METHOD_FRAGMENT = `
335
+ fragment PaymentMethod on PaymentMethod {
336
+ id
337
+ name
338
+ provider
339
+ type
340
+ icon
341
+ description
342
+ isDefault
343
+ supportedCurrencies
344
+ position
345
+ }
346
+ `;
347
+ const AVAILABLE_PAYMENT_METHODS_FRAGMENT = `
348
+ fragment AvailablePaymentMethods on AvailablePaymentMethods {
349
+ methods { ...PaymentMethod }
350
+ defaultMethod { ...PaymentMethod }
351
+ }
352
+ ${PAYMENT_METHOD_FRAGMENT}
353
+ `;
266
354
  const PAYMENT_SESSION_FRAGMENT = `
267
355
  fragment PaymentSession on PaymentSession {
268
356
  id
@@ -286,6 +374,58 @@ export const CART_QUERY = composeOperation(`
286
374
  }
287
375
  ${CART_FRAGMENT}
288
376
  `);
377
+ /**
378
+ * cartAvailableShippingMethods — cart-aware shipping discovery for the
379
+ * checkout shipping step. Backend uses the cart's contents (subtotal,
380
+ * physical-item weight) and surfaces a `DIGITAL_ONLY_NO_SHIPPING` userError
381
+ * when the cart has no shippable items. Each method carries `deliveryType`
382
+ * so the storefront can render a pickup-point / locker picker without
383
+ * inferring the type from the method name.
384
+ */
385
+ export const CART_AVAILABLE_SHIPPING_METHODS_QUERY = composeOperation(`
386
+ query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
387
+ cart(id: $cartId) {
388
+ id
389
+ requiresShipping
390
+ availableShippingMethods(address: $address) {
391
+ methods { ...AvailableShippingMethod }
392
+ freeShippingProgress { ...FreeShippingProgress }
393
+ userErrors { ...UserError }
394
+ }
395
+ }
396
+ }
397
+ ${AVAILABLE_SHIPPING_METHOD_FRAGMENT}
398
+ ${FREE_SHIPPING_PROGRESS_FRAGMENT}
399
+ ${USER_ERROR_FRAGMENT}
400
+ `);
401
+ /**
402
+ * availablePaymentMethods — shop-level list of active payment methods
403
+ * (default + sorted by merchant position). Independent of cart contents
404
+ * today; storefront fetches once for the checkout payment step.
405
+ */
406
+ export const AVAILABLE_PAYMENT_METHODS_QUERY = composeOperation(`
407
+ query AvailablePaymentMethods {
408
+ availablePaymentMethods {
409
+ ...AvailablePaymentMethods
410
+ }
411
+ }
412
+ ${AVAILABLE_PAYMENT_METHODS_FRAGMENT}
413
+ `);
414
+ /**
415
+ * orderByToken — guest order lookup by opaque access token (returned by
416
+ * `cartComplete.order.accessToken`). Optional `email` adds defense-in-depth:
417
+ * if supplied it must match the order's buyer email case-insensitively, else
418
+ * the query returns `null` (same shape as an invalid token). Rate-limited by
419
+ * the backend (5/min per IP+shop); no cache (`Cache-Control: no-store`).
420
+ */
421
+ export const ORDER_BY_TOKEN_QUERY = composeOperation(`
422
+ query OrderByToken($token: String!, $email: String) {
423
+ orderByToken(token: $token, email: $email) {
424
+ ...Order
425
+ }
426
+ }
427
+ ${ORDER_FRAGMENT}
428
+ `);
289
429
  // ---------------------------------------------------------------------------
290
430
  // Mutations
291
431
  // ---------------------------------------------------------------------------
@@ -361,6 +501,24 @@ export const CART_UPDATE_NOTE = composeOperation(`
361
501
  ${USER_ERROR_FRAGMENT}
362
502
  ${CART_WARNING_FRAGMENT}
363
503
  `);
504
+ /**
505
+ * cartUpdateAttributes — replaces (NOT merges) the cart's custom `[{ key, value }]`
506
+ * attribute pairs. Free-form metadata visible to the merchant in admin: delivery
507
+ * instructions, gift-wrap flags, B2B PO numbers. Backend limit: 250 pairs / 255-char
508
+ * keys (rejected with `CART_ATTRIBUTES_LIMIT_EXCEEDED` userError).
509
+ */
510
+ export const CART_UPDATE_ATTRIBUTES = composeOperation(`
511
+ mutation CartUpdateAttributes($id: ID!, $attributes: [CartAttributeInput!]!) {
512
+ cartUpdateAttributes(id: $id, attributes: $attributes) {
513
+ cart { ...Cart }
514
+ userErrors { ...UserError }
515
+ warnings { ...CartWarning }
516
+ }
517
+ }
518
+ ${CART_FRAGMENT}
519
+ ${USER_ERROR_FRAGMENT}
520
+ ${CART_WARNING_FRAGMENT}
521
+ `);
364
522
  export const CART_UPDATE_BUYER_IDENTITY = composeOperation(`
365
523
  mutation CartUpdateBuyerIdentity($id: ID!, $buyerIdentity: CartBuyerIdentityInput!) {
366
524
  cartUpdateBuyerIdentity(id: $id, buyerIdentity: $buyerIdentity) {
@@ -3,16 +3,23 @@
3
3
  * formatted string. Wraps `formatPrice` from core so consumers don't need to
4
4
  * import the formatter manually.
5
5
  *
6
- * Headless: no styling, no currency context dependency. `currency` is required
7
- * render explicitly from your cart/product/shop data. This keeps the
8
- * component usable both inside `StorefrontProvider` and in isolated rendering
9
- * (server components, e-mail templates, PDFs).
6
+ * Headless: no styling, no Context dependency. `currency` is required
7
+ * render explicitly from your cart/product/shop data. `locale` is optional;
8
+ * pass it for deterministic output, or omit it to let the component fall
9
+ * back to the runtime default (`navigator.language` in browsers, `LANG` in
10
+ * Node). This keeps the component usable both inside `<StorefrontProvider>`
11
+ * and in isolated rendering (server components, e-mail templates, PDFs).
12
+ *
13
+ * Need the locale to follow the storefront's i18n state without passing it
14
+ * to every `<Money>`? Use `useFormatPrice()` from `@doswiftly/storefront-sdk/react`
15
+ * — it pulls the active language from `useLanguageStore` and returns a
16
+ * memoised `formatPrice` function.
10
17
  *
11
18
  * @example
12
19
  * ```tsx
13
- * <Money amount={9990} currency="PLN" /> // → "99,90 zł"
14
- * <Money amount={1295} currency="USD" /> // → "$12.95"
15
- * <Money amount={0} currency="EUR" /> // → "0,00 €"
20
+ * <Money amount={9990} currency="PLN" locale="pl-PL" /> // → "99,90 zł"
21
+ * <Money amount={1295} currency="USD" locale="en-US" /> // → "$12.95"
22
+ * <Money amount={0} currency="EUR" locale="de-DE" /> // → "0,00 €"
16
23
  * ```
17
24
  */
18
25
  export interface MoneyProps {
@@ -20,6 +27,13 @@ export interface MoneyProps {
20
27
  amount: number;
21
28
  /** ISO 4217 currency code (PLN, EUR, USD, …). Required — no implicit default. */
22
29
  currency: string;
30
+ /**
31
+ * BCP 47 locale tag (`'pl-PL'`, `'en-US'`, `'de-DE'`, …). Drives the symbol
32
+ * placement, decimal mark, and grouping separator. When omitted, the runtime
33
+ * default is used (`navigator.language` in browsers, `LANG` in Node) — pass
34
+ * an explicit locale for deterministic output.
35
+ */
36
+ locale?: string;
23
37
  /** Optional class for styling — keeps the component headless. */
24
38
  className?: string;
25
39
  /**
@@ -28,5 +42,5 @@ export interface MoneyProps {
28
42
  */
29
43
  as?: 'span' | 'div' | 'p' | 'strong' | 'em' | 'output';
30
44
  }
31
- export declare function Money({ amount, currency, className, as: As }: MoneyProps): import("react/jsx-runtime").JSX.Element;
45
+ export declare function Money({ amount, currency, locale, className, as: As }: MoneyProps): import("react/jsx-runtime").JSX.Element;
32
46
  //# sourceMappingURL=Money.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Money.d.ts","sourceRoot":"","sources":["../../../src/react/components/Money.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,GAAG,GAAG,QAAQ,GAAG,IAAI,GAAG,QAAQ,CAAC;CACxD;AAED,wBAAgB,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,EAAW,EAAE,EAAE,UAAU,2CAMjF"}
1
+ {"version":3,"file":"Money.d.ts","sourceRoot":"","sources":["../../../src/react/components/Money.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,GAAG,GAAG,QAAQ,GAAG,IAAI,GAAG,QAAQ,CAAC;CACxD;AAED,wBAAgB,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,EAAW,EAAE,EAAE,UAAU,2CASzF"}
@@ -3,25 +3,32 @@
3
3
  * formatted string. Wraps `formatPrice` from core so consumers don't need to
4
4
  * import the formatter manually.
5
5
  *
6
- * Headless: no styling, no currency context dependency. `currency` is required
7
- * render explicitly from your cart/product/shop data. This keeps the
8
- * component usable both inside `StorefrontProvider` and in isolated rendering
9
- * (server components, e-mail templates, PDFs).
6
+ * Headless: no styling, no Context dependency. `currency` is required
7
+ * render explicitly from your cart/product/shop data. `locale` is optional;
8
+ * pass it for deterministic output, or omit it to let the component fall
9
+ * back to the runtime default (`navigator.language` in browsers, `LANG` in
10
+ * Node). This keeps the component usable both inside `<StorefrontProvider>`
11
+ * and in isolated rendering (server components, e-mail templates, PDFs).
12
+ *
13
+ * Need the locale to follow the storefront's i18n state without passing it
14
+ * to every `<Money>`? Use `useFormatPrice()` from `@doswiftly/storefront-sdk/react`
15
+ * — it pulls the active language from `useLanguageStore` and returns a
16
+ * memoised `formatPrice` function.
10
17
  *
11
18
  * @example
12
19
  * ```tsx
13
- * <Money amount={9990} currency="PLN" /> // → "99,90 zł"
14
- * <Money amount={1295} currency="USD" /> // → "$12.95"
15
- * <Money amount={0} currency="EUR" /> // → "0,00 €"
20
+ * <Money amount={9990} currency="PLN" locale="pl-PL" /> // → "99,90 zł"
21
+ * <Money amount={1295} currency="USD" locale="en-US" /> // → "$12.95"
22
+ * <Money amount={0} currency="EUR" locale="de-DE" /> // → "0,00 €"
16
23
  * ```
17
24
  */
18
25
  'use client';
19
26
  import { jsx as _jsx } from "react/jsx-runtime";
20
27
  import { formatPrice } from '../../core/format';
21
- export function Money({ amount, currency, className, as: As = 'span' }) {
28
+ export function Money({ amount, currency, locale, className, as: As = 'span' }) {
22
29
  const formatted = formatPrice({
23
30
  amount: (amount / 100).toString(),
24
31
  currencyCode: currency,
25
- });
32
+ }, locale);
26
33
  return _jsx(As, { className: className, children: formatted });
27
34
  }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * useCart — server-driven cart hook bound to an explicit `cartId`.
3
+ *
4
+ * Sister of {@link useCartManager}: same `CartClient` actions, different
5
+ * lifecycle. `useCartManager` reads the cart id from the `cart-id` cookie and
6
+ * runs auto-recovery on stale carts (cookie-first flow — browser-rendered
7
+ * storefront). `useCart` takes the cart id as a prop and never touches the
8
+ * cookie, which is what SSR, deep-link recovery, and admin "view this cart"
9
+ * UIs need.
10
+ *
11
+ * State management follows the platform's store pattern: a vanilla Zustand
12
+ * store is created per hook mount via `useMemo` (component-scoped, no
13
+ * module-level singleton — Inv-3 of the SDK), and React subscribes through
14
+ * `useStore`. Changing `cartId` recreates the store; refetches are explicit
15
+ * (`refetch()` or any mutation triggers one).
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Server component / route handler hands the cartId to the client tree.
20
+ * function CheckoutPage({ cartId }: { cartId: string }) {
21
+ * const { cart, isLoading, error, addItems, updateAttributes } = useCart(cartId);
22
+ *
23
+ * if (isLoading) return <Spinner />;
24
+ * if (error) return <ErrorBanner error={error} />;
25
+ * if (!cart) return null;
26
+ *
27
+ * return <CheckoutForm cart={cart} onAdd={addItems} onAttrs={updateAttributes} />;
28
+ * }
29
+ * ```
30
+ */
31
+ import type { CartMutationOutcome } from '../../core/cart/cart-client';
32
+ import type { Cart, CartAddressInput, CartAttributeInput, CartBuyerIdentityInput, CartLineInput, CartLineUpdateInput } from '../../core/cart/types';
33
+ /** Names of mutations exposed by the hook — useful for telemetry / spinners. */
34
+ export type ServerCartOperation = 'fetch' | 'addItems' | 'updateItems' | 'removeItems' | 'updateBuyerIdentity' | 'setShippingAddress' | 'updateDiscountCodes' | 'updateNote' | 'updateAttributes';
35
+ export interface UseCartOptions {
36
+ /**
37
+ * Fetch the cart automatically when the hook mounts. Set to `false` when the
38
+ * server has already pre-rendered the cart and you want to seed the store
39
+ * instead of double-fetching. Default `true`.
40
+ */
41
+ autoFetch?: boolean;
42
+ /**
43
+ * Pre-loaded cart used as the initial store value — e.g. the cart already
44
+ * resolved on the server during SSR. Combine with `autoFetch: false` to avoid
45
+ * a duplicate round-trip on hydration.
46
+ */
47
+ initialCart?: Cart | null;
48
+ }
49
+ export interface UseCartResult {
50
+ /** Currently loaded cart, or null before the first fetch / when not found. */
51
+ cart: Cart | null;
52
+ /** True while any operation (fetch or mutation) is in flight. */
53
+ isLoading: boolean;
54
+ /** Last error from any operation, or null. */
55
+ error: Error | null;
56
+ /** Which operation produced the current `isLoading` / `error`, if any. */
57
+ operation: ServerCartOperation | null;
58
+ /** Re-fetch the cart from the backend. Resolves with the fresh cart or null. */
59
+ refetch: () => Promise<Cart | null>;
60
+ /** Append line items to the cart. Resolves with `{ cart, warnings }`. */
61
+ addItems: (lines: CartLineInput[]) => Promise<CartMutationOutcome>;
62
+ /** Update line items (quantity, attributes) by their line IDs. */
63
+ updateItems: (lines: CartLineUpdateInput[]) => Promise<CartMutationOutcome>;
64
+ /** Remove line items by their line IDs. */
65
+ removeItems: (lineIds: string[]) => Promise<CartMutationOutcome>;
66
+ /** Update buyer identity (email, phone, country, customer link, language). */
67
+ updateBuyerIdentity: (buyerIdentity: CartBuyerIdentityInput) => Promise<CartMutationOutcome>;
68
+ /** Set the shipping address (full replace). */
69
+ setShippingAddress: (address: CartAddressInput) => Promise<CartMutationOutcome>;
70
+ /** Apply discount codes (replace-all — pass `[]` to clear). */
71
+ updateDiscountCodes: (codes: string[]) => Promise<CartMutationOutcome>;
72
+ /** Update the cart note / gift message. */
73
+ updateNote: (note: string) => Promise<CartMutationOutcome>;
74
+ /** Replace the cart's custom `{ key, value }` attribute pairs. */
75
+ updateAttributes: (attributes: CartAttributeInput[]) => Promise<CartMutationOutcome>;
76
+ }
77
+ export declare function useCart(cartId: string, options?: UseCartOptions): UseCartResult;
78
+ //# sourceMappingURL=use-cart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-cart.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-cart.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AASH,OAAO,KAAK,EAAc,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,KAAK,EACV,IAAI,EACJ,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,aAAa,EACb,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAM/B,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,GAC3B,OAAO,GACP,UAAU,GACV,aAAa,GACb,aAAa,GACb,qBAAqB,GACrB,oBAAoB,GACpB,qBAAqB,GACrB,YAAY,GACZ,kBAAkB,CAAC;AAEvB,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,0EAA0E;IAC1E,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAEtC,gFAAgF;IAChF,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACpC,yEAAyE;IACzE,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnE,kEAAkE;IAClE,WAAW,EAAE,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC5E,2CAA2C;IAC3C,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjE,8EAA8E;IAC9E,mBAAmB,EAAE,CAAC,aAAa,EAAE,sBAAsB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC7F,+CAA+C;IAC/C,kBAAkB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAChF,+DAA+D;IAC/D,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACvE,2CAA2C;IAC3C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC3D,kEAAkE;IAClE,gBAAgB,EAAE,CAAC,UAAU,EAAE,kBAAkB,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACtF;AAoFD,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CA4CnF"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * useCart — server-driven cart hook bound to an explicit `cartId`.
3
+ *
4
+ * Sister of {@link useCartManager}: same `CartClient` actions, different
5
+ * lifecycle. `useCartManager` reads the cart id from the `cart-id` cookie and
6
+ * runs auto-recovery on stale carts (cookie-first flow — browser-rendered
7
+ * storefront). `useCart` takes the cart id as a prop and never touches the
8
+ * cookie, which is what SSR, deep-link recovery, and admin "view this cart"
9
+ * UIs need.
10
+ *
11
+ * State management follows the platform's store pattern: a vanilla Zustand
12
+ * store is created per hook mount via `useMemo` (component-scoped, no
13
+ * module-level singleton — Inv-3 of the SDK), and React subscribes through
14
+ * `useStore`. Changing `cartId` recreates the store; refetches are explicit
15
+ * (`refetch()` or any mutation triggers one).
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Server component / route handler hands the cartId to the client tree.
20
+ * function CheckoutPage({ cartId }: { cartId: string }) {
21
+ * const { cart, isLoading, error, addItems, updateAttributes } = useCart(cartId);
22
+ *
23
+ * if (isLoading) return <Spinner />;
24
+ * if (error) return <ErrorBanner error={error} />;
25
+ * if (!cart) return null;
26
+ *
27
+ * return <CheckoutForm cart={cart} onAdd={addItems} onAttrs={updateAttributes} />;
28
+ * }
29
+ * ```
30
+ */
31
+ 'use client';
32
+ import { useEffect, useMemo } from 'react';
33
+ import { createStore } from 'zustand/vanilla';
34
+ import { useStore } from 'zustand';
35
+ import { useStorefrontClientContext } from '../providers/storefront-client-provider';
36
+ function createServerCartStore(opts) {
37
+ const { cartClient, cartId, initialCart } = opts;
38
+ const store = createStore(() => ({
39
+ cart: initialCart ?? null,
40
+ isLoading: false,
41
+ error: null,
42
+ operation: null,
43
+ }));
44
+ async function run(operation, exec) {
45
+ store.setState({ isLoading: true, error: null, operation });
46
+ try {
47
+ const result = await exec();
48
+ store.setState({ isLoading: false, error: null, operation: null });
49
+ return result;
50
+ }
51
+ catch (err) {
52
+ const error = err instanceof Error ? err : new Error(String(err));
53
+ store.setState({ isLoading: false, error, operation });
54
+ throw err;
55
+ }
56
+ }
57
+ async function runMutation(operation, exec) {
58
+ const outcome = await run(operation, exec);
59
+ store.setState({ cart: outcome.cart });
60
+ return outcome;
61
+ }
62
+ return {
63
+ store,
64
+ async refetch() {
65
+ const cart = await run('fetch', () => cartClient.get(cartId));
66
+ store.setState({ cart });
67
+ return cart;
68
+ },
69
+ addItems: (lines) => runMutation('addItems', () => cartClient.addItems(cartId, lines)),
70
+ updateItems: (lines) => runMutation('updateItems', () => cartClient.updateItems(cartId, lines)),
71
+ removeItems: (lineIds) => runMutation('removeItems', () => cartClient.removeItems(cartId, lineIds)),
72
+ updateBuyerIdentity: (buyerIdentity) => runMutation('updateBuyerIdentity', () => cartClient.updateBuyerIdentity(cartId, buyerIdentity)),
73
+ setShippingAddress: (address) => runMutation('setShippingAddress', () => cartClient.setShippingAddress({ cartId, address })),
74
+ updateDiscountCodes: (codes) => runMutation('updateDiscountCodes', () => cartClient.updateDiscountCodes(cartId, codes)),
75
+ updateNote: (note) => runMutation('updateNote', () => cartClient.updateNote(cartId, note)),
76
+ updateAttributes: (attributes) => runMutation('updateAttributes', () => cartClient.updateAttributes(cartId, attributes)),
77
+ };
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Hook
81
+ // ---------------------------------------------------------------------------
82
+ export function useCart(cartId, options = {}) {
83
+ const { cartClient } = useStorefrontClientContext();
84
+ const { autoFetch = true, initialCart } = options;
85
+ // Recreate the store when `cartId` or the underlying client changes. The
86
+ // useMemo dependency list captures store identity (component-scoped, not
87
+ // module-level — Inv-3). Mutations capture the store instance for stable
88
+ // references via the api object.
89
+ const api = useMemo(() => createServerCartStore({ cartClient, cartId, initialCart }),
90
+ // initialCart deliberately excluded — it only seeds the FIRST mount; we do
91
+ // not want to reset the store when the parent re-renders with a stale
92
+ // server snapshot.
93
+ // eslint-disable-next-line react-hooks/exhaustive-deps
94
+ [cartClient, cartId]);
95
+ const state = useStore(api.store);
96
+ // Initial fetch — runs once per (cartId, cartClient) pair when autoFetch is
97
+ // enabled. Catches and stores any error via the runner; the caller observes
98
+ // it through `error` rather than an unhandled promise.
99
+ useEffect(() => {
100
+ if (!autoFetch)
101
+ return;
102
+ api.refetch().catch(() => {
103
+ // already captured in store state via the runner
104
+ });
105
+ }, [api, autoFetch]);
106
+ return {
107
+ cart: state.cart,
108
+ isLoading: state.isLoading,
109
+ error: state.error,
110
+ operation: state.operation,
111
+ refetch: api.refetch,
112
+ addItems: api.addItems,
113
+ updateItems: api.updateItems,
114
+ removeItems: api.removeItems,
115
+ updateBuyerIdentity: api.updateBuyerIdentity,
116
+ setShippingAddress: api.setShippingAddress,
117
+ updateDiscountCodes: api.updateDiscountCodes,
118
+ updateNote: api.updateNote,
119
+ updateAttributes: api.updateAttributes,
120
+ };
121
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Convenience hooks that pull the active language from `<StorefrontProvider>`
3
+ * and forward it to the core format utilities. Use these inside the provider
4
+ * tree for boilerplate-free locale-aware formatting; outside the provider,
5
+ * fall back to the vanilla `formatPrice` / `formatDate` / etc. from
6
+ * `@doswiftly/storefront-sdk` with an explicit `locale` argument.
7
+ *
8
+ * Each hook returns a memoised function so it's safe to use directly in
9
+ * render. The function accepts an optional final `localeOverride` argument
10
+ * that wins over the store value — handy for per-call overrides (e.g. a
11
+ * "show in US format" toggle on one element while the rest of the UI stays
12
+ * in Polish).
13
+ *
14
+ * Resolution order per call:
15
+ * 1. `localeOverride` argument (highest)
16
+ * 2. `useLanguageStore().language` from `<StorefrontProvider>`
17
+ * 3. Runtime default (`Intl.NumberFormat().resolvedOptions().locale`)
18
+ */
19
+ import { type PriceMoney } from '../../core/format';
20
+ /**
21
+ * Memoised `formatPrice` bound to the active language from `<StorefrontProvider>`.
22
+ * Pass `localeOverride` as the second argument to override the store value for
23
+ * a single call.
24
+ *
25
+ * @example
26
+ * const formatPrice = useFormatPrice();
27
+ * return <span>{formatPrice(item.price)}</span>;
28
+ *
29
+ * @example
30
+ * // Per-call override:
31
+ * const formatPrice = useFormatPrice();
32
+ * <span>{formatPrice(item.price, 'en-US')}</span>
33
+ */
34
+ export declare function useFormatPrice(): (price: PriceMoney | null | undefined, localeOverride?: string) => string;
35
+ /**
36
+ * Memoised `formatAmount` bound to the active language from `<StorefrontProvider>`.
37
+ *
38
+ * @example
39
+ * const formatAmount = useFormatAmount();
40
+ * return <span>{formatAmount(item.totalPrice.amount, item.totalPrice.currencyCode)}</span>;
41
+ */
42
+ export declare function useFormatAmount(): (amount: string | number, currencyCode: string, localeOverride?: string) => string;
43
+ /**
44
+ * Memoised `formatPriceRange` bound to the active language from `<StorefrontProvider>`.
45
+ *
46
+ * @example
47
+ * const formatPriceRange = useFormatPriceRange();
48
+ * return <span>{formatPriceRange(min, max)}</span>;
49
+ */
50
+ export declare function useFormatPriceRange(): (minPrice: PriceMoney, maxPrice: PriceMoney, localeOverride?: string) => string;
51
+ /**
52
+ * Memoised `formatDate` bound to the active language from `<StorefrontProvider>`.
53
+ *
54
+ * @example
55
+ * const formatDate = useFormatDate();
56
+ * return <span>{formatDate(order.processedAt)}</span>;
57
+ */
58
+ export declare function useFormatDate(): (date: Date | string, localeOverride?: string) => string;
59
+ /**
60
+ * Memoised `formatDateTime` bound to the active language from `<StorefrontProvider>`.
61
+ *
62
+ * @example
63
+ * const formatDateTime = useFormatDateTime();
64
+ * return <span>{formatDateTime(event.timestamp)}</span>;
65
+ */
66
+ export declare function useFormatDateTime(): (date: Date | string, localeOverride?: string) => string;
67
+ /**
68
+ * Memoised `formatNumber` bound to the active language from `<StorefrontProvider>`.
69
+ *
70
+ * @example
71
+ * const formatNumber = useFormatNumber();
72
+ * return <span>{formatNumber(points.balance)}</span>;
73
+ */
74
+ export declare function useFormatNumber(): (num: number, localeOverride?: string) => string;
75
+ /**
76
+ * Memoised `getCurrencySymbol` bound to the active language from
77
+ * `<StorefrontProvider>`. The returned symbol is locale-dependent — see
78
+ * `getCurrencySymbol` in core for examples.
79
+ *
80
+ * @example
81
+ * const getCurrencySymbol = useGetCurrencySymbol();
82
+ * return <span>{getCurrencySymbol(shop.currencyCode)}</span>;
83
+ */
84
+ export declare function useGetCurrencySymbol(): (code: string, localeOverride?: string) => string;
85
+ //# sourceMappingURL=use-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-format.d.ts","sourceRoot":"","sources":["../../../src/react/hooks/use-format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,EAQL,KAAK,UAAU,EAChB,MAAM,mBAAmB,CAAC;AA8B3B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI,CAChC,KAAK,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EACpC,cAAc,CAAC,EAAE,MAAM,KACpB,MAAM,CAMV;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,CACjC,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,YAAY,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,KACpB,MAAM,CAOV;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,IAAI,CACrC,QAAQ,EAAE,UAAU,EACpB,QAAQ,EAAE,UAAU,EACpB,cAAc,CAAC,EAAE,MAAM,KACpB,MAAM,CAOV;AAMD;;;;;;GAMG;AACH,wBAAgB,aAAa,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,KAAK,MAAM,CAMxF;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,CACnC,IAAI,EAAE,IAAI,GAAG,MAAM,EACnB,cAAc,CAAC,EAAE,MAAM,KACpB,MAAM,CAMV;AAMD;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,KAAK,MAAM,CAMlF;AAMD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,KAAK,MAAM,CAMxF"}