@zappie/sdk 0.2.0 → 0.3.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 (129) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cart/branch.js.map +0 -1
  3. package/dist/cart/errors.js.map +0 -1
  4. package/dist/cart/index.js.map +0 -1
  5. package/dist/cart/item.d.ts +1 -1
  6. package/dist/cart/item.js.map +0 -1
  7. package/dist/cart/preview.js.map +0 -1
  8. package/dist/cart/price.js.map +0 -1
  9. package/dist/cart/quote-client.js.map +0 -1
  10. package/dist/cart/quote-response.js.map +0 -1
  11. package/dist/cart/quote.d.ts +1 -1
  12. package/dist/cart/reconcile.js.map +0 -1
  13. package/dist/cart/source-menu.js.map +0 -1
  14. package/dist/cart/storage.js.map +0 -1
  15. package/dist/cart/types.js.map +0 -1
  16. package/dist/cart/validate.js.map +0 -1
  17. package/dist/cart/wire.js.map +0 -1
  18. package/dist/cart/working-set.js.map +0 -1
  19. package/dist/checkout/client.js.map +0 -1
  20. package/dist/checkout/embed.js.map +0 -1
  21. package/dist/checkout/error.js.map +0 -1
  22. package/dist/checkout/index.js.map +0 -1
  23. package/dist/checkout/react.js.map +0 -1
  24. package/dist/checkout/readiness.js.map +0 -1
  25. package/dist/checkout/sdk.js.map +0 -1
  26. package/dist/checkout/session.js.map +0 -1
  27. package/dist/checkout/types.js.map +0 -1
  28. package/dist/checkout-defaults/index.js.map +0 -1
  29. package/dist/{client-B377AzNC.d.ts → client-DHxakZrv.d.ts} +2 -2
  30. package/dist/client.js.map +0 -1
  31. package/dist/config/index.js.map +0 -1
  32. package/dist/config.js.map +0 -1
  33. package/dist/customer/client.js.map +0 -1
  34. package/dist/customer/error.js.map +0 -1
  35. package/dist/customer/index.js.map +0 -1
  36. package/dist/customer/navigation.js.map +0 -1
  37. package/dist/customer/react.js.map +0 -1
  38. package/dist/customer/request.js.map +0 -1
  39. package/dist/customer/sdk.js.map +0 -1
  40. package/dist/customer/types.js.map +0 -1
  41. package/dist/customer/watch-order.js.map +0 -1
  42. package/dist/debug.js +1 -1
  43. package/dist/debug.js.map +0 -1
  44. package/dist/errors.js.map +0 -1
  45. package/dist/index.d.ts +6 -6
  46. package/dist/index.js +1 -1
  47. package/dist/index.js.map +0 -1
  48. package/dist/integrations/index.d.ts +2 -2
  49. package/dist/integrations/index.js.map +0 -1
  50. package/dist/internal/abort.js.map +0 -1
  51. package/dist/internal/cache.js.map +0 -1
  52. package/dist/internal/customer-http.js.map +0 -1
  53. package/dist/internal/digest.js.map +0 -1
  54. package/dist/internal/emitter.js.map +0 -1
  55. package/dist/internal/money-math.js.map +0 -1
  56. package/dist/internal/return-path.js.map +0 -1
  57. package/dist/internal.d.ts +2 -2
  58. package/dist/internal.js.map +0 -1
  59. package/dist/{models-tf9rNoki.d.ts → models-C-kEg1Id.d.ts} +1 -1
  60. package/dist/models.js.map +0 -1
  61. package/dist/money.js.map +0 -1
  62. package/dist/persist/catalogue.d.ts +3 -3
  63. package/dist/persist/catalogue.js.map +0 -1
  64. package/dist/places/client.js.map +0 -1
  65. package/dist/places/error.js.map +0 -1
  66. package/dist/places/index.js.map +0 -1
  67. package/dist/places/request.js.map +0 -1
  68. package/dist/places/sdk.js.map +0 -1
  69. package/dist/places/search.js.map +0 -1
  70. package/dist/places/types.js.map +0 -1
  71. package/dist/pricing/index.js.map +0 -1
  72. package/dist/react/cart.js.map +0 -1
  73. package/dist/react/hooks.js.map +0 -1
  74. package/dist/react/index.js.map +0 -1
  75. package/dist/react/provider.js.map +0 -1
  76. package/dist/redirect/index.js.map +0 -1
  77. package/dist/server/auth/crypto.js.map +0 -1
  78. package/dist/server/auth/jwt.js.map +0 -1
  79. package/dist/server/auth/session-store.js.map +0 -1
  80. package/dist/server/edge-snapshot.js.map +0 -1
  81. package/dist/server/index.d.ts +1 -1
  82. package/dist/server/index.js.map +0 -1
  83. package/dist/server/security.js.map +0 -1
  84. package/dist/server/storefront-url/build-v1.js.map +0 -1
  85. package/dist/server/storefront-url/build-v2.js.map +0 -1
  86. package/dist/server/storefront-url/build.js.map +0 -1
  87. package/dist/server/storefront-url/canonical.js.map +0 -1
  88. package/dist/server/storefront-url/contract.js.map +0 -1
  89. package/dist/server/storefront-url/index.js.map +0 -1
  90. package/dist/server/storefront-url/installation.js.map +0 -1
  91. package/dist/server/storefront-url/params.js.map +0 -1
  92. package/dist/server/storefront-url/parse-v1.js.map +0 -1
  93. package/dist/server/storefront-url/parse-v2.js.map +0 -1
  94. package/dist/server/storefront-url/parse.js.map +0 -1
  95. package/dist/server/storefront-url/public-id.js.map +0 -1
  96. package/dist/server/storefront-url/redirect.js.map +0 -1
  97. package/dist/server/storefront-url/sign-in.js.map +0 -1
  98. package/dist/server/storefront-url/types.js.map +0 -1
  99. package/dist/server/storefront-url/url.js.map +0 -1
  100. package/dist/server/webhook.js.map +0 -1
  101. package/dist/services/index.d.ts +21 -0
  102. package/dist/services/index.js +24 -0
  103. package/dist/services/index.js.map +6 -0
  104. package/dist/site-config/helpers.js.map +0 -1
  105. package/dist/site-config/sections.js.map +0 -1
  106. package/dist/site-config/types.js.map +0 -1
  107. package/dist/site-config/validate.js.map +0 -1
  108. package/dist/template/index.d.ts +2 -2
  109. package/dist/template/index.js +1 -1
  110. package/dist/template/index.js.map +1 -2
  111. package/dist/template/react.js.map +0 -1
  112. package/dist/testing/index.d.ts +1 -1
  113. package/dist/testing/index.js +2 -0
  114. package/dist/transport/category-page.js.map +0 -1
  115. package/dist/transport/mappers.js.map +0 -1
  116. package/dist/transport/presentation.js.map +0 -1
  117. package/dist/transport/quote-body.js.map +0 -1
  118. package/dist/transport/rest.js.map +0 -1
  119. package/dist/transport/search.d.ts +1 -1
  120. package/dist/transport/search.js.map +0 -1
  121. package/dist/transport/selected-menu.d.ts +3 -3
  122. package/dist/transport/selected-menu.js.map +0 -1
  123. package/dist/transport/store.js.map +0 -1
  124. package/dist/transport/types.js.map +0 -1
  125. package/dist/{types-B3Ghmpmy.d.ts → types-CxY5C6AN.d.ts} +1 -1
  126. package/dist/url/index.js.map +0 -1
  127. package/dist/visits/index.js.map +0 -1
  128. package/dist/visits/page-views.js.map +0 -1
  129. package/package.json +6 -2
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/checkout/sdk.ts"],
4
- "sourcesContent": ["// The isolated checkout bundle imports the installed core so errors retain their identity.\n// eslint-disable-next-line no-restricted-imports\nexport { ZappieError, money } from \"@zappie/sdk\";\n// eslint-disable-next-line no-restricted-imports\nexport type { Currency, Quote, WireLine, WireMoney } from \"@zappie/sdk\";\n// The checkout entry's public name for the cart's wire fulfilment, shared with the core rather than bundled twice.\n// eslint-disable-next-line no-restricted-imports\nexport { wireFulfilment as checkoutFulfilment } from \"@zappie/sdk/internal\";\n"],
5
4
  "mappings": ";AAEA,SAAS,aAAa,aAAa;AAKnC,SAA2B,sBAA0B;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/checkout/session.ts"],
4
- "sourcesContent": ["import { type Currency, money, ZappieError } from \"./sdk\";\nimport type { CheckoutSession } from \"./types\";\n\nfunction object(value: unknown): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n return value as Record<string, unknown>;\n}\n\nfunction string(value: unknown): string {\n if (typeof value !== \"string\") {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n return value;\n}\n\nfunction currency(value: unknown): Currency {\n try {\n return money(0, string(value)).currency;\n } catch {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n}\n\nexport function mapCheckoutSession(value: unknown): CheckoutSession {\n const session = object(value);\n const amount = object(session.amount);\n const status = string(session.status);\n if (\n !([\"open\", \"completed\", \"expired\", \"cancelled\"] as string[]).includes(\n status,\n )\n ) {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n const expiresAt = string(session.expires_at);\n if (!Number.isFinite(Date.parse(expiresAt))) {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n if (!Number.isSafeInteger(amount.amount_minor)) {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n if (session.order_id !== null && typeof session.order_id !== \"string\") {\n throw new ZappieError(\"server_error\", \"Invalid checkout session response\");\n }\n return {\n id: string(session.id),\n status: status as CheckoutSession[\"status\"],\n ...(typeof session.url === \"string\" ? { url: session.url } : {}),\n amount: {\n amount_minor: amount.amount_minor as number,\n currency: currency(amount.currency),\n },\n order_id: session.order_id,\n return_url: string(session.return_url),\n ...(typeof session.cancel_url === \"string\"\n ? { cancel_url: session.cancel_url }\n : {}),\n expires_at: expiresAt,\n };\n}\n"],
5
4
  "mappings": ";AAAA,SAAwB,OAAO,mBAAmB;AAGlD,SAAS,OAAO,OAAyC;AACvD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,SAAO;AACT;AAEA,SAAS,OAAO,OAAwB;AACtC,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAA0B;AAC1C,MAAI;AACF,WAAO,MAAM,GAAG,OAAO,KAAK,CAAC,EAAE;AAAA,EACjC,QAAQ;AACN,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACF;AAEO,SAAS,mBAAmB,OAAiC;AAClE,QAAM,UAAU,OAAO,KAAK;AAC5B,QAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,QAAM,SAAS,OAAO,QAAQ,MAAM;AACpC,MACE,CAAE,CAAC,QAAQ,aAAa,WAAW,WAAW,EAAe;AAAA,IAC3D;AAAA,EACF,GACA;AACA,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,QAAM,YAAY,OAAO,QAAQ,UAAU;AAC3C,MAAI,CAAC,OAAO,SAAS,KAAK,MAAM,SAAS,CAAC,GAAG;AAC3C,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,MAAI,CAAC,OAAO,cAAc,OAAO,YAAY,GAAG;AAC9C,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,MAAI,QAAQ,aAAa,QAAQ,OAAO,QAAQ,aAAa,UAAU;AACrE,UAAM,IAAI,YAAY,gBAAgB,mCAAmC;AAAA,EAC3E;AACA,SAAO;AAAA,IACL,IAAI,OAAO,QAAQ,EAAE;AAAA,IACrB;AAAA,IACA,GAAI,OAAO,QAAQ,QAAQ,WAAW,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,IAC9D,QAAQ;AAAA,MACN,cAAc,OAAO;AAAA,MACrB,UAAU,SAAS,OAAO,QAAQ;AAAA,IACpC;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,YAAY,OAAO,QAAQ,UAAU;AAAA,IACrC,GAAI,OAAO,QAAQ,eAAe,WAC9B,EAAE,YAAY,QAAQ,WAAW,IACjC,CAAC;AAAA,IACL,YAAY;AAAA,EACd;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": [],
4
- "sourcesContent": [],
5
4
  "mappings": "",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/checkout-defaults/index.ts"],
