@zappie/sdk 0.0.0-stage → 0.1.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 (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +337 -2
  3. package/dist/cart/item.d.ts +10 -0
  4. package/dist/cart/item.js +1 -0
  5. package/dist/cart/item.js.map +1 -0
  6. package/dist/cart/quote.d.ts +15 -0
  7. package/dist/cart/quote.js +1 -0
  8. package/dist/cart/quote.js.map +1 -0
  9. package/dist/checkout/index.d.ts +141 -0
  10. package/dist/checkout/index.js +1 -0
  11. package/dist/checkout/index.js.map +1 -0
  12. package/dist/chunk-I55A5QXJ.js +2 -0
  13. package/dist/chunk-I55A5QXJ.js.map +1 -0
  14. package/dist/chunk-VG7NKIKR.js +2 -0
  15. package/dist/chunk-VG7NKIKR.js.map +1 -0
  16. package/dist/client-BctSrEhO.d.ts +725 -0
  17. package/dist/config/index.d.ts +83 -0
  18. package/dist/config/index.js +1 -0
  19. package/dist/customer/index.d.ts +175 -0
  20. package/dist/customer/index.js +3 -0
  21. package/dist/customer/index.js.map +1 -0
  22. package/dist/customer/react.d.ts +31 -0
  23. package/dist/customer/react.js +3 -0
  24. package/dist/customer/react.js.map +1 -0
  25. package/dist/debug.d.ts +45 -0
  26. package/dist/debug.js +1 -0
  27. package/dist/debug.js.map +1 -0
  28. package/dist/index.d.ts +84 -0
  29. package/dist/index.js +1 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/internal.d.ts +14 -0
  32. package/dist/internal.js +1 -0
  33. package/dist/internal.js.map +1 -0
  34. package/dist/models-Dj-VkdU1.d.ts +230 -0
  35. package/dist/persist/catalogue.d.ts +2 -0
  36. package/dist/persist/catalogue.js +2 -0
  37. package/dist/persist/catalogue.js.map +1 -0
  38. package/dist/places/index.d.ts +121 -0
  39. package/dist/places/index.js +1 -0
  40. package/dist/places/index.js.map +1 -0
  41. package/dist/react/index.d.ts +79 -0
  42. package/dist/react/index.js +2 -0
  43. package/dist/react/index.js.map +1 -0
  44. package/dist/server/index.d.ts +235 -0
  45. package/dist/server/index.js +2 -0
  46. package/dist/server/index.js.map +1 -0
  47. package/dist/template/index.d.ts +16 -0
  48. package/dist/template/index.js +3 -0
  49. package/dist/template/index.js.map +1 -0
  50. package/dist/template/react.d.ts +13 -0
  51. package/dist/template/react.js +3 -0
  52. package/dist/template/react.js.map +1 -0
  53. package/dist/testing/index.d.ts +64 -0
  54. package/dist/testing/index.js +2 -0
  55. package/dist/testing/index.js.map +1 -0
  56. package/dist/transport/category-page.d.ts +17 -0
  57. package/dist/transport/category-page.js +2 -0
  58. package/dist/transport/category-page.js.map +1 -0
  59. package/dist/transport/search.d.ts +7 -0
  60. package/dist/transport/search.js +2 -0
  61. package/dist/transport/search.js.map +1 -0
  62. package/dist/transport/selected-menu.d.ts +2 -0
  63. package/dist/transport/selected-menu.js +2 -0
  64. package/dist/transport/selected-menu.js.map +1 -0
  65. package/dist/transport/store.d.ts +10 -0
  66. package/dist/transport/store.js +2 -0
  67. package/dist/transport/store.js.map +1 -0
  68. package/dist/url/index.d.ts +157 -0
  69. package/dist/url/index.js +2 -0
  70. package/dist/url/index.js.map +1 -0
  71. package/dist/visits/index.d.ts +89 -0
  72. package/dist/visits/index.js +1 -0
  73. package/dist/visits/index.js.map +1 -0
  74. package/package.json +104 -3
@@ -0,0 +1,230 @@
1
+ declare const MoneyBrand: unique symbol;
2
+ /** A normalized ISO 4217 currency code. */
3
+ type Currency = string;
4
+ /** A frozen amount in safe integer minor units. Create it with money(). */
5
+ type Money = {
6
+ readonly amountMinor: number;
7
+ readonly currency: Currency;
8
+ readonly [MoneyBrand]: true;
9
+ };
10
+ /** Validate an amount in minor units and freeze it without rounding. */
11
+ declare function money(amountMinor: number, currency: Currency): Money;
12
+ /** Currency presentation options. */
13
+ type MoneyFormatOptions = {
14
+ showZeroDecimals?: boolean;
15
+ compact?: boolean;
16
+ sign?: "auto" | "always";
17
+ };
18
+ /** Format amounts for display. Use server quotes for final prices. */
19
+ interface Formatters {
20
+ money(value: Money, opts?: MoneyFormatOptions): string;
21
+ }
22
+ /** Format money with currency-specific minor units and locale defaults. */
23
+ declare const format: Formatters;
24
+
25
+ /** Payment methods you can display. Checkout decides which are available. */
26
+ type PaymentMethodKind = "card" | "bank_transfer" | "wallet" | "zap" | "apple_pay" | "google_pay";
27
+ /** Fulfilment capabilities supplied by the service. */
28
+ type Fulfilment = {
29
+ pickup: boolean;
30
+ delivery: boolean;
31
+ dineIn: boolean;
32
+ scheduling: boolean;
33
+ };
34
+ /** An opening interval in local time, including whether the day is disabled. */
35
+ type OpeningTime = {
36
+ day: 0 | 1 | 2 | 3 | 4 | 5 | 6;
37
+ opens: string;
38
+ closes: string;
39
+ disabled?: boolean;
40
+ };
41
+ /** Calendar exception to the weekly hours. */
42
+ type SpecialHours = {
43
+ date: string;
44
+ closed: boolean;
45
+ opens?: string;
46
+ closes?: string;
47
+ };
48
+ /** Branch details, opening hours and fulfilment options. */
49
+ type Branch = {
50
+ id: string;
51
+ slug: string;
52
+ name: string;
53
+ timezone?: string;
54
+ address?: string;
55
+ phone?: string;
56
+ coordinates?: {
57
+ lat: number;
58
+ lng: number;
59
+ };
60
+ pickupAddress?: {
61
+ placeId?: string;
62
+ coordinates?: {
63
+ lat: number;
64
+ lng: number;
65
+ };
66
+ };
67
+ socialLinks?: {
68
+ type: string;
69
+ url: string;
70
+ }[];
71
+ openingTimes: OpeningTime[];
72
+ specialHours?: SpecialHours[];
73
+ isOpen: boolean;
74
+ closedUntil?: string;
75
+ nextChangeAt?: string;
76
+ isDefault?: boolean;
77
+ fulfilment: Fulfilment;
78
+ minimumOrder?: Money;
79
+ };
80
+ /** Public storefront identity and branch directory. */
81
+ type Store = {
82
+ id: string;
83
+ slug: string;
84
+ name: string;
85
+ currency: Currency;
86
+ timezone: string;
87
+ description?: string;
88
+ /** Merchant choices declared by the active template manifest. */
89
+ templateSettings?: Record<string, string | number | boolean>;
90
+ brand: {
91
+ logoUrl?: string;
92
+ coverImageUrl?: string;
93
+ accent?: string;
94
+ };
95
+ fulfilment: Fulfilment;
96
+ paymentMethods: PaymentMethodKind[];
97
+ branches: Branch[];
98
+ };
99
+ /** Availability schedule in the owning branch's local timezone. */
100
+ type MenuSchedule = {
101
+ openingTimes: OpeningTime[];
102
+ preorder?: boolean;
103
+ preorderNoticeHours?: number;
104
+ preorderMaxDays?: number;
105
+ };
106
+ /** A source menu retained when the branch exposes several menus. */
107
+ type MenuSection = {
108
+ /** Placement count before you load items. Omitted when unknown. */
109
+ placementCount?: number;
110
+ paginated?: boolean;
111
+ id: string;
112
+ name: string;
113
+ categoryIds: string[];
114
+ /** Merchant rich text. Sanitize it before you render it. */
115
+ infoBanner?: string;
116
+ schedule?: MenuSchedule;
117
+ };
118
+ /** A branch catalogue that stores each item once across categories. */
119
+ type Menu = {
120
+ /** Partial snapshots never satisfy a complete menu read. */
121
+ completeness?: "partial" | "complete";
122
+ id: string;
123
+ branchId: string;
124
+ name: string;
125
+ version: string;
126
+ categories: Category[];
127
+ items: Record<string, Item>;
128
+ menus?: MenuSection[];
129
+ schedule?: MenuSchedule;
130
+ };
131
+ /** Ordered category membership. */
132
+ type Category = {
133
+ id: string;
134
+ name: string;
135
+ description?: string;
136
+ itemIds: string[];
137
+ };
138
+ /** An exact variant with its selected attributes, price and availability. */
139
+ type Variant = {
140
+ /** A low-stock hint for shoppers. This does not give a stock balance. */
141
+ lowStock?: boolean;
142
+ id: string;
143
+ attributeChoices: Record<string, string>;
144
+ price: Money;
145
+ isAvailable: boolean;
146
+ };
147
+ /** Item details and choices, with at least one exact variant. */
148
+ type Item = {
149
+ /** Exact source placements returned by targeted item reads. */
150
+ placements?: {
151
+ menuId: string;
152
+ categoryId: string;
153
+ }[];
154
+ /** Stock text for display. Use isAvailable and the server quote to check eligibility. */
155
+ stockLabel?: string;
156
+ promo?: {
157
+ name: string;
158
+ };
159
+ id: string;
160
+ name: string;
161
+ description?: string;
162
+ images: string[];
163
+ price: Money;
164
+ compareAtPrice?: Money;
165
+ isAvailable: boolean;
166
+ quantity: {
167
+ min: number;
168
+ max?: number;
169
+ step: number;
170
+ };
171
+ variants: [Variant, ...Variant[]];
172
+ optionGroups: OptionGroup[];
173
+ tags: string[];
174
+ };
175
+ /** Conditions that change option rules when selected choices match. */
176
+ type OptionCondition = {
177
+ groupId: string;
178
+ choiceIds: string[];
179
+ match: "any" | "all";
180
+ count?: {
181
+ op: "eq" | "gte" | "lte";
182
+ value: number;
183
+ };
184
+ setRequired?: boolean | null;
185
+ setMin?: number | null;
186
+ setMax?: number | null;
187
+ };
188
+ /** Addon or exact-variant attribute selection rules. */
189
+ type OptionGroup = {
190
+ id: string;
191
+ name: string;
192
+ helpText?: string;
193
+ kind: "select" | "text" | "number" | "date" | "time" | "toggle";
194
+ required: boolean;
195
+ isAvailable?: boolean;
196
+ isInformational?: boolean;
197
+ selection: {
198
+ min: number;
199
+ max?: number;
200
+ };
201
+ isAttribute: boolean;
202
+ choices: Choice[];
203
+ input?: {
204
+ maxLength?: number;
205
+ placeholder?: string;
206
+ };
207
+ conditions?: OptionCondition[];
208
+ };
209
+ /** A selectable option. Attribute prices are zero to avoid charging twice. */
210
+ type Choice = {
211
+ /** An attribute price adjustment for display. Cart calculations exclude it. */
212
+ priceHint?: Money;
213
+ id: string;
214
+ name: string;
215
+ price: Money;
216
+ isAvailable: boolean;
217
+ quantity?: {
218
+ min: number;
219
+ max?: number;
220
+ };
221
+ };
222
+ /** One page of a source menu category. This is a partial branch catalogue. */
223
+ type CategoryPage = {
224
+ menu: Menu;
225
+ menuId: string;
226
+ categoryId: string;
227
+ nextCursor?: string;
228
+ };
229
+
230
+ export { type Branch as B, type CategoryPage as C, type Formatters as F, type Item as I, type Money as M, type OptionGroup as O, type PaymentMethodKind as P, type Store as S, type Variant as V, type Menu as a, type Category as b, type Choice as c, type Currency as d, type Fulfilment as e, type MenuSchedule as f, type MenuSection as g, type MoneyFormatOptions as h, type OpeningTime as i, type OptionCondition as j, type SpecialHours as k, format as l, money as m };
@@ -0,0 +1,2 @@
1
+ export { K as CATALOGUE_MAX_AGE_MS, L as CATALOGUE_MAX_BYTES, N as CatalogueScope, O as SavedCatalogue, P as catalogueDigest, U as catalogueKey, V as createCatalogueStorage, X as createSavedCatalogue } from '../client-BctSrEhO.js';
2
+ import '../models-Dj-VkdU1.js';
@@ -0,0 +1,2 @@
1
+ var C=864e5,x=16777216;function A(s){return JSON.stringify(["catalogue:v1",s.store,s.branchId,s.orderContext,s.menuId])}function S(s,d={}){let m=d.maxAgeMs??864e5,l=d.maxBytes??16777216;function g(){return new Promise(t=>{let e=false,n=setTimeout(()=>o(),1e3);function o(r){if(e){r?.close();return}e=true,clearTimeout(n),t(r);}try{let r=indexedDB.open(`zappie:catalogue:v1:${s}`,1);r.onupgradeneeded=()=>{r.result.createObjectStore("catalogues",{keyPath:"key"});},r.onerror=()=>o(),r.onblocked=()=>o(),r.onsuccess=()=>o(r.result);}catch{o();}})}function i(t){typeof requestIdleCallback=="function"?requestIdleCallback(t,{timeout:2e3}):setTimeout(t,0);}function c(t){i(()=>{g().then(e=>{if(e)try{let n=e.transaction("catalogues","readwrite");n.oncomplete=n.onabort=n.onerror=()=>e.close(),t(n.objectStore("catalogues"));}catch{e.close();}});});}return {async read(t){if(t.store!==s)return;let e=await g();if(e)return new Promise(n=>{let o=A(t);try{let r=e.transaction("catalogues","readonly");r.oncomplete=()=>e.close(),r.onabort=r.onerror=()=>{e.close(),n(void 0);};let p=r.objectStore("catalogues").get(o);p.onsuccess=()=>{let a=p.result,f=Date.now();if(!a||!Number.isFinite(a.savedAt)||f<a.savedAt||f-a.savedAt>=m||a.menu?.branchId!==t.branchId||a.scope?.store!==s||A(a.scope)!==o||!Array.isArray(a.coverage?.menuIds)||a.coverage.menuIds.length!==1||a.coverage.menuIds[0]!==t.menuId||!a.etag){a&&c(u=>u.delete(o)),n(void 0);return}c(u=>{let y=u.get(o);y.onsuccess=()=>{if(y.result)try{u.put({...y.result,usedAt:f});}catch{}};}),n(a);};}catch{e.close(),n(void 0);}})},save(t){t.scope.store===s&&c(e=>{try{let n=new TextEncoder().encode(JSON.stringify(t)).length;if(n>l)return;let o=A(t.scope),r=e.getAll();r.onsuccess=()=>{let p=Date.now(),a=r.result.filter(u=>u.key!==o).sort((u,y)=>u.usedAt-y.usedAt),f=a.reduce((u,y)=>u+y.bytes,n);for(let u of a)(p-u.savedAt>=m||f>l)&&(e.delete(u.key),f-=u.bytes);try{e.put({...t,key:o,bytes:n,usedAt:p});}catch{}};}catch{}});}}}async function b(s){let d=c=>Array.isArray(c)?c.map(d):c&&typeof c=="object"?Object.fromEntries(Object.entries(c).sort(([t],[e])=>t.localeCompare(e)).map(([t,e])=>[t,d(e)])):c,{version:m,...l}=s,g=new TextEncoder().encode(JSON.stringify(d(l))),i=await crypto.subtle.digest("SHA-256",g);return Array.from(new Uint8Array(i),c=>c.toString(16).padStart(2,"0")).join("")}function h(s,d){let m=S(d.store),l=A(d),g;return {async restore(){let i=await m.read(d);return i&&(g=b(i.menu),s._internal.cache.set(l,i,i.etag)),i},async publish(i){let c=g,t=b(i.menu);g=t;let e=await t,n=e!==await c,o={...i,etag:e};return n&&s._internal.cache.set(l,o,e),m.save(o),n}}}export{C as CATALOGUE_MAX_AGE_MS,x as CATALOGUE_MAX_BYTES,b as catalogueDigest,A as catalogueKey,S as createCatalogueStorage,h as createSavedCatalogue};//# sourceMappingURL=catalogue.js.map
2
+ //# sourceMappingURL=catalogue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/persist/catalogue.ts"],"names":["CATALOGUE_MAX_AGE_MS","CATALOGUE_MAX_BYTES","catalogueKey","scope","createCatalogueStorage","store","options","maxAge","maxBytes","open","resolve","settled","timer","finish","db","request","idle","work","mutate","tx","key","value","now","table","latest","bytes","entries","entry","a","b","total","sum","catalogueDigest","menu","canonical","_version","content","hash","byte","createSavedCatalogue","client","storage","lastDigest","saved","previous","next","digest","changed"],"mappings":"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","file":"catalogue.js","sourcesContent":["import type { CategoryPage, Menu } from \"../models\";\n\n/**\n * The store, branch, order context and source menu that identify a saved catalogue.\n */\nexport type CatalogueScope = {\n store: string;\n branchId: string;\n orderContext: \"delivery\" | \"pickup\" | \"dineIn\";\n menuId: string;\n};\n/** The saved coverage. A complete source menu still covers only part of the branch. */\nexport type SavedCatalogue = {\n scope: CatalogueScope;\n menu: Menu;\n coverage: { menuIds: string[]; complete: boolean };\n pages: { categoryId: string; cursor?: string; page: CategoryPage }[];\n etag: string;\n savedAt: number;\n /**\n * The host's catalogue revision when saved. An unchanged revision means the\n * saved copy is current and needs no background refresh.\n */\n revision?: string;\n};\ntype RecordValue = SavedCatalogue & {\n key: string;\n bytes: number;\n usedAt: number;\n};\n/**\n * Default saved-catalogue lifetime in milliseconds: 24 hours.\n */\nexport const CATALOGUE_MAX_AGE_MS = 24 * 60 * 60 * 1000;\n/**\n * Default per-store catalogue storage budget in serialized bytes: 16 MiB.\n */\nexport const CATALOGUE_MAX_BYTES = 16 * 1024 * 1024;\n/**\n * Build a versioned JSON storage key from the store, branch, order context and menu ID.\n */\nexport function catalogueKey(scope: CatalogueScope) {\n return JSON.stringify([\n \"catalogue:v1\",\n scope.store,\n scope.branchId,\n scope.orderContext,\n scope.menuId,\n ]);\n}\n/** Opt-in browser persistence. Storage failures never escape into shopper UI. */\nexport function createCatalogueStorage(\n store: string,\n options: { maxAgeMs?: number; maxBytes?: number } = {},\n) {\n const maxAge = options.maxAgeMs ?? CATALOGUE_MAX_AGE_MS;\n const maxBytes = options.maxBytes ?? CATALOGUE_MAX_BYTES;\n function open(): Promise<IDBDatabase | undefined> {\n return new Promise((resolve) => {\n let settled = false;\n const timer = setTimeout(() => finish(), 1000);\n function finish(db?: IDBDatabase) {\n if (settled) {\n db?.close();\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve(db);\n }\n try {\n const request = indexedDB.open(`zappie:catalogue:v1:${store}`, 1);\n request.onupgradeneeded = () => {\n request.result.createObjectStore(\"catalogues\", { keyPath: \"key\" });\n };\n request.onerror = () => finish();\n request.onblocked = () => finish();\n request.onsuccess = () => finish(request.result);\n } catch {\n finish();\n }\n });\n }\n function idle(work: () => void) {\n if (typeof requestIdleCallback === \"function\") {\n requestIdleCallback(work, { timeout: 2000 });\n } else {\n setTimeout(work, 0);\n }\n }\n function mutate(work: (table: IDBObjectStore) => void) {\n idle(() => {\n void open().then((db) => {\n if (!db) {\n return;\n }\n try {\n const tx = db.transaction(\"catalogues\", \"readwrite\");\n tx.oncomplete = tx.onabort = tx.onerror = () => db.close();\n work(tx.objectStore(\"catalogues\"));\n } catch {\n db.close();\n }\n });\n });\n }\n return {\n async read(scope: CatalogueScope): Promise<SavedCatalogue | undefined> {\n if (scope.store !== store) {\n return;\n }\n const db = await open();\n if (!db) {\n return;\n }\n return new Promise((resolve) => {\n const key = catalogueKey(scope);\n try {\n const tx = db.transaction(\"catalogues\", \"readonly\");\n tx.oncomplete = () => db.close();\n tx.onabort = tx.onerror = () => {\n db.close();\n resolve(undefined);\n };\n const request = tx.objectStore(\"catalogues\").get(key);\n request.onsuccess = () => {\n const value = request.result as RecordValue | undefined;\n const now = Date.now();\n if (\n !value ||\n !Number.isFinite(value.savedAt) ||\n now < value.savedAt ||\n now - value.savedAt >= maxAge ||\n value.menu?.branchId !== scope.branchId ||\n value.scope?.store !== store ||\n catalogueKey(value.scope) !== key ||\n !Array.isArray(value.coverage?.menuIds) ||\n value.coverage.menuIds.length !== 1 ||\n value.coverage.menuIds[0] !== scope.menuId ||\n !value.etag\n ) {\n if (value) {\n mutate((table) => table.delete(key));\n }\n resolve(undefined);\n return;\n }\n mutate((table) => {\n const latest = table.get(key);\n latest.onsuccess = () => {\n if (latest.result) {\n try {\n table.put({ ...latest.result, usedAt: now });\n } catch {\n // A quota/security failure only disables this cache write.\n }\n }\n };\n });\n resolve(value);\n };\n } catch {\n db.close();\n resolve(undefined);\n }\n });\n },\n save(value: SavedCatalogue) {\n if (value.scope.store !== store) {\n return;\n }\n mutate((table) => {\n try {\n const bytes = new TextEncoder().encode(JSON.stringify(value)).length;\n if (bytes > maxBytes) {\n return;\n }\n const key = catalogueKey(value.scope);\n const request = table.getAll();\n request.onsuccess = () => {\n const now = Date.now();\n const entries = (request.result as RecordValue[])\n .filter((entry) => entry.key !== key)\n .sort((a, b) => a.usedAt - b.usedAt);\n let total = entries.reduce(\n (sum, entry) => sum + entry.bytes,\n bytes,\n );\n for (const entry of entries) {\n if (now - entry.savedAt >= maxAge || total > maxBytes) {\n table.delete(entry.key);\n total -= entry.bytes;\n }\n }\n try {\n table.put({ ...value, key, bytes, usedAt: now });\n } catch {\n // Quota failures can throw synchronously as well as abort a transaction.\n }\n };\n } catch {\n // Serialization/storage failures are a cache miss, never a form error.\n }\n });\n },\n };\n}\n\n/** Digest excludes transport versions: publication follows shopper-visible content. */\nexport async function catalogueDigest(menu: Menu): Promise<string> {\n const canonical = (value: unknown): unknown => {\n if (Array.isArray(value)) {\n return value.map(canonical);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, entry]) => [key, canonical(entry)]),\n );\n }\n return value;\n };\n const { version: _version, ...content } = menu;\n const bytes = new TextEncoder().encode(JSON.stringify(canonical(content)));\n const hash = await crypto.subtle.digest(\"SHA-256\", bytes);\n return Array.from(new Uint8Array(hash), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n/** A saved source menu that stays separate from the complete branch catalogue. */\nexport function createSavedCatalogue(\n client: import(\"../client\").Zappie,\n scope: CatalogueScope,\n) {\n const storage = createCatalogueStorage(scope.store);\n const key = catalogueKey(scope);\n // Digests run after the saved copy has rendered, never in front of it.\n let lastDigest: Promise<string> | undefined;\n return {\n async restore() {\n const saved = await storage.read(scope);\n if (saved) {\n lastDigest = catalogueDigest(saved.menu);\n client._internal.cache.set(key, saved, saved.etag);\n }\n return saved;\n },\n /** Save in the background and return whether the content changed. */\n async publish(value: SavedCatalogue) {\n const previous = lastDigest;\n const next = catalogueDigest(value.menu);\n lastDigest = next;\n const digest = await next;\n const changed = digest !== (await previous);\n const saved = { ...value, etag: digest };\n if (changed) {\n client._internal.cache.set(key, saved, digest);\n }\n storage.save(saved);\n return changed;\n },\n };\n}\n"]}
@@ -0,0 +1,121 @@
1
+ import { ZappieError } from '@zappie/sdk';
2
+
3
+ /**
4
+ * An autocomplete match with its place ID, primary text and optional secondary text.
5
+ */
6
+ type PlaceSuggestion = {
7
+ place_id: string;
8
+ text: string;
9
+ secondary?: string;
10
+ };
11
+ /**
12
+ * A resolved place with address text, coordinates and an optional route distance in metres.
13
+ */
14
+ type Place = {
15
+ place_id: string;
16
+ line1: string;
17
+ city?: string;
18
+ country?: string;
19
+ lat: number;
20
+ lng: number;
21
+ route?: {
22
+ distance_meters: number;
23
+ };
24
+ };
25
+ /**
26
+ * Address text, a place ID or coordinates you can submit for a delivery-zone check.
27
+ */
28
+ type ZoneAddress = {
29
+ line1?: string;
30
+ city?: string;
31
+ place_id?: string;
32
+ lat?: number;
33
+ lng?: number;
34
+ };
35
+ /**
36
+ * The branch and delivery address to check, with an optional selected delivery region.
37
+ */
38
+ type ZoneCheckRequest = {
39
+ branch_id: string;
40
+ address: ZoneAddress;
41
+ delivery_region_id?: string;
42
+ };
43
+ /**
44
+ * Delivery eligibility, optional region and estimate, fee and any reason delivery is unavailable.
45
+ */
46
+ type ZoneCheck = {
47
+ deliverable: boolean;
48
+ delivery_region_id?: string;
49
+ fee: {
50
+ amount_minor: number;
51
+ currency: string;
52
+ } | null;
53
+ eta_minutes?: number[];
54
+ reason: "outside_zone" | "branch_no_delivery" | "address_unresolved" | "region_required" | null;
55
+ };
56
+ /**
57
+ * A publishable API key and optional endpoint, version and fetch overrides for place lookups.
58
+ */
59
+ type PlacesClientOptions = {
60
+ key: string;
61
+ baseUrl?: string;
62
+ version?: string;
63
+ fetch?: typeof fetch;
64
+ customerBase?: string;
65
+ };
66
+ /**
67
+ * An optional abort signal for cancelling a places request.
68
+ */
69
+ type SignalOptions = {
70
+ signal?: AbortSignal;
71
+ };
72
+ /**
73
+ * The branch for a place lookup, with an optional search session token and abort signal.
74
+ */
75
+ type PlaceOptions = SignalOptions & {
76
+ branchId: string;
77
+ sessionToken?: string;
78
+ };
79
+ /**
80
+ * Optional debounce interval in milliseconds and branch ID for an address search controller.
81
+ */
82
+ type AddressSearchOptions = {
83
+ debounceMs?: number;
84
+ branchId?: string;
85
+ };
86
+
87
+ /**
88
+ * Create a client for address autocomplete, place lookup and delivery-zone checks.
89
+ * Also provides reverse lookup, static-map URLs and debounced address search controllers.
90
+ *
91
+ * @param options - Your publishable key and optional transport settings.
92
+ * @returns A places client. Creating it validates the key format without fetching data.
93
+ */
94
+ declare function createPlaces(options: PlacesClientOptions): {
95
+ autocomplete(query: string, sessionToken: string, read?: SignalOptions): Promise<PlaceSuggestion[]>;
96
+ place(id: string, read: PlaceOptions): Promise<Place>;
97
+ reverse(lat: number, lng: number, read?: SignalOptions): Promise<Place>;
98
+ staticMapUrl(lat: number, lng: number, branchId?: string): string;
99
+ zoneCheck(body: ZoneCheckRequest, read?: SignalOptions): Promise<ZoneCheck>;
100
+ createAddressSearch(searchOptions?: AddressSearchOptions): {
101
+ suggest(query: string): Promise<PlaceSuggestion[]>;
102
+ select(placeId: string): Promise<Place>;
103
+ dispose: () => void;
104
+ };
105
+ };
106
+ /**
107
+ * The place lookup, address search and delivery-zone operations returned by createPlaces.
108
+ */
109
+ type PlacesClient = ReturnType<typeof createPlaces>;
110
+
111
+ /**
112
+ * A places API failure with its HTTP status, API code and optional validation issues.
113
+ * Statuses 429 and 5xx are marked retryable.
114
+ */
115
+ declare class PlacesError extends ZappieError {
116
+ readonly apiCode: string;
117
+ readonly issues?: unknown;
118
+ constructor(status: number, apiCode: string, message: string, param?: string, issues?: unknown);
119
+ }
120
+
121
+ export { type AddressSearchOptions, type Place, type PlaceOptions, type PlaceSuggestion, type PlacesClient, type PlacesClientOptions, PlacesError, type SignalOptions, type ZoneAddress, type ZoneCheck, type ZoneCheckRequest, createPlaces };
@@ -0,0 +1 @@
1
+ import {ZappieError,RestTransport,API_VERSION}from'@zappie/sdk';import {validateConfig}from'@zappie/sdk/internal';function P(n){return n===400||n===422?"invalid_request":n===401?"unauthorized":n===404?"not_found":n===429?"rate_limited":"server_error"}async function b(n,a){let s=await n.json().catch(()=>({})),o=s&&typeof s=="object"?s:{},e="error"in o&&o.error&&typeof o.error=="object"?o.error:{};return {apiCode:"code"in e&&typeof e.code=="string"?e.code:"server_error",message:"message"in e&&typeof e.message=="string"?e.message:a,..."param"in e&&typeof e.param=="string"?{param:e.param}:{},..."issues"in e?{issues:e.issues}:{}}}function E(n,a){return a?.throwIfAborted(),new Promise((s,o)=>{let i=setTimeout(()=>{a?.removeEventListener("abort",r),s();},n),r=()=>{clearTimeout(i),a?.removeEventListener("abort",r),o(a?.reason??new DOMException("Aborted","AbortError"));};a?.addEventListener("abort",r,{once:true});})}async function y(n,a,s,o){for(let e=0;e<2;e++)try{return await n(a,{...s,credentials:"same-origin",headers:{Accept:"application/json",...s.headers}})}catch(i){if(s.signal?.aborted)throw s.signal.reason??i;if(e===1)throw new ZappieError("network_error",o,{cause:i,retryable:true});await E(400,s.signal??void 0);}throw new ZappieError("network_error",o,{retryable:true})}var u=class extends ZappieError{constructor(s,o,e,i,r){super(P(s),e,{status:s,retryable:s===429||s>=500,...i!==void 0?{param:i}:{}});this.apiCode=o;r!==void 0&&(this.issues=r);}};function v(n,a,s,o){return async(e,i={})=>{let r=await y(o,n+e,{...i,redirect:"manual",headers:{Authorization:`Bearer ${a}`,"Zappie-Version":s,...i.headers}},"Could not reach the places service");if(!r.ok){let t=await b(r,`The places service answered ${r.status}`);throw new u(r.status,t.apiCode,t.message,t.param,t.issues)}return await r.json()}}var g=()=>new DOMException("Superseded","AbortError");function A(n,a={}){let s=a.debounceMs??250,o=crypto.randomUUID(),e,i,r,t=0,c=()=>{t++,i!==void 0&&(clearTimeout(i),i=void 0),e?.abort(g()),e=void 0,r?.(g()),r=void 0;};return {suggest(l){c();let p=t;return l.trim()?new Promise((d,f)=>{r=f,i=setTimeout(async()=>{i=void 0,e=new AbortController;try{let m=await n.autocomplete(l.trim(),o,{signal:e.signal});p===t&&d(m);}catch(m){p===t&&f(m);}finally{p===t&&(r=void 0,e=void 0);}},s);}):Promise.resolve([])},async select(l){c();let p=t;e=new AbortController;try{let d=await n.place(l,{...a.branchId!==void 0?{branchId:a.branchId}:{},sessionToken:o,signal:e.signal});if(p!==t)throw g();return o=crypto.randomUUID(),d}finally{p===t&&(e=void 0);}},dispose:c}}var R="/_zappie/api";function k(n){validateConfig({store:"places",key:n.key});let a=new RestTransport({...n.baseUrl?{baseUrl:n.baseUrl}:{},...n.fetch?{fetch:n.fetch}:{}}).baseUrl,s=(n.customerBase??R).replace(/\/+$/,""),o=v(a,n.key,n.version??API_VERSION,n.fetch??globalThis.fetch),e=(r,t)=>{let c=new URLSearchParams;t.branchId!==void 0&&c.set("branch_id",t.branchId),t.sessionToken!==void 0&&c.set("session",t.sessionToken);let l=c.size>0?`?${c}`:"";return o(`/places/${encodeURIComponent(r)}${l}`,{method:"GET",...t.signal?{signal:t.signal}:{}})},i={async autocomplete(r,t,c={}){if(!r.trim())return [];let l=new URLSearchParams({q:r.trim(),session:t});return (await o(`/places/autocomplete?${l}`,{method:"GET",...c.signal?{signal:c.signal}:{}})).suggestions},place(r,t){return e(r,t)},reverse(r,t,c={}){let l=new URLSearchParams({lat:String(r),lng:String(t)});return o(`/places/reverse?${l}`,{method:"GET",...c.signal?{signal:c.signal}:{}})},staticMapUrl(r,t,c){let l=new URLSearchParams({lat:String(r),lng:String(t)});return c!==void 0&&l.set("branch_id",c),`${s}/places/static-map?${l}`},zoneCheck(r,t={}){return o("/delivery/zones/check",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),...t.signal?{signal:t.signal}:{}})},createAddressSearch(r={}){return A({autocomplete:i.autocomplete,place:e},r)}};return i}export{u as PlacesError,k as createPlaces};//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/customer-http.ts","../../src/places/error.ts","../../src/places/request.ts","../../src/places/search.ts","../../src/places/client.ts"],"names":["stableErrorCode","status","apiError","response","fallback","body","envelope","value","wait","ms","signal","resolve","reject","timer","aborted","requestWithRetry","fetcher","input","init","message","attempt","cause","ZappieError","PlacesError","apiCode","param","issues","createPlacesRequest","base","key","version","path","createAddressSearch","client","options","debounceMs","token","controller","rejectPending","revision","cancel","query","current","failure","placeId","DEFAULT_CUSTOMER_BASE","createPlaces","validateConfig","RestTransport","customerBase","request","API_VERSION","readPlace","id","read","params","suffix","sessionToken","lat","lng","branchId","searchOptions"],"mappings":"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","file":"index.js","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","import { stableErrorCode } from \"../internal/customer-http\";\nimport { ZappieError } from \"./sdk\";\n\n/**\n * A places API failure with its HTTP status, API code and optional validation issues.\n * Statuses 429 and 5xx are marked retryable.\n */\nexport class PlacesError 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","import { apiError, requestWithRetry } from \"../internal/customer-http\";\nimport { PlacesError } from \"./error\";\n\nexport type PlacesRequest = <T>(path: string, init?: RequestInit) => Promise<T>;\n\nexport function createPlacesRequest(\n base: string,\n key: string,\n version: string,\n fetcher: typeof fetch,\n): PlacesRequest {\n return async <T>(path: string, init: RequestInit = {}): Promise<T> => {\n const response = await requestWithRetry(\n fetcher,\n base + path,\n {\n ...init,\n // Workers reject redirect: \"error\"; !response.ok below refuses a redirect.\n redirect: \"manual\",\n headers: {\n Authorization: `Bearer ${key}`,\n \"Zappie-Version\": version,\n ...init.headers,\n },\n },\n \"Could not reach the places service\",\n );\n if (!response.ok) {\n const value = await apiError(\n response,\n `The places service answered ${response.status}`,\n );\n throw new PlacesError(\n response.status,\n value.apiCode,\n value.message,\n value.param,\n value.issues,\n );\n }\n return (await response.json()) as T;\n };\n}\n","import type { AddressSearchOptions, Place, PlaceSuggestion } from \"./types\";\n\ntype SearchClient = {\n autocomplete(\n query: string,\n token: string,\n options: { signal?: AbortSignal },\n ): Promise<PlaceSuggestion[]>;\n place(\n id: string,\n options: {\n branchId?: string;\n sessionToken?: string;\n signal?: AbortSignal;\n },\n ): Promise<Place>;\n};\n\nconst aborted = () => new DOMException(\"Superseded\", \"AbortError\");\n\nexport function createAddressSearch(\n client: SearchClient,\n options: AddressSearchOptions = {},\n) {\n const debounceMs = options.debounceMs ?? 250;\n let token = crypto.randomUUID();\n let controller: AbortController | undefined;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let rejectPending: ((reason: unknown) => void) | undefined;\n let revision = 0;\n const cancel = () => {\n revision++;\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n controller?.abort(aborted());\n controller = undefined;\n rejectPending?.(aborted());\n rejectPending = undefined;\n };\n return {\n suggest(query: string): Promise<PlaceSuggestion[]> {\n cancel();\n const current = revision;\n if (!query.trim()) {\n return Promise.resolve([]);\n }\n return new Promise((resolve, reject) => {\n rejectPending = reject;\n timer = setTimeout(async () => {\n timer = undefined;\n controller = new AbortController();\n try {\n const value = await client.autocomplete(query.trim(), token, {\n signal: controller.signal,\n });\n if (current === revision) {\n resolve(value);\n }\n } catch (failure) {\n if (current === revision) {\n reject(failure);\n }\n } finally {\n if (current === revision) {\n rejectPending = undefined;\n controller = undefined;\n }\n }\n }, debounceMs);\n });\n },\n async select(placeId: string): Promise<Place> {\n cancel();\n const current = revision;\n controller = new AbortController();\n try {\n const value = await client.place(placeId, {\n ...(options.branchId !== undefined\n ? { branchId: options.branchId }\n : {}),\n sessionToken: token,\n signal: controller.signal,\n });\n if (current !== revision) {\n throw aborted();\n }\n token = crypto.randomUUID();\n return value;\n } finally {\n if (current === revision) {\n controller = undefined;\n }\n }\n },\n dispose: cancel,\n };\n}\n","import { createPlacesRequest } from \"./request\";\nimport { API_VERSION, RestTransport, validateConfig } from \"./sdk\";\nimport { createAddressSearch } from \"./search\";\nimport type {\n AddressSearchOptions,\n Place,\n PlaceOptions,\n PlacesClientOptions,\n PlaceSuggestion,\n SignalOptions,\n ZoneCheck,\n ZoneCheckRequest,\n} from \"./types\";\n\nconst DEFAULT_CUSTOMER_BASE = \"/_zappie/api\";\n\n/**\n * Create a client for address autocomplete, place lookup and delivery-zone checks.\n * Also provides reverse lookup, static-map URLs and debounced address search controllers.\n *\n * @param options - Your publishable key and optional transport settings.\n * @returns A places client. Creating it validates the key format without fetching data.\n */\nexport function createPlaces(options: PlacesClientOptions) {\n validateConfig({ store: \"places\", key: options.key });\n const base = new RestTransport({\n ...(options.baseUrl ? { baseUrl: options.baseUrl } : {}),\n ...(options.fetch ? { fetch: options.fetch } : {}),\n }).baseUrl;\n const customerBase = (options.customerBase ?? DEFAULT_CUSTOMER_BASE).replace(\n /\\/+$/,\n \"\",\n );\n const request = createPlacesRequest(\n base,\n options.key,\n options.version ?? API_VERSION,\n options.fetch ?? globalThis.fetch,\n );\n const readPlace = (\n id: string,\n read: SignalOptions & { branchId?: string; sessionToken?: string },\n ) => {\n const params = new URLSearchParams();\n if (read.branchId !== undefined) {\n params.set(\"branch_id\", read.branchId);\n }\n if (read.sessionToken !== undefined) {\n params.set(\"session\", read.sessionToken);\n }\n const suffix = params.size > 0 ? `?${params}` : \"\";\n return request<Place>(`/places/${encodeURIComponent(id)}${suffix}`, {\n method: \"GET\",\n ...(read.signal ? { signal: read.signal } : {}),\n });\n };\n const client = {\n async autocomplete(\n query: string,\n sessionToken: string,\n read: SignalOptions = {},\n ): Promise<PlaceSuggestion[]> {\n if (!query.trim()) {\n return [];\n }\n const params = new URLSearchParams({\n q: query.trim(),\n session: sessionToken,\n });\n const result = await request<{ suggestions: PlaceSuggestion[] }>(\n `/places/autocomplete?${params}`,\n { method: \"GET\", ...(read.signal ? { signal: read.signal } : {}) },\n );\n return result.suggestions;\n },\n place(id: string, read: PlaceOptions): Promise<Place> {\n return readPlace(id, read);\n },\n reverse(lat: number, lng: number, read: SignalOptions = {}) {\n const params = new URLSearchParams({\n lat: String(lat),\n lng: String(lng),\n });\n return request<Place>(`/places/reverse?${params}`, {\n method: \"GET\",\n ...(read.signal ? { signal: read.signal } : {}),\n });\n },\n staticMapUrl(lat: number, lng: number, branchId?: string) {\n const params = new URLSearchParams({\n lat: String(lat),\n lng: String(lng),\n });\n if (branchId !== undefined) {\n params.set(\"branch_id\", branchId);\n }\n return `${customerBase}/places/static-map?${params}`;\n },\n zoneCheck(body: ZoneCheckRequest, read: SignalOptions = {}) {\n return request<ZoneCheck>(\"/delivery/zones/check\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n ...(read.signal ? { signal: read.signal } : {}),\n });\n },\n createAddressSearch(searchOptions: AddressSearchOptions = {}) {\n return createAddressSearch(\n {\n autocomplete: client.autocomplete,\n place: readPlace,\n },\n searchOptions,\n );\n },\n };\n return client;\n}\n\n/**\n * The place lookup, address search and delivery-zone operations returned by createPlaces.\n */\nexport type PlacesClient = ReturnType<typeof createPlaces>;\n"]}
@@ -0,0 +1,79 @@
1
+ export { VERSION } from '../index.js';
2
+ import { I as Item, a as Menu, S as Store, M as Money } from '../models-Dj-VkdU1.js';
3
+ import { c as ZappieError, Z as Zappie, d as CartModule, e as CartLine, b as CartFulfilment, f as CartState, g as CartIssue } from '../client-BctSrEhO.js';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { ReactNode } from 'react';
6
+
7
+ /** Subscribe to storefront identity and branch-directory data. */
8
+ declare function useStore(): {
9
+ data: Store | undefined;
10
+ error: ZappieError | undefined;
11
+ isLoading: boolean;
12
+ };
13
+ /** Subscribe to a branch catalogue and refresh stale data in the background. */
14
+ declare function useMenu(branch?: string, options?: {
15
+ metadataOnly?: boolean;
16
+ }): {
17
+ data: Menu | undefined;
18
+ error: ZappieError | undefined;
19
+ isLoading: boolean;
20
+ isStale: boolean;
21
+ };
22
+ /** Select one item from the branch catalogue without duplicating a request. */
23
+ declare function useItem(id: string, branch?: string): {
24
+ data: Item | undefined;
25
+ error: ZappieError | undefined;
26
+ isLoading: boolean;
27
+ };
28
+
29
+ /** Supply initial storefront data before children subscribe. Use one client per server request. */
30
+ declare function ZappieProvider({ client, initialData, children, }: {
31
+ client: Zappie;
32
+ initialData?: {
33
+ store?: Store;
34
+ menu?: Menu;
35
+ };
36
+ children: ReactNode;
37
+ }): react_jsx_runtime.JSX.Element;
38
+ /** Read your storefront client from the nearest ZappieProvider. */
39
+ declare function useZappie(): Zappie;
40
+
41
+ /** Load saved cart state after mount. Server and initial hydration snapshots are empty. */
42
+ declare function useCart(): {
43
+ actions: Readonly<CartModule>;
44
+ branchId?: string | undefined;
45
+ lines: readonly CartLine[];
46
+ count: number;
47
+ subtotal: Money | null;
48
+ fulfilment: CartFulfilment | null;
49
+ promoCode: string | null;
50
+ state: CartState;
51
+ issues: readonly CartIssue[];
52
+ isHydrated: boolean;
53
+ };
54
+ /**
55
+ * Subscribe to the cart and return the line with the given ID, or undefined if it is absent.
56
+ * Use within a ZappieProvider. Server and initial hydration snapshots contain no lines.
57
+ */
58
+ declare function useCartLine(lineId: string): Readonly<{
59
+ id: string;
60
+ itemId: string;
61
+ menuId?: string;
62
+ name: string;
63
+ quantity: number;
64
+ choices: readonly Readonly<{
65
+ groupId: string;
66
+ choiceId: string;
67
+ quantity: number;
68
+ }>[];
69
+ inputs: Readonly<Record<string, string>>;
70
+ variantId?: string;
71
+ note?: string;
72
+ unitPrice: Money;
73
+ lineSubtotal: Money;
74
+ addedAt: string;
75
+ }> | undefined;
76
+ /** Read the cart validation catalogue, including held items, without fetching it. */
77
+ declare function useCartCatalogue(): Menu | undefined;
78
+
79
+ export { ZappieProvider, useCart, useCartCatalogue, useCartLine, useItem, useMenu, useStore, useZappie };
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import {f}from'../chunk-VG7NKIKR.js';export{o as VERSION}from'../chunk-VG7NKIKR.js';import {a}from'../chunk-I55A5QXJ.js';import {createContext,useMemo,useContext,useSyncExternalStore,useEffect}from'react';import {jsx}from'react/jsx-runtime';var h=createContext(null);function M({client:e,initialData:t,children:r}){let o=useMemo(()=>(t?.store&&!e.store.peek()&&e._internal.hydrateStore(t.store),t?.menu&&!e._internal.cache.snapshot(`menu:${t.menu.branchId}`).value&&e.menu.hydrate(t.menu),e),[e,t]);return jsx(h.Provider,{value:o,children:r})}function s(){let e=useContext(h);if(!e)throw new Error("useZappie requires ZappieProvider");return e}function v(){let e=s(),t=e._internal.cache,r=useSyncExternalStore(t.subscribe,()=>t.snapshot("store"),()=>t.snapshot("store"));return useEffect(()=>{let o=new AbortController;return e.store.get({signal:o.signal}).catch(()=>{}),()=>o.abort()},[e]),{data:r.value,error:r.error,isLoading:!r.value&&!r.error}}function x(e,t={}){let r=t.metadataOnly===true,o=s(),u=v(),n=o._internal.cache,a,i;try{a=o._internal.menuKey(e),r&&!n.snapshot(a).value&&(a+=":partial");}catch(p){a="menu:invalid",i=p;}let c=useSyncExternalStore(n.subscribe,()=>n.snapshot(a),()=>n.snapshot(a));useEffect(()=>{if(!u.data||i||r&&n.snapshot(o._internal.menuKey(e)).value)return;let p=new AbortController;return o.menu.get({...e?{branch:e}:{},...r?{metadataOnly:true}:{},signal:p.signal}).catch(()=>{}),()=>p.abort()},[o,e,u.data,i?.code,r,n]);let m=i??c.error??u.error;return {data:c.value,error:m,isLoading:!c.value&&!m,isStale:n.stale(a)}}function O(e,t){let r=x(t),o=r.data&&Object.prototype.hasOwnProperty.call(r.data.items,e)?r.data.items[e]:void 0,u=!!r.data&&!o,n=useMemo(()=>u?new a("item_not_found","Item not found"):void 0,[u,e]);return {data:o,error:r.error??n,isLoading:r.isLoading}}function l(){let{cart:e}=s(),t=useSyncExternalStore(e.subscribe,e.getSnapshot,()=>f);return useEffect(()=>e.mount(),[e]),{...t,actions:e}}function L(e){return l().lines.find(t=>t.id===e)}function I(){let e=s();return l(),e._internal.cartCatalogue()}export{M as ZappieProvider,l as useCart,I as useCartCatalogue,L as useCartLine,O as useItem,x as useMenu,v as useStore,s as useZappie};//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/react/provider.tsx","../../src/react/hooks.ts","../../src/react/cart.ts"],"names":["Context","createContext","ZappieProvider","client","initialData","children","value","useMemo","jsx","useZappie","useContext","useStore","cache","snapshot","useSyncExternalStore","useEffect","abort","useMenu","branch","options","metadataOnly","store","key","branchError","error","useItem","id","menu","data","missing","notFound","ZappieError","useCart","cart","EMPTY_CART","useCartLine","lineId","line","useCartCatalogue"],"mappings":"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","file":"index.js","sourcesContent":["\"use client\";\nimport type { ReactNode } from \"react\";\nimport { createContext, useContext, useMemo } from \"react\";\nimport type { Zappie } from \"../client\";\nimport type { Menu, Store } from \"../models\";\nconst Context = createContext<Zappie | null>(null);\n/** Supply initial storefront data before children subscribe. Use one client per server request. */\nexport function ZappieProvider({\n client,\n initialData,\n children,\n}: {\n client: Zappie;\n initialData?: { store?: Store; menu?: Menu };\n children: ReactNode;\n}) {\n const value = useMemo(() => {\n if (initialData?.store && !client.store.peek()) {\n client._internal.hydrateStore(initialData.store);\n }\n if (\n initialData?.menu &&\n !client._internal.cache.snapshot(`menu:${initialData.menu.branchId}`)\n .value\n ) {\n client.menu.hydrate(initialData.menu);\n }\n return client;\n }, [client, initialData]);\n return <Context.Provider value={value}>{children}</Context.Provider>;\n}\n/** Read your storefront client from the nearest ZappieProvider. */\nexport function useZappie(): Zappie {\n const client = useContext(Context);\n if (!client) {\n throw new Error(\"useZappie requires ZappieProvider\");\n }\n return client;\n}\n","\"use client\";\nimport { useEffect, useMemo, useSyncExternalStore } from \"react\";\nimport { ZappieError } from \"../errors\";\nimport type { Menu, Store } from \"../models\";\nimport { useZappie } from \"./provider\";\n/** Subscribe to storefront identity and branch-directory data. */\nexport function useStore() {\n const client = useZappie();\n const cache = client._internal.cache;\n const snapshot = useSyncExternalStore(\n cache.subscribe,\n () => cache.snapshot<Store>(\"store\"),\n () => cache.snapshot<Store>(\"store\"),\n );\n useEffect(() => {\n const abort = new AbortController();\n void client.store.get({ signal: abort.signal }).catch(() => {});\n return () => abort.abort();\n }, [client]);\n return {\n data: snapshot.value,\n error: snapshot.error,\n isLoading: !snapshot.value && !snapshot.error,\n };\n}\n/** Subscribe to a branch catalogue and refresh stale data in the background. */\nexport function useMenu(\n branch?: string,\n options: { metadataOnly?: boolean } = {},\n) {\n const metadataOnly = options.metadataOnly === true;\n const client = useZappie();\n const store = useStore();\n const cache = client._internal.cache;\n let key: string;\n let branchError: ZappieError | undefined;\n try {\n key = client._internal.menuKey(branch);\n if (metadataOnly && !cache.snapshot<Menu>(key).value) {\n key += \":partial\";\n }\n } catch (error) {\n key = \"menu:invalid\";\n branchError = error as ZappieError;\n }\n const snapshot = useSyncExternalStore(\n cache.subscribe,\n () => cache.snapshot<Menu>(key),\n () => cache.snapshot<Menu>(key),\n );\n useEffect(() => {\n if (!store.data || branchError) {\n return;\n }\n if (\n metadataOnly &&\n cache.snapshot<Menu>(client._internal.menuKey(branch)).value\n ) {\n return;\n }\n const abort = new AbortController();\n void client.menu\n .get({\n ...(branch ? { branch } : {}),\n ...(metadataOnly ? { metadataOnly: true } : {}),\n signal: abort.signal,\n })\n .catch(() => {});\n return () => abort.abort();\n }, [client, branch, store.data, branchError?.code, metadataOnly, cache]);\n const error = branchError ?? snapshot.error ?? store.error;\n return {\n data: snapshot.value,\n error,\n isLoading: !snapshot.value && !error,\n isStale: cache.stale(key),\n };\n}\n/** Select one item from the branch catalogue without duplicating a request. */\nexport function useItem(id: string, branch?: string) {\n const menu = useMenu(branch);\n const data =\n menu.data && Object.prototype.hasOwnProperty.call(menu.data.items, id)\n ? menu.data.items[id]\n : undefined;\n const missing = Boolean(menu.data) && !data;\n const notFound = useMemo(\n () =>\n missing ? new ZappieError(\"item_not_found\", \"Item not found\") : undefined,\n [missing, id],\n );\n return {\n data,\n error: menu.error ?? notFound,\n isLoading: menu.isLoading,\n };\n}\n","\"use client\";\nimport { useEffect, useSyncExternalStore } from \"react\";\nimport { EMPTY_CART } from \"../cart\";\nimport { useZappie } from \"./provider\";\n/** Load saved cart state after mount. Server and initial hydration snapshots are empty. */\nexport function useCart() {\n const { cart } = useZappie();\n const snapshot = useSyncExternalStore(\n cart.subscribe,\n cart.getSnapshot,\n () => EMPTY_CART,\n );\n useEffect(() => cart.mount(), [cart]);\n return { ...snapshot, actions: cart };\n}\n/**\n * Subscribe to the cart and return the line with the given ID, or undefined if it is absent.\n * Use within a ZappieProvider. Server and initial hydration snapshots contain no lines.\n */\nexport function useCartLine(lineId: string) {\n return useCart().lines.find((line) => line.id === lineId);\n}\n/** Read the cart validation catalogue, including held items, without fetching it. */\nexport function useCartCatalogue() {\n const client = useZappie();\n useCart();\n return client._internal.cartCatalogue();\n}\n"]}