@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,83 @@
1
+ /**
2
+ * Default site budgets: 200 KB gzipped JavaScript, 50 KB gzipped CSS and 2500 ms LCP.
3
+ */
4
+ declare const DEFAULT_BUDGETS: {
5
+ readonly jsKbGz: 200;
6
+ readonly cssKbGz: 50;
7
+ readonly lcpMs: 2500;
8
+ };
9
+ /**
10
+ * Package names and patterns allowed globally without a site-specific allowlist entry.
11
+ */
12
+ declare const GLOBAL_PACKAGE_ALLOWLIST: readonly ["react", "react-dom", "react-router", "@react-router/*", "@zappie/*", "tailwindcss", "@tailwindcss/*", "clsx", "tailwind-merge", "class-variance-authority", "lucide-react", "date-fns", "embla-carousel-react", "framer-motion/motion", "zod", "@radix-ui/*", "sonner", "vaul"];
13
+ /**
14
+ * The store, theme and optional build policies you export from zappie.config.ts.
15
+ */
16
+ type ZappieSiteConfig = {
17
+ store: string;
18
+ branches?: string[];
19
+ theme: string;
20
+ prerender?: string[];
21
+ menu?: {
22
+ hiddenCategories?: string[];
23
+ };
24
+ packages?: {
25
+ allow?: string[];
26
+ };
27
+ outbound?: {
28
+ allow?: string[];
29
+ };
30
+ /** Each budget left out uses DEFAULT_BUDGETS. */
31
+ budgets?: {
32
+ jsKbGz?: number;
33
+ cssKbGz?: number;
34
+ lcpMs?: number;
35
+ override?: {
36
+ reason: string;
37
+ };
38
+ };
39
+ analytics?: {
40
+ forward?: {
41
+ ga4?: string;
42
+ metaPixel?: string;
43
+ };
44
+ };
45
+ };
46
+ /**
47
+ * A configuration diagnostic with its field path, stable code, message and severity.
48
+ */
49
+ type ConfigIssue = {
50
+ path: string;
51
+ code: "invalid_type" | "missing_required" | "unknown_key" | "invalid_format" | "duplicate" | "redundant_allowlist" | "budget_override";
52
+ message: string;
53
+ severity: "error" | "warning";
54
+ };
55
+ /**
56
+ * Configuration validation results. Warnings alone do not make valid false.
57
+ */
58
+ type ConfigValidation = {
59
+ valid: boolean;
60
+ issues: ConfigIssue[];
61
+ };
62
+
63
+ /**
64
+ * Validate a site configuration and collect errors and warnings without changing it.
65
+ * Checks required fields, unknown keys, allowlists, budgets and analytics settings.
66
+ *
67
+ * @param input - The configuration value to inspect.
68
+ * @returns Diagnostics and a valid flag that is true when there are no errors.
69
+ */
70
+ declare function validateConfig(input: unknown): ConfigValidation;
71
+
72
+ /**
73
+ * Return your site configuration unchanged with TypeScript checking.
74
+ * Use validateConfig to check values at runtime.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * export default defineConfig({ store: "my-store", theme: "./theme.json" });
79
+ * ```
80
+ */
81
+ declare function defineConfig(config: ZappieSiteConfig): ZappieSiteConfig;
82
+
83
+ export { type ConfigIssue, type ConfigValidation, DEFAULT_BUDGETS, GLOBAL_PACKAGE_ALLOWLIST, type ZappieSiteConfig, defineConfig, validateConfig };
@@ -0,0 +1 @@
1
+ var l={jsKbGz:200,cssKbGz:50,lcpMs:2500},f=["react","react-dom","react-router","@react-router/*","@zappie/*","tailwindcss","@tailwindcss/*","clsx","tailwind-merge","class-variance-authority","lucide-react","date-fns","embla-carousel-react","framer-motion/motion","zod","@radix-ui/*","sonner","vaul"];var u=e=>typeof e=="object"&&e!==null&&!Array.isArray(e);function v(e,n){let o=Array.from({length:n.length+1},(s,t)=>t);for(let s=1;s<=e.length;s+=1){let t=[s];for(let i=1;i<=n.length;i+=1)t[i]=Math.min((t[i-1]??0)+1,(o[i]??0)+1,(o[i-1]??0)+(e[s-1]===n[i-1]?0:1));o=t;}return o[n.length]??1/0}function g(e,n,o,s){for(let t of Object.keys(e)){if(n.includes(t))continue;let i=n.map(a=>({candidate:a,score:v(t,a)})).sort((a,x)=>a.score-x.score)[0],r=i&&i.score<=2?i.candidate:void 0;s({path:o?`${o}.${t}`:t,code:"unknown_key",message:r?`Unknown field "${t}". Rename it to "${r}".`:`Unknown field "${t}". Remove it from the configuration.`,severity:"error"});}}var p=e=>typeof e=="string"&&e.trim().length>0,m=e=>e.length<=214&&/^(?:@[a-z0-9][a-z0-9._~-]*\/[a-z0-9][a-z0-9._~-]*|[a-z0-9][a-z0-9._~-]*)$/.test(e),y=e=>f.some(n=>n.endsWith("/*")?e.startsWith(n.slice(0,-1)):e===n);function h(e){let o=e.startsWith("*.")?e.slice(2):e;return !o.includes(".")||/^\d+(?:\.\d+){3}$/.test(o)||o.length>253||o.includes("..")?false:o.split(".").every(s=>s.length>0&&s.length<=63&&/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(s))}function d(e,n,o,s,t){if(e!==void 0){if(!u(e)){s({path:n,code:"invalid_type",message:`Expected an object. Set ${n} to an object or remove it.`,severity:"error"});return}g(e,o,n,s),t(e);}}function b(e,n,o){d(e,"budgets",["jsKbGz","cssKbGz","lcpMs","override"],n,s=>{d(s.override,"budgets.override",["reason"],n,t=>{(typeof t.reason!="string"||!t.reason.trim())&&o("budgets.override.reason","invalid_format","Expected a non-blank reason. Explain why the default budget must be raised.");});for(let t of ["jsKbGz","cssKbGz","lcpMs"]){let i=s[t];if(i!==void 0){if(typeof i!="number"||!Number.isFinite(i)||i<=0)o(`budgets.${t}`,"invalid_type",`Expected a positive finite number. Set budgets.${t} to a number greater than zero.`);else if(i>l[t]){let r=`The ${t} budget exceeds the ${l[t]} default.`;s.override===void 0?o(`budgets.${t}`,"budget_override",`${r} Add budgets.override.reason to explain the increase.`):n({path:`budgets.${t}`,code:"budget_override",message:`${r} Lower it to the default when the exception is no longer needed.`,severity:"warning"});}}}});}function w(e,n,o){d(e,"analytics",["forward"],n,s=>{d(s.forward,"analytics.forward",["ga4","metaPixel"],n,t=>{t.ga4!==void 0&&(typeof t.ga4!="string"||!/^G-[A-Z0-9]+$/.test(t.ga4))&&o("analytics.forward.ga4","invalid_format","Expected a GA4 measurement id beginning with G-. Use a value such as G-XXXXXXXXXX."),t.metaPixel!==void 0&&(typeof t.metaPixel!="string"||!/^\d+$/.test(t.metaPixel))&&o("analytics.forward.metaPixel","invalid_format","Expected a numeric Meta Pixel id. Use digits only.");});});}var k=["store","branches","theme","prerender","menu","packages","outbound","budgets","analytics"];function _(e){let n=[],o=t=>n.push(t),s=(t,i,r)=>o({path:t,code:i,message:r,severity:"error"});return u(e)?(g(e,k,"",o),E(e.store,"store",s),A(e.theme,s),c(e.branches,"branches",s,{nonempty:true,unique:true,slug:true}),C(e.prerender,s),d(e.menu,"menu",["hiddenCategories"],o,t=>{c(t.hiddenCategories,"menu.hiddenCategories",s);}),d(e.packages,"packages",["allow"],o,t=>{c(t.allow,"packages.allow",s)?.forEach((r,a)=>{y(r)?o({path:`packages.allow[${a}]`,code:"redundant_allowlist",message:`${r} is already globally allowed. Remove it from packages.allow.`,severity:"warning"}):m(r)||s(`packages.allow[${a}]`,"invalid_format","Expected a lowercase npm package name. Use a package name such as lucide-react or @scope/name.");});}),d(e.outbound,"outbound",["allow"],o,t=>{c(t.allow,"outbound.allow",s)?.forEach((r,a)=>{h(r)||s(`outbound.allow[${a}]`,"invalid_format","Expected a hostname or leading *. subdomain pattern without a scheme, path, port or IP. Use a value such as api.example.com.");});}),b(e.budgets,o,s),w(e.analytics,o,s),{valid:!n.some(t=>t.severity==="error"),issues:n}):{valid:false,issues:[{path:"$",code:"invalid_type",message:"Expected a configuration object. Export an object from zappie.config.ts.",severity:"error"}]}}function E(e,n,o){p(e)||o(n,e===void 0?"missing_required":"invalid_type",`Expected a non-empty storefront slug. Set ${n} to the slug used by createZappie().`);}function A(e,n){typeof e!="string"?n("theme",e===void 0?"missing_required":"invalid_type","Expected a relative .json path. Set theme to a value such as ./theme.json."):(!/\.json$/.test(e)||/^(?:[a-z][a-z\d+.-]*:|\/|\\)/i.test(e)||e.includes("\\"))&&n("theme","invalid_format","Expected a relative .json path. Use a value such as ./theme.json.");}function c(e,n,o,s={}){if(e!==void 0){if(!Array.isArray(e)||e.some(t=>typeof t!="string")){o(n,"invalid_type",`Expected an array of strings. Set ${n} to string values.`);return}return s.nonempty&&e.length===0&&o(n,"invalid_format",`Expected at least one value. Add a value to ${n} or remove the field.`),e.forEach((t,i)=>{s.slug&&!p(t)&&o(`${n}[${i}]`,"invalid_format","Expected a non-empty branch slug. Remove the empty value or use a branch slug.");}),s.unique&&new Set(e).size!==e.length&&o(n,"duplicate",`Expected unique values. Remove duplicates from ${n}.`),e}}function C(e,n){c(e,"prerender",n,{unique:true})?.forEach((s,t)=>{s.startsWith("/")||n(`prerender[${t}]`,"invalid_format","Expected a route starting with /. Prefix the route with /.");});}function P(e){return e}export{l as DEFAULT_BUDGETS,f as GLOBAL_PACKAGE_ALLOWLIST,P as defineConfig,_ as validateConfig};
@@ -0,0 +1,175 @@
1
+ import { ZappieError } from '@zappie/sdk';
2
+
3
+ /**
4
+ * Whether the customer session is unchecked, anonymous or authenticated.
5
+ */
6
+ type AuthState = "unknown" | "anonymous" | "authenticated";
7
+ /**
8
+ * The customer identity exposed by the session endpoint, with optional name and masked phone.
9
+ */
10
+ type SessionCustomer = {
11
+ id: string;
12
+ given_name?: string;
13
+ phone_masked?: string;
14
+ };
15
+ /**
16
+ * A session customer with an optional family name returned by the profile endpoint.
17
+ */
18
+ type CustomerProfile = SessionCustomer & {
19
+ family_name?: string;
20
+ };
21
+ /**
22
+ * A customer address with its saved ID, optional coordinates and delivery instructions.
23
+ */
24
+ type SavedAddress = {
25
+ id: string;
26
+ line1: string;
27
+ landmark?: string;
28
+ label?: "home" | "work" | "other";
29
+ place_id?: string;
30
+ lat?: number;
31
+ lng?: number;
32
+ instructions?: string;
33
+ };
34
+ /**
35
+ * Address fields you submit to save an address, before the service assigns an ID.
36
+ */
37
+ type CreateAddress = Omit<SavedAddress, "id">;
38
+ /**
39
+ * A customer API amount in minor currency units, paired with its currency code.
40
+ */
41
+ type Money = {
42
+ amount_minor: number;
43
+ currency: string;
44
+ };
45
+ /**
46
+ * A selected option recorded on an order line, including its name, quantity and price.
47
+ */
48
+ type OrderChoice = {
49
+ group_id: string;
50
+ choice_id: string;
51
+ name: string;
52
+ price: Money;
53
+ quantity: number;
54
+ };
55
+ /**
56
+ * An ordered item with its recorded prices, selected options and optional variant and notes.
57
+ */
58
+ type OrderLine = {
59
+ item_id: string;
60
+ variant_id?: string;
61
+ name: string;
62
+ quantity: number;
63
+ unit_price: Money;
64
+ total: Money;
65
+ notes?: string;
66
+ choices: OrderChoice[];
67
+ };
68
+ /**
69
+ * A customer order with fulfilment, payment totals, lines and a status timeline.
70
+ */
71
+ type CustomerOrder = {
72
+ id: string;
73
+ status: "pending" | "accepted" | "ready" | "out_for_delivery" | "arrived" | "completed" | "cancelled" | "rejected";
74
+ fulfilment: "delivery" | "pickup";
75
+ branch_id?: string;
76
+ currency: string;
77
+ subtotal: Money;
78
+ delivery_fee: Money;
79
+ discount: Money;
80
+ total: Money;
81
+ paid: boolean;
82
+ amount_paid: Money;
83
+ lines: OrderLine[];
84
+ placed_at: string;
85
+ updated_at: string;
86
+ scheduled_at?: string;
87
+ timeline: {
88
+ status: string;
89
+ at: string;
90
+ }[];
91
+ eta_minutes?: number;
92
+ contact_phone?: string;
93
+ dropoff_address?: {
94
+ line1?: string;
95
+ lat?: number;
96
+ lng?: number;
97
+ };
98
+ payment?: {
99
+ status: "paid" | "unpaid";
100
+ method?: string;
101
+ };
102
+ };
103
+ /**
104
+ * A page of customer orders and the cursor for the next page, or null at the end.
105
+ */
106
+ type OrdersPage = {
107
+ orders: CustomerOrder[];
108
+ next_cursor: string | null;
109
+ };
110
+ /**
111
+ * Endpoint and fetch overrides plus an optional initial session customer.
112
+ * Omit initialCustomer for an unknown session. Pass null for an anonymous session.
113
+ */
114
+ type CustomerClientOptions = {
115
+ customerBase?: string;
116
+ sessionPath?: string;
117
+ fetch?: typeof fetch;
118
+ initialCustomer?: SessionCustomer | null;
119
+ };
120
+ /**
121
+ * An order-update callback, optional cancellation signal and fallback polling interval in milliseconds.
122
+ */
123
+ type WatchOrderOptions = {
124
+ onOrder(order: CustomerOrder): void;
125
+ signal?: AbortSignal;
126
+ pollMs?: number;
127
+ };
128
+ /**
129
+ * Receives the current auth state and customer when the customer client publishes a change.
130
+ */
131
+ type CustomerListener = (state: AuthState, customer: SessionCustomer | null) => void;
132
+
133
+ /**
134
+ * Create a client for customer sessions, saved addresses and orders.
135
+ * Browser visibility and pageshow events refresh the session. Call dispose when you stop using the client.
136
+ *
137
+ * @param options - Endpoint overrides, fetch implementation and optional initial customer.
138
+ * @returns A customer client with session subscriptions and sign-in and sign-out navigation.
139
+ */
140
+ declare function createCustomer(options?: CustomerClientOptions): {
141
+ readonly state: AuthState;
142
+ readonly customer: SessionCustomer | null;
143
+ session: () => Promise<SessionCustomer | null>;
144
+ /** Stops the visibility and back/forward-cache re-checks and drops listeners. */
145
+ dispose(): void;
146
+ onChange(listener: CustomerListener): () => void;
147
+ signIn(returnTo?: string): void;
148
+ signOut(): void;
149
+ profile: () => Promise<CustomerProfile>;
150
+ addresses(): Promise<SavedAddress[]>;
151
+ addAddress: (body: CreateAddress) => Promise<SavedAddress>;
152
+ removeAddress: (id: string) => Promise<void>;
153
+ orders(options?: {
154
+ cursor?: string;
155
+ limit?: number;
156
+ }): Promise<OrdersPage>;
157
+ order: (id: string) => Promise<CustomerOrder>;
158
+ watchOrder(id: string, watchOptions: WatchOrderOptions): () => void;
159
+ };
160
+ /**
161
+ * The session, profile, address and order operations returned by createCustomer.
162
+ */
163
+ type CustomerClient = ReturnType<typeof createCustomer>;
164
+
165
+ /**
166
+ * A customer API failure with its HTTP status, API code and optional validation issues.
167
+ * Statuses 429 and 5xx are marked retryable.
168
+ */
169
+ declare class CustomerError extends ZappieError {
170
+ readonly apiCode: string;
171
+ readonly issues?: unknown;
172
+ constructor(status: number, apiCode: string, message: string, param?: string, issues?: unknown);
173
+ }
174
+
175
+ export { type AuthState, type CreateAddress, type CustomerClient, type CustomerClientOptions, CustomerError, type CustomerListener, type CustomerOrder, type CustomerProfile, type Money, type OrderChoice, type OrderLine, type OrdersPage, type SavedAddress, type SessionCustomer, type WatchOrderOptions, createCustomer };
@@ -0,0 +1,3 @@
1
+ import {ZappieError}from'@zappie/sdk';function O(e){return e===400||e===422?"invalid_request":e===401?"unauthorized":e===404?"not_found":e===429?"rate_limited":"server_error"}async function w(e,r){let s=await e.json().catch(()=>({})),a=s&&typeof s=="object"?s:{},n="error"in a&&a.error&&typeof a.error=="object"?a.error:{};return {apiCode:"code"in n&&typeof n.code=="string"?n.code:"server_error",message:"message"in n&&typeof n.message=="string"?n.message:r,..."param"in n&&typeof n.param=="string"?{param:n.param}:{},..."issues"in n?{issues:n.issues}:{}}}function E(e,r){return r?.throwIfAborted(),new Promise((s,a)=>{let o=setTimeout(()=>{r?.removeEventListener("abort",i),s();},e),i=()=>{clearTimeout(o),r?.removeEventListener("abort",i),a(r?.reason??new DOMException("Aborted","AbortError"));};r?.addEventListener("abort",i,{once:true});})}async function v(e,r,s,a){for(let n=0;n<2;n++)try{return await e(r,{...s,credentials:"same-origin",headers:{Accept:"application/json",...s.headers}})}catch(o){if(s.signal?.aborted)throw s.signal.reason??o;if(n===1)throw new ZappieError("network_error",a,{cause:o,retryable:true});await E(400,s.signal??void 0);}throw new ZappieError("network_error",a,{retryable:true})}var p=class extends ZappieError{constructor(s,a,n,o,i){super(O(s),n,{status:s,retryable:s===429||s>=500,...o!==void 0?{param:o}:{}});this.apiCode=a;i!==void 0&&(this.issues=i);}};function T(e){return !e||!e.startsWith("/")||e.startsWith("//")||e.startsWith("/\\")?"/":e}function A(){return typeof location>"u"?"/":location.pathname+location.search}function _(e){if(typeof document>"u")return;let r=document.createElement("a");r.href=`/_zappie/login?return=${encodeURIComponent(e)}`,r.target="_top",r.click();}function x(e){if(typeof document>"u")return;let r=document.createElement("form");r.method="POST",r.action=e,r.target="_top",r.hidden=true,document.body.appendChild(r),r.submit(),r.remove();}function R(e,r,s){return async(a,n={})=>{let o=await v(r,e+a,n,"Could not reach the customer service");if(o.status===401&&s(),!o.ok){let i=await w(o,`The customer service answered ${o.status}`);throw new p(o.status,i.apiCode,i.message,i.param,i.issues)}if(o.status!==204)return await o.json()}}var L=new Set(["completed","cancelled","rejected"]);function P(e,r,s,a){let n=new AbortController,o=a.pollMs??15e3,i,l=0,c=false,m=()=>{c||(c=true,i?.close(),n.abort(),a.signal?.removeEventListener("abort",m));},h=u=>{a.onOrder(u),L.has(u.status)&&m();},y=async()=>{for(;!c;){try{h(await s(r,n.signal));}catch(u){if(u instanceof p&&u.status!==void 0&&u.status>=400&&u.status<500){m();return}}c||await E(o,n.signal).catch(()=>{});}},g=()=>{if(c||typeof EventSource>"u"){y();return}i=new EventSource(`${e}/me/orders/${encodeURIComponent(r)}/events`),i.addEventListener("status",u=>{l=0;let t;try{t=JSON.parse(u.data);}catch{return}h(t);}),i.onerror=()=>{i?.close(),l++,l>=2?y():g();};};return a.signal?.addEventListener("abort",m,{once:true}),a.signal?.aborted?m():g(),m}var k="/_zappie/api",q="/_zappie/session";function I(e,r){return e?.id===r?.id&&e?.given_name===r?.given_name&&e?.phone_masked===r?.phone_masked}function W(e={}){let r=(e.customerBase??k).replace(/\/+$/,""),s=e.sessionPath??q,a=e.fetch??globalThis.fetch,n=e.initialCustomer??null,o=e.initialCustomer===void 0?"unknown":n?"authenticated":"anonymous",i=new Set,l=(t,d)=>{if(!(o===d&&I(n,t))){o=d,n=t,globalThis.__zappieEmit?.("auth:changed",{state:o,customer:n});for(let f of i)f(o,n);}},c=R(r,a,()=>{l(null,"anonymous");}),m=(t,d)=>c(`/me/orders/${encodeURIComponent(t)}`,{method:"GET",...d?{signal:d}:{}}),h,y=async()=>{let t=await v(a,s,{method:"GET",cache:"no-store"},"Could not check the customer session");if(!t.ok){let C=await w(t,`The session service answered ${t.status}`);throw new p(t.status,C.apiCode,C.message,C.param,C.issues)}let f=(await t.json()).customer??null;return l(f,f?"authenticated":"anonymous"),f},g=()=>(h??(h=y().finally(()=>{h=void 0;})),h),u=()=>{(typeof document>"u"||document.visibilityState==="visible")&&g().catch(()=>{});};return typeof document<"u"&&document.addEventListener("visibilitychange",u),typeof window<"u"&&window.addEventListener("pageshow",u),{get state(){return o},get customer(){return n},session:g,dispose(){typeof document<"u"&&document.removeEventListener("visibilitychange",u),typeof window<"u"&&window.removeEventListener("pageshow",u),i.clear();},onChange(t){return i.add(t),()=>{i.delete(t);}},signIn(t){_(T(t??A()));},signOut(){x("/_zappie/logout");},profile:()=>c("/me",{method:"GET"}),async addresses(){return (await c("/me/addresses",{method:"GET"})).addresses},addAddress:t=>c("/me/addresses",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}),removeAddress:t=>c(`/me/addresses/${encodeURIComponent(t)}`,{method:"DELETE"}),orders(t={}){let d=new URLSearchParams;t.limit!==void 0&&d.set("limit",String(t.limit)),t.cursor!==void 0&&d.set("cursor",t.cursor);let f=d.size>0?`?${d}`:"";return c(`/me/orders${f}`,{method:"GET"})},order:t=>m(t),watchOrder(t,d){return P(r,t,m,d)}}}
2
+ export{p as CustomerError,W as createCustomer};//# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/internal/customer-http.ts","../../src/customer/error.ts","../../src/customer/navigation.ts","../../src/customer/request.ts","../../src/customer/watch-order.ts","../../src/customer/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","CustomerError","apiCode","param","issues","safeReturnTo","currentPath","signIn","path","link","signOut","form","createCustomerRequest","base","unauthorized","final","watchOrder","id","read","options","controller","pollMs","source","failures","stopped","stop","publish","order","poll","failure","connect","event","next","DEFAULT_CUSTOMER_BASE","DEFAULT_SESSION_PATH","same","left","right","createCustomer","sessionPath","customer","state","listeners","update","nextState","listener","request","readOrder","pending","check","refresh","recheck","returnTo","query","suffix","watchOptions"],"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 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","export function safeReturnTo(value: string | undefined): string {\n if (!value || !value.startsWith(\"/\") || value.startsWith(\"//\")) {\n return \"/\";\n }\n if (value.startsWith(\"/\\\\\")) {\n return \"/\";\n }\n return 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","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","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","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"]}
@@ -0,0 +1,31 @@
1
+ import * as _zappie_sdk_customer from '@zappie/sdk/customer';
2
+ import { CustomerClient, SessionCustomer } from '@zappie/sdk/customer';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ /**
7
+ * Provide a customer client to descendant hooks and check its session if the state is unknown.
8
+ * You remain responsible for disposing the client when it is no longer needed.
9
+ */
10
+ declare function CustomerProvider({ client, children, }: {
11
+ client: CustomerClient;
12
+ children: ReactNode;
13
+ }): react_jsx_runtime.JSX.Element;
14
+ /**
15
+ * Subscribe to the nearest CustomerProvider and return auth state, customer and session actions.
16
+ * Throws if you have not mounted a CustomerProvider.
17
+ */
18
+ declare function useAuth(): {
19
+ state: _zappie_sdk_customer.AuthState;
20
+ customer: SessionCustomer | null;
21
+ signIn: (returnTo?: string) => void;
22
+ signOut: () => void;
23
+ refresh: () => Promise<SessionCustomer | null>;
24
+ };
25
+ /**
26
+ * Subscribe to the nearest CustomerProvider and return its session customer, or null.
27
+ * Throws if you have not mounted a CustomerProvider.
28
+ */
29
+ declare function useCustomer(): SessionCustomer | null;
30
+
31
+ export { CustomerProvider, useAuth, useCustomer };
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import {createContext,useEffect,useCallback,useSyncExternalStore,useMemo,useContext}from'react';import {jsx}from'react/jsx-runtime';var s=createContext(null);function g({client:e,children:t}){return useEffect(()=>{e.state==="unknown"&&e.session().catch(()=>{});},[e]),jsx(s.Provider,{value:e,children:t})}function a(){let e=useContext(s);if(!e)throw new Error("CustomerProvider is missing");return e}function d(e){let t=e.customer;return [e.state,t?.id,t?.given_name,t?.phone_masked].join(":")}function f(){let e=a(),t=useCallback(u=>e.onChange(()=>u()),[e]),n=useCallback(()=>d(e),[e]),r=useSyncExternalStore(t,n,n);return useMemo(()=>({state:e.state,customer:e.customer,signIn:e.signIn,signOut:e.signOut,refresh:e.session}),[e,r])}function h(){return f().customer}export{g as CustomerProvider,f as useAuth,h as useCustomer};//# sourceMappingURL=react.js.map
3
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/customer/react.tsx"],"names":["CustomerContext","createContext","CustomerProvider","client","children","useEffect","jsx","useClient","useContext","key","customer","useAuth","subscribe","useCallback","notify","snapshot","value","useSyncExternalStore","useMemo","useCustomer"],"mappings":"oIAaA,IAAMA,CAAAA,CAAkBC,aAAAA,CAAqC,IAAI,CAAA,CAM1D,SAASC,CAAAA,CAAiB,CAC/B,OAAAC,CAAAA,CACA,QAAA,CAAAC,CACF,CAAA,CAGG,CACD,OAAAC,SAAAA,CAAU,IAAM,CACVF,CAAAA,CAAO,QAAU,SAAA,EACdA,CAAAA,CAAO,SAAQ,CAAE,KAAA,CAAM,IAAG,CAAA,CAAY,EAE/C,CAAA,CAAG,CAACA,CAAM,CAAC,EAETG,GAAAA,CAACN,CAAAA,CAAgB,QAAA,CAAhB,CAAyB,MAAOG,CAAAA,CAC9B,QAAA,CAAAC,EACH,CAEJ,CAEA,SAASG,CAAAA,EAA4B,CACnC,IAAMJ,CAAAA,CAASK,WAAWR,CAAe,CAAA,CACzC,GAAI,CAACG,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,6BAA6B,CAAA,CAE/C,OAAOA,CACT,CAEA,SAASM,CAAAA,CAAIN,CAAAA,CAAgC,CAC3C,IAAMO,CAAAA,CAAWP,CAAAA,CAAO,QAAA,CACxB,OAAO,CACLA,CAAAA,CAAO,KAAA,CACPO,CAAAA,EAAU,GACVA,CAAAA,EAAU,UAAA,CACVA,CAAAA,EAAU,YACZ,EAAE,IAAA,CAAK,GAAG,CACZ,CAMO,SAASC,GAAU,CACxB,IAAMR,CAAAA,CAASI,CAAAA,GACTK,CAAAA,CAAYC,WAAAA,CACfC,GAAuBX,CAAAA,CAAO,QAAA,CAAS,IAAMW,CAAAA,EAAQ,CAAA,CACtD,CAACX,CAAM,CACT,CAAA,CACMY,EAAWF,WAAAA,CAAY,IAAMJ,EAAIN,CAAM,CAAA,CAAG,CAACA,CAAM,CAAC,CAAA,CAElDa,CAAAA,CAAQC,qBAAqBL,CAAAA,CAAWG,CAAAA,CAAUA,CAAQ,CAAA,CAChE,OAAOG,OAAAA,CACL,KAAO,CACL,KAAA,CAAOf,CAAAA,CAAO,MACd,QAAA,CAAUA,CAAAA,CAAO,SACjB,MAAA,CAAQA,CAAAA,CAAO,MAAA,CACf,OAAA,CAASA,EAAO,OAAA,CAChB,OAAA,CAASA,EAAO,OAClB,CAAA,CAAA,CACA,CAACA,CAAAA,CAAQa,CAAK,CAChB,CACF,CAMO,SAASG,CAAAA,EAAsC,CACpD,OAAOR,CAAAA,GAAU,QACnB","file":"react.js","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"]}
@@ -0,0 +1,45 @@
1
+ import { C as CartLineInput, Z as Zappie, a as Quote } from './client-BctSrEhO.js';
2
+ import { M as Money, a as Menu, B as Branch } from './models-Dj-VkdU1.js';
3
+
4
+ type StoredLine = CartLineInput & {
5
+ id: string;
6
+ name: string;
7
+ addedAt: string;
8
+ unitPrice?: Money;
9
+ };
10
+
11
+ type DebugEvent = {
12
+ name: string;
13
+ detail: unknown;
14
+ at: string;
15
+ };
16
+ type ZappieDebug = {
17
+ version: string;
18
+ store: string;
19
+ /** Read this cart's branch and full menu through its client. */
20
+ catalogue(): Promise<{
21
+ menu: Menu;
22
+ branch: Branch;
23
+ }>;
24
+ cart: (() => ReturnType<Zappie["cart"]["getSnapshot"]>) & {
25
+ /**
26
+ * Save test cart lines for the next reload. Reload to reconcile them with today's menu.
27
+ * Your unitPrice is only a local estimate, like a price edited in browser storage.
28
+ * The server re-prices every line and never trusts this estimate.
29
+ */
30
+ inject(input: {
31
+ lines: (CartLineInput & Pick<StoredLine, "unitPrice">)[];
32
+ }): Promise<void>;
33
+ };
34
+ quote: () => Quote | undefined;
35
+ events: DebugEvent[];
36
+ };
37
+ declare global {
38
+ interface Window {
39
+ __zappie?: ZappieDebug;
40
+ }
41
+ var __zappieEmit: ((name: string, detail: unknown) => void) | undefined;
42
+ }
43
+ declare function installDebug(client: Zappie): void;
44
+
45
+ export { type DebugEvent, type ZappieDebug, installDebug };
package/dist/debug.js ADDED
@@ -0,0 +1 @@
1
+ var c="0.1.0";function g(t){if(typeof window>"u"||t.mode!=="test")return;let s=[],i,r=(e,n)=>{let o={name:e,detail:n,at:new Date().toISOString()};s.push(o),window.dispatchEvent(new CustomEvent("zappie:event",{detail:o}));};globalThis.__zappieEmit=r,window.__zappie={version:c,store:t.config.store,async catalogue(){await t.cart.hydrate();let e=(await t.store.get()).branches.find(n=>n.id===t.cart.getSnapshot().branchId);return {branch:e,menu:await t.menu.get({branch:e.id})}},cart:Object.assign(()=>t.cart.getSnapshot(),{async inject(e){if(await t.cart.hydrate(),e.lines.length>100||e.lines.some(a=>!Number.isInteger(a.quantity??1)||(a.quantity??1)<1||(a.quantity??1)>99))throw new Error("Use up to 100 cart lines with whole quantities from 1 to 99.");let n=t.cart.getSnapshot(),o={v:1,menuVersion:t._internal.cartCatalogue().version,updatedAt:Date.now(),fulfilment:n.fulfilment,promoCode:n.promoCode,lines:e.lines.map((a,d)=>({...a,id:`debug_${d}`,name:a.itemId,quantity:a.quantity??1,choices:(a.choices??[]).map(p=>({...p,quantity:p.quantity??1})),inputs:a.inputs??{},addedAt:new Date().toISOString()}))},u=t.config.cart?.storageScope;(t.config.storage??window.localStorage).setItem(`zappie:cart:v1:${t.config.store}:${n.branchId}${u?`:${u}`:""}`,JSON.stringify(o));}}),quote:()=>i,events:s},t.on("add_to_cart",e=>r("add_to_cart",e)),t.cart.subscribe(()=>{let e=t.cart.state,n="quote"in e?e.quote:void 0;n&&n!==i&&(i=n,r("quote",n));});}export{g as installDebug};//# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/debug.ts"],"names":["VERSION","installDebug","client","events","quote","emit","name","detail","event","branch","value","input","line","snapshot","index","choice","scope","state","next"],"mappings":"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","file":"debug.js","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\nconst VERSION = \"0.1.0\";\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"]}
@@ -0,0 +1,84 @@
1
+ import { T as Transport, R as RequestCtx, Q as QuoteRequest, m as WireQuote, n as MenuResponse, o as SelectedMenuRequest, p as SelectedMenuResponse, q as SearchRequest, r as SearchPage, s as MenuVersion, c as ZappieError, g as CartIssue, C as CartLineInput, e as CartLine } from './client-BctSrEhO.js';
2
+ export { A as API_VERSION, t as CartFix, b as CartFulfilment, u as CartIssueCode, d as CartModule, w as CartSnapshot, f as CartState, x as CartSyncAdapter, y as CartSyncDocument, D as DeliveryAddress, E as ErrorDetails, M as MenuResource, a as Quote, z as ReadOptions, S as StorageLike, i as StoreResource, B as WireFix, W as WireFulfilment, F as WireIssue, G as WireLine, H as WireMoney, Z as Zappie, h as ZappieClientOptions, h as ZappieConfig, l as ZappieErrorCode, j as ZappieEvents, I as createZappie, J as isZappieError } from './client-BctSrEhO.js';
3
+ import { S as Store, C as CategoryPage, I as Item, O as OptionGroup, M as Money, a as Menu, B as Branch } from './models-Dj-VkdU1.js';
4
+ 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-Dj-VkdU1.js';
5
+
6
+ /** REST connection options. A custom fetch can provide tracing or mock responses. */
7
+ type RestTransportOptions = {
8
+ baseUrl?: string;
9
+ fetch?: typeof fetch;
10
+ };
11
+ /** REST read adapter for the versioned storefront API. */
12
+ declare class RestTransport implements Transport {
13
+ readonly baseUrl: string;
14
+ private fetcher;
15
+ constructor(opts?: RestTransportOptions);
16
+ private request;
17
+ private json;
18
+ quote(ctx: RequestCtx, body: QuoteRequest): Promise<WireQuote>;
19
+ getStore(ctx: RequestCtx): Promise<Store>;
20
+ getMenu(ctx: RequestCtx & {
21
+ branchId: string;
22
+ ifNoneMatch?: string;
23
+ metadataOnly?: boolean;
24
+ }): Promise<MenuResponse>;
25
+ getSelectedMenu(ctx: SelectedMenuRequest): Promise<SelectedMenuResponse>;
26
+ search(ctx: SearchRequest): Promise<SearchPage>;
27
+ getCategoryPage(ctx: RequestCtx & {
28
+ branchId: string;
29
+ menuId: string;
30
+ categoryId: string;
31
+ cursor?: string;
32
+ }): Promise<CategoryPage>;
33
+ getMenuVersion(ctx: RequestCtx & {
34
+ branchId: string;
35
+ }): Promise<MenuVersion>;
36
+ getItem(ctx: RequestCtx & {
37
+ branchId: string;
38
+ itemId: string;
39
+ }): Promise<Item>;
40
+ }
41
+
42
+ declare class ZappieCartError extends ZappieError {
43
+ readonly issues: CartIssue[];
44
+ constructor(issues: CartIssue[]);
45
+ }
46
+
47
+ /** Presentation preview shares the cart's conditions, exact-variant resolution and pricing. */
48
+ declare function previewItem(item: Item, raw: CartLineInput): {
49
+ groups: OptionGroup[];
50
+ unitPrice: Money;
51
+ total: Money;
52
+ };
53
+
54
+ /**
55
+ * Estimate one item's price from its selected variant and non-attribute option quantities.
56
+ * Uses the item price when no variant is selected and throws if a selected variant or option is missing.
57
+ *
58
+ * @param item - The catalogue item containing prices and options.
59
+ * @param line - The variant and choices to price. Each option quantity is treated as at least one.
60
+ * @returns A local unit-price estimate. Use a server quote for checkout totals.
61
+ */
62
+ declare function estimateUnitPrice(item: Item, line: Pick<CartLine, "choices" | "variantId">): Money;
63
+
64
+ /** Never guess the first of several menus: their schedules may differ. */
65
+ declare function resolveSourceMenu(menu: Menu, itemId: string, menuId?: string): string | undefined;
66
+
67
+ /**
68
+ * Check a cart line against catalogue membership, availability, quantities and option rules.
69
+ * Applies conditional group rules and checks that the selection resolves to an available variant.
70
+ *
71
+ * @param item - The current item, or undefined if it is no longer available.
72
+ * @param raw - The shopper's proposed line.
73
+ * @param ctx - The menu and branch context for validation.
74
+ * @returns Local blocking issues with suggested fixes, or an empty array for a valid line.
75
+ */
76
+ declare function validateLine(item: Item | undefined, raw: CartLineInput, ctx: {
77
+ menu: Menu;
78
+ branch: Branch;
79
+ }): CartIssue[];
80
+
81
+ /** Public package release version. */
82
+ declare const VERSION = "0.0.0";
83
+
84
+ export { Branch, CartIssue, CartLine, CartLineInput, CategoryPage, Item, Menu, MenuResponse, MenuVersion, Money, OptionGroup, QuoteRequest, RequestCtx, RestTransport, type RestTransportOptions, SearchPage, SearchRequest, Store, Transport, VERSION, WireQuote, ZappieCartError, ZappieError, estimateUnitPrice, previewItem, resolveSourceMenu, validateLine };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export{g as API_VERSION,k as RestTransport,o as VERSION,a as ZappieCartError,m as createZappie,b as estimateUnitPrice,n as previewItem,c as resolveSourceMenu,d as validateLine}from'./chunk-VG7NKIKR.js';export{a as ZappieError,f as format,b as isZappieError,e as money}from'./chunk-I55A5QXJ.js';//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,14 @@
1
+ import { b as CartFulfilment, W as WireFulfilment } from './client-BctSrEhO.js';
2
+ export { v as validateConfig } from './client-BctSrEhO.js';
3
+ import './models-Dj-VkdU1.js';
4
+
5
+ /**
6
+ * Convert a cart fulfilment selection to the API field names used by quotes and checkout.
7
+ * Includes delivery address details, a schedule or a dine-in table when present.
8
+ *
9
+ * @param f - The shopper's selected fulfilment.
10
+ * @returns Fulfilment fields without the branch ID. Checkout sessions accept pickup or delivery only.
11
+ */
12
+ declare function wireFulfilment(f: CartFulfilment): WireFulfilment;
13
+
14
+ export { wireFulfilment };
@@ -0,0 +1 @@
1
+ export{h as validateConfig,e as wireFulfilment}from'./chunk-VG7NKIKR.js';import'./chunk-I55A5QXJ.js';//# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"internal.js"}