4
- "sourcesContent": ["// The declarations import the installed core so shared types keep one identity.\n// eslint-disable-next-line no-restricted-imports\nimport type { Branch, CartFulfilment, DeliveryAddress } from \"@zappie/sdk\";\n// eslint-disable-next-line no-restricted-imports\nimport type {\n CustomerClient,\n SavedAddress,\n SessionCustomer,\n} from \"@zappie/sdk/customer\";\n\n/**\n * How the branch prices delivery. Only `\"fixed\"` lets an address without a place ID stand.\n */\nexport type DeliveryFeeType = \"region\" | \"distance\" | \"fixed\";\n\n/**\n * The browser storage the remembered address uses. Pass `null` to remember nothing.\n */\nexport type AddressStorage = Pick<\n Storage,\n \"getItem\" | \"setItem\" | \"removeItem\"\n>;\n\n/**\n * The phone a checkout shows, and the `contact_phone` to send.\n */\nexport type PhoneDefault = {\n /** The typed number, else the signed-in customer's masked number, else \"\". */\n display: string;\n /** The typed number. Absent when the account's number applies: checkout then uses the verified number. */\n contactPhone?: string;\n /** True when `display` is the signed-in customer's masked number. */\n fromAccount: boolean;\n};\n\n/**\n * What the defaults are worked out from.\n */\nexport type CheckoutDefaultsInput = {\n branch: Branch;\n /** The cart's fulfilment. Its type and its address are the shopper's choice and are kept. */\n fulfilment: CartFulfilment | null;\n customer?: SessionCustomer | null;\n /** The signed-in customer's saved addresses, in the order `/me/addresses` returns them. */\n savedAddresses?: readonly SavedAddress[];\n /** The phone typed in this session. */\n typedPhone?: string;\n /** Defaults to `\"distance\"`, as the default storefront does. */\n feeType?: DeliveryFeeType;\n /** Defaults to `localStorage`. */\n storage?: AddressStorage | null;\n};\n\n/**\n * The contact details a checkout starts from.\n */\nexport type CheckoutDefaults = {\n phone: PhoneDefault;\n /** The cart's delivery address, else the last one this shopper used on this device, else the first saved one. */\n address?: DeliveryAddress;\n /** The cart's region, else the branch's only region. */\n deliveryRegionId?: string;\n /** The cart's scheduled time when it belongs to this branch and parses. */\n scheduledFor?: string;\n};\n\n// The default storefront's key and shape, so a host page and its templates share one entry.\nconst KEY = \"zappie_delivery_address\";\n\ntype Stored = {\n /** The signed-in customer who used the address. Absent for a guest. */\n customerId?: unknown;\n address?: unknown;\n placeId?: unknown;\n latitude?: unknown;\n longitude?: unknown;\n};\n\nfunction local(storage: AddressStorage | null | undefined) {\n if (storage !== undefined) {\n return storage;\n }\n try {\n return typeof window === \"undefined\" ? null : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction attempt<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction point(lat: unknown, lng: unknown) {\n return typeof lat === \"number\" &&\n typeof lng === \"number\" &&\n Number.isFinite(lat) &&\n Number.isFinite(lng)\n ? { coordinates: { lat, lng } }\n : {};\n}\n\nfunction address(\n line1: string | undefined,\n placeId: string | undefined,\n lat: unknown,\n lng: unknown,\n): DeliveryAddress | undefined {\n return line1\n ? { line1, ...(placeId ? { placeId } : {}), ...point(lat, lng) }\n : undefined;\n}\n\n/**\n * The phone to show: the one typed in this session, otherwise the signed-in customer's.\n * The session holds only a masked number, so send no `contact_phone` for it.\n *\n * @param typed - The phone typed in this session.\n * @param customer - The signed-in customer, or null.\n */\nexport function defaultPhone(\n typed: string | undefined,\n customer: SessionCustomer | null | undefined,\n): PhoneDefault {\n const value = typed?.trim();\n if (value) {\n return { display: value, contactPhone: value, fromAccount: false };\n }\n const masked = customer?.phone_masked;\n return masked\n ? { display: masked, fromAccount: true }\n : { display: \"\", fromAccount: false };\n}\n\n/**\n * The last delivery address this shopper used on this device, or undefined. An address a guest\n * used is offered only to a guest, and one a customer used only to that customer. Under\n * distance or region fees, an entry without a place ID counts as none and is removed.\n *\n * @param options - The fee type, storage and signed-in customer's ID.\n */\nexport function rememberedAddress(\n options: {\n feeType?: DeliveryFeeType;\n storage?: AddressStorage | null;\n customerId?: string | null;\n } = {},\n): DeliveryAddress | undefined {\n const storage = local(options.storage);\n const raw = storage ? attempt(() => storage.getItem(KEY)) : undefined;\n if (!raw) {\n return undefined;\n }\n const stored = attempt(() => JSON.parse(raw) as Stored);\n if (text(stored?.customerId) !== (options.customerId || undefined)) {\n return undefined;\n }\n const placeId = text(stored?.placeId);\n if (!placeId && options.feeType !== \"fixed\") {\n attempt(() => storage?.removeItem(KEY));\n return undefined;\n }\n return address(\n text(stored?.address),\n placeId,\n stored?.latitude,\n stored?.longitude,\n );\n}\n\n/**\n * Remember the delivery address the shopper used, on this device only. Stores the address\n * line, place ID, coordinates and the customer's ID, nothing else. Does nothing where storage\n * is blocked.\n *\n * @param value - The address, or null to forget it.\n * @param storage - Defaults to `localStorage`.\n * @param customerId - The signed-in customer's ID. Leave it out for a guest.\n */\nexport function rememberAddress(\n value: DeliveryAddress | null,\n storage?: AddressStorage | null,\n customerId?: string | null,\n): void {\n const target = local(storage);\n if (!target) {\n return;\n }\n if (!value?.line1.trim()) {\n attempt(() => target.removeItem(KEY));\n return;\n }\n attempt(() =>\n target.setItem(\n KEY,\n JSON.stringify({\n ...(customerId ? { customerId } : {}),\n address: value.line1,\n placeId: value.placeId ?? \"\",\n latitude: value.coordinates?.lat,\n longitude: value.coordinates?.lng,\n }),\n ),\n );\n}\n\n// Remove the remembered address if this customer used it, so the next shopper never sees it.\nfunction forget(\n customerId: string,\n storage: AddressStorage | null | undefined,\n) {\n const target = local(storage);\n const raw = target ? attempt(() => target.getItem(KEY)) : undefined;\n const stored = raw ? attempt(() => JSON.parse(raw) as Stored) : undefined;\n if (text(stored?.customerId) === customerId) {\n attempt(() => target?.removeItem(KEY));\n }\n}\n\n/**\n * The address to start from: the last one this shopper used on this device, otherwise the\n * customer's first saved address. Under distance or region fees, an address without a place\n * ID counts as none.\n *\n * @param options - Saved addresses, fee type, storage and the signed-in customer's ID.\n */\nexport function defaultAddress(\n options: {\n savedAddresses?: readonly SavedAddress[];\n feeType?: DeliveryFeeType;\n storage?: AddressStorage | null;\n customerId?: string | null;\n } = {},\n): DeliveryAddress | undefined {\n const remembered = rememberedAddress(options);\n if (remembered) {\n return remembered;\n }\n const first = options.savedAddresses?.[0];\n const placeId = text(first?.place_id);\n if (!first || (!placeId && options.feeType !== \"fixed\")) {\n return undefined;\n }\n return address(text(first.line1), placeId, first.lat, first.lng);\n}\n\n/**\n * The region to start from: the one already chosen, otherwise the branch's only region.\n *\n * @param branch - The cart's branch.\n * @param chosen - The region already chosen.\n */\nexport function defaultRegion(\n branch: Branch,\n chosen?: string,\n): string | undefined {\n if (chosen) {\n return chosen;\n }\n const regions = branch.deliveryRegions ?? [];\n return regions.length === 1 ? regions[0]?.id : undefined;\n}\n\n/**\n * The cart's scheduled time if it belongs to this branch and parses, otherwise undefined.\n * Drop a time this returns undefined for: it can never become valid here.\n *\n * @param fulfilment - The cart's fulfilment.\n * @param branchId - The branch the shopper is ordering from.\n */\nexport function restoreSchedule(\n fulfilment: CartFulfilment | null,\n branchId: string,\n): string | undefined {\n const at =\n fulfilment && fulfilment.type !== \"dine_in\"\n ? fulfilment.scheduledFor\n : undefined;\n return at &&\n fulfilment?.branchId === branchId &&\n Number.isFinite(Date.parse(at))\n ? at\n : undefined;\n}\n\n/**\n * The contact details a checkout starts from, by the default storefront's rule.\n * It reads the cart and never changes it, so the fulfilment type stays the shopper's.\n *\n * @param input - The branch, cart fulfilment, customer and what was typed.\n */\nexport function checkoutDefaults(\n input: CheckoutDefaultsInput,\n): CheckoutDefaults {\n const delivery =\n input.fulfilment?.type === \"delivery\" ? input.fulfilment : undefined;\n const address =\n delivery?.address ??\n defaultAddress({ ...input, customerId: input.customer?.id ?? null });\n const deliveryRegionId = defaultRegion(\n input.branch,\n delivery?.deliveryRegionId,\n );\n const scheduledFor = restoreSchedule(input.fulfilment, input.branch.id);\n return {\n phone: defaultPhone(input.typedPhone, input.customer),\n ...(address ? { address } : {}),\n ...(deliveryRegionId ? { deliveryRegionId } : {}),\n ...(scheduledFor ? { scheduledFor } : {}),\n };\n}\n\n/**\n * The cart fulfilment with the defaults applied: the only region filled in and a stale\n * schedule dropped. Same type, same address. Returns the same object when nothing changes.\n *\n * @param fulfilment - The cart's fulfilment.\n * @param branch - The cart's branch.\n */\nexport function prefillFulfilment<T extends CartFulfilment | null>(\n fulfilment: T,\n branch: Branch,\n): T {\n if (!fulfilment || fulfilment.type === \"dine_in\") {\n return fulfilment;\n }\n const scheduledFor = restoreSchedule(fulfilment, branch.id);\n const regionId =\n fulfilment.type === \"delivery\"\n ? defaultRegion(branch, fulfilment.deliveryRegionId)\n : undefined;\n const dropSchedule = fulfilment.scheduledFor !== undefined && !scheduledFor;\n const addRegion =\n fulfilment.type === \"delivery\" && regionId !== fulfilment.deliveryRegionId;\n if (!dropSchedule && !addRegion) {\n return fulfilment;\n }\n const next: Exclude<CartFulfilment, { type: \"dine_in\" }> = { ...fulfilment };\n if (dropSchedule) {\n delete next.scheduledFor;\n }\n if (addRegion && next.type === \"delivery\" && regionId) {\n next.deliveryRegionId = regionId;\n }\n return next as T;\n}\n\n/**\n * Work out the defaults now and again whenever the shopper signs in or out. On sign-in it\n * fetches the saved addresses first. On sign-out it forgets the address the customer used on\n * this device. A failed fetch counts as none. A slower, older run never lands last.\n *\n * @param customer - The customer client.\n * @param read - Returns the current inputs, read on each run.\n * @param listener - Receives each result.\n * @returns Stops listening.\n */\nexport function watchCheckoutDefaults(\n customer: Pick<\n CustomerClient,\n \"state\" | \"customer\" | \"onChange\" | \"addresses\"\n >,\n read: () => Omit<CheckoutDefaultsInput, \"customer\" | \"savedAddresses\">,\n listener: (defaults: CheckoutDefaults) => void,\n): () => void {\n let run = 0;\n let stopped = false;\n let signedIn: string | undefined;\n const publish = (savedAddresses: readonly SavedAddress[] | undefined) => {\n listener(\n checkoutDefaults({\n ...read(),\n customer: customer.customer,\n ...(savedAddresses ? { savedAddresses } : {}),\n }),\n );\n };\n const refresh = () => {\n const current = ++run;\n const id =\n customer.state === \"authenticated\" ? customer.customer?.id : undefined;\n if (signedIn && signedIn !== id) {\n forget(signedIn, read().storage);\n }\n signedIn = id;\n if (!id) {\n publish(undefined);\n return;\n }\n void customer.addresses().then(\n (saved) => {\n if (!stopped && current === run) {\n publish(saved);\n }\n },\n () => {\n if (!stopped && current === run) {\n publish(undefined);\n }\n },\n );\n };\n const off = customer.onChange((state, next) => {\n const id = state === \"authenticated\" ? next?.id : undefined;\n if (id !== signedIn) {\n refresh();\n }\n });\n refresh();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
4
  "mappings": ";AAmEA,IAAM,MAAM;AAWZ,SAAS,MAAM,SAA4C;AACzD,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,EACT;AACA,MAAI;AACF,WAAO,OAAO,WAAW,cAAc,OAAO,OAAO;AAAA,EACvD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,QAAW,IAA4B;AAC9C,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KAAK,OAAoC;AAChD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAEA,SAAS,MAAM,KAAc,KAAc;AACzC,SAAO,OAAO,QAAQ,YACpB,OAAO,QAAQ,YACf,OAAO,SAAS,GAAG,KACnB,OAAO,SAAS,GAAG,IACjB,EAAE,aAAa,EAAE,KAAK,IAAI,EAAE,IAC5B,CAAC;AACP;AAEA,SAAS,QACP,OACA,SACA,KACA,KAC6B;AAC7B,SAAO,QACH,EAAE,OAAO,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,GAAI,GAAG,MAAM,KAAK,GAAG,EAAE,IAC7D;AACN;AASO,SAAS,aACd,OACA,UACc;AACd,QAAM,QAAQ,OAAO,KAAK;AAC1B,MAAI,OAAO;AACT,WAAO,EAAE,SAAS,OAAO,cAAc,OAAO,aAAa,MAAM;AAAA,EACnE;AACA,QAAM,SAAS,UAAU;AACzB,SAAO,SACH,EAAE,SAAS,QAAQ,aAAa,KAAK,IACrC,EAAE,SAAS,IAAI,aAAa,MAAM;AACxC;AASO,SAAS,kBACd,UAII,CAAC,GACwB;AAC7B,QAAM,UAAU,MAAM,QAAQ,OAAO;AACrC,QAAM,MAAM,UAAU,QAAQ,MAAM,QAAQ,QAAQ,GAAG,CAAC,IAAI;AAC5D,MAAI,CAAC,KAAK;AACR,WAAO;AAAA,EACT;AACA,QAAM,SAAS,QAAQ,MAAM,KAAK,MAAM,GAAG,CAAW;AACtD,MAAI,KAAK,QAAQ,UAAU,OAAO,QAAQ,cAAc,SAAY;AAClE,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,QAAQ,OAAO;AACpC,MAAI,CAAC,WAAW,QAAQ,YAAY,SAAS;AAC3C,YAAQ,MAAM,SAAS,WAAW,GAAG,CAAC;AACtC,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,KAAK,QAAQ,OAAO;AAAA,IACpB;AAAA,IACA,QAAQ;AAAA,IACR,QAAQ;AAAA,EACV;AACF;AAWO,SAAS,gBACd,OACA,SACA,YACM;AACN,QAAM,SAAS,MAAM,OAAO;AAC5B,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AACA,MAAI,CAAC,OAAO,MAAM,KAAK,GAAG;AACxB,YAAQ,MAAM,OAAO,WAAW,GAAG,CAAC;AACpC;AAAA,EACF;AACA;AAAA,IAAQ,MACN,OAAO;AAAA,MACL;AAAA,MACA,KAAK,UAAU;AAAA,QACb,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,QACnC,SAAS,MAAM;AAAA,QACf,SAAS,MAAM,WAAW;AAAA,QAC1B,UAAU,MAAM,aAAa;AAAA,QAC7B,WAAW,MAAM,aAAa;AAAA,MAChC,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAGA,SAAS,OACP,YACA,SACA;AACA,QAAM,SAAS,MAAM,OAAO;AAC5B,QAAM,MAAM,SAAS,QAAQ,MAAM,OAAO,QAAQ,GAAG,CAAC,IAAI;AAC1D,QAAM,SAAS,MAAM,QAAQ,MAAM,KAAK,MAAM,GAAG,CAAW,IAAI;AAChE,MAAI,KAAK,QAAQ,UAAU,MAAM,YAAY;AAC3C,YAAQ,MAAM,QAAQ,WAAW,GAAG,CAAC;AAAA,EACvC;AACF;AASO,SAAS,eACd,UAKI,CAAC,GACwB;AAC7B,QAAM,aAAa,kBAAkB,OAAO;AAC5C,MAAI,YAAY;AACd,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,QAAQ,iBAAiB,CAAC;AACxC,QAAM,UAAU,KAAK,OAAO,QAAQ;AACpC,MAAI,CAAC,SAAU,CAAC,WAAW,QAAQ,YAAY,SAAU;AACvD,WAAO;AAAA,EACT;AACA,SAAO,QAAQ,KAAK,MAAM,KAAK,GAAG,SAAS,MAAM,KAAK,MAAM,GAAG;AACjE;AAQO,SAAS,cACd,QACA,QACoB;AACpB,MAAI,QAAQ;AACV,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,mBAAmB,CAAC;AAC3C,SAAO,QAAQ,WAAW,IAAI,QAAQ,CAAC,GAAG,KAAK;AACjD;AASO,SAAS,gBACd,YACA,UACoB;AACpB,QAAM,KACJ,cAAc,WAAW,SAAS,YAC9B,WAAW,eACX;AACN,SAAO,MACL,YAAY,aAAa,YACzB,OAAO,SAAS,KAAK,MAAM,EAAE,CAAC,IAC5B,KACA;AACN;AAQO,SAAS,iBACd,OACkB;AAClB,QAAM,WACJ,MAAM,YAAY,SAAS,aAAa,MAAM,aAAa;AAC7D,QAAMA,WACJ,UAAU,WACV,eAAe,EAAE,GAAG,OAAO,YAAY,MAAM,UAAU,MAAM,KAAK,CAAC;AACrE,QAAM,mBAAmB;AAAA,IACvB,MAAM;AAAA,IACN,UAAU;AAAA,EACZ;AACA,QAAM,eAAe,gBAAgB,MAAM,YAAY,MAAM,OAAO,EAAE;AACtE,SAAO;AAAA,IACL,OAAO,aAAa,MAAM,YAAY,MAAM,QAAQ;AAAA,IACpD,GAAIA,WAAU,EAAE,SAAAA,SAAQ,IAAI,CAAC;AAAA,IAC7B,GAAI,mBAAmB,EAAE,iBAAiB,IAAI,CAAC;AAAA,IAC/C,GAAI,eAAe,EAAE,aAAa,IAAI,CAAC;AAAA,EACzC;AACF;AASO,SAAS,kBACd,YACA,QACG;AACH,MAAI,CAAC,cAAc,WAAW,SAAS,WAAW;AAChD,WAAO;AAAA,EACT;AACA,QAAM,eAAe,gBAAgB,YAAY,OAAO,EAAE;AAC1D,QAAM,WACJ,WAAW,SAAS,aAChB,cAAc,QAAQ,WAAW,gBAAgB,IACjD;AACN,QAAM,eAAe,WAAW,iBAAiB,UAAa,CAAC;AAC/D,QAAM,YACJ,WAAW,SAAS,cAAc,aAAa,WAAW;AAC5D,MAAI,CAAC,gBAAgB,CAAC,WAAW;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,OAAqD,EAAE,GAAG,WAAW;AAC3E,MAAI,cAAc;AAChB,WAAO,KAAK;AAAA,EACd;AACA,MAAI,aAAa,KAAK,SAAS,cAAc,UAAU;AACrD,SAAK,mBAAmB;AAAA,EAC1B;AACA,SAAO;AACT;AAYO,SAAS,sBACd,UAIA,MACA,UACY;AACZ,MAAI,MAAM;AACV,MAAI,UAAU;AACd,MAAI;AACJ,QAAM,UAAU,CAAC,mBAAwD;AACvE;AAAA,MACE,iBAAiB;AAAA,QACf,GAAG,KAAK;AAAA,QACR,UAAU,SAAS;AAAA,QACnB,GAAI,iBAAiB,EAAE,eAAe,IAAI,CAAC;AAAA,MAC7C,CAAC;AAAA,IACH;AAAA,EACF;AACA,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,EAAE;AAClB,UAAM,KACJ,SAAS,UAAU,kBAAkB,SAAS,UAAU,KAAK;AAC/D,QAAI,YAAY,aAAa,IAAI;AAC/B,aAAO,UAAU,KAAK,EAAE,OAAO;AAAA,IACjC;AACA,eAAW;AACX,QAAI,CAAC,IAAI;AACP,cAAQ,MAAS;AACjB;AAAA,IACF;AACA,SAAK,SAAS,UAAU,EAAE;AAAA,MACxB,CAAC,UAAU;AACT,YAAI,CAAC,WAAW,YAAY,KAAK;AAC/B,kBAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,MACA,MAAM;AACJ,YAAI,CAAC,WAAW,YAAY,KAAK;AAC/B,kBAAQ,MAAS;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,SAAS,SAAS,CAAC,OAAO,SAAS;AAC7C,UAAM,KAAK,UAAU,kBAAkB,MAAM,KAAK;AAClD,QAAI,OAAO,UAAU;AACnB,cAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACD,UAAQ;AACR,SAAO,MAAM;AACX,cAAU;AACV,QAAI;AAAA,EACN;AACF;",
6
5
  "names": ["address"]
7
6
  }
