@cimplify/sdk 0.104.0 → 0.104.1

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 (71) hide show
  1. package/dist/advanced.d.mts +3 -7
  2. package/dist/advanced.d.ts +3 -7
  3. package/dist/advanced.js +23 -23
  4. package/dist/advanced.mjs +4 -4
  5. package/dist/{client-BuLcOWLv.d.ts → catalogue-BlQ85Uma.d.mts} +415 -763
  6. package/dist/{client-D5AoobFc.d.mts → catalogue-C8TqOj9K.d.ts} +415 -763
  7. package/dist/{payment-g0HFkEDU.d.mts → checkout-BqK9XUz5.d.mts} +3824 -1446
  8. package/dist/{payment-g0HFkEDU.d.ts → checkout-BqK9XUz5.d.ts} +3824 -1446
  9. package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
  10. package/dist/{chunk-2WPVUIHX.mjs → chunk-6G3ANKQA.mjs} +1030 -360
  11. package/dist/{chunk-E4K2VQDD.js → chunk-BWEIMKLF.js} +541 -16
  12. package/dist/{chunk-IQFFSKE2.js → chunk-DQEXY542.js} +1345 -746
  13. package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
  14. package/dist/{chunk-YVAEY3VC.js → chunk-HYFA67A7.js} +1034 -364
  15. package/dist/{chunk-5RT4HYMP.mjs → chunk-L5SNEU2O.mjs} +524 -15
  16. package/dist/{chunk-4BKOKHE3.js → chunk-RBNBSQI7.mjs} +217 -19
  17. package/dist/{chunk-H3RATH4B.mjs → chunk-SFUJMS5E.js} +261 -2
  18. package/dist/{chunk-DYIAX3WI.mjs → chunk-U54CWUUT.mjs} +11 -2
  19. package/dist/{chunk-OKADBZAI.mjs → chunk-UFOWPN3D.mjs} +808 -574
  20. package/dist/{chunk-OCCNG6MO.mjs → chunk-V4457OKB.mjs} +1310 -722
  21. package/dist/{chunk-5BDYDZ4U.js → chunk-WXREIBFW.js} +892 -657
  22. package/dist/{chunk-7PDEX54K.js → chunk-XAUSDMMZ.js} +13 -3
  23. package/dist/{client-BVNWjk6W.d.ts → client-DhXeiUrf.d.ts} +2 -2
  24. package/dist/{client-fM716k7G.d.mts → client-mXiUrV4X.d.mts} +2 -2
  25. package/dist/{index-BtcoqrY1.d.ts → index-B0AdZvrM.d.mts} +9 -4
  26. package/dist/{index-D7Tf3v7c.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
  27. package/dist/index-DPWI5XcU.d.ts +8 -0
  28. package/dist/index-DVfUh-ci.d.mts +8 -0
  29. package/dist/index.d.mts +31 -14
  30. package/dist/index.d.ts +31 -14
  31. package/dist/index.js +305 -133
  32. package/dist/index.mjs +6 -6
  33. package/dist/mock/cli.mjs +1442 -386
  34. package/dist/mock/library.js +1168 -352
  35. package/dist/mock/library.mjs +1168 -352
  36. package/dist/mock/msw.js +1168 -352
  37. package/dist/mock/msw.mjs +1168 -352
  38. package/dist/{price-BLIeAb8K.d.ts → price-6hQIIac9.d.ts} +1 -1
  39. package/dist/{price-Czq_cTC_.d.mts → price-BUDv1rI7.d.mts} +1 -1
  40. package/dist/react.d.mts +10 -5
  41. package/dist/react.d.ts +10 -5
  42. package/dist/react.js +227 -197
  43. package/dist/react.mjs +90 -60
  44. package/dist/{server-DKK_D-PP.d.ts → server-DJ0mSs9i.d.mts} +31 -57
  45. package/dist/{server-BkVhOIwn.d.mts → server-DSTX76Ki.d.ts} +31 -57
  46. package/dist/server.d.mts +3 -4
  47. package/dist/server.d.ts +3 -4
  48. package/dist/server.js +11 -11
  49. package/dist/server.mjs +5 -5
  50. package/dist/styles.css +2 -2
  51. package/dist/testing/msw.d.mts +2 -2
  52. package/dist/testing/msw.d.ts +2 -2
  53. package/dist/testing/msw.js +4 -4
  54. package/dist/testing/msw.mjs +3 -3
  55. package/dist/testing/suite.d.mts +5 -6
  56. package/dist/testing/suite.d.ts +5 -6
  57. package/dist/testing/suite.js +29 -28
  58. package/dist/testing/suite.mjs +10 -9
  59. package/dist/testing.d.mts +2177 -14
  60. package/dist/testing.d.ts +2177 -14
  61. package/dist/testing.js +167 -89
  62. package/dist/testing.mjs +24 -10
  63. package/dist/utils.d.mts +3 -4
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +35 -31
  66. package/dist/utils.mjs +2 -2
  67. package/package.json +9 -9
  68. package/registry/order-history.json +1 -1
  69. package/registry/order-summary.json +1 -1
  70. package/dist/link-D9iSVxt5.d.mts +0 -308
  71. package/dist/link-D9iSVxt5.d.ts +0 -308
package/dist/mock/cli.mjs CHANGED
@@ -7,16 +7,164 @@ import { Http2ServerRequest, constants } from 'http2';
7
7
  import { Readable } from 'stream';
8
8
  import { createHmac } from 'crypto';
9
9
 
10
- // ../node_modules/.bun/@hono+node-server@2.0.5+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/constants-BLSFu_RU.mjs
10
+ // ../node_modules/.bun/@hono+node-server@2.1.1+804d2c5c04916069/node_modules/@hono/node-server/dist/constants-BLSFu_RU.mjs
11
11
  var X_ALREADY_SENT = "x-hono-already-sent";
12
12
 
13
- // ../node_modules/.bun/@hono+node-server@2.0.5+8bc17dcb8a4189a6/node_modules/@hono/node-server/dist/index.mjs
13
+ // ../node_modules/.bun/@hono+node-server@2.1.1+804d2c5c04916069/node_modules/@hono/node-server/dist/index.mjs
14
14
  var RequestError = class extends Error {
15
15
  constructor(message, options) {
16
16
  super(message, options);
17
17
  this.name = "RequestError";
18
18
  }
19
19
  };
20
+ var nonJoinedHeaders = /* @__PURE__ */ new Set([
21
+ "age",
22
+ "authorization",
23
+ "content-length",
24
+ "content-type",
25
+ "etag",
26
+ "expires",
27
+ "from",
28
+ "host",
29
+ "if-modified-since",
30
+ "if-unmodified-since",
31
+ "last-modified",
32
+ "location",
33
+ "max-forwards",
34
+ "proxy-authorization",
35
+ "referer",
36
+ "retry-after",
37
+ "server",
38
+ "user-agent"
39
+ ]);
40
+ var validHeaderName = /^[!#$%&'*+\-.^_`|~\dA-Za-z]+$/;
41
+ var isHttpWhitespace = (code) => code === 9 || code === 10 || code === 13 || code === 32;
42
+ var normalizeHeaderValue = (value) => {
43
+ if (!isHttpWhitespace(value.charCodeAt(0)) && !isHttpWhitespace(value.charCodeAt(value.length - 1))) return value;
44
+ let start = 0;
45
+ let end = value.length;
46
+ while (start < end && isHttpWhitespace(value.charCodeAt(start))) start++;
47
+ while (end > start && isHttpWhitespace(value.charCodeAt(end - 1))) end--;
48
+ return value.slice(start, end);
49
+ };
50
+ var forbiddenHeaderValue = /[\0\r\n]/;
51
+ var GlobalHeaders = globalThis.Headers;
52
+ var materializeHeaders = (rawHeaders, HeadersCtor = GlobalHeaders) => {
53
+ const headers = new HeadersCtor();
54
+ for (let i = 0; i < rawHeaders.length; i += 2) {
55
+ const name = rawHeaders[i];
56
+ if (!name.startsWith(":")) headers.append(name, rawHeaders[i + 1]);
57
+ }
58
+ return headers;
59
+ };
60
+ var RequestHeaders = class {
61
+ #incoming;
62
+ #rawHeaders;
63
+ #headers;
64
+ #invalidValue;
65
+ constructor(incoming) {
66
+ this.#incoming = incoming;
67
+ if (incoming instanceof Http2ServerRequest) this.#rawHeaders = incoming.rawHeaders.slice();
68
+ }
69
+ get #lazyRawHeaders() {
70
+ return this.#rawHeaders ??= this.#incoming.rawHeaders.slice();
71
+ }
72
+ get #native() {
73
+ if (!this.#headers) {
74
+ this.#headers = materializeHeaders(this.#lazyRawHeaders);
75
+ this.#rawHeaders = void 0;
76
+ }
77
+ return this.#headers;
78
+ }
79
+ #normalizedName(name) {
80
+ if (typeof name !== "string") return;
81
+ if (!validHeaderName.test(name)) throw new TypeError(`Invalid header name: ${name}`);
82
+ return name.toLowerCase();
83
+ }
84
+ #lookupHttp1(lowerName) {
85
+ const headers = this.#incoming instanceof Http2ServerRequest ? void 0 : this.#incoming.headers;
86
+ if (!headers || nonJoinedHeaders.has(lowerName) || lowerName === "set-cookie" || lowerName === "__proto__") return;
87
+ if (!Object.hasOwn(headers, lowerName)) return null;
88
+ const rawValue = headers[lowerName];
89
+ if (typeof rawValue === "string") {
90
+ const value = normalizeHeaderValue(rawValue);
91
+ return forbiddenHeaderValue.test(value) ? void 0 : value;
92
+ }
93
+ }
94
+ #lookup(rawHeaders, lowerName) {
95
+ const separator = lowerName === "cookie" ? "; " : ", ";
96
+ let value = null;
97
+ for (let i = 0; i < rawHeaders.length; i += 2) {
98
+ const rawName = rawHeaders[i];
99
+ if (rawName.length === lowerName.length && rawName.toLowerCase() === lowerName) {
100
+ const rawValue = normalizeHeaderValue(rawHeaders[i + 1]);
101
+ if (forbiddenHeaderValue.test(rawValue)) {
102
+ this.#invalidValue = true;
103
+ return;
104
+ }
105
+ value = value === null ? rawValue : value + separator + rawValue;
106
+ }
107
+ }
108
+ return value;
109
+ }
110
+ append(name, value) {
111
+ this.#native.append(name, value);
112
+ }
113
+ delete(name) {
114
+ this.#native.delete(name);
115
+ }
116
+ get(name) {
117
+ const lowerName = this.#normalizedName(name);
118
+ if (lowerName && !this.#headers && !this.#invalidValue) {
119
+ const http1Value = this.#lookupHttp1(lowerName);
120
+ if (http1Value !== void 0) return http1Value;
121
+ const value = this.#lookup(this.#lazyRawHeaders, lowerName);
122
+ if (value !== void 0) return value;
123
+ }
124
+ return this.#native.get(name);
125
+ }
126
+ has(name) {
127
+ const lowerName = this.#normalizedName(name);
128
+ if (lowerName && !this.#headers && !this.#invalidValue) {
129
+ const http1Value = this.#lookupHttp1(lowerName);
130
+ if (http1Value !== void 0) return http1Value !== null;
131
+ const value = this.#lookup(this.#lazyRawHeaders, lowerName);
132
+ if (value !== void 0) return value !== null;
133
+ }
134
+ return this.#native.has(name);
135
+ }
136
+ set(name, value) {
137
+ this.#native.set(name, value);
138
+ }
139
+ getSetCookie() {
140
+ return this.#native.getSetCookie();
141
+ }
142
+ keys() {
143
+ return this.#native.keys();
144
+ }
145
+ values() {
146
+ return this.#native.values();
147
+ }
148
+ entries() {
149
+ return this.#native.entries();
150
+ }
151
+ forEach(callback, thisArg) {
152
+ this.#native.forEach((value, key) => {
153
+ callback.call(thisArg, value, key, this);
154
+ });
155
+ }
156
+ [Symbol.iterator]() {
157
+ return this.entries();
158
+ }
159
+ };
160
+ Object.defineProperty(RequestHeaders.prototype, /* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom"), { value: function(depth, options, inspectFn) {
161
+ return `Headers (lightweight) ${inspectFn(Object.fromEntries(this), {
162
+ ...options,
163
+ depth: depth == null ? null : depth - 1
164
+ })}`;
165
+ } });
166
+ Object.setPrototypeOf(RequestHeaders.prototype, GlobalHeaders.prototype);
167
+ var newHeadersFromIncoming = (incoming) => globalThis.Headers === GlobalHeaders ? new RequestHeaders(incoming) : materializeHeaders(incoming.rawHeaders, globalThis.Headers);
20
168
  var reValidRequestUrl = /^\/[!#$&-;=?-\[\]_a-z~]*$/;
21
169
  var reDotSegment = /\/\.\.?(?:[/?#]|$)/;
22
170
  var reValidHost = /^[a-z0-9._-]+(?::(?:[1-5]\d{3,4}|[6-9]\d{3}))?$/;
@@ -49,16 +197,55 @@ var Request$1 = class extends GlobalRequest {
49
197
  super(input, options);
50
198
  }
51
199
  };
52
- var newHeadersFromIncoming = (incoming) => {
53
- const headerRecord = [];
54
- const rawHeaders = incoming.rawHeaders;
55
- for (let i = 0, len = rawHeaders.length; i < len; i += 2) {
56
- const key = rawHeaders[i];
57
- if (key.charCodeAt(0) !== 58) headerRecord.push([key, rawHeaders[i + 1]]);
58
- }
59
- return new Headers(headerRecord);
60
- };
61
200
  var wrapBodyStream = /* @__PURE__ */ Symbol("wrapBodyStream");
201
+ var byteExactEncodings = /* @__PURE__ */ new Set([
202
+ "latin1",
203
+ "binary",
204
+ "hex",
205
+ "base64",
206
+ "base64url"
207
+ ]);
208
+ var isByteExactEncoding = (encoding) => encoding === null || byteExactEncodings.has(encoding);
209
+ var bodyBufferedBeforeDisconnectKey = /* @__PURE__ */ Symbol("bodyBufferedBeforeDisconnect");
210
+ var bodyBufferedLengthBeforeDisconnectKey = /* @__PURE__ */ Symbol("bodyBufferedLengthBeforeDisconnect");
211
+ var toBufferChunk = (chunk, encoding) => Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding ?? "utf8");
212
+ var isRecoverableDisconnectedIncoming = (incoming) => !(incoming instanceof Http2ServerRequest) && !!incoming.complete && !!incoming.readableAborted && typeof incoming.read === "function" && isByteExactEncoding(incoming.readableEncoding);
213
+ var recordBodyBufferedBeforeDisconnect = (incoming) => {
214
+ if (incoming.readableDidRead || !isRecoverableDisconnectedIncoming(incoming)) return;
215
+ const incomingWithRecovery = incoming;
216
+ incomingWithRecovery[bodyBufferedLengthBeforeDisconnectKey] ??= incoming.readableLength;
217
+ };
218
+ var readBodyBufferedBeforeDisconnect = (incoming, chunks) => {
219
+ if (incoming.readableDidRead && !chunks || !isRecoverableDisconnectedIncoming(incoming)) return;
220
+ const incomingWithRecovery = incoming;
221
+ if (incomingWithRecovery[bodyBufferedBeforeDisconnectKey] !== void 0) return incomingWithRecovery[bodyBufferedBeforeDisconnectKey];
222
+ let result;
223
+ const errored = incoming.errored;
224
+ if (errored && errored.code !== "ECONNRESET") result = errored;
225
+ else if (incomingWithRecovery[bodyBufferedLengthBeforeDisconnectKey] !== void 0 && incoming.readableLength !== incomingWithRecovery[bodyBufferedLengthBeforeDisconnectKey]) result = newBodyUnusableError();
226
+ else {
227
+ const bodyChunks = chunks ?? [];
228
+ const chunk = incoming.read();
229
+ if (chunk !== null) bodyChunks.push(toBufferChunk(chunk, incoming.readableEncoding));
230
+ const buffer = bodyChunks.length === 1 ? bodyChunks[0] : Buffer.concat(bodyChunks);
231
+ result = buffer;
232
+ const contentLength = incoming.headers["content-length"];
233
+ if (typeof contentLength === "string" && /^\d+$/.test(contentLength)) {
234
+ const expectedLength = Number(contentLength);
235
+ if (Number.isSafeInteger(expectedLength) && buffer.length !== expectedLength) result = newBodyUnusableError();
236
+ }
237
+ }
238
+ incomingWithRecovery[bodyBufferedBeforeDisconnectKey] = result;
239
+ return result;
240
+ };
241
+ var enqueueBufferedBody = (controller, buffered) => {
242
+ if (buffered instanceof Error) {
243
+ controller.error(buffered);
244
+ return;
245
+ }
246
+ if (buffered.length > 0) controller.enqueue(buffered);
247
+ controller.close();
248
+ };
62
249
  var newRequestFromIncoming = (method, url, headers, incoming, abortController) => {
63
250
  const init = {
64
251
  method,
@@ -81,6 +268,13 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
81
268
  let reader;
82
269
  init.body = new ReadableStream({ async pull(controller) {
83
270
  try {
271
+ if (!reader) {
272
+ const buffered = readBodyBufferedBeforeDisconnect(incoming);
273
+ if (buffered !== void 0) {
274
+ enqueueBufferedBody(controller, buffered);
275
+ return;
276
+ }
277
+ }
84
278
  reader ||= Readable.toWeb(incoming).getReader();
85
279
  const { done, value } = await reader.read();
86
280
  if (done) controller.close();
@@ -89,7 +283,13 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
89
283
  controller.error(error);
90
284
  }
91
285
  } });
92
- } else init.body = Readable.toWeb(incoming);
286
+ } else {
287
+ const buffered = readBodyBufferedBeforeDisconnect(incoming);
288
+ if (buffered !== void 0) init.body = new ReadableStream({ start(controller) {
289
+ enqueueBufferedBody(controller, buffered);
290
+ } });
291
+ else init.body = Readable.toWeb(incoming);
292
+ }
93
293
  return new Request$1(url, init);
