@zappie/sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/cart/errors.js +13 -0
- package/dist/cart/errors.js.map +7 -0
- package/dist/cart/index.js +768 -0
- package/dist/cart/index.js.map +7 -0
- package/dist/cart/item.js +12 -1
- package/dist/cart/item.js.map +7 -1
- package/dist/cart/preview.js +32 -0
- package/dist/cart/preview.js.map +7 -0
- package/dist/cart/price.js +25 -0
- package/dist/cart/price.js.map +7 -0
- package/dist/cart/quote-client.js +76 -0
- package/dist/cart/quote-client.js.map +7 -0
- package/dist/cart/quote-response.js +178 -0
- package/dist/cart/quote-response.js.map +7 -0
- package/dist/cart/reconcile.js +108 -0
- package/dist/cart/reconcile.js.map +7 -0
- package/dist/cart/source-menu.js +21 -0
- package/dist/cart/source-menu.js.map +7 -0
- package/dist/cart/storage.js +174 -0
- package/dist/cart/storage.js.map +7 -0
- package/dist/cart/types.js +1 -0
- package/dist/cart/types.js.map +7 -0
- package/dist/cart/validate.js +265 -0
- package/dist/cart/validate.js.map +7 -0
- package/dist/cart/wire.js +42 -0
- package/dist/cart/wire.js.map +7 -0
- package/dist/cart/working-set.js +80 -0
- package/dist/cart/working-set.js.map +7 -0
- package/dist/checkout/client.js +102 -0
- package/dist/checkout/client.js.map +7 -0
- package/dist/checkout/error.js +21 -0
- package/dist/checkout/error.js.map +7 -0
- package/dist/checkout/index.js +13 -1
- package/dist/checkout/index.js.map +7 -1
- package/dist/checkout/readiness.js +36 -0
- package/dist/checkout/readiness.js.map +7 -0
- package/dist/checkout/sdk.js +9 -0
- package/dist/checkout/sdk.js.map +7 -0
- package/dist/checkout/session.js +58 -0
- package/dist/checkout/session.js.map +7 -0
- package/dist/checkout/types.js +1 -0
- package/dist/checkout/types.js.map +7 -0
- package/dist/client.js +253 -0
- package/dist/client.js.map +7 -0
- package/dist/config/index.js +13 -1
- package/dist/config/index.js.map +7 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +7 -0
- package/dist/customer/client.js +149 -0
- package/dist/customer/client.js.map +7 -0
- package/dist/customer/error.js +20 -0
- package/dist/customer/error.js.map +7 -0
- package/dist/customer/index.js +8 -3
- package/dist/customer/index.js.map +7 -1
- package/dist/customer/navigation.js +45 -0
- package/dist/customer/navigation.js.map +7 -0
- package/dist/customer/react.js +67 -2
- package/dist/customer/react.js.map +7 -1
- package/dist/customer/request.js +37 -0
- package/dist/customer/request.js.map +7 -0
- package/dist/customer/sdk.js +6 -0
- package/dist/customer/sdk.js.map +7 -0
- package/dist/customer/types.js +1 -0
- package/dist/customer/types.js.map +7 -0
- package/dist/customer/watch-order.js +80 -0
- package/dist/customer/watch-order.js.map +7 -0
- package/dist/debug.js +78 -1
- package/dist/debug.js.map +7 -1
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +7 -0
- package/dist/index.js +29 -1
- package/dist/index.js.map +7 -1
- package/dist/internal/abort.js +29 -0
- package/dist/internal/abort.js.map +7 -0
- package/dist/internal/cache.js +142 -0
- package/dist/internal/cache.js.map +7 -0
- package/dist/internal/customer-http.js +74 -0
- package/dist/internal/customer-http.js.map +7 -0
- package/dist/internal/digest.js +15 -0
- package/dist/internal/digest.js.map +7 -0
- package/dist/internal/emitter.js +27 -0
- package/dist/internal/emitter.js.map +7 -0
- package/dist/internal/money-math.js +30 -0
- package/dist/internal/money-math.js.map +7 -0
- package/dist/internal.js +9 -1
- package/dist/internal.js.map +7 -1
- package/dist/models.js +1 -0
- package/dist/models.js.map +7 -0
- package/dist/money.js +58 -0
- package/dist/money.js.map +7 -0
- package/dist/persist/catalogue.js +206 -2
- package/dist/persist/catalogue.js.map +7 -1
- package/dist/places/client.js +97 -0
- package/dist/places/client.js.map +7 -0
- package/dist/places/error.js +20 -0
- package/dist/places/error.js.map +7 -0
- package/dist/places/index.js +8 -1
- package/dist/places/index.js.map +7 -1
- package/dist/places/request.js +40 -0
- package/dist/places/request.js.map +7 -0
- package/dist/places/sdk.js +10 -0
- package/dist/places/sdk.js.map +7 -0
- package/dist/places/search.js +80 -0
- package/dist/places/search.js.map +7 -0
- package/dist/places/types.js +1 -0
- package/dist/places/types.js.map +7 -0
- package/dist/react/cart.js +30 -0
- package/dist/react/cart.js.map +7 -0
- package/dist/react/hooks.js +91 -0
- package/dist/react/hooks.js.map +7 -0
- package/dist/react/index.js +18 -1
- package/dist/react/index.js.map +7 -1
- package/dist/react/provider.js +34 -0
- package/dist/react/provider.js.map +7 -0
- package/dist/server/index.js +32 -2
- package/dist/server/index.js.map +7 -1
- package/dist/server/storefront-url/build-v1.js +75 -0
- package/dist/server/storefront-url/build-v1.js.map +7 -0
- package/dist/server/storefront-url/build-v2.js +79 -0
- package/dist/server/storefront-url/build-v2.js.map +7 -0
- package/dist/server/storefront-url/build.js +19 -0
- package/dist/server/storefront-url/build.js.map +7 -0
- package/dist/server/storefront-url/contract.js +25 -0
- package/dist/server/storefront-url/contract.js.map +7 -0
- package/dist/server/storefront-url/index.js +16 -0
- package/dist/server/storefront-url/index.js.map +7 -0
- package/dist/server/storefront-url/installation.js +58 -0
- package/dist/server/storefront-url/installation.js.map +7 -0
- package/dist/server/storefront-url/params.js +72 -0
- package/dist/server/storefront-url/params.js.map +7 -0
- package/dist/server/storefront-url/parse-v1.js +135 -0
- package/dist/server/storefront-url/parse-v1.js.map +7 -0
- package/dist/server/storefront-url/parse-v2.js +139 -0
- package/dist/server/storefront-url/parse-v2.js.map +7 -0
- package/dist/server/storefront-url/parse.js +9 -0
- package/dist/server/storefront-url/parse.js.map +7 -0
- package/dist/server/storefront-url/redirect.js +30 -0
- package/dist/server/storefront-url/redirect.js.map +7 -0
- package/dist/server/storefront-url/sign-in.js +17 -0
- package/dist/server/storefront-url/sign-in.js.map +7 -0
- package/dist/server/storefront-url/types.js +55 -0
- package/dist/server/storefront-url/types.js.map +7 -0
- package/dist/server/storefront-url/url.js +61 -0
- package/dist/server/storefront-url/url.js.map +7 -0
- package/dist/server/webhook.js +96 -0
- package/dist/server/webhook.js.map +7 -0
- package/dist/site-config/helpers.js +59 -0
- package/dist/site-config/helpers.js.map +7 -0
- package/dist/site-config/sections.js +105 -0
- package/dist/site-config/sections.js.map +7 -0
- package/dist/site-config/types.js +31 -0
- package/dist/site-config/types.js.map +7 -0
- package/dist/site-config/validate.js +165 -0
- package/dist/site-config/validate.js.map +7 -0
- package/dist/template/index.js +46 -3
- package/dist/template/index.js.map +7 -1
- package/dist/template/react.js +81 -2
- package/dist/template/react.js.map +7 -1
- package/dist/testing/index.js +451 -2
- package/dist/testing/index.js.map +7 -1
- package/dist/transport/category-page.js +21 -2
- package/dist/transport/category-page.js.map +7 -1
- package/dist/transport/mappers.js +326 -0
- package/dist/transport/mappers.js.map +7 -0
- package/dist/transport/rest.js +313 -0
- package/dist/transport/rest.js.map +7 -0
- package/dist/transport/search.js +32 -2
- package/dist/transport/search.js.map +7 -1
- package/dist/transport/selected-menu.js +79 -2
- package/dist/transport/selected-menu.js.map +7 -1
- package/dist/transport/store.js +120 -2
- package/dist/transport/store.js.map +7 -1
- package/dist/transport/types.js +1 -0
- package/dist/transport/types.js.map +7 -0
- package/dist/url/index.js +16 -2
- package/dist/url/index.js.map +7 -1
- package/dist/visits/index.js +62 -1
- package/dist/visits/index.js.map +7 -1
- package/dist/visits/page-views.js +103 -0
- package/dist/visits/page-views.js.map +7 -0
- package/package.json +3 -3
- package/dist/cart/quote.js +0 -1
- package/dist/cart/quote.js.map +0 -1
- package/dist/chunk-I55A5QXJ.js +0 -2
- package/dist/chunk-I55A5QXJ.js.map +0 -1
- package/dist/chunk-LFBQICYU.js +0 -2
- package/dist/chunk-LFBQICYU.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["// tsup replaces __ZAPPIE_SDK_VERSION__ with package.json's version. Source run\n// without a build, as in a plain Node script, gets the development label.\n// Kept inline, not in a shared module, so the core chunks don't split differently.\ndeclare const __ZAPPIE_SDK_VERSION__: string | undefined;\n\n/** The installed @zappie/sdk version. */\nexport const VERSION: string =\n typeof __ZAPPIE_SDK_VERSION__ === \"string\"\n ? __ZAPPIE_SDK_VERSION__\n : \"0.0.0-development\";\nexport { createZappie } from \"./client\";\nexport type {\n MenuResource,\n ReadOptions,\n StoreResource,\n Zappie,\n ZappieEvents,\n} from \"./client\";\nexport { API_VERSION } from \"./config\";\nexport type {\n StorageLike,\n ZappieConfig as ZappieClientOptions,\n ZappieConfig,\n} from \"./config\";\nexport { ZappieError, isZappieError } from \"./errors\";\nexport type { ErrorDetails, ZappieErrorCode } from \"./errors\";\nexport * from \"./models\";\nexport { format, money } from \"./money\";\nexport type { Currency, Formatters, Money, MoneyFormatOptions } from \"./money\";\nexport { RestTransport } from \"./transport/rest\";\nexport type { RestTransportOptions } from \"./transport/rest\";\nexport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchPage,\n SearchRequest,\n Transport,\n} from \"./transport/types\";\n\nexport { ZappieCartError } from \"./cart/errors\";\nexport { previewItem } from \"./cart/preview\";\nexport { estimateUnitPrice } from \"./cart/price\";\nexport { resolveSourceMenu } from \"./cart/source-menu\";\nexport type * from \"./cart/types\";\nexport { validateLine } from \"./cart/validate\";\n"],
|
|
5
|
+
"mappings": ";AAUA,SAAS,oBAAoB;AAQ7B,SAAS,mBAAmB;AAM5B,SAAS,aAAa,qBAAqB;AAE3C,cAAc;AACd,SAAS,QAAQ,aAAa;AAE9B,SAAS,qBAAqB;AAW9B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAElC,SAAS,oBAAoB;AAvCtB,IAAM,UACX,OACI,UACA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/internal/abort.ts
|
|
2
|
+
function abortReason(signal) {
|
|
3
|
+
return signal?.reason ?? new DOMException("The request was aborted", "AbortError");
|
|
4
|
+
}
|
|
5
|
+
function throwIfAborted(signal) {
|
|
6
|
+
if (signal?.aborted) {
|
|
7
|
+
throw abortReason(signal);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function delay(ms, signal) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
throwIfAborted(signal);
|
|
13
|
+
const abort = () => {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
reject(abortReason(signal));
|
|
16
|
+
};
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
signal?.removeEventListener("abort", abort);
|
|
19
|
+
resolve();
|
|
20
|
+
}, ms);
|
|
21
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
abortReason,
|
|
26
|
+
delay,
|
|
27
|
+
throwIfAborted
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=abort.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/abort.ts"],
|
|
4
|
+
"sourcesContent": ["export function abortReason(signal?: AbortSignal): unknown {\n return (\n signal?.reason ?? new DOMException(\"The request was aborted\", \"AbortError\")\n );\n}\nexport function throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw abortReason(signal);\n }\n}\nexport function delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n throwIfAborted(signal);\n const abort = () => {\n clearTimeout(timer);\n reject(abortReason(signal));\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", abort);\n resolve();\n }, ms);\n signal?.addEventListener(\"abort\", abort, { once: true });\n });\n}\n"],
|
|
5
|
+
"mappings": ";AAAO,SAAS,YAAY,QAA+B;AACzD,SACE,QAAQ,UAAU,IAAI,aAAa,2BAA2B,YAAY;AAE9E;AACO,SAAS,eAAe,QAA4B;AACzD,MAAI,QAAQ,SAAS;AACnB,UAAM,YAAY,MAAM;AAAA,EAC1B;AACF;AACO,SAAS,MAAM,IAAY,QAAqC;AACrE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,mBAAe,MAAM;AACrB,UAAM,QAAQ,MAAM;AAClB,mBAAa,KAAK;AAClB,aAAO,YAAY,MAAM,CAAC;AAAA,IAC5B;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,cAAQ,oBAAoB,SAAS,KAAK;AAC1C,cAAQ;AAAA,IACV,GAAG,EAAE;AACL,YAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;AAAA,EACzD,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/internal/cache.ts
|
|
2
|
+
import { asError } from "../errors.js";
|
|
3
|
+
import { abortReason, throwIfAborted } from "./abort.js";
|
|
4
|
+
var REFRESH_BACKOFF_MS = 1e4;
|
|
5
|
+
var EMPTY = Object.freeze({
|
|
6
|
+
fetchedAt: 0,
|
|
7
|
+
loading: false
|
|
8
|
+
});
|
|
9
|
+
var Cache = class {
|
|
10
|
+
constructor(onError) {
|
|
11
|
+
this.onError = onError;
|
|
12
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
13
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
14
|
+
this.flights = /* @__PURE__ */ new Map();
|
|
15
|
+
this.subscribe = (fn) => {
|
|
16
|
+
this.listeners.add(fn);
|
|
17
|
+
return () => {
|
|
18
|
+
this.listeners.delete(fn);
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
snapshot(key) {
|
|
23
|
+
return this.entries.get(key) ?? EMPTY;
|
|
24
|
+
}
|
|
25
|
+
notify() {
|
|
26
|
+
for (const fn of [...this.listeners]) {
|
|
27
|
+
fn();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
set(key, value, etag) {
|
|
31
|
+
this.entries.set(key, {
|
|
32
|
+
value,
|
|
33
|
+
fetchedAt: Date.now(),
|
|
34
|
+
loading: false,
|
|
35
|
+
...etag !== void 0 ? { etag } : {}
|
|
36
|
+
});
|
|
37
|
+
this.notify();
|
|
38
|
+
}
|
|
39
|
+
stale(key) {
|
|
40
|
+
const entry = this.snapshot(key);
|
|
41
|
+
return entry.value !== void 0 && Date.now() - entry.fetchedAt >= 6e4;
|
|
42
|
+
}
|
|
43
|
+
async get(key, loader, signal) {
|
|
44
|
+
throwIfAborted(signal);
|
|
45
|
+
const entry = this.snapshot(key);
|
|
46
|
+
const age = Date.now() - entry.fetchedAt;
|
|
47
|
+
if (entry.value !== void 0 && age < 6e4) {
|
|
48
|
+
return entry.value;
|
|
49
|
+
}
|
|
50
|
+
const background = entry.value !== void 0 && age < 6e5;
|
|
51
|
+
if (background && entry.failedAt !== void 0 && Date.now() - entry.failedAt < REFRESH_BACKOFF_MS) {
|
|
52
|
+
return entry.value;
|
|
53
|
+
}
|
|
54
|
+
let flight = this.flights.get(key);
|
|
55
|
+
if (!flight) {
|
|
56
|
+
const controller = new AbortController();
|
|
57
|
+
flight = {
|
|
58
|
+
controller,
|
|
59
|
+
users: 0,
|
|
60
|
+
keepAlive: background,
|
|
61
|
+
promise: Promise.resolve()
|
|
62
|
+
};
|
|
63
|
+
const current2 = flight;
|
|
64
|
+
this.flights.set(key, current2);
|
|
65
|
+
const loading = { ...entry, loading: true };
|
|
66
|
+
this.entries.set(key, loading);
|
|
67
|
+
this.notify();
|
|
68
|
+
current2.promise = Promise.resolve().then(() => {
|
|
69
|
+
throwIfAborted(controller.signal);
|
|
70
|
+
return loader(controller.signal, entry);
|
|
71
|
+
}).then((result) => {
|
|
72
|
+
if (this.entries.get(key) === loading && !controller.signal.aborted) {
|
|
73
|
+
this.set(key, result.value, result.etag);
|
|
74
|
+
result.onStored?.();
|
|
75
|
+
}
|
|
76
|
+
return result.value;
|
|
77
|
+
}).catch((error) => {
|
|
78
|
+
if (this.entries.get(key) === loading) {
|
|
79
|
+
this.entries.set(
|
|
80
|
+
key,
|
|
81
|
+
controller.signal.aborted ? { ...entry, loading: false } : {
|
|
82
|
+
...entry,
|
|
83
|
+
loading: false,
|
|
84
|
+
error: asError(error),
|
|
85
|
+
failedAt: Date.now()
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
this.notify();
|
|
89
|
+
if (!controller.signal.aborted) {
|
|
90
|
+
this.onError(asError(error));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw controller.signal.aborted ? abortReason(controller.signal) : asError(error);
|
|
94
|
+
}).finally(() => {
|
|
95
|
+
if (this.flights.get(key) === current2) {
|
|
96
|
+
this.flights.delete(key);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (background) {
|
|
101
|
+
flight.keepAlive = true;
|
|
102
|
+
void flight.promise.catch(() => {
|
|
103
|
+
});
|
|
104
|
+
return entry.value;
|
|
105
|
+
}
|
|
106
|
+
const current = flight;
|
|
107
|
+
current.users++;
|
|
108
|
+
return new Promise((resolve, reject) => {
|
|
109
|
+
let done = false;
|
|
110
|
+
const finish = (callback) => {
|
|
111
|
+
if (done) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
done = true;
|
|
115
|
+
signal?.removeEventListener("abort", abort);
|
|
116
|
+
current.users--;
|
|
117
|
+
callback();
|
|
118
|
+
};
|
|
119
|
+
const abort = () => {
|
|
120
|
+
finish(() => reject(abortReason(signal)));
|
|
121
|
+
if (current.users === 0 && !current.keepAlive) {
|
|
122
|
+
current.controller.abort(abortReason(signal));
|
|
123
|
+
if (this.flights.get(key) === current) {
|
|
124
|
+
this.flights.delete(key);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
129
|
+
current.promise.then(
|
|
130
|
+
(value) => finish(() => resolve(value)),
|
|
131
|
+
(error) => finish(() => reject(error))
|
|
132
|
+
);
|
|
133
|
+
if (signal?.aborted) {
|
|
134
|
+
abort();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
export {
|
|
140
|
+
Cache
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/cache.ts"],
|
|
4
|
+
"sourcesContent": ["import { asError } from \"../errors\";\nimport type { ZappieError } from \"../errors\";\nimport { abortReason, throwIfAborted } from \"./abort\";\nexport type CacheEntry<T> = {\n value?: T;\n fetchedAt: number;\n etag?: string;\n error?: ZappieError;\n failedAt?: number;\n loading: boolean;\n};\n/** After a failed background refresh, stale data is served without retrying for this long. */\nconst REFRESH_BACKOFF_MS = 10_000;\ntype Loaded<T> = { value: T; etag?: string; onStored?: () => void };\ntype Flight = {\n promise: Promise<unknown>;\n controller: AbortController;\n users: number;\n keepAlive: boolean;\n};\nconst EMPTY: CacheEntry<never> = Object.freeze({\n fetchedAt: 0,\n loading: false,\n});\n/** Client-local SWR storage. Subscribers receive stable snapshots until an entry changes. */\nexport class Cache {\n private entries = new Map<string, CacheEntry<unknown>>();\n private listeners = new Set<() => void>();\n private flights = new Map<string, Flight>();\n constructor(private onError: (error: ZappieError) => void) {}\n snapshot<T>(key: string): CacheEntry<T> {\n return (this.entries.get(key) ?? EMPTY) as CacheEntry<T>;\n }\n subscribe = (fn: () => void) => {\n this.listeners.add(fn);\n return () => {\n this.listeners.delete(fn);\n };\n };\n private notify() {\n for (const fn of [...this.listeners]) {\n fn();\n }\n }\n set<T>(key: string, value: T, etag?: string) {\n this.entries.set(key, {\n value,\n fetchedAt: Date.now(),\n loading: false,\n ...(etag !== undefined ? { etag } : {}),\n });\n this.notify();\n }\n stale(key: string) {\n const entry = this.snapshot(key);\n return entry.value !== undefined && Date.now() - entry.fetchedAt >= 60_000;\n }\n async get<T>(\n key: string,\n loader: (signal: AbortSignal, entry: CacheEntry<T>) => Promise<Loaded<T>>,\n signal?: AbortSignal,\n ): Promise<T> {\n throwIfAborted(signal);\n const entry = this.snapshot<T>(key);\n const age = Date.now() - entry.fetchedAt;\n if (entry.value !== undefined && age < 60_000) {\n return entry.value;\n }\n const background = entry.value !== undefined && age < 600_000;\n if (\n background &&\n entry.failedAt !== undefined &&\n Date.now() - entry.failedAt < REFRESH_BACKOFF_MS\n ) {\n return entry.value!;\n }\n let flight = this.flights.get(key);\n if (!flight) {\n const controller = new AbortController();\n flight = {\n controller,\n users: 0,\n keepAlive: background,\n promise: Promise.resolve(),\n };\n const current = flight;\n this.flights.set(key, current);\n const loading = { ...entry, loading: true };\n this.entries.set(key, loading);\n this.notify();\n current.promise = Promise.resolve()\n .then(() => {\n throwIfAborted(controller.signal);\n return loader(controller.signal, entry);\n })\n .then((result) => {\n if (this.entries.get(key) === loading && !controller.signal.aborted) {\n this.set(key, result.value, result.etag);\n result.onStored?.();\n }\n return result.value;\n })\n .catch((error) => {\n if (this.entries.get(key) === loading) {\n this.entries.set(\n key,\n controller.signal.aborted\n ? { ...entry, loading: false }\n : {\n ...entry,\n loading: false,\n error: asError(error),\n failedAt: Date.now(),\n },\n );\n this.notify();\n if (!controller.signal.aborted) {\n this.onError(asError(error));\n }\n }\n throw controller.signal.aborted\n ? abortReason(controller.signal)\n : asError(error);\n })\n .finally(() => {\n if (this.flights.get(key) === current) {\n this.flights.delete(key);\n }\n });\n }\n if (background) {\n flight.keepAlive = true;\n void flight.promise.catch(() => {});\n return entry.value!;\n }\n const current = flight;\n current.users++;\n return new Promise<T>((resolve, reject) => {\n let done = false;\n const finish = (callback: () => void) => {\n if (done) {\n return;\n }\n done = true;\n signal?.removeEventListener(\"abort\", abort);\n current.users--;\n callback();\n };\n const abort = () => {\n finish(() => reject(abortReason(signal)));\n if (current.users === 0 && !current.keepAlive) {\n current.controller.abort(abortReason(signal));\n if (this.flights.get(key) === current) {\n this.flights.delete(key);\n }\n }\n };\n signal?.addEventListener(\"abort\", abort, { once: true });\n current.promise.then(\n (value) => finish(() => resolve(value as T)),\n (error) => finish(() => reject(error)),\n );\n if (signal?.aborted) {\n abort();\n }\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["current"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/internal/customer-http.ts
|
|
2
|
+
import { ZappieError } from "@zappie/sdk";
|
|
3
|
+
function stableErrorCode(status) {
|
|
4
|
+
if (status === 400 || status === 422) {
|
|
5
|
+
return "invalid_request";
|
|
6
|
+
}
|
|
7
|
+
if (status === 401) {
|
|
8
|
+
return "unauthorized";
|
|
9
|
+
}
|
|
10
|
+
if (status === 404) {
|
|
11
|
+
return "not_found";
|
|
12
|
+
}
|
|
13
|
+
if (status === 429) {
|
|
14
|
+
return "rate_limited";
|
|
15
|
+
}
|
|
16
|
+
return "server_error";
|
|
17
|
+
}
|
|
18
|
+
async function apiError(response, fallback) {
|
|
19
|
+
const body = await response.json().catch(() => ({}));
|
|
20
|
+
const envelope = body && typeof body === "object" ? body : {};
|
|
21
|
+
const value = "error" in envelope && envelope.error && typeof envelope.error === "object" ? envelope.error : {};
|
|
22
|
+
return {
|
|
23
|
+
apiCode: "code" in value && typeof value.code === "string" ? value.code : "server_error",
|
|
24
|
+
message: "message" in value && typeof value.message === "string" ? value.message : fallback,
|
|
25
|
+
..."param" in value && typeof value.param === "string" ? { param: value.param } : {},
|
|
26
|
+
..."issues" in value ? { issues: value.issues } : {}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function wait(ms, signal) {
|
|
30
|
+
signal?.throwIfAborted();
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const done = () => {
|
|
33
|
+
signal?.removeEventListener("abort", aborted);
|
|
34
|
+
resolve();
|
|
35
|
+
};
|
|
36
|
+
const timer = setTimeout(done, ms);
|
|
37
|
+
const aborted = () => {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
signal?.removeEventListener("abort", aborted);
|
|
40
|
+
reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
41
|
+
};
|
|
42
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function requestWithRetry(fetcher, input, init, message) {
|
|
46
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
return await fetcher(input, {
|
|
49
|
+
...init,
|
|
50
|
+
credentials: "same-origin",
|
|
51
|
+
headers: { Accept: "application/json", ...init.headers }
|
|
52
|
+
});
|
|
53
|
+
} catch (cause) {
|
|
54
|
+
if (init.signal?.aborted) {
|
|
55
|
+
throw init.signal.reason ?? cause;
|
|
56
|
+
}
|
|
57
|
+
if (attempt === 1) {
|
|
58
|
+
throw new ZappieError("network_error", message, {
|
|
59
|
+
cause,
|
|
60
|
+
retryable: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
await wait(400, init.signal ?? void 0);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
throw new ZappieError("network_error", message, { retryable: true });
|
|
67
|
+
}
|
|
68
|
+
export {
|
|
69
|
+
apiError,
|
|
70
|
+
requestWithRetry,
|
|
71
|
+
stableErrorCode,
|
|
72
|
+
wait
|
|
73
|
+
};
|
|
74
|
+
//# sourceMappingURL=customer-http.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/customer-http.ts"],
|
|
4
|
+
"sourcesContent": ["// Isolated customer-facing bundles import the installed core so error identity is shared.\n// eslint-disable-next-line no-restricted-imports\nimport { ZappieError } from \"@zappie/sdk\";\n\nexport type ApiErrorDetails = {\n apiCode: string;\n message: string;\n param?: string;\n issues?: unknown;\n};\n\nexport function stableErrorCode(status: number) {\n if (status === 400 || status === 422) {\n return \"invalid_request\" as const;\n }\n if (status === 401) {\n return \"unauthorized\" as const;\n }\n if (status === 404) {\n return \"not_found\" as const;\n }\n if (status === 429) {\n return \"rate_limited\" as const;\n }\n return \"server_error\" as const;\n}\n\nexport async function apiError(\n response: Response,\n fallback: string,\n): Promise<ApiErrorDetails> {\n const body = await response.json().catch(() => ({}));\n const envelope = body && typeof body === \"object\" ? body : {};\n const value =\n \"error\" in envelope && envelope.error && typeof envelope.error === \"object\"\n ? envelope.error\n : {};\n return {\n apiCode:\n \"code\" in value && typeof value.code === \"string\"\n ? value.code\n : \"server_error\",\n message:\n \"message\" in value && typeof value.message === \"string\"\n ? value.message\n : fallback,\n ...(\"param\" in value && typeof value.param === \"string\"\n ? { param: value.param }\n : {}),\n ...(\"issues\" in value ? { issues: value.issues } : {}),\n };\n}\n\nexport function wait(ms: number, signal?: AbortSignal): Promise<void> {\n signal?.throwIfAborted();\n return new Promise((resolve, reject) => {\n const done = () => {\n signal?.removeEventListener(\"abort\", aborted);\n resolve();\n };\n const timer = setTimeout(done, ms);\n const aborted = () => {\n clearTimeout(timer);\n signal?.removeEventListener(\"abort\", aborted);\n reject(signal?.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", aborted, { once: true });\n });\n}\n\nexport async function requestWithRetry(\n fetcher: typeof fetch,\n input: string,\n init: RequestInit,\n message: string,\n): Promise<Response> {\n for (let attempt = 0; attempt < 2; attempt++) {\n try {\n return await fetcher(input, {\n ...init,\n credentials: \"same-origin\",\n headers: { Accept: \"application/json\", ...init.headers },\n });\n } catch (cause) {\n if (init.signal?.aborted) {\n throw init.signal.reason ?? cause;\n }\n if (attempt === 1) {\n throw new ZappieError(\"network_error\", message, {\n cause,\n retryable: true,\n });\n }\n await wait(400, init.signal ?? undefined);\n }\n }\n throw new ZappieError(\"network_error\", message, { retryable: true });\n}\n"],
|
|
5
|
+
"mappings": ";AAEA,SAAS,mBAAmB;AASrB,SAAS,gBAAgB,QAAgB;AAC9C,MAAI,WAAW,OAAO,WAAW,KAAK;AACpC,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,KAAK;AAClB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAsB,SACpB,UACA,UAC0B;AAC1B,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACnD,QAAM,WAAW,QAAQ,OAAO,SAAS,WAAW,OAAO,CAAC;AAC5D,QAAM,QACJ,WAAW,YAAY,SAAS,SAAS,OAAO,SAAS,UAAU,WAC/D,SAAS,QACT,CAAC;AACP,SAAO;AAAA,IACL,SACE,UAAU,SAAS,OAAO,MAAM,SAAS,WACrC,MAAM,OACN;AAAA,IACN,SACE,aAAa,SAAS,OAAO,MAAM,YAAY,WAC3C,MAAM,UACN;AAAA,IACN,GAAI,WAAW,SAAS,OAAO,MAAM,UAAU,WAC3C,EAAE,OAAO,MAAM,MAAM,IACrB,CAAC;AAAA,IACL,GAAI,YAAY,QAAQ,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,EACtD;AACF;AAEO,SAAS,KAAK,IAAY,QAAqC;AACpE,UAAQ,eAAe;AACvB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,OAAO,MAAM;AACjB,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,cAAQ;AAAA,IACV;AACA,UAAM,QAAQ,WAAW,MAAM,EAAE;AACjC,UAAM,UAAU,MAAM;AACpB,mBAAa,KAAK;AAClB,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,aAAO,QAAQ,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,IACpE;AACA,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC3D,CAAC;AACH;AAEA,eAAsB,iBACpB,SACA,OACA,MACA,SACmB;AACnB,WAAS,UAAU,GAAG,UAAU,GAAG,WAAW;AAC5C,QAAI;AACF,aAAO,MAAM,QAAQ,OAAO;AAAA,QAC1B,GAAG;AAAA,QACH,aAAa;AAAA,QACb,SAAS,EAAE,QAAQ,oBAAoB,GAAG,KAAK,QAAQ;AAAA,MACzD,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,KAAK,QAAQ,SAAS;AACxB,cAAM,KAAK,OAAO,UAAU;AAAA,MAC9B;AACA,UAAI,YAAY,GAAG;AACjB,cAAM,IAAI,YAAY,iBAAiB,SAAS;AAAA,UAC9C;AAAA,UACA,WAAW;AAAA,QACb,CAAC;AAAA,MACH;AACA,YAAM,KAAK,KAAK,KAAK,UAAU,MAAS;AAAA,IAC1C;AAAA,EACF;AACA,QAAM,IAAI,YAAY,iBAAiB,SAAS,EAAE,WAAW,KAAK,CAAC;AACrE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/internal/digest.ts
|
|
2
|
+
async function payloadKey(payload) {
|
|
3
|
+
const digest = await globalThis.crypto.subtle.digest(
|
|
4
|
+
"SHA-256",
|
|
5
|
+
new TextEncoder().encode(payload)
|
|
6
|
+
);
|
|
7
|
+
return Array.from(
|
|
8
|
+
new Uint8Array(digest),
|
|
9
|
+
(byte) => byte.toString(16).padStart(2, "0")
|
|
10
|
+
).join("");
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
payloadKey
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=digest.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/digest.ts"],
|
|
4
|
+
"sourcesContent": ["/** Payload digests avoid putting notes, addresses or credentials in request headers. */\nexport async function payloadKey(payload: string): Promise<string> {\n const digest = await globalThis.crypto.subtle.digest(\n \"SHA-256\",\n new TextEncoder().encode(payload),\n );\n return Array.from(new Uint8Array(digest), (byte) =>\n byte.toString(16).padStart(2, \"0\"),\n ).join(\"\");\n}\n"],
|
|
5
|
+
"mappings": ";AACA,eAAsB,WAAW,SAAkC;AACjE,QAAM,SAAS,MAAM,WAAW,OAAO,OAAO;AAAA,IAC5C;AAAA,IACA,IAAI,YAAY,EAAE,OAAO,OAAO;AAAA,EAClC;AACA,SAAO,MAAM;AAAA,IAAK,IAAI,WAAW,MAAM;AAAA,IAAG,CAAC,SACzC,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAAA,EACnC,EAAE,KAAK,EAAE;AACX;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/internal/emitter.ts
|
|
2
|
+
function createEmitter() {
|
|
3
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
return {
|
|
5
|
+
on(event, fn) {
|
|
6
|
+
const set = listeners.get(event) ?? /* @__PURE__ */ new Set();
|
|
7
|
+
listeners.set(event, set);
|
|
8
|
+
set.add(fn);
|
|
9
|
+
return () => {
|
|
10
|
+
set.delete(fn);
|
|
11
|
+
};
|
|
12
|
+
},
|
|
13
|
+
emit(event, payload) {
|
|
14
|
+
for (const fn of [...listeners.get(event) ?? []]) {
|
|
15
|
+
try {
|
|
16
|
+
fn(payload);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("Zappie event listener failed", error);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
createEmitter
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/emitter.ts"],
|
|
4
|
+
"sourcesContent": ["import type { CartIssueCode } from \"../cart/types\";\nimport type { ZappieError } from \"../errors\";\nimport type { Money } from \"../money\";\n/** Events currently emitted by the read-only SDK. */\nexport type ZappieEvents = {\n search: { query: string; resultCount: number };\n warning: { message: string };\n cart_expired: { branchId: string };\n add_to_cart: { itemId: string; quantity: number; value: Money };\n remove_from_cart: { itemId: string; quantity: number; value: Money };\n update_cart_line: { lineId: string; itemId: string; quantity: number };\n view_cart: Record<string, never>;\n cart_issue: { code: CartIssueCode };\n quote_failed: { code: string };\n \"menu:updated\": { branchId: string; version: string };\n error: { error: ZappieError };\n};\nexport function createEmitter() {\n const listeners = new Map<\n keyof ZappieEvents,\n Set<(payload: never) => void>\n >();\n return {\n on<E extends keyof ZappieEvents>(\n event: E,\n fn: (payload: ZappieEvents[E]) => void,\n ) {\n const set = listeners.get(event) ?? new Set();\n listeners.set(event, set);\n set.add(fn as (payload: never) => void);\n return () => {\n set.delete(fn as (payload: never) => void);\n };\n },\n emit<E extends keyof ZappieEvents>(event: E, payload: ZappieEvents[E]) {\n for (const fn of [...(listeners.get(event) ?? [])]) {\n try {\n fn(payload as never);\n } catch (error) {\n console.error(\"Zappie event listener failed\", error);\n }\n }\n },\n };\n}\n"],
|
|
5
|
+
"mappings": ";AAiBO,SAAS,gBAAgB;AAC9B,QAAM,YAAY,oBAAI,IAGpB;AACF,SAAO;AAAA,IACL,GACE,OACA,IACA;AACA,YAAM,MAAM,UAAU,IAAI,KAAK,KAAK,oBAAI,IAAI;AAC5C,gBAAU,IAAI,OAAO,GAAG;AACxB,UAAI,IAAI,EAA8B;AACtC,aAAO,MAAM;AACX,YAAI,OAAO,EAA8B;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,KAAmC,OAAU,SAA0B;AACrE,iBAAW,MAAM,CAAC,GAAI,UAAU,IAAI,KAAK,KAAK,CAAC,CAAE,GAAG;AAClD,YAAI;AACF,aAAG,OAAgB;AAAA,QACrB,SAAS,OAAO;AACd,kBAAQ,MAAM,gCAAgC,KAAK;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// src/internal/money-math.ts
|
|
2
|
+
import { ZappieError } from "../errors.js";
|
|
3
|
+
import { money } from "../money.js";
|
|
4
|
+
function sumMoney(values, zero) {
|
|
5
|
+
let amount = zero.amountMinor;
|
|
6
|
+
for (const value of values) {
|
|
7
|
+
if (value.currency !== zero.currency) {
|
|
8
|
+
throw new ZappieError(
|
|
9
|
+
"invalid_request",
|
|
10
|
+
"Cannot combine different currencies"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
amount = money(amount + value.amountMinor, zero.currency).amountMinor;
|
|
14
|
+
}
|
|
15
|
+
return money(amount, zero.currency);
|
|
16
|
+
}
|
|
17
|
+
function multiplyMoney(value, quantity) {
|
|
18
|
+
if (!Number.isSafeInteger(quantity)) {
|
|
19
|
+
throw new ZappieError(
|
|
20
|
+
"invalid_request",
|
|
21
|
+
"Money requires an integer quantity"
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return money(value.amountMinor * quantity, value.currency);
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
multiplyMoney,
|
|
28
|
+
sumMoney
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=money-math.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/internal/money-math.ts"],
|
|
4
|
+
"sourcesContent": ["import { ZappieError } from \"../errors\";\nimport type { Money } from \"../money\";\nimport { money } from \"../money\";\nexport function sumMoney(values: readonly Money[], zero: Money): Money {\n let amount = zero.amountMinor;\n for (const value of values) {\n if (value.currency !== zero.currency) {\n throw new ZappieError(\n \"invalid_request\",\n \"Cannot combine different currencies\",\n );\n }\n amount = money(amount + value.amountMinor, zero.currency).amountMinor;\n }\n return money(amount, zero.currency);\n}\nexport function multiplyMoney(value: Money, quantity: number): Money {\n if (!Number.isSafeInteger(quantity)) {\n throw new ZappieError(\n \"invalid_request\",\n \"Money requires an integer quantity\",\n );\n }\n return money(value.amountMinor * quantity, value.currency);\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,mBAAmB;AAE5B,SAAS,aAAa;AACf,SAAS,SAAS,QAA0B,MAAoB;AACrE,MAAI,SAAS,KAAK;AAClB,aAAW,SAAS,QAAQ;AAC1B,QAAI,MAAM,aAAa,KAAK,UAAU;AACpC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,aAAS,MAAM,SAAS,MAAM,aAAa,KAAK,QAAQ,EAAE;AAAA,EAC5D;AACA,SAAO,MAAM,QAAQ,KAAK,QAAQ;AACpC;AACO,SAAS,cAAc,OAAc,UAAyB;AACnE,MAAI,CAAC,OAAO,cAAc,QAAQ,GAAG;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,MAAM,MAAM,cAAc,UAAU,MAAM,QAAQ;AAC3D;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/internal.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
// src/internal.ts
|
|
2
|
+
import * as publicSdk from "./index.js";
|
|
3
|
+
import { wireFulfilment } from "./cart/wire.js";
|
|
4
|
+
import { validateConfig } from "./config.js";
|
|
5
|
+
export {
|
|
6
|
+
validateConfig,
|
|
7
|
+
wireFulfilment
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=internal.js.map
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/internal.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Utilities for Zappie's own SDK entries and public packages.\n * This entry is outside the SDK's public semantic-versioning contract.\n *\n * @packageDocumentation\n * @internal\n */\nimport * as publicSdk from \"./index\";\n\nexport { wireFulfilment } from \"./cart/wire\";\nexport { validateConfig } from \"./config\";\n\n// Keep the core build graph shared rather than splitting its common chunks for this entry.\nvoid publicSdk;\n"],
|
|
5
|
+
"mappings": ";AAOA,YAAY,eAAe;AAE3B,SAAS,sBAAsB;AAC/B,SAAS,sBAAsB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/models.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=models.js.map
|
package/dist/money.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/money.ts
|
|
2
|
+
import { ZappieError } from "./errors.js";
|
|
3
|
+
var ZERO_DIGIT_CURRENCIES = " AFN ALL BIF CLP DJF GNF IQD IRR ISK JPY KMF KPW KRW LAK LBP MGA MMK PYG RSD RWF SLL SOS SYP UGX UYI VND VUV XAF XOF XPF YER ";
|
|
4
|
+
var THREE_DIGIT_CURRENCIES = " BHD JOD KWD LYD OMR TND ";
|
|
5
|
+
var FOUR_DIGIT_CURRENCIES = " CLF UYW ";
|
|
6
|
+
var API_ONLY_CURRENCIES = " BOV CHE CHW CLF COU MXV USN UYI UYW VED ";
|
|
7
|
+
var contains = (codes, currency) => codes.includes(` ${currency} `);
|
|
8
|
+
var intlCurrencies;
|
|
9
|
+
function isCurrency(code) {
|
|
10
|
+
if (!/^[A-Z]{3}$/.test(code)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (typeof Intl.supportedValuesOf !== "function") {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
intlCurrencies ?? (intlCurrencies = new Set(Intl.supportedValuesOf("currency")));
|
|
17
|
+
return contains(API_ONLY_CURRENCIES, code) || intlCurrencies.has(code);
|
|
18
|
+
}
|
|
19
|
+
function currencyDigits(currency) {
|
|
20
|
+
if (contains(ZERO_DIGIT_CURRENCIES, currency)) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
if (contains(THREE_DIGIT_CURRENCIES, currency)) {
|
|
24
|
+
return 3;
|
|
25
|
+
}
|
|
26
|
+
return contains(FOUR_DIGIT_CURRENCIES, currency) ? 4 : 2;
|
|
27
|
+
}
|
|
28
|
+
function money(amountMinor, currency) {
|
|
29
|
+
const normalized = typeof currency === "string" ? currency.trim().toUpperCase() : "";
|
|
30
|
+
if (!Number.isSafeInteger(amountMinor) || !isCurrency(normalized)) {
|
|
31
|
+
throw new ZappieError(
|
|
32
|
+
"invalid_request",
|
|
33
|
+
"Money requires safe integer minor units and a valid ISO 4217 currency"
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return Object.freeze({ amountMinor, currency: normalized });
|
|
37
|
+
}
|
|
38
|
+
var format = {
|
|
39
|
+
money(value, opts = {}) {
|
|
40
|
+
const normalized = money(value.amountMinor, value.currency);
|
|
41
|
+
const locale = normalized.currency === "NGN" ? "en-NG" : "en";
|
|
42
|
+
const digits = currencyDigits(normalized.currency);
|
|
43
|
+
return new Intl.NumberFormat(locale, {
|
|
44
|
+
style: "currency",
|
|
45
|
+
currency: normalized.currency,
|
|
46
|
+
currencyDisplay: "narrowSymbol",
|
|
47
|
+
minimumFractionDigits: opts.showZeroDecimals || normalized.amountMinor % 10 ** digits !== 0 ? digits : 0,
|
|
48
|
+
maximumFractionDigits: digits,
|
|
49
|
+
notation: opts.compact ? "compact" : "standard",
|
|
50
|
+
signDisplay: opts.sign ?? "auto"
|
|
51
|
+
}).format(normalized.amountMinor / 10 ** digits);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
export {
|
|
55
|
+
format,
|
|
56
|
+
money
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=money.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/money.ts"],
|
|
4
|
+
"sourcesContent": ["import { ZappieError } from \"./errors\";\ndeclare const MoneyBrand: unique symbol;\n/** A normalized ISO 4217 currency code. */\nexport type Currency = string;\n// Minor-unit digits match the API, which scales amount_minor with\n// github.com/bojanz/currency v1.2.0 (pkg/currency.Digits). Intl differs for COP,\n// HUF, IDR, PKR and RSD, so it only presents the amount. Unlisted codes use 2.\nconst ZERO_DIGIT_CURRENCIES =\n \" AFN ALL BIF CLP DJF GNF IQD IRR ISK JPY KMF KPW KRW LAK LBP MGA MMK PYG RSD RWF SLL SOS SYP UGX UYI VND VUV XAF XOF XPF YER \";\nconst THREE_DIGIT_CURRENCIES = \" BHD JOD KWD LYD OMR TND \";\nconst FOUR_DIGIT_CURRENCIES = \" CLF UYW \";\n// API codes that Intl.supportedValuesOf(\"currency\") doesn't list.\nconst API_ONLY_CURRENCIES = \" BOV CHE CHW CLF COU MXV USN UYI UYW VED \";\nconst contains = (codes: string, currency: string) =>\n codes.includes(` ${currency} `);\nlet intlCurrencies: Set<string> | undefined;\nfunction isCurrency(code: string): boolean {\n if (!/^[A-Z]{3}$/.test(code)) {\n return false;\n }\n if (typeof Intl.supportedValuesOf !== \"function\") {\n return true;\n }\n intlCurrencies ??= new Set(Intl.supportedValuesOf(\"currency\"));\n return contains(API_ONLY_CURRENCIES, code) || intlCurrencies.has(code);\n}\nfunction currencyDigits(currency: Currency): number {\n if (contains(ZERO_DIGIT_CURRENCIES, currency)) {\n return 0;\n }\n if (contains(THREE_DIGIT_CURRENCIES, currency)) {\n return 3;\n }\n return contains(FOUR_DIGIT_CURRENCIES, currency) ? 4 : 2;\n}\n/** A frozen amount in safe integer minor units. Create it with money(). */\nexport type Money = {\n readonly amountMinor: number;\n readonly currency: Currency;\n readonly [MoneyBrand]: true;\n};\n/** Validate an amount in minor units and freeze it without rounding. */\nexport function money(amountMinor: number, currency: Currency): Money {\n const normalized =\n typeof currency === \"string\" ? currency.trim().toUpperCase() : \"\";\n if (!Number.isSafeInteger(amountMinor) || !isCurrency(normalized)) {\n throw new ZappieError(\n \"invalid_request\",\n \"Money requires safe integer minor units and a valid ISO 4217 currency\",\n );\n }\n return Object.freeze({ amountMinor, currency: normalized }) as Money;\n}\n/** Currency presentation options. */\nexport type MoneyFormatOptions = {\n showZeroDecimals?: boolean;\n compact?: boolean;\n sign?: \"auto\" | \"always\";\n};\n/** Format amounts for display. Use server quotes for final prices. */\nexport interface Formatters {\n money(value: Money, opts?: MoneyFormatOptions): string;\n}\n/** Format money with currency-specific minor units and locale defaults. */\nexport const format: Formatters = {\n money(value, opts = {}) {\n const normalized = money(value.amountMinor, value.currency);\n const locale = normalized.currency === \"NGN\" ? \"en-NG\" : \"en\";\n const digits = currencyDigits(normalized.currency);\n return new Intl.NumberFormat(locale, {\n style: \"currency\",\n currency: normalized.currency,\n currencyDisplay: \"narrowSymbol\",\n minimumFractionDigits:\n opts.showZeroDecimals || normalized.amountMinor % 10 ** digits !== 0\n ? digits\n : 0,\n maximumFractionDigits: digits,\n notation: opts.compact ? \"compact\" : \"standard\",\n signDisplay: opts.sign ?? \"auto\",\n }).format(normalized.amountMinor / 10 ** digits);\n },\n};\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,mBAAmB;AAO5B,IAAM,wBACJ;AACF,IAAM,yBAAyB;AAC/B,IAAM,wBAAwB;AAE9B,IAAM,sBAAsB;AAC5B,IAAM,WAAW,CAAC,OAAe,aAC/B,MAAM,SAAS,IAAI,QAAQ,GAAG;AAChC,IAAI;AACJ,SAAS,WAAW,MAAuB;AACzC,MAAI,CAAC,aAAa,KAAK,IAAI,GAAG;AAC5B,WAAO;AAAA,EACT;AACA,MAAI,OAAO,KAAK,sBAAsB,YAAY;AAChD,WAAO;AAAA,EACT;AACA,sCAAmB,IAAI,IAAI,KAAK,kBAAkB,UAAU,CAAC;AAC7D,SAAO,SAAS,qBAAqB,IAAI,KAAK,eAAe,IAAI,IAAI;AACvE;AACA,SAAS,eAAe,UAA4B;AAClD,MAAI,SAAS,uBAAuB,QAAQ,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,MAAI,SAAS,wBAAwB,QAAQ,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,SAAO,SAAS,uBAAuB,QAAQ,IAAI,IAAI;AACzD;AAQO,SAAS,MAAM,aAAqB,UAA2B;AACpE,QAAM,aACJ,OAAO,aAAa,WAAW,SAAS,KAAK,EAAE,YAAY,IAAI;AACjE,MAAI,CAAC,OAAO,cAAc,WAAW,KAAK,CAAC,WAAW,UAAU,GAAG;AACjE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,OAAO,OAAO,EAAE,aAAa,UAAU,WAAW,CAAC;AAC5D;AAYO,IAAM,SAAqB;AAAA,EAChC,MAAM,OAAO,OAAO,CAAC,GAAG;AACtB,UAAM,aAAa,MAAM,MAAM,aAAa,MAAM,QAAQ;AAC1D,UAAM,SAAS,WAAW,aAAa,QAAQ,UAAU;AACzD,UAAM,SAAS,eAAe,WAAW,QAAQ;AACjD,WAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,MACnC,OAAO;AAAA,MACP,UAAU,WAAW;AAAA,MACrB,iBAAiB;AAAA,MACjB,uBACE,KAAK,oBAAoB,WAAW,cAAc,MAAM,WAAW,IAC/D,SACA;AAAA,MACN,uBAAuB;AAAA,MACvB,UAAU,KAAK,UAAU,YAAY;AAAA,MACrC,aAAa,KAAK,QAAQ;AAAA,IAC5B,CAAC,EAAE,OAAO,WAAW,cAAc,MAAM,MAAM;AAAA,EACjD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|