@@ -1,5 +1,5 @@
1
- import { a as Menu, C as CategoryPage, S as Store, I as Item, M as Money, F as Formatters } from './models-tf9rNoki.js';
2
- import { Q as QuoteRequest, g as WireQuote, i as CartIssueCode, d as CartModule, b as CartLineInput } from './types-B3Ghmpmy.js';
1
+ import { a as Menu, C as CategoryPage, S as Store, I as Item, M as Money, F as Formatters } from './models-C-kEg1Id.js';
2
+ import { Q as QuoteRequest, g as WireQuote, i as CartIssueCode, d as CartModule, b as CartLineInput } from './types-CxY5C6AN.js';
3
3
 
4
4
  /** Stable public error codes. */
5
5
  type ZappieErrorCode = "network_error" | "timeout" | "rate_limited" | "unauthorized" | "forbidden_origin" | "not_found" | "store_not_found" | "branch_not_found" | "item_not_found" | "cart_invalid" | "invalid_request" | "version_unsupported" | "server_error";
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client.ts"],
4
- "sourcesContent": ["import { createCart } from \"./cart\";\nimport { createWorkingSet } from \"./cart/working-set\";\nimport type { ZappieConfig } from \"./config\";\nimport { API_VERSION, validateConfig } from \"./config\";\nimport { ZappieError } from \"./errors\";\nimport { Cache } from \"./internal/cache\";\nimport type { ZappieEvents } from \"./internal/emitter\";\nimport { createEmitter } from \"./internal/emitter\";\nimport type { Branch, CategoryPage, Item, Menu, Store } from \"./models\";\nimport { format } from \"./money\";\nimport { RestTransport } from \"./transport/rest\";\nimport type { MenuVersion, SearchPage } from \"./transport/types\";\n/** A catalogue fulfilment mode accepted by menu reads. */\nexport type MenuFulfilment = \"delivery\" | \"pickup\" | \"dineIn\";\n/** Optional branch selection and cancellation for resource reads. */\nexport type ReadOptions = { branch?: string; signal?: AbortSignal };\n/** Optional branch and fulfilment selection for catalogue reads. */\nexport type MenuReadOptions = ReadOptions & { fulfilment?: MenuFulfilment };\n/** Read-only store resource. */\nexport interface StoreResource {\n get(opts?: { signal?: AbortSignal }): Promise<Store>;\n peek(): Store | undefined;\n}\n/** Read-only branch catalogue and item resources. */\nexport interface MenuResource {\n search(\n query: string,\n opts?: ReadOptions & { cursor?: string },\n ): Promise<SearchPage>;\n get(opts?: MenuReadOptions & { metadataOnly?: boolean }): Promise<Menu>;\n page(\n menuId: string,\n categoryId: string,\n opts?: ReadOptions & { cursor?: string },\n ): Promise<CategoryPage>;\n peek(branch?: string): Menu | undefined;\n version(opts?: MenuReadOptions): Promise<MenuVersion>;\n item(id: string, opts?: ReadOptions): Promise<Item>;\n hydrate(menu: Menu): void;\n}\n/** A storefront client and its isolated resource state. */\nexport type Zappie = ReturnType<typeof buildClient>;\nexport function buildClient(input: ZappieConfig, secret = false) {\n const mode = validateConfig(input, secret);\n const config = Object.freeze({\n ...input,\n version: input.version ?? API_VERSION,\n telemetry: input.telemetry ?? true,\n });\n const transport =\n input.transport ??\n new RestTransport({\n ...(input.baseUrl ? { baseUrl: input.baseUrl } : {}),\n ...(input.fetch ? { fetch: input.fetch } : {}),\n });\n const events = createEmitter();\n const cache = new Cache((error) => events.emit(\"error\", { error }));\n const ctx = (signal?: AbortSignal) => ({\n key: config.key,\n version: config.version,\n ...(signal ? { signal } : {}),\n });\n const store: StoreResource = {\n get: (opts = {}) =>\n cache.get(\n \"store\",\n async (signal) => ({ value: await transport.getStore(ctx(signal)) }),\n opts.signal,\n ),\n peek: () => cache.snapshot<Store>(\"store\").value,\n };\n const resolveBranch = (value: Store, requested?: string): Branch => {\n const selected = requested ?? config.branch;\n const branch = selected\n ? value.branches.find((b) => b.id === selected || b.slug === selected)\n : (value.branches.find((b) => b.isDefault) ?? value.branches[0]);\n if (!branch) {\n throw new ZappieError(\n \"branch_not_found\",\n \"No matching storefront branch\",\n { param: \"branch\" },\n );\n }\n return branch;\n };\n let hydratedBranch: string | undefined;\n const branchKey = (requested?: string) => {\n const value = store.peek();\n if (value) {\n return resolveBranch(value, requested).id;\n }\n // A slug can't be resolved without the store. A hydrated menu is the configured\n // branch's, so a read for that branch (by slug or id) uses it rather than missing.\n const selected = requested ?? config.branch;\n if (\n hydratedBranch &&\n (selected === undefined ||\n selected === config.branch ||\n selected === hydratedBranch)\n ) {\n return hydratedBranch;\n }\n return selected ?? \"default\";\n };\n const menuKey = (requested?: string) => `menu:${branchKey(requested)}`;\n const menu: MenuResource = {\n async search(query, opts = {}) {\n if (!transport.search) {\n throw new ZappieError(\n \"invalid_request\",\n \"Transport does not support search\",\n );\n }\n const branch = resolveBranch(await store.get(opts), opts.branch);\n return transport.search({\n ...ctx(opts.signal),\n branchId: branch.id,\n query,\n ...(opts.cursor ? { cursor: opts.cursor } : {}),\n });\n },\n async get(opts = {}) {\n const branch = resolveBranch(await store.get(opts), opts.branch);\n return cache.get<Menu>(\n `${menuKey(branch.id)}${opts.fulfilment ? `:${opts.fulfilment}` : \"\"}${opts.metadataOnly ? \":partial\" : \"\"}`,\n async (signal, entry) => {\n const response = await transport.getMenu({\n ...ctx(signal),\n branchId: branch.id,\n fulfilment: opts.fulfilment,\n ...(opts.metadataOnly ? { metadataOnly: true } : {}),\n ...(entry.etag ? { ifNoneMatch: entry.etag } : {}),\n });\n if (\"notModified\" in response) {\n if (!entry.value) {\n throw new ZappieError(\n \"server_error\",\n \"Received 304 without a cached menu\",\n );\n }\n return {\n value: entry.value,\n ...((response.etag ?? entry.etag)\n ? { etag: response.etag ?? entry.etag }\n : {}),\n };\n }\n if (!opts.metadataOnly && response.menu.completeness === \"partial\") {\n throw new ZappieError(\n \"server_error\",\n \"Partial catalogue cannot satisfy a complete read\",\n );\n }\n if (response.menu.branchId !== branch.id) {\n throw new ZappieError(\n \"server_error\",\n \"Menu belongs to a different branch\",\n );\n }\n return {\n value: response.menu,\n etag: response.etag ?? response.menu.version,\n onStored: () => {\n if (\n entry.value &&\n entry.value.version !== response.menu.version\n ) {\n events.emit(\"menu:updated\", {\n branchId: branch.id,\n version: response.menu.version,\n });\n }\n },\n };\n },\n opts.signal,\n );\n },\n peek: (branch) =>\n cache.snapshot<Menu>(menuKey(branch)).value ??\n cache.snapshot<Menu>(`${menuKey(branch)}:partial`).value,\n async page(menuId, categoryId, opts = {}) {\n if (!transport.getCategoryPage) {\n throw new ZappieError(\n \"invalid_request\",\n \"Transport does not support category pages\",\n );\n }\n const branch = resolveBranch(await store.get(opts), opts.branch);\n return transport.getCategoryPage({\n ...ctx(opts.signal),\n branchId: branch.id,\n menuId,\n categoryId,\n ...(opts.cursor ? { cursor: opts.cursor } : {}),\n });\n },\n async version(opts = {}) {\n const branch = resolveBranch(await store.get(opts), opts.branch);\n return transport.getMenuVersion({\n ...ctx(opts.signal),\n branchId: branch.id,\n fulfilment: opts.fulfilment,\n });\n },\n async item(id, opts = {}) {\n const branch = resolveBranch(await store.get(opts), opts.branch);\n if (\n !menu.peek(branch.id) ||\n menu.peek(branch.id)?.completeness === \"partial\"\n ) {\n await menu.get({ ...opts, metadataOnly: true });\n }\n return readItem(id, branch.id, false, opts.signal);\n },\n hydrate(value) {\n hydratedBranch ??= value.branchId;\n const previous = cache.snapshot<Menu>(`menu:${value.branchId}`).value;\n cache.set(\n `menu:${value.branchId}${value.completeness === \"partial\" ? \":partial\" : \"\"}`,\n value,\n value.version,\n );\n if (previous && previous.version !== value.version) {\n events.emit(\"menu:updated\", {\n branchId: value.branchId,\n version: value.version,\n });\n }\n },\n };\n const itemValues = new Map<string, Item>();\n const peekItem = (id: string, branch: string) =>\n itemValues.get(`${branch}:${id}`);\n const readItem = async (\n id: string,\n branch: string,\n fresh = false,\n signal?: AbortSignal,\n ) => {\n const complete = cache.snapshot<Menu>(`menu:${branch}`).value;\n if (!fresh && complete && Object.hasOwn(complete.items, id)) {\n return complete.items[id]!;\n }\n const load = (requestSignal = signal) =>\n transport.getItem({\n ...ctx(requestSignal),\n branchId: branch,\n itemId: id,\n });\n try {\n const value = fresh\n ? await load()\n : await cache.get(\n `item:${branch}:${id}`,\n async (requestSignal) => ({ value: await load(requestSignal) }),\n signal,\n );\n if (fresh) {\n cache.set(`item:${branch}:${id}`, value);\n }\n itemValues.set(`${branch}:${id}`, value);\n return value;\n } catch (error) {\n if ((error as { code?: string }).code === \"item_not_found\") {\n itemValues.delete(`${branch}:${id}`);\n cache.set(`item:${branch}:${id}`, undefined);\n }\n throw error;\n }\n };\n const workingSet = createWorkingSet({\n menu,\n store,\n transport,\n context: ctx,\n peekItem,\n readItem,\n });\n const { cart, setLegacyReader, cartCatalogue } = createCart({\n workingSet,\n config,\n menu,\n store,\n transport,\n context: ctx,\n events,\n });\n return Object.freeze({\n cart,\n config,\n mode,\n store,\n menu,\n format,\n on: events.on,\n _internal: {\n cartCatalogue,\n setLegacyReader,\n transport,\n cache,\n emit: events.emit,\n menuKey,\n hydrateStore: (value: Store) => cache.set(\"store\", value),\n },\n });\n}\n/** Create a client with your publishable key. Requests start when you read a resource. */\nexport function createZappie(config: ZappieConfig) {\n const client = buildClient(config);\n if (client.mode === \"test\" && typeof window !== \"undefined\") {\n void import(\"./debug\").then(({ installDebug }) => installDebug(client));\n }\n return client;\n}\nexport type { ZappieEvents };\n"],
5
4
  "mappings": "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",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/site-config/index.ts"],
4
- "sourcesContent": ["import type { ZappieSiteConfig } from \"./types\";\n\n/**\n * Return your site configuration unchanged with TypeScript checking.\n * Use validateConfig to check values at runtime.\n *\n * @example\n * ```ts\n * export default defineConfig({ store: \"my-store\", theme: \"./theme.json\" });\n * ```\n */\nexport function defineConfig(config: ZappieSiteConfig): ZappieSiteConfig {\n return config;\n}\n\nexport { DEFAULT_BUDGETS, GLOBAL_PACKAGE_ALLOWLIST } from \"./types\";\nexport type { ConfigIssue, ConfigValidation, ZappieSiteConfig } from \"./types\";\nexport { validateConfig } from \"./validate\";\n"],
5
4
  "mappings": ";AAeA,SAAS,iBAAiB,gCAAgC;AAE1D,SAAS,sBAAsB;AANxB,SAAS,aAAa,QAA4C;AACvE,SAAO;AACT;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/config.ts"],