94
294
  };
95
295
  var getRequestCache = /* @__PURE__ */ Symbol("getRequestCache");
@@ -131,8 +331,10 @@ var normalizeIncomingMethod = (method) => {
131
331
  case "GET":
132
332
  case "HEAD":
133
333
  case "OPTIONS":
334
+ case "PATCH":
134
335
  case "POST":
135
336
  case "PUT":
337
+ case "QUERY":
136
338
  return method;
137
339
  }
138
340
  const upper = method.toUpperCase();
@@ -180,11 +382,23 @@ var readRawBodyIfAvailable = (request) => {
180
382
  const incoming = request[incomingKey];
181
383
  if ("rawBody" in incoming && incoming.rawBody instanceof Buffer) return incoming.rawBody;
182
384
  };
385
+ var normalizeAbortError = (request, incoming) => {
386
+ if (incoming.errored) return incoming.errored;
387
+ const reason = request[abortReasonKey];
388
+ if (reason !== void 0) return reason instanceof Error ? reason : new Error(String(reason));
389
+ return /* @__PURE__ */ new Error("Client connection prematurely closed.");
390
+ };
183
391
  var readBodyDirect = (request) => {
184
392
  if (request[bodyBufferKey]) return Promise.resolve(request[bodyBufferKey]);
185
393
  if (request[bodyReadPromiseKey]) return request[bodyReadPromiseKey];
186
394
  const incoming = request[incomingKey];
187
- if (Readable.isDisturbed(incoming)) return rejectBodyUnusable();
395
+ if (incoming.readableDidRead) return rejectBodyUnusable();
396
+ const buffered = readBodyBufferedBeforeDisconnect(incoming);
397
+ if (buffered !== void 0) {
398
+ if (buffered instanceof Error) return Promise.reject(buffered);
399
+ request[bodyBufferKey] = buffered;
400
+ return Promise.resolve(buffered);
401
+ }
188
402
  const promise = new Promise((resolve2, reject) => {
189
403
  const chunks = [];
190
404
  let settled = false;
@@ -194,8 +408,22 @@ var readBodyDirect = (request) => {
194
408
  cleanup();
195
409
  callback();
196
410
  };
411
+ const recoverCompleteBodyAfterDisconnect = (error) => {
412
+ const streamError = incoming.errored ?? error;
413
+ if (!isRecoverableDisconnectedIncoming(incoming) || streamError && streamError.code !== "ECONNRESET") return false;
414
+ finish(() => {
415
+ const recovered = readBodyBufferedBeforeDisconnect(incoming, chunks);
416
+ if (recovered instanceof Error) reject(recovered);
417
+ else if (recovered === void 0) reject(error ?? normalizeAbortError(request, incoming));
418
+ else {
419
+ request[bodyBufferKey] = recovered;
420
+ resolve2(recovered);
421
+ }
422
+ });
423
+ return true;
424
+ };
197
425
  const onData = (chunk) => {
198
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
426
+ chunks.push(toBufferChunk(chunk, incoming.readableEncoding));
199
427
  };
200
428
  const onEnd = () => {
201
429
  finish(() => {
@@ -205,6 +433,7 @@ var readBodyDirect = (request) => {
205
433
  });
206
434
  };
207
435
  const onError = (error) => {
436
+ if (recoverCompleteBodyAfterDisconnect(error)) return;
208
437
  finish(() => {
209
438
  reject(error);
210
439
  });
@@ -214,17 +443,9 @@ var readBodyDirect = (request) => {
214
443
  onEnd();
215
444
  return;
216
445
  }
446
+ if (recoverCompleteBodyAfterDisconnect()) return;
217
447
  finish(() => {
218
- if (incoming.errored) {
219
- reject(incoming.errored);
220
- return;
221
- }
222
- const reason = request[abortReasonKey];
223
- if (reason !== void 0) {
224
- reject(reason instanceof Error ? reason : new Error(String(reason)));
225
- return;
226
- }
227
- reject(/* @__PURE__ */ new Error("Client connection prematurely closed."));
448
+ reject(normalizeAbortError(request, incoming));
228
449
  });
229
450
  };
230
451
  const cleanup = () => {
@@ -403,23 +624,23 @@ var Response$1 = class Response$12 {
403
624
  const liveHeaders = cache && cache[2] instanceof Headers ? cache[2] : void 0;
404
625
  delete this[cacheKey];
405
626
  return this[responseCache] ||= new GlobalResponse(this.#body, liveHeaders ? {
406
- ...this.#init,
627
+ status: this.#init?.status,
628
+ statusText: this.#init?.statusText,
407
629
  headers: liveHeaders
408
630
  } : this.#init);
409
631
  }
410
632
  constructor(body, init) {
411
633
  let headers;
412
634
  this.#body = body;
413
- if (init instanceof Response$12) {
635
+ if (init instanceof GlobalResponse) {
414
636
  const cachedGlobalResponse = init[responseCache];
415
637
  if (cachedGlobalResponse) {
416
638
  this.#init = cachedGlobalResponse;
417
639
  this[getResponseCache]();
418
640
  return;
419
- } else {
420
- this.#init = init.#init;
421
- headers = new Headers(init.headers);
422
641
  }
642
+ this.#init = init instanceof Response$12 ? init.#init : init;
643
+ headers = new Headers(init.headers);
423
644
  } else this.#init = init;
424
645
  if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
425
646
  init?.status || 200,
@@ -597,7 +818,10 @@ var drainIncoming = (incoming) => {
597
818
  const forceClose = () => {
598
819
  cleanup();
599
820
  const socket = incoming.socket;
600
- if (socket && !socket.destroyed) socket.destroySoon();
821
+ if (socket && !socket.destroyed) {
822
+ if (typeof socket.destroySoon === "function") socket.destroySoon();
823
+ else if (typeof socket.destroy === "function") socket.destroy();
824
+ }
601
825
  };
602
826
  const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
603
827
  timer.unref?.();
@@ -611,8 +835,13 @@ var drainIncoming = (incoming) => {
611
835
  incoming.resume();
612
836
  };
613
837
  var makeCloseHandler = (req, incoming, outgoing, needsBodyCleanup) => () => {
614
- if (incoming.errored) req[abortRequest](incoming.errored.toString());
615
- else if (!outgoing.writableFinished) req[abortRequest]("Client connection prematurely closed.");
838
+ if (incoming.errored) {
839
+ recordBodyBufferedBeforeDisconnect(incoming);
840
+ req[abortRequest](incoming.errored.toString());
841
+ } else if (!outgoing.writableFinished) {
842
+ recordBodyBufferedBeforeDisconnect(incoming);
843
+ req[abortRequest]("Client connection prematurely closed.");
844
+ }
616
845
  if (needsBodyCleanup && !incoming.readableEnded) setTimeout(() => {
617
846
  if (!incoming.readableEnded) setTimeout(() => {
618
847
  drainIncoming(incoming);
@@ -863,10 +1092,18 @@ var setupWebSocket = (options) => {
863
1092
  waiterMap.delete(request);
864
1093
  }
865
1094
  });
1095
+ const rejectWaiter = (request) => {
1096
+ const waiter = waiterMap.get(request);
1097
+ if (waiter) {
1098
+ waiterMap.delete(request);
1099
+ waiter.reject(/* @__PURE__ */ new Error("WebSocket handshake aborted"));
1100
+ }
1101
+ };
866
1102
  const waitForWebSocket = (request, connectionSymbol) => {
867
- return new Promise((resolve2) => {
1103
+ return new Promise((resolve2, reject) => {
868
1104
  waiterMap.set(request, {
869
1105
  resolve: resolve2,
1106
+ reject,
870
1107
  connectionSymbol
871
1108
  });
872
1109
  });
@@ -893,16 +1130,19 @@ var setupWebSocket = (options) => {
893
1130
  }
894
1131
  const waiter = waiterMap.get(request);
895
1132
  if (!waiter || waiter.connectionSymbol !== env[CONNECTION_SYMBOL_KEY]) {
896
- waiterMap.delete(request);
1133
+ rejectWaiter(request);
897
1134
  if (server.listenerCount("upgrade") === 1) rejectUpgradeRequest(socket, status, responseHeaders);
898
1135
  return;
899
1136
  }
900
1137
  const addResponseHeaders = (headers) => {
901
1138
  appendResponseHeaders(headers, responseHeaders);
902
1139
  };
1140
+ const reclaimWaiterOnClose = () => rejectWaiter(request);
1141
+ socket.once("close", reclaimWaiterOnClose);
903
1142
  wss.on("headers", addResponseHeaders);
904
1143
  try {
905
1144
  wss.handleUpgrade(request, socket, head, (ws) => {
1145
+ socket.off("close", reclaimWaiterOnClose);
906
1146
  wss.emit("connection", ws, request);
907
1147
  });
908
1148
  } finally {
@@ -940,7 +1180,7 @@ var serve = (options, listeningListener) => {
940
1180
  return server;
941
1181
  };
942
1182
 
943
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/compose.js
1183
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/compose.js
944
1184
  var compose = (middleware, onError, onNotFound) => {
945
1185
  return (context, next) => {
946
1186
  let index = -1;
@@ -984,21 +1224,46 @@ var compose = (middleware, onError, onNotFound) => {
984
1224
  };
985
1225
  };
986
1226
 
987
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request/constants.js
1227
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request/constants.js
988
1228
  var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
989
1229
 
990
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/body.js
1230
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/buffer.js
1231
+ var bufferToFormData = (arrayBuffer, contentType2) => {
1232
+ const response = new Response(arrayBuffer, {
1233
+ headers: {
1234
+ // Normalize the media type (case-insensitive) while keeping parameters like the boundary
1235
+ "Content-Type": contentType2.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase())
1236
+ }
1237
+ });
1238
+ return response.formData();
1239
+ };
1240
+
1241
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/body.js
1242
+ var isRawRequest = (request) => "headers" in request;
991
1243
  var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
992
1244
  const { all = false, dot = false } = options;
993
- const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;
1245
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
994
1246
  const contentType2 = headers.get("Content-Type");
995
- if (contentType2?.startsWith("multipart/form-data") || contentType2?.startsWith("application/x-www-form-urlencoded")) {
1247
+ const mediaType = contentType2?.split(";")[0].trim().toLowerCase();
1248
+ if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
996
1249
  return parseFormData(request, { all, dot });
997
1250
  }
998
1251
  return {};
999
1252
  };
1000
1253
  async function parseFormData(request, options) {
1001
- const formData = await request.formData();
1254
+ if (!isRawRequest(request) && request.bodyCache.formData) {
1255
+ return convertFormDataToBodyData(
1256
+ await request.bodyCache.formData,
1257
+ options
1258
+ );
1259
+ }
1260
+ const headers = isRawRequest(request) ? request.headers : request.raw.headers;
1261
+ const arrayBuffer = await request.arrayBuffer();
1262
+ const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
1263
+ if (!isRawRequest(request)) {
1264
+ request.bodyCache.formData = formDataPromise;
1265
+ }
1266
+ const formData = await formDataPromise;
1002
1267
  if (formData) {
1003
1268
  return convertFormDataToBodyData(formData, options);
1004
1269
  }
@@ -1058,7 +1323,7 @@ var handleParsingNestedValues = (form, key, value) => {
1058
1323
  });
1059
1324
  };
1060
1325
 
1061
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/url.js
1326
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/url.js
1062
1327
  var splitPath = (path) => {
1063
1328
  const paths = path.split("/");
1064
1329
  if (paths[0] === "") {
@@ -1164,13 +1429,13 @@ var checkOptionalParameter = (path) => {
1164
1429
  if (segment !== "" && !/\:/.test(segment)) {
1165
1430
  basePath += "/" + segment;
1166
1431
  } else if (/\:/.test(segment)) {
1167
- if (/\?/.test(segment)) {
1432
+ if (segment.charCodeAt(segment.length - 1) === 63) {
1168
1433
  if (results.length === 0 && basePath === "") {
1169
1434
  results.push("/");
1170
1435
  } else {
1171
1436
  results.push(basePath);
1172
1437
  }
1173
- const optionalSegment = segment.replace("?", "");
1438
+ const optionalSegment = segment.slice(0, -1);
1174
1439
  basePath += "/" + optionalSegment;
1175
1440
  results.push(basePath);
1176
1441
  } else {
@@ -1180,18 +1445,16 @@ var checkOptionalParameter = (path) => {
1180
1445
  });
1181
1446
  return results.filter((v, i, a) => a.indexOf(v) === i);
1182
1447
  };
1448
+ var tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str;
1183
1449
  var _decodeURI = (value) => {
1184
- if (!/[%+]/.test(value)) {
1185
- return value;
1186
- }
1187
1450
  if (value.indexOf("+") !== -1) {
1188
1451
  value = value.replace(/\+/g, " ");
1189
1452
  }
1190
- return value.indexOf("%") !== -1 ? tryDecode(value, decodeURIComponent_) : value;
1453
+ return tryDecodeURIComponent(value);
1191
1454
  };
1192
1455
  var _getQueryParam = (url, key, multiple) => {
1193
1456
  let encoded;
1194
- if (!multiple && key && !/[%+]/.test(key)) {
1457
+ if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) {
1195
1458
  let keyIndex2 = url.indexOf("?", 8);
1196
1459
  if (keyIndex2 === -1) {
1197
1460
  return void 0;
@@ -1215,7 +1478,7 @@ var _getQueryParam = (url, key, multiple) => {
1215
1478
  return void 0;
1216
1479
  }
1217
1480
  }
1218
- const results = {};
1481
+ const results = /* @__PURE__ */ Object.create(null);
1219
1482
  encoded ??= /[%+]/.test(url);
1220
1483
  let keyIndex = url.indexOf("?", 8);
1221
1484
  while (keyIndex !== -1) {
@@ -1261,8 +1524,7 @@ var getQueryParams = (url, key) => {
1261
1524
  };
1262
1525
  var decodeURIComponent_ = decodeURIComponent;
1263
1526
 
1264
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/request.js
1265
- var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
1527
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request.js
1266
1528
  var HonoRequest = class {
1267
1529
  /**
1268
1530
  * `.raw` can get the raw Request object.
@@ -1301,7 +1563,6 @@ var HonoRequest = class {
1301
1563
  this.raw = request;
1302
1564
  this.path = path;
1303
1565
  this.#matchResult = matchResult;
1304
- this.#validatedData = {};
1305
1566
  }
1306
1567
  param(key) {
1307
1568
  return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
@@ -1309,7 +1570,7 @@ var HonoRequest = class {
1309
1570
  #getDecodedParam(key) {
1310
1571
  const paramKey = this.#matchResult[0][this.routeIndex][1][key];
1311
1572
  const param = this.#getParamValue(paramKey);
1312
- return param && /\%/.test(param) ? tryDecodeURIComponent(param) : param;
1573
+ return param && tryDecodeURIComponent(param);
1313
1574
  }
1314
1575
  #getAllDecodedParams() {
1315
1576
  const decoded = {};
@@ -1317,7 +1578,7 @@ var HonoRequest = class {
1317
1578
  for (const key of keys) {
1318
1579
  const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
1319
1580
  if (value !== void 0) {
1320
- decoded[key] = /\%/.test(value) ? tryDecodeURIComponent(value) : value;
1581
+ decoded[key] = tryDecodeURIComponent(value);
1321
1582
  }
1322
1583
  }
1323
1584
  return decoded;
@@ -1335,7 +1596,7 @@ var HonoRequest = class {
1335
1596
  if (name) {
1336
1597
  return this.raw.headers.get(name) ?? void 0;
1337
1598
  }
1338
- const headerData = {};
1599
+ const headerData = /* @__PURE__ */ Object.create(null);
1339
1600
  this.raw.headers.forEach((value, key) => {
1340
1601
  headerData[key] = value;
1341
1602
  });
@@ -1350,8 +1611,7 @@ var HonoRequest = class {
1350
1611
  if (cachedBody) {
1351
1612
  return cachedBody;
1352
1613
  }
1353
- const anyCachedKey = Object.keys(bodyCache)[0];
1354
- if (anyCachedKey) {
1614
+ for (const anyCachedKey in bodyCache) {
1355
1615
  return bodyCache[anyCachedKey].then((body) => {
1356
1616
  if (anyCachedKey === "json") {
1357
1617
  body = JSON.stringify(body);
@@ -1454,10 +1714,10 @@ var HonoRequest = class {
1454
1714
  * @param data - The validated data to add.
1455
1715
  */
1456
1716
  addValidatedData(target, data) {
1457
- this.#validatedData[target] = data;
1717
+ (this.#validatedData ??= {})[target] = data;
1458
1718
  }
1459
1719
  valid(target) {
1460
- return this.#validatedData[target];
1720
+ return this.#validatedData?.[target];
1461
1721
  }
1462
1722
  /**
1463
1723
  * `.url()` can get the request url strings.
@@ -1544,7 +1804,7 @@ var HonoRequest = class {
1544
1804
  }
1545
1805
  };
1546
1806
 
1547
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/html.js
1807
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/html.js
1548
1808
  var HtmlEscapedCallbackPhase = {
1549
1809
  Stringify: 1};
1550
1810
  var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
@@ -1575,7 +1835,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
1575
1835
  }
1576
1836
  };
1577
1837
 
1578
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/context.js
1838
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/context.js
1579
1839
  var TEXT_PLAIN = "text/plain; charset=UTF-8";
1580
1840
  var setDefaultContentType = (contentType2, headers) => {
1581
1841
  return {
@@ -1848,11 +2108,11 @@ var Context = class {
1848
2108
  return Object.fromEntries(this.#var);
1849
2109
  }
1850
2110
  #newResponse(data, arg, headers) {
1851
- const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();
1852
- if (typeof arg === "object" && "headers" in arg) {
1853
- const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);
1854
- for (const [key, value] of argHeaders) {
1855
- if (key.toLowerCase() === "set-cookie") {
2111
+ let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
2112
+ if (typeof arg === "object" && arg.headers) {
2113
+ responseHeaders ??= new Headers();
2114
+ for (const [key, value] of new Headers(arg.headers)) {
2115
+ if (key === "set-cookie") {
1856
2116
  responseHeaders.append(key, value);
1857
2117
  } else {
1858
2118
  responseHeaders.set(key, value);
@@ -1860,19 +2120,34 @@ var Context = class {
1860
2120
  }
1861
2121
  }
1862
2122
  if (headers) {
1863
- for (const [k, v] of Object.entries(headers)) {
1864
- if (typeof v === "string") {
1865
- responseHeaders.set(k, v);
1866
- } else {
1867
- responseHeaders.delete(k);
1868
- for (const v2 of v) {
1869
- responseHeaders.append(k, v2);
2123
+ if (!responseHeaders) {
2124
+ let count = 0;
2125
+ for (const k in headers) {
2126
+ if (++count > 1 || typeof headers[k] !== "string") {
2127
+ responseHeaders = new Headers();
2128
+ break;
2129
+ }
2130
+ }
2131
+ }
2132
+ if (responseHeaders) {
2133
+ for (const k in headers) {
2134
+ const v = headers[k];
2135
+ if (typeof v === "string") {
2136
+ responseHeaders.set(k, v);
2137
+ } else {
2138
+ responseHeaders.delete(k);
2139
+ for (const v2 of v) {
2140
+ responseHeaders.append(k, v2);
2141
+ }
1870
2142
  }
1871
2143
  }
1872
2144
  }
1873
2145
  }
1874
2146
  const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
1875
- return createResponseInstance(data, { status, headers: responseHeaders });
2147
+ return createResponseInstance(data, {
2148
+ status,
2149
+ headers: responseHeaders ?? headers
2150
+ });
1876
2151
  }
1877
2152
  newResponse = (...args) => this.#newResponse(...args);
1878
2153
  /**
@@ -1982,18 +2257,18 @@ var Context = class {
1982
2257
  };
1983
2258
  };
1984
2259
 
1985
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router.js
2260
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router.js
1986
2261
  var METHOD_NAME_ALL = "ALL";
1987
2262
  var METHOD_NAME_ALL_LOWERCASE = "all";
1988
- var METHODS = ["get", "post", "put", "delete", "options", "patch"];
2263
+ var METHODS = ["get", "post", "put", "delete", "options", "patch", "query"];
1989
2264
  var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
1990
2265
  var UnsupportedPathError = class extends Error {
1991
2266
  };
1992
2267
 
1993
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/utils/constants.js
2268
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/constants.js
1994
2269
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
1995
2270
 
1996
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono-base.js
2271
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono-base.js
1997
2272
  var notFoundHandler = (c) => {
1998
2273
  return c.text("404 Not Found", 404);
1999
2274
  };
@@ -2012,6 +2287,7 @@ var Hono = class _Hono {
2012
2287
  delete;
2013
2288
  options;
2014
2289
  patch;
2290
+ query;
2015
2291
  all;
2016
2292
  on;
2017
2293
  use;
@@ -2311,8 +2587,8 @@ var Hono = class _Hono {
2311
2587
  * @see {@link https://hono.dev/docs/api/hono#fetch}
2312
2588
  *
2313
2589
  * @param {Request} request - request Object of request
2314
- * @param {Env} Env - env Object
2315
- * @param {ExecutionContext} - context of execution
2590
+ * @param {Env} env - env Object
2591
+ * @param {ExecutionContext} executionCtx - context of execution
2316
2592
  * @returns {Response | Promise<Response>} response of request
2317
2593
  *
2318
2594
  */
@@ -2369,7 +2645,7 @@ var Hono = class _Hono {
2369
2645
  };
2370
2646
  };
2371
2647
 
2372
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/matcher.js
2648
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/matcher.js
2373
2649
  var emptyParam = [];
2374
2650
  function match(method, path) {
2375
2651
  const matchers = this.buildAllMatchers();
@@ -2390,7 +2666,7 @@ function match(method, path) {
2390
2666
  return match2(method, path);
2391
2667
  }
2392
2668
 
2393
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/node.js
2669
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/node.js
2394
2670
  var LABEL_REG_EXP_STR = "[^/]+";
2395
2671
  var ONLY_WILDCARD_REG_EXP_STR = ".*";
2396
2672
  var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
@@ -2404,7 +2680,7 @@ function compareKey(a, b) {
2404
2680
  return 1;
2405
2681
  }
2406
2682
  if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
2407
- return 1;
2683
+ return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
2408
2684
  } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
2409
2685
  return -1;
2410
2686
  }
@@ -2416,76 +2692,75 @@ function compareKey(a, b) {
2416
2692
  return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
2417
2693
  }
2418
2694
  var Node = class _Node {
2695
+ // handler index of a dynamic path, or -1 for a static path terminal
2419
2696
  #index;
2420
2697
  #varIndex;
2421
2698
  #children = /* @__PURE__ */ Object.create(null);
2422
- insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
2423
- if (tokens.length === 0) {
2424
- if (this.#index !== void 0) {
2425
- throw PATH_ERROR;
2426
- }
2427
- if (pathErrorCheckOnly) {
2428
- return;
2429
- }
2430
- this.#index = index;
2431
- return;
2432
- }
2433
- const [token, ...restTokens] = tokens;
2434
- const pattern = token === "*" ? restTokens.length === 0 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
2435
- let node;
2436
- if (pattern) {
2437
- const name = pattern[1];
2438
- let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
2439
- if (name && pattern[2]) {
2440
- if (regexpStr === ".*") {
2441
- throw PATH_ERROR;
2442
- }
2443
- regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
2444
- if (/\((?!\?:)/.test(regexpStr)) {
2445
- throw PATH_ERROR;
2446
- }
2447
- }
2448
- node = this.#children[regexpStr];
2449
- if (!node) {
2450
- if (Object.keys(this.#children).some(
2451
- (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
2452
- )) {
2453
- throw PATH_ERROR;
2699
+ insert(tokens, index, paramMap, context, isStatic) {
2700
+ let node = this;
2701
+ for (let i = 0, len = tokens.length; i < len; i++) {
2702
+ const token = tokens[i];
2703
+ const pattern = token.length === 1 ? token === "*" ? i === len - 1 ? ["", "", ONLY_WILDCARD_REG_EXP_STR] : ["", "", LABEL_REG_EXP_STR] : null : token === "/*" ? ["", "", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
2704
+ let nextNode;
2705
+ if (pattern) {
2706
+ const name = pattern[1];
2707
+ let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
2708
+ if (name && pattern[2]) {
2709
+ if (regexpStr === ".*") {
2710
+ throw PATH_ERROR;
2711
+ }
2712
+ regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
2713
+ if (/\((?!\?:)/.test(regexpStr)) {
2714
+ throw PATH_ERROR;
2715
+ }
2716
+ if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
2717
+ throw PATH_ERROR;
2718
+ }
2454
2719
  }
2455
- if (pathErrorCheckOnly) {
2456
- return;
2720
+ nextNode = node.#children[regexpStr];
2721
+ if (!nextNode) {
2722
+ if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
2723
+ for (const k in node.#children) {
2724
+ if (
2725
+ // a single-char pattern coexists with single-char literals as a literal does
2726
+ (regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
2727
+ ) {
2728
+ throw PATH_ERROR;
2729
+ }
2730
+ }
2731
+ }
2732
+ nextNode = node.#children[regexpStr] = new _Node();
2457
2733
  }
2458
- node = this.#children[regexpStr] = new _Node();
2459
2734
  if (name !== "") {
2460
- node.#varIndex = context.varIndex++;
2461
- }
2462
- }
2463
- if (!pathErrorCheckOnly && name !== "") {
2464
- paramMap.push([name, node.#varIndex]);
2465
- }
2466
- } else {
2467
- node = this.#children[token];
2468
- if (!node) {
2469
- if (Object.keys(this.#children).some(
2470
- (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
2471
- )) {
2472
- throw PATH_ERROR;
2735
+ nextNode.#varIndex ??= context.varIndex++;
2736
+ paramMap.push([name, nextNode.#varIndex]);
2473
2737
  }
2474
- if (pathErrorCheckOnly) {
2475
- return;
2738
+ } else {
2739
+ nextNode = node.#children[token];
2740
+ if (!nextNode) {
2741
+ for (const k in node.#children) {
2742
+ if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
2743
+ throw PATH_ERROR;
2744
+ }
2745
+ }
2746
+ nextNode = node.#children[token] = new _Node();
2476
2747
  }
2477
- node = this.#children[token] = new _Node();
2478
2748
  }
2749
+ node = nextNode;
2750
+ }
2751
+ if (node.#index !== void 0) {
2752
+ throw PATH_ERROR;
2479
2753
  }
2480
- node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
2754
+ node.#index = isStatic ? -1 : index;
2481
2755
  }
2482
2756
  buildRegExpStr() {
2483
2757
  const childKeys = Object.keys(this.#children).sort(compareKey);
2484
2758
  const strList = childKeys.map((k) => {
2485
2759
  const c = this.#children[k];
2486
- return (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + c.buildRegExpStr();
2487
- });
2488
- if (typeof this.#index === "number") {
2760
+ const childStr = c.buildRegExpStr();
2761
+ return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
2762
+ }).filter(Boolean);
2763
+ if (typeof this.#index === "number" && this.#index !== -1) {
2489
2764
  strList.unshift(`#${this.#index}`);
2490
2765
  }
2491
2766
  if (strList.length === 0) {
@@ -2498,16 +2773,24 @@ var Node = class _Node {
2498
2773
  }
2499
2774
  };
2500
2775
 
2501
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/trie.js
2776
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/trie.js
2502
2777
  var Trie = class {
2503
2778
  #context = { varIndex: 0 };
2504
2779
  #root = new Node();
2505
- insert(path, index, pathErrorCheckOnly) {
2780
+ #index = 0;
2781
+ // dynamic path -> [handler index, param assoc]; static paths are not registered
2782
+ paths = /* @__PURE__ */ Object.create(null);
2783
+ insert(path, isStatic) {
2784
+ if (isStatic) {
2785
+ this.#root.insert(path.split(""), 0, [], this.#context, true);
2786
+ return;
2787
+ }
2506
2788
  const paramAssoc = [];
2507
2789
  const groups = [];
2790
+ let markedPath = path;
2508
2791
  for (let i = 0; ; ) {
2509
2792
  let replaced = false;
2510
- path = path.replace(/\{[^}]+\}/g, (m) => {
2793
+ markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
2511
2794
  const mark = `@\\${i}`;
2512
2795
  groups[i] = [mark, m];
2513
2796
  i++;
@@ -2518,7 +2801,7 @@ var Trie = class {
2518
2801
  break;
2519
2802
  }
2520
2803
  }
2521
- const tokens = path.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2804
+ const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
2522
2805
  for (let i = groups.length - 1; i >= 0; i--) {
2523
2806
  const [mark] = groups[i];
2524
2807
  for (let j = tokens.length - 1; j >= 0; j--) {
@@ -2528,8 +2811,8 @@ var Trie = class {
2528
2811
  }
2529
2812
  }
2530
2813
  }
2531
- this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);
2532
- return paramAssoc;
2814
+ this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
2815
+ this.paths[path] = [this.#index++, paramAssoc];
2533
2816
  }
2534
2817
  buildRegExp() {
2535
2818
  let regexp = this.#root.buildRegExpStr();
@@ -2554,8 +2837,7 @@ var Trie = class {
2554
2837
  }
2555
2838
  };
2556
2839
 
2557
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/reg-exp-router/router.js
2558
- var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
2840
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/router.js
2559
2841
  var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
2560
2842
  function buildWildcardRegExp(path) {
2561
2843
  return wildcardRegExpCache[path] ??= new RegExp(
@@ -2568,63 +2850,6 @@ function buildWildcardRegExp(path) {
2568
2850
  function clearWildcardRegExpCache() {
2569
2851
  wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
2570
2852
  }
2571
- function buildMatcherFromPreprocessedRoutes(routes) {
2572
- const trie = new Trie();
2573
- const handlerData = [];
2574
- if (routes.length === 0) {
2575
- return nullMatcher;
2576
- }
2577
- const routesWithStaticPathFlag = routes.map(
2578
- (route) => [!/\*|\/:/.test(route[0]), ...route]
2579
- ).sort(
2580
- ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
2581
- );
2582
- const staticMap = /* @__PURE__ */ Object.create(null);
2583
- for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
2584
- const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
2585
- if (pathErrorCheckOnly) {
2586
- staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2587
- } else {
2588
- j++;
2589
- }
2590
- let paramAssoc;
2591
- try {
2592
- paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
2593
- } catch (e) {
2594
- throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
2595
- }
2596
- if (pathErrorCheckOnly) {
2597
- continue;
2598
- }
2599
- handlerData[j] = handlers.map(([h, paramCount]) => {
2600
- const paramIndexMap = /* @__PURE__ */ Object.create(null);
2601
- paramCount -= 1;
2602
- for (; paramCount >= 0; paramCount--) {
2603
- const [key, value] = paramAssoc[paramCount];
2604
- paramIndexMap[key] = value;
2605
- }
2606
- return [h, paramIndexMap];
2607
- });
2608
- }
2609
- const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
2610
- for (let i = 0, len = handlerData.length; i < len; i++) {
2611
- for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
2612
- const map = handlerData[i][j]?.[1];
2613
- if (!map) {
2614
- continue;
2615
- }
2616
- const keys = Object.keys(map);
2617
- for (let k = 0, len3 = keys.length; k < len3; k++) {
2618
- map[keys[k]] = paramReplacementMap[map[keys[k]]];
2619
- }
2620
- }
2621
- }
2622
- const handlerMap = [];
2623
- for (const i in indexReplacementMap) {
2624
- handlerMap[i] = handlerData[indexReplacementMap[i]];
2625
- }
2626
- return [regexp, handlerMap, staticMap];
2627
- }
2628
2853
  function findMiddleware(middleware, path) {
2629
2854
  if (!middleware) {
2630
2855
  return void 0;
@@ -2640,9 +2865,18 @@ var RegExpRouter = class {
2640
2865
  name = "RegExpRouter";
2641
2866
  #middleware;
2642
2867
  #routes;
2868
+ #tries;
2643
2869
  constructor() {
2644
2870
  this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2645
2871
  this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
2872
+ this.#tries = { [METHOD_NAME_ALL]: new Trie() };
2873
+ }
2874
+ #insertPath(method, path) {
2875
+ try {
2876
+ this.#tries[method].insert(path, !/\*|\/:/.test(path));
2877
+ } catch (e) {
2878
+ throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
2879
+ }
2646
2880
  }
2647
2881
  add(method, path, handler) {
2648
2882
  const middleware = this.#middleware;
@@ -2651,10 +2885,12 @@ var RegExpRouter = class {
2651
2885
  throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
2652
2886
  }
2653
2887
  if (!middleware[method]) {
2888
+ this.#tries[method] = new Trie();
2654
2889
  [middleware, routes].forEach((handlerMap) => {
2655
2890
  handlerMap[method] = /* @__PURE__ */ Object.create(null);
2656
2891
  Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
2657
2892
  handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
2893
+ this.#insertPath(method, p);
2658
2894
  });
2659
2895
  });
2660
2896
  }
@@ -2664,13 +2900,12 @@ var RegExpRouter = class {
2664
2900
  const paramCount = (path.match(/\/:/g) || []).length;
2665
2901
  if (/\*$/.test(path)) {
2666
2902
  const re = buildWildcardRegExp(path);
2667
- if (method === METHOD_NAME_ALL) {
2668
- Object.keys(middleware).forEach((m) => {
2669
- middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
2670
- });
2671
- } else {
2672
- middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
2673
- }
2903
+ Object.keys(middleware).forEach((m) => {
2904
+ if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) {
2905
+ this.#insertPath(m, path);
2906
+ middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
2907
+ }
2908
+ });
2674
2909
  Object.keys(middleware).forEach((m) => {
2675
2910
  if (method === METHOD_NAME_ALL || method === m) {
2676
2911
  Object.keys(middleware[m]).forEach((p) => {
@@ -2692,9 +2927,12 @@ var RegExpRouter = class {
2692
2927
  const path2 = paths[i];
2693
2928
  Object.keys(routes).forEach((m) => {
2694
2929
  if (method === METHOD_NAME_ALL || method === m) {
2695
- routes[m][path2] ||= [
2696
- ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
2697
- ];
2930
+ if (!routes[m][path2]) {
2931
+ this.#insertPath(m, path2);
2932
+ routes[m][path2] = [
2933
+ ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
2934
+ ];
2935
+ }
2698
2936
  routes[m][path2].push([handler, paramCount - len + i + 1]);
2699
2937
  }
2700
2938
  });
@@ -2706,33 +2944,58 @@ var RegExpRouter = class {
2706
2944
  Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
2707
2945
  matchers[method] ||= this.#buildMatcher(method);
2708
2946
  });
2709
- this.#middleware = this.#routes = void 0;
2947
+ this.#middleware = this.#routes = this.#tries = void 0;
2710
2948
  clearWildcardRegExpCache();
2711
2949
  return matchers;
2712
2950
  }
2713
2951
  #buildMatcher(method) {
2714
- const routes = [];
2715
- let hasOwnRoute = method === METHOD_NAME_ALL;
2716
- [this.#middleware, this.#routes].forEach((r) => {
2717
- const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];
2718
- if (ownRoute.length !== 0) {
2719
- hasOwnRoute ||= true;
2720
- routes.push(...ownRoute);
2721
- } else if (method !== METHOD_NAME_ALL) {
2722
- routes.push(
2723
- ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])
2724
- );
2952
+ const middleware = this.#middleware[method];
2953
+ const routes = this.#routes[method];
2954
+ const trie = this.#tries[method];
2955
+ const staticMap = /* @__PURE__ */ Object.create(null);
2956
+ const handlerData = [];
2957
+ [middleware, routes].forEach((r) => {
2958
+ for (const path in r) {
2959
+ const handlers = r[path];
2960
+ const pathData = trie.paths[path];
2961
+ if (!pathData) {
2962
+ staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
2963
+ continue;
2964
+ }
2965
+ const paramAssoc = pathData[1];
2966
+ handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
2967
+ const paramIndexMap = /* @__PURE__ */ Object.create(null);
2968
+ paramCount -= 1;
2969
+ for (; paramCount >= 0; paramCount--) {
2970
+ const [key, value] = paramAssoc[paramCount];
2971
+ paramIndexMap[key] = value;
2972
+ }
2973
+ return [h, paramIndexMap];
2974
+ });
2725
2975
  }
2726
2976
  });
2727
- if (!hasOwnRoute) {
2728
- return null;
2729
- } else {
2730
- return buildMatcherFromPreprocessedRoutes(routes);
2977
+ const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
2978
+ for (let i = 0, len = handlerData.length; i < len; i++) {
2979
+ for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
2980
+ const map = handlerData[i][j]?.[1];
2981
+ if (!map) {
2982
+ continue;
2983
+ }
2984
+ const keys = Object.keys(map);
2985
+ for (let k = 0, len3 = keys.length; k < len3; k++) {
2986
+ map[keys[k]] = paramReplacementMap[map[keys[k]]];
2987
+ }
2988
+ }
2989
+ }
2990
+ const handlerMap = [];
2991
+ for (const i in indexReplacementMap) {
2992
+ handlerMap[i] = handlerData[indexReplacementMap[i]];
2731
2993
  }
2994
+ return [regexp, handlerMap, staticMap];
2732
2995
  }
2733
2996
  };
2734
2997
 
2735
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/smart-router/router.js
2998
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/smart-router/router.js
2736
2999
  var SmartRouter = class {
2737
3000
  name = "SmartRouter";
2738
3001
  #routers = [];
@@ -2787,7 +3050,7 @@ var SmartRouter = class {
2787
3050
  }
2788
3051
  };
2789
3052
 
2790
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/node.js
3053
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/node.js
2791
3054
  var emptyParams = /* @__PURE__ */ Object.create(null);
2792
3055
  var hasChildren = (children) => {
2793
3056
  for (const _ in children) {
@@ -2921,9 +3184,18 @@ var Node2 = class _Node2 {
2921
3184
  if (m) {
2922
3185
  params[name] = m[0];
2923
3186
  this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
3187
+ if (m[0].length === restPathString.length && child.#children["*"]) {
3188
+ this.#pushHandlerSets(
3189
+ handlerSets,
3190
+ child.#children["*"],
3191
+ method,
3192
+ node.#params,
3193
+ params
3194
+ );
3195
+ }
2924
3196
  if (hasChildren(child.#children)) {
2925
3197
  child.#params = params;
2926
- const componentCount = m[0].match(/\//)?.length ?? 0;
3198
+ const componentCount = m[0].match(/\//g)?.length ?? 0;
2927
3199
  const targetCurNodes = curNodesQueue[componentCount] ||= [];
2928
3200
  targetCurNodes.push(child);
2929
3201
  }
@@ -2962,7 +3234,7 @@ var Node2 = class _Node2 {
2962
3234
  }
2963
3235
  };
2964
3236
 
2965
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/router/trie-router/router.js
3237
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/router.js
2966
3238
  var TrieRouter = class {
2967
3239
  name = "TrieRouter";
2968
3240
  #node;
@@ -2984,7 +3256,7 @@ var TrieRouter = class {
2984
3256
  }
2985
3257
  };
2986
3258
 
2987
- // ../node_modules/.bun/hono@4.12.25/node_modules/hono/dist/hono.js
3259
+ // ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono.js
2988
3260
  var Hono2 = class extends Hono {
2989
3261
  /**
2990
3262
  * Creates an instance of the Hono class.
@@ -3000,9 +3272,75 @@ var Hono2 = class extends Hono {
3000
3272
  };
3001
3273
 
3002
3274
  // src/types/common.ts
3275
+ var SUPPORTED_CURRENCY_CODES = /* @__PURE__ */ new Set([
3276
+ "USD",
3277
+ "EUR",
3278
+ "GBP",
3279
+ "JPY",
3280
+ "CNY",
3281
+ "CHF",
3282
+ "CAD",
3283
+ "AUD",
3284
+ "GHS",
3285
+ "NGN",
3286
+ "KES",
3287
+ "ZAR",
3288
+ "XOF",
3289
+ "XAF",
3290
+ "EGP",
3291
+ "TZS",
3292
+ "UGX",
3293
+ "RWF",
3294
+ "ETB",
3295
+ "ZMW",
3296
+ "BWP",
3297
+ "MUR",
3298
+ "NAD",
3299
+ "MWK",
3300
+ "AOA",
3301
+ "CDF",
3302
+ "GMD",
3303
+ "GNF",
3304
+ "LRD",
3305
+ "SLL",
3306
+ "MZN",
3307
+ "BIF",
3308
+ "INR",
3309
+ "BRL",
3310
+ "COP",
3311
+ "MXN",
3312
+ "KRW",
3313
+ "TRY",
3314
+ "THB",
3315
+ "MYR",
3316
+ "PHP",
3317
+ "IDR",
3318
+ "VND",
3319
+ "SGD",
3320
+ "HKD",
3321
+ "TWD",
3322
+ "AED",
3323
+ "SAR",
3324
+ "ILS"
3325
+ ]);
3326
+ function isSupportedCurrency(code) {
3327
+ return SUPPORTED_CURRENCY_CODES.has(code);
3328
+ }
3329
+ var warnedCurrencies = /* @__PURE__ */ new Set();
3330
+ function currencyCode(value) {
3331
+ const normalized = value.trim().toUpperCase();
3332
+ if (!isSupportedCurrency(normalized) && !warnedCurrencies.has(normalized)) {
3333
+ warnedCurrencies.add(normalized);
3334
+ console.warn(
3335
+ `[Cimplify] Unrecognized currency code "${normalized}". It may not format correctly.`
3336
+ );
3337
+ }
3338
+ return normalized;
3339
+ }
3003
3340
  var ErrorCode = {
3004
3341
  NOT_FOUND: "NOT_FOUND",
3005
- VALIDATION_ERROR: "VALIDATION_ERROR"};
3342
+ VALIDATION_ERROR: "VALIDATION_ERROR",
3343
+ CART_EMPTY: "CART_EMPTY"};
3006
3344
  var DOCS_ERROR_BASE_URL = "https://docs.cimplify.io/reference/error-codes";
3007
3345
  function docsUrlForCode(code) {
3008
3346
  return `${DOCS_ERROR_BASE_URL}#${code.toLowerCase().replace(/_/g, "-")}`;
@@ -3060,6 +3398,10 @@ var ERROR_SUGGESTIONS = {
3060
3398
  CHECKOUT_NOT_READY: "Checkout elements are still initializing. Wait for readiness before submit.",
3061
3399
  CANCELLED: "Checkout was cancelled. Preserve cart state and allow shopper to retry.",
3062
3400
  REQUEST_TIMEOUT: "Provider call timed out. Poll payment/order status before issuing another charge attempt.",
3401
+ CHECKOUT_AUTHORITY_REQUIRED: "Provide an exact cart or purchase-intent authority before starting checkout.",
3402
+ AUTH_CONFIG_REQUIRED: "Configure the OAuth client and redirect URI before mounting embedded authentication.",
3403
+ AUTH_CALLBACK_MISSING_TOKEN: "The authentication callback returned no access token. Restart sign-in before checkout.",
3404
+ AUTH_START_FAILED: "Embedded authentication could not start. Verify the OAuth configuration and retry.",
3063
3405
  POPUP_BLOCKED: "Browser blocked provider popup. Ask shopper to enable popups and retry.",
3064
3406
  FX_QUOTE_FAILED: "Failed to lock FX quote. Retry currency quote or fallback to base currency."
3065
3407
  };
@@ -3103,7 +3445,7 @@ function authMiddleware(deps) {
3103
3445
  await next();
3104
3446
  };
3105
3447
  }
3106
- var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
3448
+ var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|purchase-intents|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
3107
3449
  var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
3108
3450
  function idempotencyMiddleware(deps) {
3109
3451
  return async (c, next) => {
@@ -3113,7 +3455,8 @@ function idempotencyMiddleware(deps) {
3113
3455
  const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
3114
3456
  if (!key || key.length > 200) return next();
3115
3457
  const session = c.var.session;
3116
- const recordId = `${session.id}:${key}`;
3458
+ const routeScope = c.req.path.includes("/purchase-intents") ? `:${c.req.path}` : "";
3459
+ const recordId = `${session.id}${routeScope}:${key}`;
3117
3460
  const existing = deps.registry.idempotency.get(recordId);
3118
3461
  if (existing) {
3119
3462
  const expiresAt = Date.parse(existing.expires_at);
@@ -3191,8 +3534,89 @@ var PAYMENT_METHOD = {
3191
3534
  MOBILE_MONEY: "mobile_money",
3192
3535
  CARD: "card"
3193
3536
  };
3537
+ var CHECKOUT_NEXT_ACTION = {
3538
+ NONE: "none"};
3539
+ var CHECKOUT_ORDER_RESULT_STATUS = {
3540
+ CONFIRMED: "confirmed"};
3541
+ var AUTHORIZATION_TYPE = {
3542
+ OTP: "otp"};
3543
+ var DEFAULT_CURRENCY = "GHS";
3544
+
3545
+ // src/types/purchase-intent.ts
3546
+ var PURCHASE_INTENT_SOURCE_KIND = {
3547
+ CART: "cart"};
3548
+ var PURCHASING_PRINCIPAL_KIND = {
3549
+ ACCOUNT: "account",
3550
+ BUSINESS: "business",
3551
+ GUEST: "guest"};
3552
+ var PURCHASE_INTENT_STATUS = {
3553
+ OPEN: "open",
3554
+ CONVERTED: "converted"};
3555
+ var PURCHASE_INTENT_SELLABLE_KIND = {
3556
+ PRODUCT: "product",
3557
+ VARIANT: "variant"
3558
+ };
3559
+ var PURCHASE_INTENT_LINE_CONFIGURATION_KIND = {
3560
+ SIMPLE: "simple"};
3561
+ var MOBILE_MONEY_TENDER_AUTHORITY_KIND = {
3562
+ TRANSIENT: "transient"};
3563
+ var PURCHASE_INTENT_MOBILE_MONEY_NETWORK = {
3564
+ MTN: "mtn",
3565
+ TELECEL: "telecel",
3566
+ AIRTEL: "airtel"
3567
+ };
3568
+ var CHECKOUT_COMPLIANCE_LANE = {
3569
+ NATIVE: "native"};
3570
+ var CHECKOUT_COMPLIANCE_REASON = {
3571
+ PHYSICAL_GOODS_NATIVE: "physical_goods_native"};
3572
+ var PURCHASE_INTENT_PAYMENT_METHOD = {
3573
+ CARD: "card",
3574
+ MOBILE_MONEY: "mobile_money"};
3575
+ var PAYMENT_COLLECTION_MODE = {
3576
+ REQUEST_TO_PAY: "request_to_pay",
3577
+ CREDENTIAL_CHARGE: "credential_charge"};
3578
+ var PURCHASE_INTENT_PAYMENT_PROVIDER = {
3579
+ PAYSTACK: "paystack"};
3580
+ var PAYMENT_OPTION_STATUS = {
3581
+ ELIGIBLE: "eligible"};
3582
+ var PAYMENT_OPTION_NEXT_ACTION = {
3583
+ PROVIDER_APPROVAL: "provider_approval"};
3584
+ var LEGACY_CHECKOUT_PAYMENT_STATUS = {
3585
+ SUCCEEDED: "succeeded"};
3586
+ var CHECKOUT_TERMS_COMPONENT = {
3587
+ DUE_NOW: "due_now"};
3588
+ var CHECKOUT_TERMS_DELTA_KIND = {
3589
+ MONEY: "money",
3590
+ PAYMENT_OPTION: "payment_option"
3591
+ };
3592
+ var CHECKOUT_INVALIDATION_REASON = {
3593
+ PAYMENT_OPTION_CHANGED: "payment_option_changed"
3594
+ };
3595
+ var PURCHASE_INTENT_CONFIRM_STATUS = {
3596
+ ORDER_CREATED: "order_created",
3597
+ CARD_POPUP_REQUIRED: "card_popup_required",
3598
+ AUTHORIZATION_REQUIRED: "authorization_required",
3599
+ TERMS_CHANGED: "terms_changed",
3600
+ SELECTION_UNAVAILABLE: "selection_unavailable",
3601
+ SOURCE_UNAVAILABLE: "source_unavailable"
3602
+ };
3194
3603
 
3195
3604
  // src/mock/routes/v1.ts
3605
+ var PURCHASE_INTENT_ROUTE = {
3606
+ COLLECTION: "/purchase-intents",
3607
+ MEMBER: "/purchase-intents/:intent_id",
3608
+ REFRESH: "/purchase-intents/:intent_id/refresh",
3609
+ CONFIRM: "/purchase-intents/:intent_id/confirm",
3610
+ ACTION: "/purchase-intents/:intent_id/attempts/:attempt_id/actions"
3611
+ };
3612
+ var PURCHASE_INTENT_ROUTE_PARAM = {
3613
+ INTENT_ID: "intent_id",
3614
+ ATTEMPT_ID: "attempt_id"
3615
+ };
3616
+ var PAYMENT_ROUTE = {
3617
+ COLLECTION_OPTIONS: "/payments/collection-options",
3618
+ AUTHORIZATION: "/payments/authorization"
3619
+ };
3196
3620
  function buildV1(deps) {
3197
3621
  const app = new Hono2();
3198
3622
  app.use("*", chaosMiddleware(deps));
@@ -3565,28 +3989,81 @@ function buildV1(deps) {
3565
3989
  "/subscriptions/:subscription_id/skip",
3566
3990
  (c) => tryRoute(c, () => deps.subscriptions.skipNext(c.req.param("subscription_id")))
3567
3991
  );
3568
- app.get("/payments/collection-options", (c) => {
3992
+ app.get(PAYMENT_ROUTE.COLLECTION_OPTIONS, async (c) => {
3569
3993
  const currency = c.req.query("currency")?.trim().toUpperCase();
3570
3994
  if (!currency || !/^[A-Z]{3}$/.test(currency)) {
3571
3995
  return fail(c, validation("currency must be a canonical three-letter code"));
3572
3996
  }
3573
3997
  return tryRoute(c, () => ({
3574
3998
  presentment_currency: currency,
3575
- options: currency === "GHS" ? [
3999
+ options: currency === DEFAULT_CURRENCY ? [
3576
4000
  { method: PAYMENT_METHOD.CARD, network: null },
3577
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "mtn" },
3578
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "telecel" },
3579
- { method: PAYMENT_METHOD.MOBILE_MONEY, network: "airteltigo" }
4001
+ {
4002
+ method: PAYMENT_METHOD.MOBILE_MONEY,
4003
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.MTN
4004
+ },
4005
+ {
4006
+ method: PAYMENT_METHOD.MOBILE_MONEY,
4007
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.TELECEL
4008
+ },
4009
+ {
4010
+ method: PAYMENT_METHOD.MOBILE_MONEY,
4011
+ network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.AIRTEL
4012
+ }
3580
4013
  ] : currency === "USD" ? [{ method: PAYMENT_METHOD.CARD, network: null }] : []
3581
4014
  }));
3582
4015
  });
3583
- app.post("/checkout", async (c) => {
4016
+ app.post(PURCHASE_INTENT_ROUTE.COLLECTION, async (c) => {
4017
+ const body = await c.req.json().catch(() => ({}));
4018
+ return tryRoute(c, () => deps.purchaseIntents.mint(c.var.session, body));
4019
+ });
4020
+ app.get(
4021
+ PURCHASE_INTENT_ROUTE.MEMBER,
4022
+ (c) => tryRoute(
4023
+ c,
4024
+ () => deps.purchaseIntents.get(
4025
+ c.var.session,
4026
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID)
4027
+ )
4028
+ )
4029
+ );
4030
+ app.post(PURCHASE_INTENT_ROUTE.REFRESH, async (c) => {
4031
+ const body = await c.req.json().catch(() => ({}));
4032
+ return tryRoute(
4033
+ c,
4034
+ () => deps.purchaseIntents.refresh(
4035
+ c.var.session,
4036
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
4037
+ body.expected_intent_revision
4038
+ )
4039
+ );
4040
+ });
4041
+ app.post(PURCHASE_INTENT_ROUTE.CONFIRM, async (c) => {
3584
4042
  const body = await c.req.json().catch(() => ({}));
3585
- return tryRoute(c, () => deps.checkout.process(c.var.session, body));
4043
+ return tryRoute(
4044
+ c,
4045
+ () => deps.purchaseIntents.confirm(
4046
+ c.var.session,
4047
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
4048
+ body
4049
+ )
4050
+ );
3586
4051
  });
3587
- app.post("/payments/authorization", async (c) => {
4052
+ app.post(PURCHASE_INTENT_ROUTE.ACTION, async (c) => {
3588
4053
  const body = await c.req.json().catch(() => ({}));
3589
- return tryRoute(c, () => deps.checkout.authorize(body));
4054
+ return tryRoute(
4055
+ c,
4056
+ () => deps.purchaseIntents.submitAction(
4057
+ c.var.session,
4058
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
4059
+ c.req.param(PURCHASE_INTENT_ROUTE_PARAM.ATTEMPT_ID),
4060
+ body
4061
+ )
4062
+ );
4063
+ });
4064
+ app.post(PAYMENT_ROUTE.AUTHORIZATION, async (c) => {
4065
+ const body = await c.req.json().catch(() => ({}));
4066
+ return tryRoute(c, () => deps.payments.authorize(body));
3590
4067
  });
3591
4068
  app.get("/auth/status", (c) => tryRoute(c, () => deps.auth.status(c.var.session.token)));
3592
4069
  app.post("/auth/request-otp", async (c) => {
@@ -3892,6 +4369,7 @@ function createStateRegistry() {
3892
4369
  collections: createMemoryStore(),
3893
4370
  carts: createMemoryStore(),
3894
4371
  orders: createMemoryStore(),
4372
+ purchaseIntents: createMemoryStore(),
3895
4373
  subscriptions: createMemoryStore(),
3896
4374
  bookings: createMemoryStore(),
3897
4375
  services: createMemoryStore(),
@@ -4041,9 +4519,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4041
4519
  // injected into the HTML page served by the mock. Edit this file directly \u2014
4042
4520
  // type-check via tsgo + the IframeToParentMessage shape from the SDK.
4043
4521
  (function () {
4044
- var TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
4045
- var DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
4046
- var MSG = {
4522
+ const TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
4523
+ const DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
4524
+ const MSG = {
4047
4525
  INIT: "init", SET_TOKEN: "set_token", SET_CART: "set_cart", GET_DATA: "get_data",
4048
4526
  REFRESH_TOKEN: "refresh_token", LOGOUT: "logout",
4049
4527
  PROCESS_CHECKOUT: "process_checkout", ABORT_CHECKOUT: "abort_checkout",
@@ -4056,12 +4534,36 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4056
4534
  CHECKOUT_STATUS: "checkout_status", CHECKOUT_COMPLETE: "checkout_complete",
4057
4535
  ORDER_TYPE_CHANGED: "order_type_changed", REQUEST_SUBMIT: "request_submit"
4058
4536
  };
4059
- var qs = new URLSearchParams(location.search);
4060
- var AUTO = qs.get("auto") === "true";
4061
- var DEMO_OTP = qs.get("otp") || "123456";
4062
- var ORIGIN = qs.get("origin") || "*";
4063
- var NONCE = qs.get("nonce");
4064
- var state = {
4537
+ const PURCHASE_INTENT = {
4538
+ PATH: "/api/v1/purchase-intents",
4539
+ ORDER_CREATED: "order_created",
4540
+ CARD_POPUP_REQUIRED: "card_popup_required",
4541
+ AUTHORIZATION_REQUIRED: "authorization_required",
4542
+ ELIGIBLE: "eligible"
4543
+ };
4544
+ const CHECKOUT_STATUS = {
4545
+ PROCESSING: "processing",
4546
+ SUCCESS: "success",
4547
+ FAILED: "failed"
4548
+ };
4549
+ const CHECKOUT_ERROR = { FAILED: "CHECKOUT_FAILED" };
4550
+ const ORDER_STATUS = { CONFIRMED: "confirmed" };
4551
+ const TENDER = {
4552
+ CARD: "card",
4553
+ MOBILE_MONEY: "mobile_money",
4554
+ AUTHORITY_TRANSIENT: "transient",
4555
+ MTN: "mtn"
4556
+ };
4557
+ const PAYMENT_PROVIDER = { PAYSTACK: "paystack" };
4558
+ const SOURCE_KIND = { CART: "cart" };
4559
+ const AUTHORIZATION = { OTP: "otp" };
4560
+ const EXACT_ZERO_MONEY = /^[+-]?0+(?:\\.0+)?(?:[eE][+-]?\\d+)?$/;
4561
+ const qs = new URLSearchParams(location.search);
4562
+ const AUTO = qs.get("auto") === "true";
4563
+ const DEMO_OTP = qs.get("otp") || "123456";
4564
+ const ORIGIN = qs.get("origin") || "*";
4565
+ const NONCE = qs.get("nonce");
4566
+ const state = {
4065
4567
  businessId: DEFAULT_BUSINESS,
4066
4568
  token: null,
4067
4569
  cart: null,
@@ -4072,15 +4574,33 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4072
4574
  contact: null,
4073
4575
  pendingOtp: null
4074
4576
  };
4075
- var content = document.getElementById("content");
4076
- var title = document.getElementById("title");
4577
+ const content = document.getElementById("content");
4578
+ const title = document.getElementById("title");
4077
4579
 
4078
4580
  function send(msg) {
4079
4581
  if (NONCE) msg.nonce = NONCE;
4080
4582
  parent.postMessage(msg, ORIGIN);
4081
4583
  }
4584
+ function isExactlyZeroMoney(value) {
4585
+ return typeof value === "number"
4586
+ ? Object.is(value, 0) || Object.is(value, -0)
4587
+ : EXACT_ZERO_MONEY.test(String(value).trim());
4588
+ }
4082
4589
  function reportHeight() { send({ type: MSG.HEIGHT_CHANGE, height: document.documentElement.scrollHeight }); }
4083
4590
  function ready() { send({ type: MSG.READY, height: document.documentElement.scrollHeight }); }
4591
+ function responseWithBody(response) {
4592
+ return response.json().then(function (body) {
4593
+ return { status: response.status, body: body };
4594
+ });
4595
+ }
4596
+ function assertSuccessfulResponse(response, fallbackMessage) {
4597
+ if (response.status >= 400) {
4598
+ throw new Error(
4599
+ (response.body && response.body.error && response.body.error.message) || fallbackMessage
4600
+ );
4601
+ }
4602
+ return response.body;
4603
+ }
4084
4604
 
4085
4605
  function defaultAddress() {
4086
4606
  return {
@@ -4090,9 +4610,20 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4090
4610
  };
4091
4611
  }
4092
4612
  function defaultPayment(type) {
4093
- if (type === "card") return { type: "card", provider: "paystack", last_four: "4242", label: "Visa \u2022\u2022\u2022\u2022 4242" };
4094
- if (type === "cash") return { type: "cash", label: "Cash on delivery" };
4095
- return { type: "mobile_money", provider: "mtn", phone_number: "+233244000000", label: "MTN Mobile Money" };
4613
+ if (type === TENDER.CARD) {
4614
+ return {
4615
+ type: TENDER.CARD,
4616
+ provider: PAYMENT_PROVIDER.PAYSTACK,
4617
+ last_four: "4242",
4618
+ label: "Visa \u2022\u2022\u2022\u2022 4242"
4619
+ };
4620
+ }
4621
+ return {
4622
+ type: TENDER.MOBILE_MONEY,
4623
+ provider: TENDER.MTN,
4624
+ phone_number: "+233244000000",
4625
+ label: "MTN Mobile Money"
4626
+ };
4096
4627
  }
4097
4628
 
4098
4629
  function renderAuth() {
@@ -4117,12 +4648,12 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4117
4648
  ' <div class="option" data-key="lagos">5 Allen Ave, Ikeja, Lagos</div>' +
4118
4649
  '</div>' +
4119
4650
  '<button id="confirm">Use this address</button>';
4120
- var options = {
4651
+ const options = {
4121
4652
  "default": defaultAddress(),
4122
4653
  "east-legon": { street_address: "8 Ring Road", city: "Accra", region: "Greater Accra", country: "GH", latitude: 5.65, longitude: -0.16 },
4123
4654
  "lagos": { street_address: "5 Allen Avenue", city: "Lagos", region: "Lagos", country: "NG", latitude: 6.5244, longitude: 3.3792 }
4124
4655
  };
4125
- var picked = "default";
4656
+ let picked = "default";
4126
4657
  Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
4127
4658
  el.onclick = function () {
4128
4659
  picked = el.getAttribute("data-key");
@@ -4145,10 +4676,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4145
4676
  '<div class="options" id="pay-options">' +
4146
4677
  ' <div class="option selected" data-method="mobile_money">\u{1F4F1} MTN Mobile Money</div>' +
4147
4678
  ' <div class="option" data-method="card">\u{1F4B3} Card (Paystack)</div>' +
4148
- ' <div class="option" data-method="cash">\u{1F4B5} Cash on delivery</div>' +
4149
4679
  '</div>' +
4150
4680
  '<button id="confirm">Use this method</button>';
4151
- var picked = "mobile_money";
4681
+ let picked = TENDER.MOBILE_MONEY;
4152
4682
  Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
4153
4683
  el.onclick = function () {
4154
4684
  picked = el.getAttribute("data-method");
@@ -4192,7 +4722,6 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4192
4722
  '</div>' +
4193
4723
  '<label>Payment method</label>' +
4194
4724
  '<select id="pay-method">' +
4195
- ' <option value="cash">Cash</option>' +
4196
4725
  ' <option value="mobile_money">Mobile Money (MTN)</option>' +
4197
4726
  ' <option value="card">Card (Paystack)</option>' +
4198
4727
  '</select>' +
@@ -4209,65 +4738,139 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4209
4738
  }
4210
4739
 
4211
4740
  function runCheckout() {
4212
- var statusEl = document.getElementById("status");
4213
- var errEl = document.getElementById("err");
4214
- var resultEl = document.getElementById("result");
4741
+ const statusEl = document.getElementById("status");
4742
+ const errEl = document.getElementById("err");
4743
+ const resultEl = document.getElementById("result");
4215
4744
  if (errEl) errEl.textContent = "";
4216
4745
  if (!state.cart) { errEl.textContent = "No cart provided"; return; }
4217
- var customer = {
4746
+ const customer = {
4218
4747
  name: document.getElementById("cust-name").value.trim() || "Customer",
4219
4748
  email: document.getElementById("cust-email").value.trim() || "noemail@example.com",
4220
4749
  phone: document.getElementById("cust-phone").value.trim() || "+233000000000",
4221
4750
  save_details: false
4222
4751
  };
4223
- var paymentMethod = document.getElementById("pay-method").value;
4224
- var orderType = document.getElementById("order-type").value;
4752
+ const paymentMethod = document.getElementById("pay-method").value;
4753
+ const orderType = document.getElementById("order-type").value;
4225
4754
  statusEl.textContent = "Processing\u2026";
4226
- send({ type: MSG.CHECKOUT_STATUS, status: "processing", context: {} });
4755
+ send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.PROCESSING, context: {} });
4227
4756
 
4228
- var headers = { "content-type": "application/json" };
4757
+ const headers = { "content-type": "application/json" };
4229
4758
  if (state.token) headers["x-session-token"] = state.token;
4230
4759
 
4231
- fetch("/api/v1/checkout", {
4760
+ const preference = paymentMethod === TENDER.MOBILE_MONEY
4761
+ ? {
4762
+ method_kind: TENDER.MOBILE_MONEY,
4763
+ authority: {
4764
+ authority_kind: TENDER.AUTHORITY_TRANSIENT,
4765
+ phone_number: customer.phone,
4766
+ network: TENDER.MTN
4767
+ }
4768
+ }
4769
+ : { method_kind: TENDER.CARD };
4770
+ const intentKey = "mock-element:" + state.cart.id;
4771
+ fetch(PURCHASE_INTENT.PATH, {
4232
4772
  method: "POST",
4233
4773
  headers: headers,
4234
4774
  body: JSON.stringify({
4235
- cart_id: state.cart.id,
4236
- customer: customer,
4237
- order_type: orderType,
4238
- payment_method: paymentMethod,
4239
- address_info: state.address || undefined
4775
+ client_intent_key: intentKey,
4776
+ source: { kind: SOURCE_KIND.CART, cart_id: state.cart.id },
4777
+ checkout: {
4778
+ customer: customer,
4779
+ order_type: orderType,
4780
+ address_info: state.address || {},
4781
+ tender_preference: preference
4782
+ }
4240
4783
  })
4241
4784
  })
4242
- .then(function (r) { return r.json().then(function (b) { return { status: r.status, body: b }; }); })
4785
+ .then(responseWithBody)
4243
4786
  .then(function (resp) {
4244
- if (resp.status >= 400) {
4245
- var msg = (resp.body && resp.body.error && resp.body.error.message) || "Checkout failed";
4246
- send({ type: MSG.CHECKOUT_COMPLETE, success: false, error: { code: (resp.body.error && resp.body.error.code) || "CHECKOUT_FAILED", message: msg, recoverable: true } });
4247
- statusEl.textContent = "Failed";
4248
- errEl.textContent = msg;
4249
- return;
4787
+ const intent = assertSuccessfulResponse(resp, "Intent creation failed");
4788
+ const terms = intent.current_terms;
4789
+ const option = terms && terms.payment_options && terms.payment_options.filter(function (candidate) {
4790
+ return candidate.status === PURCHASE_INTENT.ELIGIBLE && candidate.method_kind === preference.method_kind;
4791
+ })[0];
4792
+ if (!terms || (!option && !isExactlyZeroMoney(terms.money.rail_amount))) {
4793
+ throw new Error("No eligible payment option");
4794
+ }
4795
+ const confirmationPath = PURCHASE_INTENT.PATH + "/" + encodeURIComponent(intent.id) + "/confirm";
4796
+ const confirmationBody = JSON.stringify({
4797
+ accepted_intent_revision: terms.intent_revision,
4798
+ accepted_terms_version: terms.terms_version,
4799
+ accepted_due_now: terms.money.due_now,
4800
+ accepted_customer_debit_amount: option
4801
+ ? option.customer_debit_amount
4802
+ : terms.money.rail_amount,
4803
+ currency: terms.money.currency,
4804
+ payment_option_id: option ? option.id : null,
4805
+ idempotency_key: "mock-confirm:" + intent.id
4806
+ });
4807
+ function confirmIntent() {
4808
+ return fetch(confirmationPath, {
4809
+ method: "POST",
4810
+ headers: headers,
4811
+ body: confirmationBody
4812
+ })
4813
+ .then(responseWithBody)
4814
+ .then(function (response) {
4815
+ return assertSuccessfulResponse(response, "Intent confirmation failed");
4816
+ });
4817
+ }
4818
+
4819
+ return confirmIntent().then(function (outcome) {
4820
+ // This mock has no external provider window. Replaying the exact
4821
+ // idempotent confirmation models the provider-approved callback.
4822
+ return outcome.status === PURCHASE_INTENT.CARD_POPUP_REQUIRED
4823
+ ? confirmIntent()
4824
+ : outcome;
4825
+ });
4826
+ })
4827
+ .then(function (outcome) {
4828
+ if (outcome.status !== PURCHASE_INTENT.AUTHORIZATION_REQUIRED) return outcome;
4829
+ return fetch(
4830
+ PURCHASE_INTENT.PATH + "/" + encodeURIComponent(outcome.intent_id) +
4831
+ "/attempts/" + encodeURIComponent(outcome.attempt_id) + "/actions",
4832
+ {
4833
+ method: "POST",
4834
+ headers: headers,
4835
+ body: JSON.stringify({ authorization: { type: AUTHORIZATION.OTP, value: DEMO_OTP } })
4836
+ }
4837
+ )
4838
+ .then(responseWithBody)
4839
+ .then(function (response) {
4840
+ return assertSuccessfulResponse(response, "Authorization failed");
4841
+ });
4842
+ })
4843
+ .then(function (outcome) {
4844
+ if (outcome.status !== PURCHASE_INTENT.ORDER_CREATED) {
4845
+ throw new Error("Checkout requires " + outcome.status.replace(/_/g, " "));
4250
4846
  }
4251
- var b = resp.body;
4252
- send({ type: MSG.CHECKOUT_STATUS, status: "success", context: { order_id: b.order_id, order_number: b.order_number, provider: b.provider } });
4847
+ send({
4848
+ type: MSG.CHECKOUT_STATUS,
4849
+ status: CHECKOUT_STATUS.SUCCESS,
4850
+ context: { order_id: outcome.order_id }
4851
+ });
4253
4852
  send({
4254
4853
  type: MSG.CHECKOUT_COMPLETE,
4255
4854
  success: true,
4256
4855
  order: {
4257
- id: b.order_id,
4258
- order_number: b.order_number,
4259
- status: b.requires_authorization ? "pending" : "confirmed",
4856
+ id: outcome.order_id,
4857
+ order_number: outcome.order_number || outcome.order_id,
4858
+ status: ORDER_STATUS.CONFIRMED,
4260
4859
  total: (state.cart && state.cart.total_price) || "0.00",
4261
4860
  currency: (state.cart && state.cart.currency) || "GHS"
4262
4861
  }
4263
4862
  });
4264
- statusEl.textContent = "Success \u2014 order " + b.order_number;
4863
+ statusEl.textContent = "Success \u2014 order " + outcome.order_id;
4265
4864
  resultEl.style.display = "block";
4266
- resultEl.textContent = JSON.stringify(b, null, 2);
4865
+ resultEl.textContent = JSON.stringify(outcome, null, 2);
4267
4866
  })
4268
4867
  .catch(function (e) {
4269
- send({ type: MSG.CHECKOUT_COMPLETE, success: false, error: { code: "NETWORK", message: String(e), recoverable: true } });
4270
- statusEl.textContent = "Network error";
4868
+ send({
4869
+ type: MSG.CHECKOUT_COMPLETE,
4870
+ success: false,
4871
+ error: { code: CHECKOUT_ERROR.FAILED, message: String(e), recoverable: true }
4872
+ });
4873
+ statusEl.textContent = "Failed";
4271
4874
  errEl.textContent = String(e);
4272
4875
  });
4273
4876
  }
@@ -4284,7 +4887,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4284
4887
  break;
4285
4888
  case MSG.SET_CART:
4286
4889
  state.cart = msg.cart;
4287
- var s = document.getElementById("status");
4890
+ const s = document.getElementById("status");
4288
4891
  if (s) s.textContent = "Cart loaded \u2014 " + (msg.cart && msg.cart.items ? msg.cart.items.length : 0) + " item(s), " + ((msg.cart && msg.cart.currency) || "GHS") + " " + ((msg.cart && msg.cart.total_price) || "0.00");
4289
4892
  reportHeight();
4290
4893
  break;
@@ -4308,7 +4911,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
4308
4911
  }
4309
4912
  break;
4310
4913
  case MSG.ABORT_CHECKOUT:
4311
- send({ type: MSG.CHECKOUT_STATUS, status: "failed", context: {} });
4914
+ send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.FAILED, context: {} });
4312
4915
  break;
4313
4916
  }
4314
4917
  }
@@ -4922,6 +5525,7 @@ function createAuthService(opts) {
4922
5525
  token: id,
4923
5526
  business_id: options.defaultBusinessId,
4924
5527
  is_authenticated: authenticated,
5528
+ account_id: authenticated ? ids.raw("account") : void 0,
4925
5529
  customer_id: authenticated ? ids.customer() : void 0,
4926
5530
  customer_name: profile?.name,
4927
5531
  customer_email: profile?.email,
@@ -5004,7 +5608,7 @@ function createAuthService(opts) {
5004
5608
  if (!s) return { is_authenticated: false, user_type: "guest" };
5005
5609
  return {
5006
5610
  is_authenticated: s.is_authenticated,
5007
- account_id: s.customer_id,
5611
+ account_id: s.account_id,
5008
5612
  user_type: s.is_authenticated ? "customer" : "guest",
5009
5613
  session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
5010
5614
  session: s
@@ -6302,88 +6906,10 @@ function createCartService(opts) {
6302
6906
  };
6303
6907
  }
6304
6908
 
6305
- // src/mock/domain/checkout.ts
6306
- function createCheckoutService(opts) {
6307
- const { registry, clock, ids, bus } = opts;
6909
+ // src/mock/domain/payment.ts
6910
+ function createPaymentService(options) {
6911
+ const { registry, clock, bus } = options;
6308
6912
  return {
6309
- process(session, body) {
6310
- if (!body.cart_id) throw validation("cart_id required");
6311
- const c = registry.carts.get(body.cart_id);
6312
- if (!c) throw notFound(`Cart ${body.cart_id} not found`);
6313
- if (c.items.length === 0) throw validation("Cart is empty", { code: "CART_EMPTY" });
6314
- const orderId = ids.order();
6315
- const orderNumber = ids.orderNumber();
6316
- const paymentId = ids.payment();
6317
- const reference = ids.payment();
6318
- const requiresAuth = body.payment_method === "mobile_money";
6319
- const isCard = body.payment_method === "card" || body.payment_method === "stripe";
6320
- const cardProvider = body.payment_method === "stripe" || body.pay_currency?.toUpperCase() === "USD" ? "stripe" : "paystack";
6321
- const order = {
6322
- id: orderId,
6323
- business_id: c.business_id,
6324
- user_friendly_id: orderNumber,
6325
- channel: c.channel,
6326
- status: requiresAuth ? "pending" : "confirmed",
6327
- payment_state: requiresAuth ? "not_paid" : "paid",
6328
- order_type: body.order_type,
6329
- customer_id: session.customer_id,
6330
- customer_name: body.customer.name,
6331
- customer_email: body.customer.email,
6332
- customer_phone: body.customer.phone,
6333
- delivery_address: body.address_info?.street_address,
6334
- delivery_fee: c.delivery_fee,
6335
- subtotal: c.subtotal,
6336
- tax_amount: c.tax_amount,
6337
- service_charge: c.service_charge,
6338
- total_discounts: c.total_discounts,
6339
- total_price: c.total_price,
6340
- currency: c.currency,
6341
- items: c.items,
6342
- applied_discount_ids: c.applied_discount_ids,
6343
- applied_discount_codes: c.applied_discount_codes,
6344
- payment_id: paymentId,
6345
- payment_reference: reference,
6346
- metadata: body.metadata,
6347
- created_at: clock.iso(),
6348
- updated_at: clock.iso()
6349
- };
6350
- registry.orders.put(orderId, order);
6351
- registry.carts.put(c.id, { ...c, status: "converted", updated_at: clock.iso() });
6352
- bus.emit("order.created", { order_id: orderId, order_number: orderNumber }, c.business_id);
6353
- if (!requiresAuth) {
6354
- bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
6355
- bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
6356
- }
6357
- const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
6358
- const result = {
6359
- order_id: orderId,
6360
- order_number: orderNumber,
6361
- payment_id: paymentId,
6362
- payment_status: requiresAuth || isCard ? "pending" : "succeeded",
6363
- payment_reference: reference,
6364
- requires_authorization: requiresAuth,
6365
- authorization_type: requiresAuth ? "otp" : void 0,
6366
- next_action: requiresAuth ? {
6367
- type: "authorization",
6368
- authorization_type: "otp",
6369
- display_text: "Approve on your mobile money app"
6370
- } : { type: "none" },
6371
- provider: isCard ? cardProvider : body.payment_method,
6372
- bill_token: billToken
6373
- };
6374
- if (isCard) {
6375
- const proc = globalThis.process;
6376
- result.client_secret = cardProvider === "stripe" ? `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}` : `access_${reference}`;
6377
- result.public_key = cardProvider === "stripe" ? proc?.env?.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY ?? "pk_test_mock_stripe" : proc?.env?.NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY ?? "pk_test_mock_paystack";
6378
- result.next_action = {
6379
- type: "card_popup",
6380
- provider: cardProvider,
6381
- client_secret: result.client_secret,
6382
- public_key: result.public_key
6383
- };
6384
- }
6385
- return result;
6386
- },
6387
6913
  authorize(body) {
6388
6914
  if (!body.payment_id?.trim()) throw validation("payment_id required");
6389
6915
  if (!body.authorization) throw validation("authorization required");
@@ -6394,7 +6920,7 @@ function createCheckoutService(opts) {
6394
6920
  const updated = {
6395
6921
  ...order,
6396
6922
  payment_state: "paid",
6397
- status: "confirmed",
6923
+ status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
6398
6924
  updated_at: clock.iso()
6399
6925
  };
6400
6926
  registry.orders.put(order.id, updated);
@@ -6405,10 +6931,538 @@ function createCheckoutService(opts) {
6405
6931
  order_number: order.user_friendly_id,
6406
6932
  payment_id: body.payment_id,
6407
6933
  payment_reference: order.payment_reference,
6408
- payment_status: "succeeded",
6934
+ payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
6409
6935
  requires_authorization: false,
6410
- next_action: { type: "none" }
6936
+ next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
6937
+ };
6938
+ }
6939
+ };
6940
+ }
6941
+
6942
+ // src/mock/domain/purchase-intent.ts
6943
+ var INTENT_LIFETIME_MS = 15 * 60 * 1e3;
6944
+ var ZERO_MONEY = "0.00";
6945
+ var PURCHASE_INTENT_MOBILE_MONEY_NETWORKS = new Set(
6946
+ Object.values(PURCHASE_INTENT_MOBILE_MONEY_NETWORK)
6947
+ );
6948
+ var MOCK_PURCHASE_INTENT = {
6949
+ OWNER_KIND: { ACCOUNT: "account", STOREFRONT_SESSION: "storefront_session" },
6950
+ CART_STATUS: { ACTIVE: "active", CONVERTED: "converted" },
6951
+ ORDER_STATUS: { PENDING: "pending", CONFIRMED: "confirmed" },
6952
+ PAYMENT_STATE: { NOT_PAID: "not_paid", PAID: "paid" },
6953
+ EVENT: {
6954
+ ORDER_CREATED: "order.created",
6955
+ PAYMENT_CAPTURED: "order.payment.captured",
6956
+ ORDER_CONFIRMED: "order.confirmed"
6957
+ },
6958
+ ID_PREFIX: { INTENT: "pi", ATTEMPT: "attempt" },
6959
+ DEFAULT_LOCATION_ID: "mock-location",
6960
+ MOCK_PAYSTACK_PUBLIC_KEY: "pk_test_mock_paystack",
6961
+ COMPLIANCE: {
6962
+ POLICY_NAME: "mock-storefront-commerce"
6963
+ }
6964
+ };
6965
+ var DECIMAL_WIRE_VALUE = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
6966
+ function isExactlyZero(value) {
6967
+ if (typeof value === "number") return Object.is(value, 0) || Object.is(value, -0);
6968
+ const match2 = DECIMAL_WIRE_VALUE.exec(value.trim());
6969
+ return Boolean(match2 && [...match2[2], ...match2[3] ?? ""].every((digit) => digit === "0"));
6970
+ }
6971
+ function sameDecimal(left, right) {
6972
+ const canonical = (value) => {
6973
+ const wire = typeof value === "number" ? String(value) : value.trim();
6974
+ const match2 = DECIMAL_WIRE_VALUE.exec(wire);
6975
+ if (!match2) return null;
6976
+ let digits = `${match2[2]}${match2[3] ?? ""}`.replace(/^0+/, "");
6977
+ if (!digits) return "0";
6978
+ let scale = BigInt((match2[3] ?? "").length) - BigInt(match2[4] ?? "0");
6979
+ while (digits.endsWith("0")) {
6980
+ digits = digits.slice(0, -1);
6981
+ scale -= 1n;
6982
+ }
6983
+ const sign = match2[1] === "-" ? "-" : "";
6984
+ return `${sign}${digits}:${scale}`;
6985
+ };
6986
+ const normalizedLeft = canonical(left);
6987
+ return normalizedLeft !== null && normalizedLeft === canonical(right);
6988
+ }
6989
+ function wireMoney(value) {
6990
+ return typeof value === "string" ? value.trim() : String(value);
6991
+ }
6992
+ function decimal(value) {
6993
+ return wireMoney(value.toFixed(2));
6994
+ }
6995
+ function paymentOptions(intentId, currency, amount, expiresAt) {
6996
+ const normalizedCurrency = currencyCode(currency);
6997
+ const normalizedAmount = wireMoney(amount);
6998
+ return [
6999
+ {
7000
+ id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.CARD}`,
7001
+ rank: 0,
7002
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.CARD,
7003
+ collection_mode: PAYMENT_COLLECTION_MODE.CREDENTIAL_CHARGE,
7004
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
7005
+ principal_amount: normalizedAmount,
7006
+ fee_amount: wireMoney(ZERO_MONEY),
7007
+ customer_debit_amount: normalizedAmount,
7008
+ currency: normalizedCurrency,
7009
+ status: PAYMENT_OPTION_STATUS.ELIGIBLE,
7010
+ expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
7011
+ expires_at: expiresAt
7012
+ },
7013
+ {
7014
+ id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY}`,
7015
+ rank: 1,
7016
+ method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
7017
+ collection_mode: PAYMENT_COLLECTION_MODE.REQUEST_TO_PAY,
7018
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
7019
+ principal_amount: normalizedAmount,
7020
+ fee_amount: wireMoney(ZERO_MONEY),
7021
+ customer_debit_amount: normalizedAmount,
7022
+ currency: normalizedCurrency,
7023
+ status: PAYMENT_OPTION_STATUS.ELIGIBLE,
7024
+ expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
7025
+ expires_at: expiresAt
7026
+ }
7027
+ ];
7028
+ }
7029
+ function termsForCart(input) {
7030
+ const total = wireMoney(input.total);
7031
+ return {
7032
+ id: `${input.intentId}:terms:${input.version}`,
7033
+ intent_revision: input.revision,
7034
+ terms_version: input.version,
7035
+ terms_digest: `mock:${input.intentId}:${input.revision}:${input.version}`,
7036
+ money: {
7037
+ currency: currencyCode(input.currency),
7038
+ subtotal: wireMoney(input.subtotal),
7039
+ discount_total: wireMoney(input.discount),
7040
+ tax_total: wireMoney(input.tax),
7041
+ delivery_total: wireMoney(input.delivery),
7042
+ statutory_total: wireMoney(ZERO_MONEY),
7043
+ total_obligation: total,
7044
+ due_now: total,
7045
+ rail_amount: total,
7046
+ store_credit_consumed: wireMoney(ZERO_MONEY),
7047
+ voucher_credit_consumed: wireMoney(ZERO_MONEY)
7048
+ },
7049
+ material: {
7050
+ line_digest: `mock-lines:${input.intentId}`,
7051
+ destination_digest: `mock-destination:${input.intentId}`,
7052
+ delivery_promise_digest: `mock-delivery:${input.intentId}`,
7053
+ entitlement_digest: `mock-entitlement:${input.intentId}`,
7054
+ cancellation_terms_digest: `mock-cancellation:${input.intentId}`
7055
+ },
7056
+ compliance: {
7057
+ policy_name: MOCK_PURCHASE_INTENT.COMPLIANCE.POLICY_NAME,
7058
+ policy_version: 1,
7059
+ lane: CHECKOUT_COMPLIANCE_LANE.NATIVE,
7060
+ reason_code: CHECKOUT_COMPLIANCE_REASON.PHYSICAL_GOODS_NATIVE,
7061
+ evidence_digest: `mock-compliance:${input.intentId}`
7062
+ },
7063
+ payment_options: isExactlyZero(input.total) ? [] : paymentOptions(input.intentId, input.currency, input.total, input.validUntil),
7064
+ valid_until: input.validUntil,
7065
+ created_at: input.now
7066
+ };
7067
+ }
7068
+ function intentView(intent) {
7069
+ const {
7070
+ intent_owner: _owner,
7071
+ mint_request: _request,
7072
+ last_confirmation: _confirmation,
7073
+ ...view
7074
+ } = intent;
7075
+ return view;
7076
+ }
7077
+ function purchasingPrincipal(session) {
7078
+ if (!session.is_authenticated) {
7079
+ return { kind: PURCHASING_PRINCIPAL_KIND.GUEST };
7080
+ }
7081
+ if (!session.account_id) throw validation("Authenticated mock session has no account authority");
7082
+ return {
7083
+ kind: PURCHASING_PRINCIPAL_KIND.ACCOUNT,
7084
+ account_id: session.account_id
7085
+ };
7086
+ }
7087
+ function assertSupportedTenderPreference(request) {
7088
+ if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) return;
7089
+ const checkout = request.checkout;
7090
+ if (!checkout) throw validation("Cart checkout input is required");
7091
+ const preference = checkout.tender_preference;
7092
+ if (preference?.method_kind !== PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY || preference.authority.authority_kind !== MOBILE_MONEY_TENDER_AUTHORITY_KIND.TRANSIENT) {
7093
+ return;
7094
+ }
7095
+ if (!PURCHASE_INTENT_MOBILE_MONEY_NETWORKS.has(preference.authority.network)) {
7096
+ throw validation("Mobile Money network is not available for PurchaseIntent checkout");
7097
+ }
7098
+ }
7099
+ function intentOwner(session) {
7100
+ if (!session.is_authenticated) {
7101
+ return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.STOREFRONT_SESSION, id: session.id };
7102
+ }
7103
+ if (!session.account_id) throw validation("Authenticated mock session has no account authority");
7104
+ return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.ACCOUNT, id: session.account_id };
7105
+ }
7106
+ function createPurchaseIntentMockService(options) {
7107
+ const { registry, clock, ids, bus } = options;
7108
+ function stored(session, intentId) {
7109
+ const intent = registry.purchaseIntents.get(intentId);
7110
+ if (!intent) throw notFound(`Purchase intent ${intentId} not found`);
7111
+ const owner = intentOwner(session);
7112
+ if (intent.intent_owner.kind !== owner.kind || intent.intent_owner.id !== owner.id) {
7113
+ throw notFound(`Purchase intent ${intentId} not found`);
7114
+ }
7115
+ return intent;
7116
+ }
7117
+ function completeProviderOrder(intent, order, attemptId) {
7118
+ const updated = {
7119
+ ...order,
7120
+ payment_state: MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID,
7121
+ status: MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED,
7122
+ updated_at: clock.iso()
7123
+ };
7124
+ registry.orders.put(order.id, updated);
7125
+ bus.emit(
7126
+ MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
7127
+ { order_id: order.id, reference: order.payment_reference },
7128
+ order.business_id
7129
+ );
7130
+ bus.emit(MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED, { order_id: order.id }, order.business_id);
7131
+ const completed = {
7132
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
7133
+ intent_id: intent.id,
7134
+ attempt_id: attemptId,
7135
+ order_id: order.id,
7136
+ order_number: order.user_friendly_id,
7137
+ payment_id: order.payment_id
7138
+ };
7139
+ registry.purchaseIntents.put(intent.id, {
7140
+ ...intent,
7141
+ updated_at: clock.iso(),
7142
+ last_confirmation: completed
7143
+ });
7144
+ return completed;
7145
+ }
7146
+ return {
7147
+ mint(session, request) {
7148
+ if (!request.client_intent_key.trim()) throw validation("client_intent_key required");
7149
+ if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
7150
+ throw validation("The mock currently prepares cart purchase intents only");
7151
+ }
7152
+ const checkoutInput = request.checkout;
7153
+ if (!checkoutInput) throw validation("Cart checkout input is required");
7154
+ assertSupportedTenderPreference(request);
7155
+ const cart = registry.carts.get(request.source.cart_id);
7156
+ if (!cart) throw notFound(`Cart ${request.source.cart_id} not found`);
7157
+ if (cart.items.length === 0) {
7158
+ throw validation("Cart is empty", { code: ErrorCode.CART_EMPTY });
7159
+ }
7160
+ const principal = purchasingPrincipal(session);
7161
+ const owner = intentOwner(session);
7162
+ const existing = registry.purchaseIntents.list(
7163
+ (candidate) => candidate.mint_request.client_intent_key === request.client_intent_key && candidate.intent_owner.kind === owner.kind && candidate.intent_owner.id === owner.id
7164
+ )[0];
7165
+ if (existing) return intentView(existing);
7166
+ const now = clock.iso();
7167
+ const expiresAt = new Date(clock.nowMs() + INTENT_LIFETIME_MS).toISOString();
7168
+ const intentId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.INTENT);
7169
+ const terms = termsForCart({
7170
+ intentId,
7171
+ revision: 1,
7172
+ version: 1,
7173
+ currency: cart.currency,
7174
+ subtotal: cart.subtotal,
7175
+ discount: cart.total_discounts,
7176
+ tax: cart.tax_amount,
7177
+ delivery: cart.delivery_fee,
7178
+ total: cart.total_price,
7179
+ now,
7180
+ validUntil: expiresAt
7181
+ });
7182
+ const intent = {
7183
+ id: intentId,
7184
+ seller_business_id: cart.business_id,
7185
+ principal,
7186
+ revision: 1,
7187
+ status: PURCHASE_INTENT_STATUS.OPEN,
7188
+ source: {
7189
+ kind: PURCHASE_INTENT_SOURCE_KIND.CART,
7190
+ source_id: cart.id,
7191
+ source_version: cart.updated_at,
7192
+ seller_business_id: cart.business_id,
7193
+ subject: {
7194
+ kind: PURCHASING_PRINCIPAL_KIND.BUSINESS,
7195
+ business_id: cart.business_id
7196
+ },
7197
+ binding_digest: `mock-cart:${cart.id}:${cart.updated_at}`
7198
+ },
7199
+ lines: cart.items.map((line, ordinal) => ({
7200
+ id: line.id,
7201
+ ordinal,
7202
+ sellable: {
7203
+ kind: line.variant_id ? PURCHASE_INTENT_SELLABLE_KIND.VARIANT : PURCHASE_INTENT_SELLABLE_KIND.PRODUCT,
7204
+ product_id: line.item_id,
7205
+ variant_id: line.variant_id,
7206
+ location_id: checkoutInput.location_id || MOCK_PURCHASE_INTENT.DEFAULT_LOCATION_ID
7207
+ },
7208
+ quantity: line.quantity,
7209
+ configuration: {
7210
+ kind: PURCHASE_INTENT_LINE_CONFIGURATION_KIND.SIMPLE,
7211
+ variant_id: line.variant_id,
7212
+ customer_inputs: line.customer_inputs
7213
+ }
7214
+ })),
7215
+ line_pricing: cart.items.map((line) => {
7216
+ const unitPrice = Number(line.price) + Number(line.add_ons_price);
7217
+ const lineSubtotal = unitPrice * line.quantity - Number(line.item_discount_amount);
7218
+ return {
7219
+ intent_line_id: line.id,
7220
+ quantity: line.quantity,
7221
+ unit_price: decimal(unitPrice),
7222
+ line_subtotal: decimal(lineSubtotal),
7223
+ currency: currencyCode(cart.currency)
7224
+ };
7225
+ }),
7226
+ current_terms: terms,
7227
+ expires_at: expiresAt,
7228
+ created_at: now,
7229
+ updated_at: now,
7230
+ intent_owner: owner,
7231
+ mint_request: request
7232
+ };
7233
+ registry.purchaseIntents.put(intentId, intent);
7234
+ return intentView(intent);
7235
+ },
7236
+ get(session, intentId) {
7237
+ return intentView(stored(session, intentId));
7238
+ },
7239
+ refresh(session, intentId, expectedRevision) {
7240
+ const intent = stored(session, intentId);
7241
+ if (intent.revision !== expectedRevision) {
7242
+ throw validation("expected_intent_revision does not match current intent revision");
7243
+ }
7244
+ if (!intent.current_terms) throw validation("Purchase intent has no current terms");
7245
+ const previous = intent.current_terms;
7246
+ const nextDelivery = Number(previous.money.delivery_total) + 1;
7247
+ const nextTotal = Number(previous.money.total_obligation) + 1;
7248
+ const now = clock.iso();
7249
+ const replacement = termsForCart({
7250
+ intentId,
7251
+ revision: intent.revision,
7252
+ version: previous.terms_version + 1,
7253
+ currency: previous.money.currency,
7254
+ subtotal: String(previous.money.subtotal),
7255
+ discount: String(previous.money.discount_total),
7256
+ tax: String(previous.money.tax_total),
7257
+ delivery: nextDelivery.toFixed(2),
7258
+ total: nextTotal.toFixed(2),
7259
+ now,
7260
+ validUntil: intent.expires_at
7261
+ });
7262
+ const updated = { ...intent, current_terms: replacement, updated_at: now };
7263
+ registry.purchaseIntents.put(intentId, updated);
7264
+ return intentView(updated);
7265
+ },
7266
+ confirm(session, intentId, request) {
7267
+ const intent = stored(session, intentId);
7268
+ if (intent.status === PURCHASE_INTENT_STATUS.CONVERTED && intent.last_confirmation) {
7269
+ const orderId2 = "order_id" in intent.last_confirmation ? intent.last_confirmation.order_id : void 0;
7270
+ const order2 = orderId2 ? registry.orders.get(orderId2) : void 0;
7271
+ if (order2?.payment_state === MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID) {
7272
+ return {
7273
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
7274
+ intent_id: intent.id,
7275
+ attempt_id: "attempt_id" in intent.last_confirmation ? intent.last_confirmation.attempt_id : ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT),
7276
+ order_id: order2.id
7277
+ };
7278
+ }
7279
+ if (order2?.payment_id && intent.last_confirmation.status === PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED) {
7280
+ return completeProviderOrder(intent, order2, intent.last_confirmation.attempt_id);
7281
+ }
7282
+ return intent.last_confirmation;
7283
+ }
7284
+ const terms = intent.current_terms;
7285
+ if (!terms) throw validation("Purchase intent has no current terms");
7286
+ const zeroRail = isExactlyZero(terms.money.rail_amount);
7287
+ const option = request.payment_option_id ? terms.payment_options.find(
7288
+ (candidate) => candidate.id === request.payment_option_id && candidate.status === PAYMENT_OPTION_STATUS.ELIGIBLE
7289
+ ) : void 0;
7290
+ const acceptedBaseTermsChanged = !sameDecimal(request.accepted_due_now, terms.money.due_now) || request.currency !== terms.money.currency;
7291
+ if (request.accepted_intent_revision !== intent.revision || request.accepted_terms_version !== terms.terms_version || acceptedBaseTermsChanged) {
7292
+ const deltas = acceptedBaseTermsChanged ? [
7293
+ {
7294
+ kind: CHECKOUT_TERMS_DELTA_KIND.MONEY,
7295
+ component: CHECKOUT_TERMS_COMPONENT.DUE_NOW,
7296
+ accepted: request.accepted_due_now,
7297
+ replacement: terms.money.due_now
7298
+ }
7299
+ ] : [
7300
+ {
7301
+ kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
7302
+ accepted_option_id: request.payment_option_id ?? null,
7303
+ accepted_customer_debit_amount: request.accepted_customer_debit_amount,
7304
+ replacement_option_id: option?.id ?? null,
7305
+ replacement_customer_debit_amount: option?.customer_debit_amount ?? null
7306
+ }
7307
+ ];
7308
+ return {
7309
+ status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
7310
+ intent_id: intent.id,
7311
+ replacement: terms,
7312
+ deltas
7313
+ };
7314
+ }
7315
+ if (!zeroRail && !option || zeroRail && request.payment_option_id != null) {
7316
+ return {
7317
+ status: PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE,
7318
+ intent_id: intent.id,
7319
+ lines: intent.lines.map((line) => ({
7320
+ line_id: line.id,
7321
+ reason: CHECKOUT_INVALIDATION_REASON.PAYMENT_OPTION_CHANGED
7322
+ }))
7323
+ };
7324
+ }
7325
+ const acceptedCustomerDebitMatches = zeroRail ? isExactlyZero(request.accepted_customer_debit_amount) : Boolean(
7326
+ option && sameDecimal(request.accepted_customer_debit_amount, option.customer_debit_amount)
7327
+ );
7328
+ if (!acceptedCustomerDebitMatches) {
7329
+ return {
7330
+ status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
7331
+ intent_id: intent.id,
7332
+ replacement: terms,
7333
+ deltas: [
7334
+ {
7335
+ kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
7336
+ accepted_option_id: request.payment_option_id ?? null,
7337
+ accepted_customer_debit_amount: request.accepted_customer_debit_amount,
7338
+ replacement_option_id: option?.id ?? null,
7339
+ replacement_customer_debit_amount: option?.customer_debit_amount ?? null
7340
+ }
7341
+ ]
7342
+ };
7343
+ }
7344
+ if (intent.mint_request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
7345
+ throw validation("The mock currently confirms cart purchase intents only");
7346
+ }
7347
+ const checkoutInput = intent.mint_request.checkout;
7348
+ if (!checkoutInput) throw validation("Cart checkout input is required");
7349
+ const cart = registry.carts.get(intent.mint_request.source.cart_id);
7350
+ if (!cart || cart.status !== MOCK_PURCHASE_INTENT.CART_STATUS.ACTIVE) {
7351
+ return { status: PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE, intent_id: intent.id };
7352
+ }
7353
+ const attemptId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT);
7354
+ const orderId = ids.order();
7355
+ const orderNumber = ids.orderNumber();
7356
+ const paymentId = zeroRail ? void 0 : ids.payment();
7357
+ const paymentReference = zeroRail ? void 0 : ids.payment();
7358
+ const needsProvider = option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD || option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY;
7359
+ const paid = zeroRail;
7360
+ const order = {
7361
+ id: orderId,
7362
+ business_id: cart.business_id,
7363
+ user_friendly_id: orderNumber,
7364
+ channel: cart.channel,
7365
+ status: paid ? MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED : MOCK_PURCHASE_INTENT.ORDER_STATUS.PENDING,
7366
+ payment_state: paid ? MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID : MOCK_PURCHASE_INTENT.PAYMENT_STATE.NOT_PAID,
7367
+ order_type: checkoutInput.order_type,
7368
+ customer_id: session.customer_id,
7369
+ customer_name: checkoutInput.customer.name,
7370
+ customer_email: checkoutInput.customer.email,
7371
+ customer_phone: checkoutInput.customer.phone,
7372
+ delivery_address: checkoutInput.address_info.street_address,
7373
+ delivery_fee: cart.delivery_fee,
7374
+ subtotal: cart.subtotal,
7375
+ tax_amount: cart.tax_amount,
7376
+ service_charge: cart.service_charge,
7377
+ total_discounts: cart.total_discounts,
7378
+ total_price: cart.total_price,
7379
+ currency: cart.currency,
7380
+ items: cart.items,
7381
+ applied_discount_ids: cart.applied_discount_ids,
7382
+ applied_discount_codes: cart.applied_discount_codes,
7383
+ payment_id: paymentId,
7384
+ payment_reference: paymentReference,
7385
+ metadata: checkoutInput.metadata,
7386
+ created_at: clock.iso(),
7387
+ updated_at: clock.iso()
6411
7388
  };
7389
+ registry.orders.put(orderId, order);
7390
+ registry.carts.put(cart.id, {
7391
+ ...cart,
7392
+ status: MOCK_PURCHASE_INTENT.CART_STATUS.CONVERTED,
7393
+ updated_at: clock.iso()
7394
+ });
7395
+ bus.emit(
7396
+ MOCK_PURCHASE_INTENT.EVENT.ORDER_CREATED,
7397
+ { order_id: orderId, order_number: orderNumber },
7398
+ cart.business_id
7399
+ );
7400
+ if (paid) {
7401
+ if (paymentReference) {
7402
+ bus.emit(
7403
+ MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
7404
+ { order_id: orderId, reference: paymentReference },
7405
+ cart.business_id
7406
+ );
7407
+ }
7408
+ bus.emit(
7409
+ MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED,
7410
+ { order_id: orderId },
7411
+ cart.business_id
7412
+ );
7413
+ }
7414
+ let confirmation;
7415
+ if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY) {
7416
+ confirmation = {
7417
+ status: PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED,
7418
+ intent_id: intent.id,
7419
+ attempt_id: attemptId,
7420
+ order_id: orderId,
7421
+ authorization_type: AUTHORIZATION_TYPE.OTP,
7422
+ display_text: "Approve on your mobile money app"
7423
+ };
7424
+ } else if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD) {
7425
+ confirmation = {
7426
+ status: PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED,
7427
+ intent_id: intent.id,
7428
+ attempt_id: attemptId,
7429
+ order_id: orderId,
7430
+ provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
7431
+ client_secret: `access_${paymentReference}`,
7432
+ public_key: MOCK_PURCHASE_INTENT.MOCK_PAYSTACK_PUBLIC_KEY
7433
+ };
7434
+ } else {
7435
+ confirmation = {
7436
+ status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
7437
+ intent_id: intent.id,
7438
+ attempt_id: attemptId,
7439
+ order_id: orderId
7440
+ };
7441
+ }
7442
+ if (needsProvider && !paymentId) {
7443
+ throw validation("Provider-backed purchase intent has no payment authority");
7444
+ }
7445
+ registry.purchaseIntents.put(intent.id, {
7446
+ ...intent,
7447
+ status: PURCHASE_INTENT_STATUS.CONVERTED,
7448
+ converted_order_id: orderId,
7449
+ updated_at: clock.iso(),
7450
+ last_confirmation: confirmation
7451
+ });
7452
+ return confirmation;
7453
+ },
7454
+ submitAction(session, intentId, attemptId, request) {
7455
+ const intent = stored(session, intentId);
7456
+ const previous = intent.last_confirmation;
7457
+ if (!previous || previous.status !== PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED || previous.attempt_id !== attemptId) {
7458
+ throw validation("Purchase-intent attempt is not awaiting authorization");
7459
+ }
7460
+ if (!request.authorization) throw validation("authorization required");
7461
+ const order = registry.orders.get(previous.order_id);
7462
+ if (!order || !order.payment_id) {
7463
+ throw notFound(`Purchase-intent attempt ${attemptId} not found`);
7464
+ }
7465
+ return completeProviderOrder(intent, order, attemptId);
6412
7466
  }
6413
7467
  };
6414
7468
  }
@@ -11724,7 +12778,7 @@ var ID_PREFIX = {
11724
12778
  product: "prod_",
11725
12779
  service: "svc_"
11726
12780
  };
11727
- var DEFAULT_CURRENCY = "GHS";
12781
+ var DEFAULT_CURRENCY2 = "GHS";
11728
12782
  function slugify(value) {
11729
12783
  return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
11730
12784
  }
@@ -11759,7 +12813,7 @@ function makeSeedContext(registry) {
11759
12813
  const business = {
11760
12814
  business_type: "other",
11761
12815
  email: `hello@${handle}.test`,
11762
- default_currency: DEFAULT_CURRENCY,
12816
+ default_currency: DEFAULT_CURRENCY2,
11763
12817
  default_offers_table_service: false,
11764
12818
  default_accepts_online_orders: true,
11765
12819
  status: "active",
@@ -11938,7 +12992,8 @@ function createDeps(options = {}) {
11938
12992
  const inventory = createInventoryService({ registry });
11939
12993
  const cart = createCartService({ registry, clock, ids, bus, inventory });
11940
12994
  const orders = createOrderService({ registry, clock, bus });
11941
- const checkout = createCheckoutService({ registry, clock, ids, bus});
12995
+ const payments = createPaymentService({ registry, clock, bus });
12996
+ const purchaseIntents = createPurchaseIntentMockService({ registry, clock, ids, bus });
11942
12997
  const subscriptions = createSubscriptionService({ registry, clock, bus });
11943
12998
  const scheduling = createSchedulingService({ registry, clock, bus });
11944
12999
  const fx = createFxService({ registry, clock, ids });
@@ -11959,7 +13014,8 @@ function createDeps(options = {}) {
11959
13014
  business,
11960
13015
  catalogue,
11961
13016
  cart,
11962
- checkout,
13017
+ payments,
13018
+ purchaseIntents,
11963
13019
  orders,
11964
13020
  subscriptions,
11965
13021
  scheduling,