4
- "sourcesContent": ["import { ZappieError } from \"./errors\";\nimport type { Transport } from \"./transport/types\";\n/** Release-pinned REST contract version, independent of the package version. */\nexport const API_VERSION = \"2026-10-01\";\n/** A storage adapter you can supply to save carts. */\nexport interface StorageLike {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n /** Provide length and key to let the SDK remove expired carts for this store. */\n readonly length?: number;\n key?(index: number): string | null;\n}\n/** Settings for your storefront client. Each client keeps its own credentials and caches. */\nexport type ZappieConfig = {\n store: string;\n key: string;\n branch?: string;\n transport?: Transport;\n baseUrl?: string;\n version?: string;\n fetch?: typeof fetch;\n /** Route cart quotes through the same-origin customer channel when set. */\n customerBase?: string;\n storage?: StorageLike;\n telemetry?: boolean;\n cart?: { ttl?: number; storageScope?: string };\n};\nexport function validateConfig(config: ZappieConfig, secret = false) {\n if (!config.store?.trim()) {\n throw new ZappieError(\"invalid_request\", \"A storefront slug is required\");\n }\n if (secret && typeof window !== \"undefined\") {\n throw new ZappieError(\n \"invalid_request\",\n \"Secret keys cannot be used in a browser\",\n );\n }\n const pattern = secret ? /^sk_(test|live)_.+$/ : /^pk_(test|live)_.+$/;\n const match = pattern.exec(config.key);\n if (!match) {\n throw new ZappieError(\n \"invalid_request\",\n secret\n ? \"A secret sk_test_ or sk_live_ key is required\"\n : \"Use a publishable pk_test_ or pk_live_ key; secret keys are never accepted by createZappie\",\n );\n }\n return match[1] as \"test\" | \"live\";\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,mBAAmB;AAGrB,IAAM,cAAc;AAyBpB,SAAS,eAAe,QAAsB,SAAS,OAAO;AACnE,MAAI,CAAC,OAAO,OAAO,KAAK,GAAG;AACzB,UAAM,IAAI,YAAY,mBAAmB,+BAA+B;AAAA,EAC1E;AACA,MAAI,UAAU,OAAO,WAAW,aAAa;AAC3C,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,SAAS,wBAAwB;AACjD,QAAM,QAAQ,QAAQ,KAAK,OAAO,GAAG;AACrC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA,SACI,kDACA;AAAA,IACN;AAAA,EACF;AACA,SAAO,MAAM,CAAC;AAChB;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/client.ts"],
4
- "sourcesContent": ["import { apiError, requestWithRetry } from \"../internal/customer-http\";\nimport { CustomerError } from \"./error\";\nimport { currentPath, safeReturnTo, signIn, signOut } from \"./navigation\";\nimport { createCustomerRequest } from \"./request\";\nimport type {\n AuthState,\n CreateAddress,\n CustomerClientOptions,\n CustomerListener,\n CustomerOrder,\n CustomerProfile,\n OrdersPage,\n SavedAddress,\n SessionCustomer,\n WatchOrderOptions,\n} from \"./types\";\nimport { watchOrder } from \"./watch-order\";\n\nconst DEFAULT_CUSTOMER_BASE = \"/_zappie/api\";\nconst DEFAULT_SESSION_PATH = \"/_zappie/session\";\n\nfunction same(\n left: SessionCustomer | null,\n right: SessionCustomer | null,\n): boolean {\n return (\n left?.id === right?.id &&\n left?.given_name === right?.given_name &&\n left?.phone_masked === right?.phone_masked\n );\n}\n\n/**\n * Create a client for customer sessions, saved addresses and orders.\n * Browser visibility and pageshow events refresh the session. Call dispose when you stop using the client.\n *\n * @param options - Endpoint overrides, fetch implementation and optional initial customer.\n * @returns A customer client with session subscriptions and sign-in and sign-out navigation.\n */\nexport function createCustomer(options: CustomerClientOptions = {}) {\n const base = (options.customerBase ?? DEFAULT_CUSTOMER_BASE).replace(\n /\\/+$/,\n \"\",\n );\n const sessionPath = options.sessionPath ?? DEFAULT_SESSION_PATH;\n const fetcher = options.fetch ?? globalThis.fetch;\n let customer = options.initialCustomer ?? null;\n let state: AuthState =\n options.initialCustomer === undefined\n ? \"unknown\"\n : customer\n ? \"authenticated\"\n : \"anonymous\";\n const listeners = new Set<CustomerListener>();\n const update = (next: SessionCustomer | null, nextState: AuthState) => {\n if (state === nextState && same(customer, next)) {\n return;\n }\n state = nextState;\n customer = next;\n globalThis.__zappieEmit?.(\"auth:changed\", { state, customer });\n for (const listener of listeners) {\n listener(state, customer);\n }\n };\n const request = createCustomerRequest(base, fetcher, () => {\n update(null, \"anonymous\");\n });\n const readOrder = (id: string, signal?: AbortSignal) =>\n request<CustomerOrder>(`/me/orders/${encodeURIComponent(id)}`, {\n method: \"GET\",\n ...(signal ? { signal } : {}),\n });\n let pending: Promise<SessionCustomer | null> | undefined;\n const check = async (): Promise<SessionCustomer | null> => {\n const response = await requestWithRetry(\n fetcher,\n sessionPath,\n { method: \"GET\", cache: \"no-store\" },\n \"Could not check the customer session\",\n );\n if (!response.ok) {\n const value = await apiError(\n response,\n `The session service answered ${response.status}`,\n );\n throw new CustomerError(\n response.status,\n value.apiCode,\n value.message,\n value.param,\n value.issues,\n );\n }\n const body = (await response.json()) as {\n customer?: SessionCustomer | null;\n };\n const next = body.customer ?? null;\n update(next, next ? \"authenticated\" : \"anonymous\");\n return next;\n };\n // Overlapping checks share one request, so a slower stale answer can't land last.\n const refresh = (): Promise<SessionCustomer | null> => {\n pending ??= check().finally(() => {\n pending = undefined;\n });\n return pending;\n };\n const recheck = () => {\n if (\n typeof document === \"undefined\" ||\n document.visibilityState === \"visible\"\n ) {\n void refresh().catch(() => undefined);\n }\n };\n if (typeof document !== \"undefined\") {\n document.addEventListener(\"visibilitychange\", recheck);\n }\n if (typeof window !== \"undefined\") {\n window.addEventListener(\"pageshow\", recheck);\n }\n return {\n get state() {\n return state;\n },\n get customer() {\n return customer;\n },\n session: refresh,\n /** Stops the visibility and back/forward-cache re-checks and drops listeners. */\n dispose() {\n if (typeof document !== \"undefined\") {\n document.removeEventListener(\"visibilitychange\", recheck);\n }\n if (typeof window !== \"undefined\") {\n window.removeEventListener(\"pageshow\", recheck);\n }\n listeners.clear();\n },\n onChange(listener: CustomerListener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n signIn(returnTo?: string) {\n signIn(safeReturnTo(returnTo ?? currentPath()));\n },\n signOut() {\n signOut(\"/_zappie/logout\");\n },\n profile: () => request<CustomerProfile>(\"/me\", { method: \"GET\" }),\n async addresses() {\n const result = await request<{ addresses: SavedAddress[] }>(\n \"/me/addresses\",\n { method: \"GET\" },\n );\n return result.addresses;\n },\n addAddress: (body: CreateAddress) =>\n request<SavedAddress>(\"/me/addresses\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n }),\n removeAddress: (id: string) =>\n request<void>(`/me/addresses/${encodeURIComponent(id)}`, {\n method: \"DELETE\",\n }),\n orders(options: { cursor?: string; limit?: number } = {}) {\n const query = new URLSearchParams();\n if (options.limit !== undefined) {\n query.set(\"limit\", String(options.limit));\n }\n if (options.cursor !== undefined) {\n query.set(\"cursor\", options.cursor);\n }\n const suffix = query.size > 0 ? `?${query}` : \"\";\n return request<OrdersPage>(`/me/orders${suffix}`, { method: \"GET\" });\n },\n order: (id: string) => readOrder(id),\n watchOrder(id: string, watchOptions: WatchOrderOptions) {\n return watchOrder(base, id, readOrder, watchOptions);\n },\n };\n}\n\n/**\n * The session, profile, address and order operations returned by createCustomer.\n */\nexport type CustomerClient = ReturnType<typeof createCustomer>;\n"],
5
4
  "mappings": ";AAAA,SAAS,UAAU,wBAAwB;AAC3C,SAAS,qBAAqB;AAC9B,SAAS,aAAa,cAAc,QAAQ,eAAe;AAC3D,SAAS,6BAA6B;AAatC,SAAS,kBAAkB;AAE3B,IAAM,wBAAwB;AAC9B,IAAM,uBAAuB;AAE7B,SAAS,KACP,MACA,OACS;AACT,SACE,MAAM,OAAO,OAAO,MACpB,MAAM,eAAe,OAAO,cAC5B,MAAM,iBAAiB,OAAO;AAElC;AASO,SAAS,eAAe,UAAiC,CAAC,GAAG;AAClE,QAAM,QAAQ,QAAQ,gBAAgB,uBAAuB;AAAA,IAC3D;AAAA,IACA;AAAA,EACF;AACA,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,UAAU,QAAQ,SAAS,WAAW;AAC5C,MAAI,WAAW,QAAQ,mBAAmB;AAC1C,MAAI,QACF,QAAQ,oBAAoB,SACxB,YACA,WACE,kBACA;AACR,QAAM,YAAY,oBAAI,IAAsB;AAC5C,QAAM,SAAS,CAAC,MAA8B,cAAyB;AACrE,QAAI,UAAU,aAAa,KAAK,UAAU,IAAI,GAAG;AAC/C;AAAA,IACF;AACA,YAAQ;AACR,eAAW;AACX,eAAW,eAAe,gBAAgB,EAAE,OAAO,SAAS,CAAC;AAC7D,eAAW,YAAY,WAAW;AAChC,eAAS,OAAO,QAAQ;AAAA,IAC1B;AAAA,EACF;AACA,QAAM,UAAU,sBAAsB,MAAM,SAAS,MAAM;AACzD,WAAO,MAAM,WAAW;AAAA,EAC1B,CAAC;AACD,QAAM,YAAY,CAAC,IAAY,WAC7B,QAAuB,cAAc,mBAAmB,EAAE,CAAC,IAAI;AAAA,IAC7D,QAAQ;AAAA,IACR,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC7B,CAAC;AACH,MAAI;AACJ,QAAM,QAAQ,YAA6C;AACzD,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,OAAO,OAAO,WAAW;AAAA,MACnC;AAAA,IACF;AACA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QAAQ,MAAM;AAAA,QAClB;AAAA,QACA,gCAAgC,SAAS,MAAM;AAAA,MACjD;AACA,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AACA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAGlC,UAAM,OAAO,KAAK,YAAY;AAC9B,WAAO,MAAM,OAAO,kBAAkB,WAAW;AACjD,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAuC;AACrD,0BAAY,MAAM,EAAE,QAAQ,MAAM;AAChC,gBAAU;AAAA,IACZ,CAAC;AACD,WAAO;AAAA,EACT;AACA,QAAM,UAAU,MAAM;AACpB,QACE,OAAO,aAAa,eACpB,SAAS,oBAAoB,WAC7B;AACA,WAAK,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,IACtC;AAAA,EACF;AACA,MAAI,OAAO,aAAa,aAAa;AACnC,aAAS,iBAAiB,oBAAoB,OAAO;AAAA,EACvD;AACA,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,iBAAiB,YAAY,OAAO;AAAA,EAC7C;AACA,SAAO;AAAA,IACL,IAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAAA,IACA,IAAI,WAAW;AACb,aAAO;AAAA,IACT;AAAA,IACA,SAAS;AAAA;AAAA,IAET,UAAU;AACR,UAAI,OAAO,aAAa,aAAa;AACnC,iBAAS,oBAAoB,oBAAoB,OAAO;AAAA,MAC1D;AACA,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,oBAAoB,YAAY,OAAO;AAAA,MAChD;AACA,gBAAU,MAAM;AAAA,IAClB;AAAA,IACA,SAAS,UAA4B;AACnC,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM;AACX,kBAAU,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,IACA,OAAO,UAAmB;AACxB,aAAO,aAAa,YAAY,YAAY,CAAC,CAAC;AAAA,IAChD;AAAA,IACA,UAAU;AACR,cAAQ,iBAAiB;AAAA,IAC3B;AAAA,IACA,SAAS,MAAM,QAAyB,OAAO,EAAE,QAAQ,MAAM,CAAC;AAAA,IAChE,MAAM,YAAY;AAChB,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA,EAAE,QAAQ,MAAM;AAAA,MAClB;AACA,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,YAAY,CAAC,SACX,QAAsB,iBAAiB;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,IACH,eAAe,CAAC,OACd,QAAc,iBAAiB,mBAAmB,EAAE,CAAC,IAAI;AAAA,MACvD,QAAQ;AAAA,IACV,CAAC;AAAA,IACH,OAAOA,WAA+C,CAAC,GAAG;AACxD,YAAM,QAAQ,IAAI,gBAAgB;AAClC,UAAIA,SAAQ,UAAU,QAAW;AAC/B,cAAM,IAAI,SAAS,OAAOA,SAAQ,KAAK,CAAC;AAAA,MAC1C;AACA,UAAIA,SAAQ,WAAW,QAAW;AAChC,cAAM,IAAI,UAAUA,SAAQ,MAAM;AAAA,MACpC;AACA,YAAM,SAAS,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK;AAC9C,aAAO,QAAoB,aAAa,MAAM,IAAI,EAAE,QAAQ,MAAM,CAAC;AAAA,IACrE;AAAA,IACA,OAAO,CAAC,OAAe,UAAU,EAAE;AAAA,IACnC,WAAW,IAAY,cAAiC;AACtD,aAAO,WAAW,MAAM,IAAI,WAAW,YAAY;AAAA,IACrD;AAAA,EACF;AACF;",
6
5
  "names": ["options"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/error.ts"],
4
- "sourcesContent": ["import { stableErrorCode } from \"../internal/customer-http\";\nimport { ZappieError } from \"./sdk\";\n\n/**\n * A customer API failure with its HTTP status, API code and optional validation issues.\n * Statuses 429 and 5xx are marked retryable.\n */\nexport class CustomerError extends ZappieError {\n readonly issues?: unknown;\n\n constructor(\n status: number,\n readonly apiCode: string,\n message: string,\n param?: string,\n issues?: unknown,\n ) {\n super(stableErrorCode(status), message, {\n status,\n retryable: status === 429 || status >= 500,\n ...(param !== undefined ? { param } : {}),\n });\n if (issues !== undefined) {\n this.issues = issues;\n }\n }\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAMrB,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAG7C,YACE,QACS,SACT,SACA,OACA,QACA;AACA,UAAM,gBAAgB,MAAM,GAAG,SAAS;AAAA,MACtC;AAAA,MACA,WAAW,WAAW,OAAO,UAAU;AAAA,MACvC,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,IACzC,CAAC;AATQ;AAUT,QAAI,WAAW,QAAW;AACxB,WAAK,SAAS;AAAA,IAChB;AAAA,EACF;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/index.ts"],
4
- "sourcesContent": ["export { createCustomer } from \"./client\";\nexport type { CustomerClient } from \"./client\";\nexport { CustomerError } from \"./error\";\nexport type * from \"./types\";\n"],
5
4
  "mappings": ";AAAA,SAAS,sBAAsB;AAE/B,SAAS,qBAAqB;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/navigation.ts"],
4
- "sourcesContent": ["import { sameOriginPath } from \"../internal/return-path\";\n\nexport function safeReturnTo(value: string | undefined): string {\n return sameOriginPath(value) ?? \"/\";\n}\n\nexport function currentPath(): string {\n if (typeof location === \"undefined\") {\n return \"/\";\n }\n return location.pathname + location.search;\n}\n\nexport function signIn(path: string): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const link = document.createElement(\"a\");\n link.href = `/_zappie/login?return=${encodeURIComponent(path)}`;\n link.target = \"_top\";\n link.click();\n}\n\nexport function signOut(path: string): void {\n if (typeof document === \"undefined\") {\n return;\n }\n const form = document.createElement(\"form\");\n form.method = \"POST\";\n form.action = path;\n form.target = \"_top\";\n form.hidden = true;\n document.body.appendChild(form);\n form.submit();\n form.remove();\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,sBAAsB;AAExB,SAAS,aAAa,OAAmC;AAC9D,SAAO,eAAe,KAAK,KAAK;AAClC;AAEO,SAAS,cAAsB;AACpC,MAAI,OAAO,aAAa,aAAa;AACnC,WAAO;AAAA,EACT;AACA,SAAO,SAAS,WAAW,SAAS;AACtC;AAEO,SAAS,OAAO,MAAoB;AACzC,MAAI,OAAO,aAAa,aAAa;AACnC;AAAA,EACF;AACA,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,OAAO,yBAAyB,mBAAmB,IAAI,CAAC;AAC7D,OAAK,SAAS;AACd,OAAK,MAAM;AACb;AAEO,SAAS,QAAQ,MAAoB;AAC1C,MAAI,OAAO,aAAa,aAAa;AACnC;AAAA,EACF;AACA,QAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,SAAS;AACd,OAAK,SAAS;AACd,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,OAAO;AACZ,OAAK,OAAO;AACd;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/react.tsx"],
4
- "sourcesContent": ["\"use client\";\n\nimport type { CustomerClient, SessionCustomer } from \"@zappie/sdk/customer\";\nimport {\n createContext,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useSyncExternalStore,\n} from \"react\";\n\nconst CustomerContext = createContext<CustomerClient | null>(null);\n\n/**\n * Provide a customer client to descendant hooks and check its session if the state is unknown.\n * You remain responsible for disposing the client when it is no longer needed.\n */\nexport function CustomerProvider({\n client,\n children,\n}: {\n client: CustomerClient;\n children: ReactNode;\n}) {\n useEffect(() => {\n if (client.state === \"unknown\") {\n void client.session().catch(() => undefined);\n }\n }, [client]);\n return (\n <CustomerContext.Provider value={client}>\n {children}\n </CustomerContext.Provider>\n );\n}\n\nfunction useClient(): CustomerClient {\n const client = useContext(CustomerContext);\n if (!client) {\n throw new Error(\"CustomerProvider is missing\");\n }\n return client;\n}\n\nfunction key(client: CustomerClient): string {\n const customer = client.customer;\n return [\n client.state,\n customer?.id,\n customer?.given_name,\n customer?.phone_masked,\n ].join(\":\");\n}\n\n/**\n * Subscribe to the nearest CustomerProvider and return auth state, customer and session actions.\n * Throws if you have not mounted a CustomerProvider.\n */\nexport function useAuth() {\n const client = useClient();\n const subscribe = useCallback(\n (notify: () => void) => client.onChange(() => notify()),\n [client],\n );\n const snapshot = useCallback(() => key(client), [client]);\n // The server reads the same client state, so an initialCustomer renders signed in on both sides.\n const value = useSyncExternalStore(subscribe, snapshot, snapshot);\n return useMemo(\n () => ({\n state: client.state,\n customer: client.customer,\n signIn: client.signIn,\n signOut: client.signOut,\n refresh: client.session,\n }),\n [client, value],\n );\n}\n\n/**\n * Subscribe to the nearest CustomerProvider and return its session customer, or null.\n * Throws if you have not mounted a CustomerProvider.\n */\nexport function useCustomer(): SessionCustomer | null {\n return useAuth().customer;\n}\n"],
5
4
  "mappings": ";;;AAGA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAqBH;AAnBJ,IAAM,kBAAkB,cAAqC,IAAI;AAM1D,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AACF,GAGG;AACD,YAAU,MAAM;AACd,QAAI,OAAO,UAAU,WAAW;AAC9B,WAAK,OAAO,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AACX,SACE,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,QAC9B,UACH;AAEJ;AAEA,SAAS,YAA4B;AACnC,QAAM,SAAS,WAAW,eAAe;AACzC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,6BAA6B;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,IAAI,QAAgC;AAC3C,QAAM,WAAW,OAAO;AACxB,SAAO;AAAA,IACL,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,EACZ,EAAE,KAAK,GAAG;AACZ;AAMO,SAAS,UAAU;AACxB,QAAM,SAAS,UAAU;AACzB,QAAM,YAAY;AAAA,IAChB,CAAC,WAAuB,OAAO,SAAS,MAAM,OAAO,CAAC;AAAA,IACtD,CAAC,MAAM;AAAA,EACT;AACA,QAAM,WAAW,YAAY,MAAM,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAExD,QAAM,QAAQ,qBAAqB,WAAW,UAAU,QAAQ;AAChE,SAAO;AAAA,IACL,OAAO;AAAA,MACL,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,MACjB,QAAQ,OAAO;AAAA,MACf,SAAS,OAAO;AAAA,MAChB,SAAS,OAAO;AAAA,IAClB;AAAA,IACA,CAAC,QAAQ,KAAK;AAAA,EAChB;AACF;AAMO,SAAS,cAAsC;AACpD,SAAO,QAAQ,EAAE;AACnB;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/request.ts"],
4
- "sourcesContent": ["import { apiError, requestWithRetry } from \"../internal/customer-http\";\nimport { CustomerError } from \"./error\";\n\nexport type CustomerRequest = <T>(\n path: string,\n init?: RequestInit,\n) => Promise<T>;\n\nexport function createCustomerRequest(\n base: string,\n fetcher: typeof fetch,\n unauthorized: () => void,\n): CustomerRequest {\n return async <T>(path: string, init: RequestInit = {}): Promise<T> => {\n const response = await requestWithRetry(\n fetcher,\n base + path,\n init,\n \"Could not reach the customer service\",\n );\n if (response.status === 401) {\n unauthorized();\n }\n if (!response.ok) {\n const value = await apiError(\n response,\n `The customer service answered ${response.status}`,\n );\n throw new CustomerError(\n response.status,\n value.apiCode,\n value.message,\n value.param,\n value.issues,\n );\n }\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n };\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,UAAU,wBAAwB;AAC3C,SAAS,qBAAqB;AAOvB,SAAS,sBACd,MACA,SACA,cACiB;AACjB,SAAO,OAAU,MAAc,OAAoB,CAAC,MAAkB;AACpE,UAAM,WAAW,MAAM;AAAA,MACrB;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,mBAAa;AAAA,IACf;AACA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QAAQ,MAAM;AAAA,QAClB;AAAA,QACA,iCAAiC,SAAS,MAAM;AAAA,MAClD;AACA,YAAM,IAAI;AAAA,QACR,SAAS;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,QACN,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AACA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/sdk.ts"],
4
- "sourcesContent": ["// The isolated customer bundle imports the installed core so errors retain their identity.\n// eslint-disable-next-line no-restricted-imports\nexport { ZappieError } from \"@zappie/sdk\";\n"],
5
4
  "mappings": ";AAEA,SAAS,mBAAmB;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": [],
4
- "sourcesContent": [],
5
4
  "mappings": "",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/customer/watch-order.ts"],
4
- "sourcesContent": ["import { wait } from \"../internal/customer-http\";\nimport { CustomerError } from \"./error\";\nimport type { CustomerOrder, WatchOrderOptions } from \"./types\";\n\nconst final = new Set([\"completed\", \"cancelled\", \"rejected\"]);\n\ntype ReadOrder = (id: string, signal?: AbortSignal) => Promise<CustomerOrder>;\n\nexport function watchOrder(\n base: string,\n id: string,\n read: ReadOrder,\n options: WatchOrderOptions,\n): () => void {\n const controller = new AbortController();\n const pollMs = options.pollMs ?? 15_000;\n let source: EventSource | undefined;\n let failures = 0;\n let stopped = false;\n const stop = () => {\n if (stopped) {\n return;\n }\n stopped = true;\n source?.close();\n controller.abort();\n options.signal?.removeEventListener(\"abort\", stop);\n };\n const publish = (order: CustomerOrder) => {\n options.onOrder(order);\n if (final.has(order.status)) {\n stop();\n }\n };\n const poll = async () => {\n while (!stopped) {\n try {\n publish(await read(id, controller.signal));\n } catch (failure) {\n if (\n failure instanceof CustomerError &&\n failure.status !== undefined &&\n failure.status >= 400 &&\n failure.status < 500\n ) {\n stop();\n return;\n }\n }\n if (!stopped) {\n await wait(pollMs, controller.signal).catch(() => undefined);\n }\n }\n };\n const connect = () => {\n if (stopped || typeof EventSource === \"undefined\") {\n void poll();\n return;\n }\n source = new EventSource(\n `${base}/me/orders/${encodeURIComponent(id)}/events`,\n );\n source.addEventListener(\"status\", (event) => {\n failures = 0;\n let next: CustomerOrder;\n try {\n next = JSON.parse((event as MessageEvent).data) as CustomerOrder;\n } catch {\n return;\n }\n publish(next);\n });\n source.onerror = () => {\n source?.close();\n failures++;\n if (failures >= 2) {\n void poll();\n } else {\n connect();\n }\n };\n };\n options.signal?.addEventListener(\"abort\", stop, { once: true });\n if (options.signal?.aborted) {\n stop();\n } else {\n connect();\n }\n return stop;\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAG9B,IAAM,QAAQ,oBAAI,IAAI,CAAC,aAAa,aAAa,UAAU,CAAC;AAIrD,SAAS,WACd,MACA,IACA,MACA,SACY;AACZ,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI;AACJ,MAAI,WAAW;AACf,MAAI,UAAU;AACd,QAAM,OAAO,MAAM;AACjB,QAAI,SAAS;AACX;AAAA,IACF;AACA,cAAU;AACV,YAAQ,MAAM;AACd,eAAW,MAAM;AACjB,YAAQ,QAAQ,oBAAoB,SAAS,IAAI;AAAA,EACnD;AACA,QAAM,UAAU,CAAC,UAAyB;AACxC,YAAQ,QAAQ,KAAK;AACrB,QAAI,MAAM,IAAI,MAAM,MAAM,GAAG;AAC3B,WAAK;AAAA,IACP;AAAA,EACF;AACA,QAAM,OAAO,YAAY;AACvB,WAAO,CAAC,SAAS;AACf,UAAI;AACF,gBAAQ,MAAM,KAAK,IAAI,WAAW,MAAM,CAAC;AAAA,MAC3C,SAAS,SAAS;AAChB,YACE,mBAAmB,iBACnB,QAAQ,WAAW,UACnB,QAAQ,UAAU,OAClB,QAAQ,SAAS,KACjB;AACA,eAAK;AACL;AAAA,QACF;AAAA,MACF;AACA,UAAI,CAAC,SAAS;AACZ,cAAM,KAAK,QAAQ,WAAW,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,MAC7D;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,MAAM;AACpB,QAAI,WAAW,OAAO,gBAAgB,aAAa;AACjD,WAAK,KAAK;AACV;AAAA,IACF;AACA,aAAS,IAAI;AAAA,MACX,GAAG,IAAI,cAAc,mBAAmB,EAAE,CAAC;AAAA,IAC7C;AACA,WAAO,iBAAiB,UAAU,CAAC,UAAU;AAC3C,iBAAW;AACX,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAO,MAAuB,IAAI;AAAA,MAChD,QAAQ;AACN;AAAA,MACF;AACA,cAAQ,IAAI;AAAA,IACd,CAAC;AACD,WAAO,UAAU,MAAM;AACrB,cAAQ,MAAM;AACd;AACA,UAAI,YAAY,GAAG;AACjB,aAAK,KAAK;AAAA,MACZ,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AACA,UAAQ,QAAQ,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAC9D,MAAI,QAAQ,QAAQ,SAAS;AAC3B,SAAK;AAAA,EACP,OAAO;AACL,YAAQ;AAAA,EACV;AACA,SAAO;AACT;",
6
5
  "names": []
7
6
  }
package/dist/debug.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/debug.ts
2
- var VERSION = true ? "0.2.0" : "0.0.0-development";
2
+ var VERSION = true ? "0.3.0" : "0.0.0-development";
3
3
  function installDebug(client) {
4
4
  if (typeof window === "undefined" || client.mode !== "test") {
5
5
  return;
package/dist/debug.js.map CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/debug.ts"],
4
- "sourcesContent": ["import type { StoredLine } from \"./cart/storage\";\nimport type { CartLineInput, Quote } from \"./cart/types\";\nimport type { Zappie } from \"./client\";\nimport type { Branch, Menu } from \"./models\";\n\n// Inline like VERSION in index.ts: tsup fills in the version from package.json.\ndeclare const __ZAPPIE_SDK_VERSION__: string | undefined;\nconst VERSION =\n typeof __ZAPPIE_SDK_VERSION__ === \"string\"\n ? __ZAPPIE_SDK_VERSION__\n : \"0.0.0-development\";\n\nexport type DebugEvent = {\n name: string;\n detail: unknown;\n at: string;\n};\n\nexport type ZappieDebug = {\n version: string;\n store: string;\n /** Read this cart's branch and full menu through its client. */\n catalogue(): Promise<{ menu: Menu; branch: Branch }>;\n cart: (() => ReturnType<Zappie[\"cart\"][\"getSnapshot\"]>) & {\n /**\n * Save test cart lines for the next reload. Reload to reconcile them with today's menu.\n * Your unitPrice is only a local estimate, like a price edited in browser storage.\n * The server re-prices every line and never trusts this estimate.\n */\n inject(input: {\n lines: (CartLineInput & Pick<StoredLine, \"unitPrice\">)[];\n }): Promise<void>;\n };\n quote: () => Quote | undefined;\n events: DebugEvent[];\n};\n\ndeclare global {\n interface Window {\n __zappie?: ZappieDebug;\n }\n // On globalThis so the lazy checkout and customer entries can reach it\n // with the shortest possible call; in a browser it is window's.\n var __zappieEmit: ((name: string, detail: unknown) => void) | undefined;\n}\n\nexport function installDebug(client: Zappie) {\n if (typeof window === \"undefined\" || client.mode !== \"test\") {\n return;\n }\n const events: DebugEvent[] = [];\n let quote: Quote | undefined;\n const emit = (name: string, detail: unknown) => {\n const event = { name, detail, at: new Date().toISOString() };\n events.push(event);\n window.dispatchEvent(new CustomEvent(\"zappie:event\", { detail: event }));\n };\n globalThis.__zappieEmit = emit;\n window.__zappie = {\n version: VERSION,\n store: client.config.store,\n async catalogue() {\n await client.cart.hydrate();\n const branch = (await client.store.get()).branches.find(\n (value) => value.id === client.cart.getSnapshot().branchId,\n )!;\n return { branch, menu: await client.menu.get({ branch: branch.id }) };\n },\n cart: Object.assign(() => client.cart.getSnapshot(), {\n async inject(input: {\n lines: (CartLineInput & Pick<StoredLine, \"unitPrice\">)[];\n }) {\n await client.cart.hydrate();\n if (\n input.lines.length > 100 ||\n input.lines.some(\n (line) =>\n !Number.isInteger(line.quantity ?? 1) ||\n (line.quantity ?? 1) < 1 ||\n (line.quantity ?? 1) > 99,\n )\n ) {\n throw new Error(\n \"Use up to 100 cart lines with whole quantities from 1 to 99.\",\n );\n }\n const snapshot = client.cart.getSnapshot();\n const value = {\n v: 1,\n menuVersion: client._internal.cartCatalogue()!.version,\n updatedAt: Date.now(),\n fulfilment: snapshot.fulfilment,\n promoCode: snapshot.promoCode,\n lines: input.lines.map((line, index) => ({\n ...line,\n id: `debug_${index}`,\n name: line.itemId,\n quantity: line.quantity ?? 1,\n choices: (line.choices ?? []).map((choice) => ({\n ...choice,\n quantity: choice.quantity ?? 1,\n })),\n inputs: line.inputs ?? {},\n addedAt: new Date().toISOString(),\n })),\n };\n const scope = client.config.cart?.storageScope;\n (client.config.storage ?? window.localStorage).setItem(\n `zappie:cart:v1:${client.config.store}:${snapshot.branchId}${scope ? `:${scope}` : \"\"}`,\n JSON.stringify(value),\n );\n },\n }),\n quote: () => quote,\n events,\n };\n client.on(\"add_to_cart\", (detail) => emit(\"add_to_cart\", detail));\n client.cart.subscribe(() => {\n const state = client.cart.state;\n const next = \"quote\" in state ? state.quote : undefined;\n if (next && next !== quote) {\n quote = next;\n emit(\"quote\", next);\n }\n });\n}\n"],
5
4
  "mappings": ";AAOA,IAAM,UACJ,OACI,UACA;AAoCC,SAAS,aAAa,QAAgB;AAC3C,MAAI,OAAO,WAAW,eAAe,OAAO,SAAS,QAAQ;AAC3D;AAAA,EACF;AACA,QAAM,SAAuB,CAAC;AAC9B,MAAI;AACJ,QAAM,OAAO,CAAC,MAAc,WAAoB;AAC9C,UAAM,QAAQ,EAAE,MAAM,QAAQ,KAAI,oBAAI,KAAK,GAAE,YAAY,EAAE;AAC3D,WAAO,KAAK,KAAK;AACjB,WAAO,cAAc,IAAI,YAAY,gBAAgB,EAAE,QAAQ,MAAM,CAAC,CAAC;AAAA,EACzE;AACA,aAAW,eAAe;AAC1B,SAAO,WAAW;AAAA,IAChB,SAAS;AAAA,IACT,OAAO,OAAO,OAAO;AAAA,IACrB,MAAM,YAAY;AAChB,YAAM,OAAO,KAAK,QAAQ;AAC1B,YAAM,UAAU,MAAM,OAAO,MAAM,IAAI,GAAG,SAAS;AAAA,QACjD,CAAC,UAAU,MAAM,OAAO,OAAO,KAAK,YAAY,EAAE;AAAA,MACpD;AACA,aAAO,EAAE,QAAQ,MAAM,MAAM,OAAO,KAAK,IAAI,EAAE,QAAQ,OAAO,GAAG,CAAC,EAAE;AAAA,IACtE;AAAA,IACA,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK,YAAY,GAAG;AAAA,MACnD,MAAM,OAAO,OAEV;AACD,cAAM,OAAO,KAAK,QAAQ;AAC1B,YACE,MAAM,MAAM,SAAS,OACrB,MAAM,MAAM;AAAA,UACV,CAAC,SACC,CAAC,OAAO,UAAU,KAAK,YAAY,CAAC,MACnC,KAAK,YAAY,KAAK,MACtB,KAAK,YAAY,KAAK;AAAA,QAC3B,GACA;AACA,gBAAM,IAAI;AAAA,YACR;AAAA,UACF;AAAA,QACF;AACA,cAAM,WAAW,OAAO,KAAK,YAAY;AACzC,cAAM,QAAQ;AAAA,UACZ,GAAG;AAAA,UACH,aAAa,OAAO,UAAU,cAAc,EAAG;AAAA,UAC/C,WAAW,KAAK,IAAI;AAAA,UACpB,YAAY,SAAS;AAAA,UACrB,WAAW,SAAS;AAAA,UACpB,OAAO,MAAM,MAAM,IAAI,CAAC,MAAM,WAAW;AAAA,YACvC,GAAG;AAAA,YACH,IAAI,SAAS,KAAK;AAAA,YAClB,MAAM,KAAK;AAAA,YACX,UAAU,KAAK,YAAY;AAAA,YAC3B,UAAU,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY;AAAA,cAC7C,GAAG;AAAA,cACH,UAAU,OAAO,YAAY;AAAA,YAC/B,EAAE;AAAA,YACF,QAAQ,KAAK,UAAU,CAAC;AAAA,YACxB,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,UAClC,EAAE;AAAA,QACJ;AACA,cAAM,QAAQ,OAAO,OAAO,MAAM;AAClC,SAAC,OAAO,OAAO,WAAW,OAAO,cAAc;AAAA,UAC7C,kBAAkB,OAAO,OAAO,KAAK,IAAI,SAAS,QAAQ,GAAG,QAAQ,IAAI,KAAK,KAAK,EAAE;AAAA,UACrF,KAAK,UAAU,KAAK;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAAA,IACD,OAAO,MAAM;AAAA,IACb;AAAA,EACF;AACA,SAAO,GAAG,eAAe,CAAC,WAAW,KAAK,eAAe,MAAM,CAAC;AAChE,SAAO,KAAK,UAAU,MAAM;AAC1B,UAAM,QAAQ,OAAO,KAAK;AAC1B,UAAM,OAAO,WAAW,QAAQ,MAAM,QAAQ;AAC9C,QAAI,QAAQ,SAAS,OAAO;AAC1B,cAAQ;AACR,WAAK,SAAS,IAAI;AAAA,IACpB;AAAA,EACF,CAAC;AACH;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/errors.ts"],
4
- "sourcesContent": ["/** Stable public error codes. */\nexport type ZappieErrorCode =\n | \"network_error\"\n | \"timeout\"\n | \"rate_limited\"\n | \"unauthorized\"\n | \"forbidden_origin\"\n | \"not_found\"\n | \"store_not_found\"\n | \"branch_not_found\"\n | \"item_not_found\"\n | \"cart_invalid\"\n | \"invalid_request\"\n | \"version_unsupported\"\n | \"server_error\";\nconst codes: readonly string[] = [\n \"network_error\",\n \"timeout\",\n \"rate_limited\",\n \"unauthorized\",\n \"forbidden_origin\",\n \"not_found\",\n \"store_not_found\",\n \"branch_not_found\",\n \"item_not_found\",\n \"cart_invalid\",\n \"invalid_request\",\n \"version_unsupported\",\n \"server_error\",\n];\n/** Context attached to an SDK failure. */\nexport type ErrorDetails = {\n status?: number;\n param?: string;\n docUrl?: string;\n requestId?: string;\n retryable?: boolean;\n cause?: unknown;\n};\n/** An SDK error you can handle by its stable code. */\nexport class ZappieError extends Error {\n readonly status?: number;\n readonly param?: string;\n readonly docUrl: string;\n readonly requestId?: string;\n readonly retryable: boolean;\n constructor(\n readonly code: ZappieErrorCode,\n message: string,\n details: ErrorDetails = {},\n ) {\n super(message, { cause: details.cause });\n this.name = \"ZappieError\";\n if (details.status !== undefined) {\n this.status = details.status;\n }\n if (details.param !== undefined) {\n this.param = details.param;\n }\n if (details.requestId !== undefined) {\n this.requestId = details.requestId;\n }\n this.docUrl = details.docUrl ?? `https://docs.zappie.ai/errors#${code}`;\n this.retryable =\n details.retryable ??\n [\"network_error\", \"timeout\", \"rate_limited\", \"server_error\"].includes(\n code,\n );\n }\n}\n/** Check whether a thrown value is a ZappieError. */\nexport function isZappieError(error: unknown): error is ZappieError {\n return error instanceof ZappieError;\n}\nexport function asError(error: unknown): ZappieError {\n return isZappieError(error)\n ? error\n : new ZappieError(\"network_error\", \"The storefront request failed\", {\n cause: error,\n });\n}\nexport function wireError(\n value: unknown,\n status: number,\n requestId?: string,\n): ZappieError {\n const envelope =\n value && typeof value === \"object\"\n ? (value as Record<string, unknown>)\n : {};\n const e =\n envelope.error && typeof envelope.error === \"object\"\n ? (envelope.error as Record<string, unknown>)\n : {};\n const fallback =\n status === 401\n ? \"unauthorized\"\n : status === 403\n ? \"forbidden_origin\"\n : status === 404\n ? \"not_found\"\n : status === 429\n ? \"rate_limited\"\n : status >= 500\n ? \"server_error\"\n : \"invalid_request\";\n const known = typeof e.code === \"string\" && codes.includes(e.code);\n const code = known\n ? (e.code as ZappieErrorCode)\n : e.code\n ? \"server_error\"\n : fallback;\n const message =\n typeof e.message === \"string\" ? e.message : \"The storefront request failed\";\n return new ZappieError(\n code,\n !known && e.code ? `${String(e.code)}: ${message}` : message,\n {\n status,\n ...(requestId ? { requestId } : {}),\n ...(typeof e.param === \"string\" ? { param: e.param } : {}),\n ...(typeof e.doc_url === \"string\" ? { docUrl: e.doc_url } : {}),\n },\n );\n}\n"],
5
4
  "mappings": ";AAeA,IAAM,QAA2B;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAWO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAMrC,YACW,MACT,SACA,UAAwB,CAAC,GACzB;AACA,UAAM,SAAS,EAAE,OAAO,QAAQ,MAAM,CAAC;AAJ9B;AAKT,SAAK,OAAO;AACZ,QAAI,QAAQ,WAAW,QAAW;AAChC,WAAK,SAAS,QAAQ;AAAA,IACxB;AACA,QAAI,QAAQ,UAAU,QAAW;AAC/B,WAAK,QAAQ,QAAQ;AAAA,IACvB;AACA,QAAI,QAAQ,cAAc,QAAW;AACnC,WAAK,YAAY,QAAQ;AAAA,IAC3B;AACA,SAAK,SAAS,QAAQ,UAAU,iCAAiC,IAAI;AACrE,SAAK,YACH,QAAQ,aACR,CAAC,iBAAiB,WAAW,gBAAgB,cAAc,EAAE;AAAA,MAC3D;AAAA,IACF;AAAA,EACJ;AACF;AAEO,SAAS,cAAc,OAAsC;AAClE,SAAO,iBAAiB;AAC1B;AACO,SAAS,QAAQ,OAA6B;AACnD,SAAO,cAAc,KAAK,IACtB,QACA,IAAI,YAAY,iBAAiB,iCAAiC;AAAA,IAChE,OAAO;AAAA,EACT,CAAC;AACP;AACO,SAAS,UACd,OACA,QACA,WACa;AACb,QAAM,WACJ,SAAS,OAAO,UAAU,WACrB,QACD,CAAC;AACP,QAAM,IACJ,SAAS,SAAS,OAAO,SAAS,UAAU,WACvC,SAAS,QACV,CAAC;AACP,QAAM,WACJ,WAAW,MACP,iBACA,WAAW,MACT,qBACA,WAAW,MACT,cACA,WAAW,MACT,iBACA,UAAU,MACR,iBACA;AACd,QAAM,QAAQ,OAAO,EAAE,SAAS,YAAY,MAAM,SAAS,EAAE,IAAI;AACjE,QAAM,OAAO,QACR,EAAE,OACH,EAAE,OACA,iBACA;AACN,QAAM,UACJ,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU;AAC9C,SAAO,IAAI;AAAA,IACT;AAAA,IACA,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,EAAE,IAAI,CAAC,KAAK,OAAO,KAAK;AAAA,IACrD;AAAA,MACE;AAAA,MACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACjC,GAAI,OAAO,EAAE,UAAU,WAAW,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,MACxD,GAAI,OAAO,EAAE,YAAY,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/D;AAAA,EACF;AACF;",
6
5
  "names": []
7
6
  }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { a as ZappieError, T as Transport, R as RequestCtx, f as MenuFulfilment, g as MenuResponse, j as SearchRequest, k as SearchPage, l as MenuVersion } from './client-B377AzNC.js';
1
+ import { a as ZappieError, T as Transport, R as RequestCtx, f as MenuFulfilment, g as MenuResponse, j as SearchRequest, k as SearchPage, l as MenuVersion } from './client-DHxakZrv.js';
2
2
  import { SelectedMenuRequest, SelectedMenuResponse } from './transport/selected-menu.js';
3
- export { A as API_VERSION, E as ErrorDetails, m as MenuReadOptions, M as MenuResource, n as ReadOptions, S as StorageLike, c as StoreResource, Z as Zappie, b as ZappieClientOptions, b as ZappieConfig, e as ZappieErrorCode, d as ZappieEvents, o as createZappie, p as isZappieError } from './client-B377AzNC.js';
4
- import { f as CartIssue, b as CartLineInput, C as CartLine, Q as QuoteRequest, g as WireQuote } from './types-B3Ghmpmy.js';
5
- export { h as CartFix, c as CartFulfilment, i as CartIssueCode, d as CartModule, j as CartSnapshot, e as CartState, k as CartSyncAdapter, l as CartSyncDocument, D as DeliveryAddress, a as Quote, m as WireFix, W as WireFulfilment, n as WireIssue, o as WireLine, p as WireMoney } from './types-B3Ghmpmy.js';
6
- import { I as Item, O as OptionGroup, M as Money, a as Menu, B as Branch, S as Store, C as CategoryPage } from './models-tf9rNoki.js';
7
- export { b as Category, c as Choice, d as Currency, F as Formatters, e as Fulfilment, f as MenuSchedule, g as MenuSection, h as MoneyFormatOptions, i as OpeningTime, j as OptionCondition, P as PaymentMethodKind, k as SpecialHours, V as Variant, l as format, m as money } from './models-tf9rNoki.js';
3
+ export { A as API_VERSION, E as ErrorDetails, m as MenuReadOptions, M as MenuResource, n as ReadOptions, S as StorageLike, c as StoreResource, Z as Zappie, b as ZappieClientOptions, b as ZappieConfig, e as ZappieErrorCode, d as ZappieEvents, o as createZappie, p as isZappieError } from './client-DHxakZrv.js';
4
+ import { f as CartIssue, b as CartLineInput, C as CartLine, Q as QuoteRequest, g as WireQuote } from './types-CxY5C6AN.js';
5
+ export { h as CartFix, c as CartFulfilment, i as CartIssueCode, d as CartModule, j as CartSnapshot, e as CartState, k as CartSyncAdapter, l as CartSyncDocument, D as DeliveryAddress, a as Quote, m as WireFix, W as WireFulfilment, n as WireIssue, o as WireLine, p as WireMoney } from './types-CxY5C6AN.js';
6
+ import { I as Item, O as OptionGroup, M as Money, a as Menu, B as Branch, S as Store, C as CategoryPage } from './models-C-kEg1Id.js';
7
+ export { b as Category, c as Choice, d as Currency, F as Formatters, e as Fulfilment, f as MenuSchedule, g as MenuSection, h as MoneyFormatOptions, i as OpeningTime, j as OptionCondition, P as PaymentMethodKind, k as SpecialHours, V as Variant, l as format, m as money } from './models-C-kEg1Id.js';
8
8
 
9
9
  declare class ZappieCartError extends ZappieError {
10
10
  readonly issues: CartIssue[];
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { ZappieError, isZappieError } from "./errors.js";
10
10
  export * from "./models.js";
11
11
  import { format, money } from "./money.js";
12
12
  import { RestTransport } from "./transport/rest.js";
13
- var VERSION = true ? "0.2.0" : "0.0.0-development";
13
+ var VERSION = true ? "0.3.0" : "0.0.0-development";
14
14
  export {
15
15
  API_VERSION,
16
16
  RestTransport,
package/dist/index.js.map CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["// tsup replaces __ZAPPIE_SDK_VERSION__ with package.json's version. Source run\n// without a build, as in a plain Node script, gets the development label.\n// Kept inline, not in a shared module, so the core chunks don't split differently.\ndeclare const __ZAPPIE_SDK_VERSION__: string | undefined;\n\n/** The installed @zappie/sdk version. */\nexport const VERSION: string =\n typeof __ZAPPIE_SDK_VERSION__ === \"string\"\n ? __ZAPPIE_SDK_VERSION__\n : \"0.0.0-development\";\nexport { ZappieCartError } from \"./cart/errors\";\nexport { previewItem } from \"./cart/preview\";\nexport { estimateUnitPrice } from \"./cart/price\";\nexport { resolveSourceMenu } from \"./cart/source-menu\";\nexport type * from \"./cart/types\";\nexport { validateLine } from \"./cart/validate\";\nexport { createZappie } from \"./client\";\nexport type {\n MenuFulfilment,\n MenuReadOptions,\n MenuResource,\n ReadOptions,\n StoreResource,\n Zappie,\n ZappieEvents,\n} from \"./client\";\nexport { API_VERSION } from \"./config\";\nexport type {\n StorageLike,\n ZappieConfig as ZappieClientOptions,\n ZappieConfig,\n} from \"./config\";\nexport { ZappieError, isZappieError } from \"./errors\";\nexport type { ErrorDetails, ZappieErrorCode } from \"./errors\";\nexport * from \"./models\";\nexport { format, money } from \"./money\";\nexport type { Currency, Formatters, Money, MoneyFormatOptions } from \"./money\";\nexport { RestTransport } from \"./transport/rest\";\nexport type { RestTransportOptions } from \"./transport/rest\";\nexport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchPage,\n SearchRequest,\n Transport,\n} from \"./transport/types\";\n"],
5
4
  "mappings": ";AAUA,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAElC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAU7B,SAAS,mBAAmB;AAM5B,SAAS,aAAa,qBAAqB;AAE3C,cAAc;AACd,SAAS,QAAQ,aAAa;AAE9B,SAAS,qBAAqB;AA/BvB,IAAM,UACX,OACI,UACA;",
6
5
  "names": []
7
6
  }
@@ -1,6 +1,6 @@
1
1
  import { RequestCtx } from '../index.js';
2
- import '../models-tf9rNoki.js';
3
- import '../types-B3Ghmpmy.js';
2
+ import '../models-C-kEg1Id.js';
3
+ import '../types-CxY5C6AN.js';
4
4
 
5
5
  /** Public integration IDs and browser keys. Your app decides when to load scripts. */
6
6
  type Integrations = {
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/integrations/index.ts"],
4
- "sourcesContent": ["import { requestWithRetry } from \"../internal/customer-http\";\nimport {\n API_VERSION,\n RestTransport,\n validateConfig,\n ZappieError,\n} from \"../places/sdk\";\nimport type { RequestCtx } from \"../transport/types\";\n\n/** Public integration IDs and browser keys. Your app decides when to load scripts. */\nexport type Integrations = {\n googleMaps?: { browserKey: string };\n googleAnalytics?: { measurementId: string };\n metaPixel?: { pixelId: string };\n};\n\n/** A backend that supplies public integration IDs and browser keys without loading scripts. */\nexport type IntegrationsTransport = {\n getIntegrations(ctx: RequestCtx): Promise<Integrations>;\n};\n\n/** Credentials and optional connection settings for the integrations read. */\nexport type IntegrationsOptions = {\n key: string;\n baseUrl?: string;\n version?: string;\n fetch?: typeof fetch;\n transport?: IntegrationsTransport;\n};\n\n/**\n * Create a client that reads public integration IDs and browser keys. It never loads tracking scripts.\n * Import this entry when your app needs the IDs, then apply your consent policy.\n */\nexport function createIntegrations(options: IntegrationsOptions) {\n validateConfig({ store: \"integrations\", key: options.key });\n const base = new RestTransport(options).baseUrl;\n return {\n /** Read enabled integrations. An absent integration has no public ID to use. */\n async get(read: { signal?: AbortSignal } = {}): Promise<Integrations> {\n const ctx = {\n key: options.key,\n version: options.version ?? API_VERSION,\n ...read,\n };\n if (options.transport) {\n return options.transport.getIntegrations(ctx);\n }\n const response = await requestWithRetry(\n options.fetch ?? ((input, init) => globalThis.fetch(input, init)),\n `${base}/store/integrations`,\n {\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${ctx.key}`,\n \"Zappie-Version\": ctx.version,\n },\n ...read,\n },\n \"Could not read storefront integrations\",\n );\n if (!response.ok) {\n throw new ZappieError(\n \"server_error\",\n \"Could not read storefront integrations\",\n { status: response.status },\n );\n }\n const value: unknown = await response.json();\n const record = (value: unknown): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ZappieError(\n \"server_error\",\n \"Invalid storefront integrations\",\n );\n }\n return value as Record<string, unknown>;\n };\n const raw = record(value);\n const id = (value: unknown, key: string): string => {\n const result = record(value)[key];\n if (typeof result !== \"string\" || !result) {\n throw new ZappieError(\n \"server_error\",\n \"Invalid storefront integration ID\",\n );\n }\n return result;\n };\n return {\n ...(raw.google_maps != null\n ? { googleMaps: { browserKey: id(raw.google_maps, \"browser_key\") } }\n : {}),\n ...(raw.google_analytics != null\n ? {\n googleAnalytics: {\n measurementId: id(raw.google_analytics, \"measurement_id\"),\n },\n }\n : {}),\n ...(raw.meta_pixel != null\n ? { metaPixel: { pixelId: id(raw.meta_pixel, \"pixel_id\") } }\n : {}),\n };\n },\n };\n}\n\n/** The integrations read returned by createIntegrations. */\nexport type IntegrationsClient = ReturnType<typeof createIntegrations>;\n"],
5
4
  "mappings": ";AAAA,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA4BA,SAAS,mBAAmB,SAA8B;AAC/D,iBAAe,EAAE,OAAO,gBAAgB,KAAK,QAAQ,IAAI,CAAC;AAC1D,QAAM,OAAO,IAAI,cAAc,OAAO,EAAE;AACxC,SAAO;AAAA;AAAA,IAEL,MAAM,IAAI,OAAiC,CAAC,GAA0B;AACpE,YAAM,MAAM;AAAA,QACV,KAAK,QAAQ;AAAA,QACb,SAAS,QAAQ,WAAW;AAAA,QAC5B,GAAG;AAAA,MACL;AACA,UAAI,QAAQ,WAAW;AACrB,eAAO,QAAQ,UAAU,gBAAgB,GAAG;AAAA,MAC9C;AACA,YAAM,WAAW,MAAM;AAAA,QACrB,QAAQ,UAAU,CAAC,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI;AAAA,QAC/D,GAAG,IAAI;AAAA,QACP;AAAA,UACE,UAAU;AAAA,UACV,SAAS;AAAA,YACP,eAAe,UAAU,IAAI,GAAG;AAAA,YAChC,kBAAkB,IAAI;AAAA,UACxB;AAAA,UACA,GAAG;AAAA,QACL;AAAA,QACA;AAAA,MACF;AACA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI;AAAA,UACR;AAAA,UACA;AAAA,UACA,EAAE,QAAQ,SAAS,OAAO;AAAA,QAC5B;AAAA,MACF;AACA,YAAM,QAAiB,MAAM,SAAS,KAAK;AAC3C,YAAM,SAAS,CAACA,WAA4C;AAC1D,YAAI,CAACA,UAAS,OAAOA,WAAU,YAAY,MAAM,QAAQA,MAAK,GAAG;AAC/D,gBAAM,IAAI;AAAA,YACR;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,eAAOA;AAAA,MACT;AACA,YAAM,MAAM,OAAO,KAAK;AACxB,YAAM,KAAK,CAACA,QAAgB,QAAwB;AAClD,cAAM,SAAS,OAAOA,MAAK,EAAE,GAAG;AAChC,YAAI,OAAO,WAAW,YAAY,CAAC,QAAQ;AACzC,gBAAM,IAAI;AAAA,YACR;AAAA,YACA;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,GAAI,IAAI,eAAe,OACnB,EAAE,YAAY,EAAE,YAAY,GAAG,IAAI,aAAa,aAAa,EAAE,EAAE,IACjE,CAAC;AAAA,QACL,GAAI,IAAI,oBAAoB,OACxB;AAAA,UACE,iBAAiB;AAAA,YACf,eAAe,GAAG,IAAI,kBAAkB,gBAAgB;AAAA,UAC1D;AAAA,QACF,IACA,CAAC;AAAA,QACL,GAAI,IAAI,cAAc,OAClB,EAAE,WAAW,EAAE,SAAS,GAAG,IAAI,YAAY,UAAU,EAAE,EAAE,IACzD,CAAC;AAAA,MACP;AAAA,IACF;AAAA,EACF;AACF;",
6
5
  "names": ["value"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/abort.ts"],
4
- "sourcesContent": ["export function abortReason(signal?: AbortSignal): unknown {\n return (\n signal?.reason ?? new DOMException(\"The request was aborted\", \"AbortError\")\n );\n}\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw abortReason(signal);\n }\n}\nexport function delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfAborted(signal);\n const abort = () => {\n clearTimeout(timer);\n reject(abortReason(signal));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", abort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n"],
5
4
  "mappings": ";AAAO,SAAS,YAAY,QAA+B;AACzD,SACE,QAAQ,UAAU,IAAI,aAAa,2BAA2B,YAAY;AAE9E;AACO,SAAS,eAAe,QAA4B;AACzD,MAAI,QAAQ,SAAS;AACnB,UAAM,YAAY,MAAM;AAAA,EAC1B;AACF;AACO,SAAS,MAAM,IAAY,QAAqC;AACrE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,mBAAe,MAAM;AACrB,UAAM,QAAQ,MAAM;AAClB,mBAAa,KAAK;AAClB,aAAO,YAAY,MAAM,CAAC;AAAA,IAC5B;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ,oBAAoB,SAAS,KAAK;AAC1C,cAAQ;AAAA,IACV,GAAG,EAAE;AACL,YAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,EACzD,CAAC;AACH;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/cache.ts"],
4
- "sourcesContent": ["import { asError } from \"../errors\";\nimport type { ZappieError } from \"../errors\";\nimport { abortReason, throwIfAborted } from \"./abort\";\nexport type CacheEntry<T> = {\n value?: T;\n fetchedAt: number;\n etag?: string;\n error?: ZappieError;\n failedAt?: number;\n loading: boolean;\n};\n/** After a failed background refresh, stale data is served without retrying for this long. */\nconst REFRESH_BACKOFF_MS = 10_000;\ntype Loaded<T> = { value: T; etag?: string; onStored?: () => void };\ntype Flight = {\n promise: Promise<unknown>;\n controller: AbortController;\n users: number;\n keepAlive: boolean;\n};\nconst EMPTY: CacheEntry<never> = Object.freeze({\n fetchedAt: 0,\n loading: false,\n});\n/** Client-local SWR storage. Subscribers receive stable snapshots until an entry changes. */\nexport class Cache {\n private entries = new Map<string, CacheEntry<unknown>>();\n private listeners = new Set<() => void>();\n private flights = new Map<string, Flight>();\n constructor(private onError: (error: ZappieError) => void) {}\n snapshot<T>(key: string): CacheEntry<T> {\n return (this.entries.get(key) ?? EMPTY) as CacheEntry<T>;\n }\n subscribe = (fn: () => void) => {\n this.listeners.add(fn);\n return () => {\n this.listeners.delete(fn);\n };\n };\n private notify() {\n for (const fn of [...this.listeners]) {\n fn();\n }\n }\n set<T>(key: string, value: T, etag?: string) {\n this.entries.set(key, {\n value,\n fetchedAt: Date.now(),\n loading: false,\n ...(etag !== undefined ? { etag } : {}),\n });\n this.notify();\n }\n stale(key: string) {\n const entry = this.snapshot(key);\n return entry.value !== undefined && Date.now() - entry.fetchedAt >= 60_000;\n }\n async get<T>(\n key: string,\n loader: (signal: AbortSignal, entry: CacheEntry<T>) => Promise<Loaded<T>>,\n signal?: AbortSignal,\n ): Promise<T> {\n throwIfAborted(signal);\n const entry = this.snapshot<T>(key);\n const age = Date.now() - entry.fetchedAt;\n if (entry.value !== undefined && age < 60_000) {\n return entry.value;\n }\n const background = entry.value !== undefined && age < 600_000;\n if (\n background &&\n entry.failedAt !== undefined &&\n Date.now() - entry.failedAt < REFRESH_BACKOFF_MS\n ) {\n return entry.value!;\n }\n let flight = this.flights.get(key);\n if (!flight) {\n const controller = new AbortController();\n flight = {\n controller,\n users: 0,\n keepAlive: background,\n promise: Promise.resolve(),\n };\n const current = flight;\n this.flights.set(key, current);\n const loading = { ...entry, loading: true };\n this.entries.set(key, loading);\n this.notify();\n current.promise = Promise.resolve()\n .then(() => {\n throwIfAborted(controller.signal);\n return loader(controller.signal, entry);\n })\n .then((result) => {\n if (this.entries.get(key) === loading && !controller.signal.aborted) {\n this.set(key, result.value, result.etag);\n result.onStored?.();\n }\n return result.value;\n })\n .catch((error) => {\n if (this.entries.get(key) === loading) {\n this.entries.set(\n key,\n controller.signal.aborted\n ? { ...entry, loading: false }\n : {\n ...entry,\n loading: false,\n error: asError(error),\n failedAt: Date.now(),\n },\n );\n this.notify();\n if (!controller.signal.aborted) {\n this.onError(asError(error));\n }\n }\n throw controller.signal.aborted\n ? abortReason(controller.signal)\n : asError(error);\n })\n .finally(() => {\n if (this.flights.get(key) === current) {\n this.flights.delete(key);\n }\n });\n }\n if (background) {\n flight.keepAlive = true;\n void flight.promise.catch(() => {});\n return entry.value!;\n }\n const current = flight;\n current.users++;\n return new Promise<T>((resolve, reject) => {\n let done = false;\n const finish = (callback: () => void) => {\n if (done) {\n return;\n }\n done = true;\n signal?.removeEventListener(\"abort\", abort);\n current.users--;\n callback();\n };\n const abort = () => {\n finish(() => reject(abortReason(signal)));\n if (current.users === 0 && !current.keepAlive) {\n current.controller.abort(abortReason(signal));\n if (this.flights.get(key) === current) {\n this.flights.delete(key);\n }\n }\n };\n signal?.addEventListener(\"abort\", abort, { once: true });\n current.promise.then(\n (value) => finish(() => resolve(value as T)),\n (error) => finish(() => reject(error)),\n );\n if (signal?.aborted) {\n abort();\n }\n });\n }\n}\n"],
5
4
  "mappings": "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",
6
5
  "names": ["current"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/customer-http.ts"],
4
- "sourcesContent": ["// Isolated customer-facing bundles import the installed core so error identity is shared.\n// eslint-disable-next-line no-restricted-imports\nimport { ZappieError } from \"@zappie/sdk\";\n\nexport type ApiErrorDetails = {\n apiCode: string;\n message: string;\n param?: string;\n issues?: unknown;\n};\n\nexport function stableErrorCode(status: number) {\n if (status === 400 || status === 422) {\n return \"invalid_request\" as const;\n }\n if (status === 401) {\n return \"unauthorized\" as const;\n }\n if (status === 404) {\n return \"not_found\" as const;\n }\n if (status === 429) {\n return \"rate_limited\" as const;\n }\n return \"server_error\" as const;\n}\n\nexport async function apiError(\n response: Response,\n fallback: string,\n): Promise<ApiErrorDetails> {\n const body = await response.json().catch(() => ({}));\n const envelope = body && typeof body === \"object\" ? body : {};\n const value =\n \"error\" in envelope && envelope.error && typeof envelope.error === \"object\"\n ? envelope.error\n : {};\n return {\n apiCode:\n \"code\" in value && typeof value.code === \"string\"\n ? value.code\n : \"server_error\",\n message:\n \"message\" in value && typeof value.message === \"string\"\n ? value.message\n : fallback,\n ...(\"param\" in value && typeof value.param === \"string\"\n ? { param: value.param }\n : {}),\n ...(\"issues\" in value ? { issues: value.issues } : {}),\n };\n}\n\nexport function wait(ms: number, signal?: AbortSignal): Promise<void> {\n signal?.throwIfAborted();\n return new Promise((resolve, reject) => {\n const done = () => {\n signal?.removeEventListener(\"abort\", aborted);\n resolve();\n };\n const timer = setTimeout(done, ms);\n const aborted = () => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", aborted);\n reject(signal?.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", aborted, { once: true });\n });\n}\n\nexport async function requestWithRetry(\n fetcher: typeof fetch,\n input: string,\n init: RequestInit,\n message: string,\n): Promise<Response> {\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n return await fetcher(input, {\n ...init,\n credentials: \"same-origin\",\n headers: { Accept: \"application/json\", ...init.headers },\n });\n } catch (cause) {\n if (init.signal?.aborted) {\n throw init.signal.reason ?? cause;\n }\n if (attempt === 1) {\n throw new ZappieError(\"network_error\", message, {\n cause,\n retryable: true,\n });\n }\n await wait(400, init.signal ?? undefined);\n }\n }\n throw new ZappieError(\"network_error\", message, { retryable: true });\n}\n"],
5
4
  "mappings": ";AAEA,SAAS,mBAAmB;AASrB,SAAS,gBAAgB,QAAgB;AAC9C,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAsB,SACpB,UACA,UAC0B;AAC1B,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACnD,QAAM,WAAW,QAAQ,OAAO,SAAS,WAAW,OAAO,CAAC;AAC5D,QAAM,QACJ,WAAW,YAAY,SAAS,SAAS,OAAO,SAAS,UAAU,WAC/D,SAAS,QACT,CAAC;AACP,SAAO;AAAA,IACL,SACE,UAAU,SAAS,OAAO,MAAM,SAAS,WACrC,MAAM,OACN;AAAA,IACN,SACE,aAAa,SAAS,OAAO,MAAM,YAAY,WAC3C,MAAM,UACN;AAAA,IACN,GAAI,WAAW,SAAS,OAAO,MAAM,UAAU,WAC3C,EAAE,OAAO,MAAM,MAAM,IACrB,CAAC;AAAA,IACL,GAAI,YAAY,QAAQ,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EACtD;AACF;AAEO,SAAS,KAAK,IAAY,QAAqC;AACpE,UAAQ,eAAe;AACvB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,OAAO,MAAM;AACjB,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,cAAQ;AAAA,IACV;AACA,UAAM,QAAQ,WAAW,MAAM,EAAE;AACjC,UAAM,UAAU,MAAM;AACpB,mBAAa,KAAK;AAClB,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,aAAO,QAAQ,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,IACpE;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC3D,CAAC;AACH;AAEA,eAAsB,iBACpB,SACA,OACA,MACA,SACmB;AACnB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,QAAI;AACF,aAAO,MAAM,QAAQ,OAAO;AAAA,QAC1B,GAAG;AAAA,QACH,aAAa;AAAA,QACb,SAAS,EAAE,QAAQ,oBAAoB,GAAG,KAAK,QAAQ;AAAA,MACzD,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,KAAK,QAAQ,SAAS;AACxB,cAAM,KAAK,OAAO,UAAU;AAAA,MAC9B;AACA,UAAI,YAAY,GAAG;AACjB,cAAM,IAAI,YAAY,iBAAiB,SAAS;AAAA,UAC9C;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,YAAM,KAAK,KAAK,KAAK,UAAU,MAAS;AAAA,IAC1C;AAAA,EACF;AACA,QAAM,IAAI,YAAY,iBAAiB,SAAS,EAAE,WAAW,KAAK,CAAC;AACrE;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/digest.ts"],
4
- "sourcesContent": ["/** Payload digests avoid putting notes, addresses or credentials in request headers. */\nexport async function payloadKey(payload: string): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(payload),\n );\n return Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n"],
5
4
  "mappings": ";AACA,eAAsB,WAAW,SAAkC;AACjE,QAAM,SAAS,MAAM,WAAW,OAAO,OAAO;AAAA,IAC5C;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,OAAO;AAAA,EAClC;AACA,SAAO,MAAM;AAAA,IAAK,IAAI,WAAW,MAAM;AAAA,IAAG,CAAC,SACzC,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EACnC,EAAE,KAAK,EAAE;AACX;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/emitter.ts"],
4
- "sourcesContent": ["import type { CartIssueCode } from \"../cart/types\";\nimport type { ZappieError } from \"../errors\";\nimport type { Money } from \"../money\";\n/** Events currently emitted by the read-only SDK. */\nexport type ZappieEvents = {\n search: { query: string; resultCount: number };\n warning: { message: string };\n cart_expired: { branchId: string };\n add_to_cart: { itemId: string; quantity: number; value: Money };\n remove_from_cart: { itemId: string; quantity: number; value: Money };\n update_cart_line: { lineId: string; itemId: string; quantity: number };\n view_cart: Record<string, never>;\n cart_issue: { code: CartIssueCode };\n quote_failed: { code: string };\n \"menu:updated\": { branchId: string; version: string };\n error: { error: ZappieError };\n};\nexport function createEmitter() {\n const listeners = new Map<\n keyof ZappieEvents,\n Set<(payload: never) => void>\n >();\n return {\n on<E extends keyof ZappieEvents>(\n event: E,\n fn: (payload: ZappieEvents[E]) => void,\n ) {\n const set = listeners.get(event) ?? new Set();\n listeners.set(event, set);\n set.add(fn as (payload: never) => void);\n return () => {\n set.delete(fn as (payload: never) => void);\n };\n },\n emit<E extends keyof ZappieEvents>(event: E, payload: ZappieEvents[E]) {\n for (const fn of [...(listeners.get(event) ?? [])]) {\n try {\n fn(payload as never);\n } catch (error) {\n console.error(\"Zappie event listener failed\", error);\n }\n }\n },\n };\n}\n"],
5
4
  "mappings": ";AAiBO,SAAS,gBAAgB;AAC9B,QAAM,YAAY,oBAAI,IAGpB;AACF,SAAO;AAAA,IACL,GACE,OACA,IACA;AACA,YAAM,MAAM,UAAU,IAAI,KAAK,KAAK,oBAAI,IAAI;AAC5C,gBAAU,IAAI,OAAO,GAAG;AACxB,UAAI,IAAI,EAA8B;AACtC,aAAO,MAAM;AACX,YAAI,OAAO,EAA8B;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,KAAmC,OAAU,SAA0B;AACrE,iBAAW,MAAM,CAAC,GAAI,UAAU,IAAI,KAAK,KAAK,CAAC,CAAE,GAAG;AAClD,YAAI;AACF,aAAG,OAAgB;AAAA,QACrB,SAAS,OAAO;AACd,kBAAQ,MAAM,gCAAgC,KAAK;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/money-math.ts"],
4
- "sourcesContent": ["import { ZappieError } from \"../errors\";\nimport type { Money } from \"../money\";\nimport { money } from \"../money\";\nexport function sumMoney(values: readonly Money[], zero: Money): Money {\n let amount = zero.amountMinor;\n for (const value of values) {\n if (value.currency !== zero.currency) {\n throw new ZappieError(\n \"invalid_request\",\n \"Cannot combine different currencies\",\n );\n }\n amount = money(amount + value.amountMinor, zero.currency).amountMinor;\n }\n return money(amount, zero.currency);\n}\nexport function multiplyMoney(value: Money, quantity: number): Money {\n if (!Number.isSafeInteger(quantity)) {\n throw new ZappieError(\n \"invalid_request\",\n \"Money requires an integer quantity\",\n );\n }\n return money(value.amountMinor * quantity, value.currency);\n}\n"],
5
4
  "mappings": ";AAAA,SAAS,mBAAmB;AAE5B,SAAS,aAAa;AACf,SAAS,SAAS,QAA0B,MAAoB;AACrE,MAAI,SAAS,KAAK;AAClB,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,aAAa,KAAK,UAAU;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,aAAS,MAAM,SAAS,MAAM,aAAa,KAAK,QAAQ,EAAE;AAAA,EAC5D;AACA,SAAO,MAAM,QAAQ,KAAK,QAAQ;AACpC;AACO,SAAS,cAAc,OAAc,UAAyB;AACnE,MAAI,CAAC,OAAO,cAAc,QAAQ,GAAG;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,MAAM,cAAc,UAAU,MAAM,QAAQ;AAC3D;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/internal/return-path.ts"],
4
- "sourcesContent": ["const ORIGIN = \"https://return.invalid\";\n\n// Resolve the path the way a browser resolves a Location header (it strips\n// tabs and newlines and reads \"\\\" as \"/\"), and keep it only if it stays on this site.\nexport function sameOriginPath(\n value: string | null | undefined,\n): string | undefined {\n if (!value?.startsWith(\"/\")) {\n return undefined;\n }\n try {\n const target = new URL(value, ORIGIN);\n return target.origin === ORIGIN\n ? `${target.pathname}${target.search}${target.hash}`\n : undefined;\n } catch {\n return undefined;\n }\n}\n"],
5
4
  "mappings": ";AAAA,IAAM,SAAS;AAIR,SAAS,eACd,OACoB;AACpB,MAAI,CAAC,OAAO,WAAW,GAAG,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,OAAO,MAAM;AACpC,WAAO,OAAO,WAAW,SACrB,GAAG,OAAO,QAAQ,GAAG,OAAO,MAAM,GAAG,OAAO,IAAI,KAChD;AAAA,EACN,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,6 +1,6 @@
1
1
  import { CartFulfilment, WireFulfilment } from './index.js';
2
- export { v as validateConfig } from './client-B377AzNC.js';
3
- import './models-tf9rNoki.js';
2
+ export { v as validateConfig } from './client-DHxakZrv.js';
3
+ import './models-C-kEg1Id.js';
4
4
 
5
5
  /**
6
6
  * Convert a cart fulfilment selection to the API field names used by quotes and checkout.
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/internal.ts"],
4
- "sourcesContent": ["/**\n * Utilities for Zappie's own SDK entries and public packages.\n * This entry is outside the SDK's public semantic-versioning contract.\n *\n * @packageDocumentation\n * @internal\n */\nimport * as publicSdk from \"./index\";\n\nexport { wireFulfilment } from \"./cart/wire\";\nexport { validateConfig } from \"./config\";\n\n// Keep the core build graph shared rather than splitting its common chunks for this entry.\nvoid publicSdk;\n"],
5
4
  "mappings": ";AAOA,YAAY,eAAe;AAE3B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;",
6
5
  "names": []
7
6
  }
@@ -80,7 +80,7 @@ type Branch = {
80
80
  name: string;
81
81
  description: string;
82
82
  }[];
83
- /** How many days ahead this branch accepts scheduled orders. */
83
+ /** How many days ahead a branch takes orders, even while closed. Zero: only while open. */
84
84
  orderAheadDays?: number;
85
85
  socialLinks?: {
86
86
  type: string;
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": [],
4
- "sourcesContent": [],
5
4
  "mappings": "",
6
5
  "names": []
7
6
  }
package/dist/money.js.map CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/money.ts"],
4
- "sourcesContent": ["import { ZappieError } from \"./errors\";\ndeclare const MoneyBrand: unique symbol;\n/** A normalized ISO 4217 currency code. */\nexport type Currency = string;\n// Minor-unit digits match the API, which scales amount_minor with\n// github.com/bojanz/currency v1.2.0 (pkg/currency.Digits). Intl differs for COP,\n// HUF, IDR, PKR and RSD, so it only presents the amount. Unlisted codes use 2.\nconst ZERO_DIGIT_CURRENCIES =\n \" AFN ALL BIF CLP DJF GNF IQD IRR ISK JPY KMF KPW KRW LAK LBP MGA MMK PYG RSD RWF SLL SOS SYP UGX UYI VND VUV XAF XOF XPF YER \";\nconst THREE_DIGIT_CURRENCIES = \" BHD JOD KWD LYD OMR TND \";\nconst FOUR_DIGIT_CURRENCIES = \" CLF UYW \";\n// API codes that Intl.supportedValuesOf(\"currency\") doesn't list.\nconst API_ONLY_CURRENCIES = \" BOV CHE CHW CLF COU MXV USN UYI UYW VED \";\nconst contains = (codes: string, currency: string) =>\n codes.includes(` ${currency} `);\nlet intlCurrencies: Set<string> | undefined;\nfunction isCurrency(code: string): boolean {\n if (!/^[A-Z]{3}$/.test(code)) {\n return false;\n }\n if (typeof Intl.supportedValuesOf !== \"function\") {\n return true;\n }\n intlCurrencies ??= new Set(Intl.supportedValuesOf(\"currency\"));\n return contains(API_ONLY_CURRENCIES, code) || intlCurrencies.has(code);\n}\nfunction currencyDigits(currency: Currency): number {\n if (contains(ZERO_DIGIT_CURRENCIES, currency)) {\n return 0;\n }\n if (contains(THREE_DIGIT_CURRENCIES, currency)) {\n return 3;\n }\n return contains(FOUR_DIGIT_CURRENCIES, currency) ? 4 : 2;\n}\n/** A frozen amount in safe integer minor units. Create it with money(). */\nexport type Money = {\n readonly amountMinor: number;\n readonly currency: Currency;\n readonly [MoneyBrand]: true;\n};\n/** Validate an amount in minor units and freeze it without rounding. */\nexport function money(amountMinor: number, currency: Currency): Money {\n const normalized =\n typeof currency === \"string\" ? currency.trim().toUpperCase() : \"\";\n if (!Number.isSafeInteger(amountMinor) || !isCurrency(normalized)) {\n throw new ZappieError(\n \"invalid_request\",\n \"Money requires safe integer minor units and a valid ISO 4217 currency\",\n );\n }\n return Object.freeze({ amountMinor, currency: normalized }) as Money;\n}\n/** Currency presentation options. */\nexport type MoneyFormatOptions = {\n showZeroDecimals?: boolean;\n compact?: boolean;\n sign?: \"auto\" | \"always\";\n};\n/** Format amounts for display. Use server quotes for final prices. */\nexport interface Formatters {\n money(value: Money, opts?: MoneyFormatOptions): string;\n}\n/** Format money with currency-specific minor units and locale defaults. */\nexport const format: Formatters = {\n money(value, opts = {}) {\n const normalized = money(value.amountMinor, value.currency);\n const locale = normalized.currency === \"NGN\" ? \"en-NG\" : \"en\";\n const digits = currencyDigits(normalized.currency);\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: normalized.currency,\n currencyDisplay: \"narrowSymbol\",\n minimumFractionDigits:\n opts.showZeroDecimals || normalized.amountMinor % 10 ** digits !== 0\n ? digits\n : 0,\n maximumFractionDigits: digits,\n notation: opts.compact ? \"compact\" : \"standard\",\n signDisplay: opts.sign ?? \"auto\",\n }).format(normalized.amountMinor / 10 ** digits);\n },\n};\n"],
5
4
  "mappings": ";AAAA,SAAS,mBAAmB;AAO5B,IAAM,wBACJ;AACF,IAAM,yBAAyB;AAC/B,IAAM,wBAAwB;AAE9B,IAAM,sBAAsB;AAC5B,IAAM,WAAW,CAAC,OAAe,aAC/B,MAAM,SAAS,IAAI,QAAQ,GAAG;AAChC,IAAI;AACJ,SAAS,WAAW,MAAuB;AACzC,MAAI,CAAC,aAAa,KAAK,IAAI,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,sBAAsB,YAAY;AAChD,WAAO;AAAA,EACT;AACA,sCAAmB,IAAI,IAAI,KAAK,kBAAkB,UAAU,CAAC;AAC7D,SAAO,SAAS,qBAAqB,IAAI,KAAK,eAAe,IAAI,IAAI;AACvE;AACA,SAAS,eAAe,UAA4B;AAClD,MAAI,SAAS,uBAAuB,QAAQ,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,MAAI,SAAS,wBAAwB,QAAQ,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,SAAO,SAAS,uBAAuB,QAAQ,IAAI,IAAI;AACzD;AAQO,SAAS,MAAM,aAAqB,UAA2B;AACpE,QAAM,aACJ,OAAO,aAAa,WAAW,SAAS,KAAK,EAAE,YAAY,IAAI;AACjE,MAAI,CAAC,OAAO,cAAc,WAAW,KAAK,CAAC,WAAW,UAAU,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,EAAE,aAAa,UAAU,WAAW,CAAC;AAC5D;AAYO,IAAM,SAAqB;AAAA,EAChC,MAAM,OAAO,OAAO,CAAC,GAAG;AACtB,UAAM,aAAa,MAAM,MAAM,aAAa,MAAM,QAAQ;AAC1D,UAAM,SAAS,WAAW,aAAa,QAAQ,UAAU;AACzD,UAAM,SAAS,eAAe,WAAW,QAAQ;AACjD,WAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,MACnC,OAAO;AAAA,MACP,UAAU,WAAW;AAAA,MACrB,iBAAiB;AAAA,MACjB,uBACE,KAAK,oBAAoB,WAAW,cAAc,MAAM,WAAW,IAC/D,SACA;AAAA,MACN,uBAAuB;AAAA,MACvB,UAAU,KAAK,UAAU,YAAY;AAAA,MACrC,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC,EAAE,OAAO,WAAW,cAAc,MAAM,MAAM;AAAA,EACjD;AACF;",
6
5
  "names": []
7
6
  }