@cimplify/sdk 0.104.0 → 0.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +3 -7
- package/dist/advanced.d.ts +3 -7
- package/dist/advanced.js +23 -23
- package/dist/advanced.mjs +4 -4
- package/dist/{client-BuLcOWLv.d.ts → catalogue-D1jkpGcr.d.ts} +415 -763
- package/dist/{client-D5AoobFc.d.mts → catalogue-DjutZ0FC.d.mts} +415 -763
- package/dist/{payment-g0HFkEDU.d.mts → checkout-XoV4HNg2.d.mts} +4156 -1498
- package/dist/{payment-g0HFkEDU.d.ts → checkout-XoV4HNg2.d.ts} +4156 -1498
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-OKADBZAI.mjs → chunk-37ZZCE7P.mjs} +808 -574
- package/dist/{chunk-5BDYDZ4U.js → chunk-3IMPJEUI.js} +892 -657
- package/dist/{chunk-YVAEY3VC.js → chunk-7PMMV5X4.js} +1040 -364
- package/dist/{chunk-H3RATH4B.mjs → chunk-AVXA4F6G.js} +293 -2
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-4BKOKHE3.js → chunk-NJYR6GGM.mjs} +244 -19
- package/dist/{chunk-DYIAX3WI.mjs → chunk-NYDVVNZI.mjs} +11 -2
- package/dist/{chunk-E4K2VQDD.js → chunk-PDTXSUHZ.js} +559 -16
- package/dist/{chunk-2WPVUIHX.mjs → chunk-Q5HX7LVE.mjs} +1036 -360
- package/dist/{chunk-5RT4HYMP.mjs → chunk-TINYV5E2.mjs} +542 -15
- package/dist/{chunk-7PDEX54K.js → chunk-UYFTOIFW.js} +13 -3
- package/dist/{chunk-OCCNG6MO.mjs → chunk-X7RPMALQ.mjs} +1310 -722
- package/dist/{chunk-IQFFSKE2.js → chunk-XHVQDEVO.js} +1345 -746
- package/dist/{client-BVNWjk6W.d.ts → client-BBZj15JJ.d.ts} +2 -2
- package/dist/{client-fM716k7G.d.mts → client-CZ8MjJ_o.d.mts} +2 -2
- package/dist/index-CZjNWbQf.d.ts +8 -0
- package/dist/index-DNxWfRHF.d.mts +8 -0
- package/dist/{index-BtcoqrY1.d.ts → index-DRMBo26v.d.mts} +9 -4
- package/dist/{index-D7Tf3v7c.d.mts → index-DweR60Cv.d.ts} +9 -4
- package/dist/index.d.mts +31 -14
- package/dist/index.d.ts +31 -14
- package/dist/index.js +325 -133
- package/dist/index.mjs +6 -6
- package/dist/mock/cli.mjs +1450 -386
- package/dist/mock/library.js +1176 -352
- package/dist/mock/library.mjs +1176 -352
- package/dist/mock/msw.js +1176 -352
- package/dist/mock/msw.mjs +1176 -352
- package/dist/{price-BLIeAb8K.d.ts → price-CUGIYRtD.d.mts} +1 -1
- package/dist/{price-Czq_cTC_.d.mts → price-De-sxcuj.d.ts} +1 -1
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +227 -197
- package/dist/react.mjs +90 -60
- package/dist/{server-DKK_D-PP.d.ts → server-Byr4d0hL.d.mts} +31 -57
- package/dist/{server-BkVhOIwn.d.mts → server-C_RIDnHU.d.ts} +31 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +11 -11
- package/dist/server.mjs +5 -5
- package/dist/styles.css +2 -2
- package/dist/testing/msw.d.mts +2 -2
- package/dist/testing/msw.d.ts +2 -2
- package/dist/testing/msw.js +4 -4
- package/dist/testing/msw.mjs +3 -3
- package/dist/testing/suite.d.mts +5 -6
- package/dist/testing/suite.d.ts +5 -6
- package/dist/testing/suite.js +29 -28
- package/dist/testing/suite.mjs +10 -9
- package/dist/testing.d.mts +2478 -14
- package/dist/testing.d.ts +2478 -14
- package/dist/testing.js +167 -89
- package/dist/testing.mjs +24 -10
- package/dist/utils.d.mts +3 -4
- package/dist/utils.d.ts +3 -4
- package/dist/utils.js +35 -31
- package/dist/utils.mjs +2 -2
- package/package.json +9 -9
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-D9iSVxt5.d.mts +0 -308
- 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.
|
|
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.
|
|
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
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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)
|
|
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)
|
|
615
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
1245
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
994
1246
|
const contentType2 = headers.get("Content-Type");
|
|
995
|
-
|
|
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
|
-
|
|
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.
|
|
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 (
|
|
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.
|
|
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
|
|
1453
|
+
return tryDecodeURIComponent(value);
|
|
1191
1454
|
};
|
|
1192
1455
|
var _getQueryParam = (url, key, multiple) => {
|
|
1193
1456
|
let encoded;
|
|
1194
|
-
if (!multiple && 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.
|
|
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 &&
|
|
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] =
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1852
|
-
if (typeof arg === "object" &&
|
|
1853
|
-
|
|
1854
|
-
for (const [key, value] of
|
|
1855
|
-
if (key
|
|
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
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
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, {
|
|
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.
|
|
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.
|
|
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.
|
|
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}
|
|
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.
|
|
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.
|
|
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,
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
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
|
-
|
|
2456
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2475
|
-
|
|
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;
|
|
2479
2750
|
}
|
|
2480
|
-
node
|
|
2751
|
+
if (node.#index !== void 0) {
|
|
2752
|
+
throw PATH_ERROR;
|
|
2753
|
+
}
|
|
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
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
2532
|
-
|
|
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.
|
|
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
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
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
|
-
|
|
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
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
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
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
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
|
+
}
|
|
2731
2989
|
}
|
|
2990
|
+
const handlerMap = [];
|
|
2991
|
+
for (const i in indexReplacementMap) {
|
|
2992
|
+
handlerMap[i] = handlerData[indexReplacementMap[i]];
|
|
2993
|
+
}
|
|
2994
|
+
return [regexp, handlerMap, staticMap];
|
|
2732
2995
|
}
|
|
2733
2996
|
};
|
|
2734
2997
|
|
|
2735
|
-
// ../node_modules/.bun/hono@4.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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,91 @@ 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_PROTECTION_STATUS = {
|
|
3587
|
+
PROTECTED: "protected"};
|
|
3588
|
+
var CHECKOUT_TERMS_COMPONENT = {
|
|
3589
|
+
DUE_NOW: "due_now"};
|
|
3590
|
+
var CHECKOUT_TERMS_DELTA_KIND = {
|
|
3591
|
+
MONEY: "money",
|
|
3592
|
+
PAYMENT_OPTION: "payment_option"
|
|
3593
|
+
};
|
|
3594
|
+
var CHECKOUT_INVALIDATION_REASON = {
|
|
3595
|
+
PAYMENT_OPTION_CHANGED: "payment_option_changed"
|
|
3596
|
+
};
|
|
3597
|
+
var PURCHASE_INTENT_CONFIRM_STATUS = {
|
|
3598
|
+
ORDER_CREATED: "order_created",
|
|
3599
|
+
CARD_POPUP_REQUIRED: "card_popup_required",
|
|
3600
|
+
AUTHORIZATION_REQUIRED: "authorization_required",
|
|
3601
|
+
TERMS_CHANGED: "terms_changed",
|
|
3602
|
+
SELECTION_UNAVAILABLE: "selection_unavailable",
|
|
3603
|
+
SOURCE_UNAVAILABLE: "source_unavailable"
|
|
3604
|
+
};
|
|
3194
3605
|
|
|
3195
3606
|
// src/mock/routes/v1.ts
|
|
3607
|
+
var PURCHASE_INTENT_ROUTE = {
|
|
3608
|
+
COLLECTION: "/purchase-intents",
|
|
3609
|
+
MEMBER: "/purchase-intents/:intent_id",
|
|
3610
|
+
REFRESH: "/purchase-intents/:intent_id/refresh",
|
|
3611
|
+
CONFIRM: "/purchase-intents/:intent_id/confirm",
|
|
3612
|
+
ACTION: "/purchase-intents/:intent_id/attempts/:attempt_id/actions"
|
|
3613
|
+
};
|
|
3614
|
+
var PURCHASE_INTENT_ROUTE_PARAM = {
|
|
3615
|
+
INTENT_ID: "intent_id",
|
|
3616
|
+
ATTEMPT_ID: "attempt_id"
|
|
3617
|
+
};
|
|
3618
|
+
var PAYMENT_ROUTE = {
|
|
3619
|
+
COLLECTION_OPTIONS: "/payments/collection-options",
|
|
3620
|
+
AUTHORIZATION: "/payments/authorization"
|
|
3621
|
+
};
|
|
3196
3622
|
function buildV1(deps) {
|
|
3197
3623
|
const app = new Hono2();
|
|
3198
3624
|
app.use("*", chaosMiddleware(deps));
|
|
@@ -3565,28 +3991,81 @@ function buildV1(deps) {
|
|
|
3565
3991
|
"/subscriptions/:subscription_id/skip",
|
|
3566
3992
|
(c) => tryRoute(c, () => deps.subscriptions.skipNext(c.req.param("subscription_id")))
|
|
3567
3993
|
);
|
|
3568
|
-
app.get(
|
|
3994
|
+
app.get(PAYMENT_ROUTE.COLLECTION_OPTIONS, async (c) => {
|
|
3569
3995
|
const currency = c.req.query("currency")?.trim().toUpperCase();
|
|
3570
3996
|
if (!currency || !/^[A-Z]{3}$/.test(currency)) {
|
|
3571
3997
|
return fail(c, validation("currency must be a canonical three-letter code"));
|
|
3572
3998
|
}
|
|
3573
3999
|
return tryRoute(c, () => ({
|
|
3574
4000
|
presentment_currency: currency,
|
|
3575
|
-
options: currency ===
|
|
4001
|
+
options: currency === DEFAULT_CURRENCY ? [
|
|
3576
4002
|
{ method: PAYMENT_METHOD.CARD, network: null },
|
|
3577
|
-
{
|
|
3578
|
-
|
|
3579
|
-
|
|
4003
|
+
{
|
|
4004
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
4005
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.MTN
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
4009
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.TELECEL
|
|
4010
|
+
},
|
|
4011
|
+
{
|
|
4012
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
4013
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.AIRTEL
|
|
4014
|
+
}
|
|
3580
4015
|
] : currency === "USD" ? [{ method: PAYMENT_METHOD.CARD, network: null }] : []
|
|
3581
4016
|
}));
|
|
3582
4017
|
});
|
|
3583
|
-
app.post(
|
|
4018
|
+
app.post(PURCHASE_INTENT_ROUTE.COLLECTION, async (c) => {
|
|
3584
4019
|
const body = await c.req.json().catch(() => ({}));
|
|
3585
|
-
return tryRoute(c, () => deps.
|
|
4020
|
+
return tryRoute(c, () => deps.purchaseIntents.mint(c.var.session, body));
|
|
3586
4021
|
});
|
|
3587
|
-
app.
|
|
4022
|
+
app.get(
|
|
4023
|
+
PURCHASE_INTENT_ROUTE.MEMBER,
|
|
4024
|
+
(c) => tryRoute(
|
|
4025
|
+
c,
|
|
4026
|
+
() => deps.purchaseIntents.get(
|
|
4027
|
+
c.var.session,
|
|
4028
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID)
|
|
4029
|
+
)
|
|
4030
|
+
)
|
|
4031
|
+
);
|
|
4032
|
+
app.post(PURCHASE_INTENT_ROUTE.REFRESH, async (c) => {
|
|
3588
4033
|
const body = await c.req.json().catch(() => ({}));
|
|
3589
|
-
return tryRoute(
|
|
4034
|
+
return tryRoute(
|
|
4035
|
+
c,
|
|
4036
|
+
() => deps.purchaseIntents.refresh(
|
|
4037
|
+
c.var.session,
|
|
4038
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
4039
|
+
body.expected_intent_revision
|
|
4040
|
+
)
|
|
4041
|
+
);
|
|
4042
|
+
});
|
|
4043
|
+
app.post(PURCHASE_INTENT_ROUTE.CONFIRM, async (c) => {
|
|
4044
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4045
|
+
return tryRoute(
|
|
4046
|
+
c,
|
|
4047
|
+
() => deps.purchaseIntents.confirm(
|
|
4048
|
+
c.var.session,
|
|
4049
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
4050
|
+
body
|
|
4051
|
+
)
|
|
4052
|
+
);
|
|
4053
|
+
});
|
|
4054
|
+
app.post(PURCHASE_INTENT_ROUTE.ACTION, async (c) => {
|
|
4055
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4056
|
+
return tryRoute(
|
|
4057
|
+
c,
|
|
4058
|
+
() => deps.purchaseIntents.submitAction(
|
|
4059
|
+
c.var.session,
|
|
4060
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
4061
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.ATTEMPT_ID),
|
|
4062
|
+
body
|
|
4063
|
+
)
|
|
4064
|
+
);
|
|
4065
|
+
});
|
|
4066
|
+
app.post(PAYMENT_ROUTE.AUTHORIZATION, async (c) => {
|
|
4067
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4068
|
+
return tryRoute(c, () => deps.payments.authorize(body));
|
|
3590
4069
|
});
|
|
3591
4070
|
app.get("/auth/status", (c) => tryRoute(c, () => deps.auth.status(c.var.session.token)));
|
|
3592
4071
|
app.post("/auth/request-otp", async (c) => {
|
|
@@ -3892,6 +4371,7 @@ function createStateRegistry() {
|
|
|
3892
4371
|
collections: createMemoryStore(),
|
|
3893
4372
|
carts: createMemoryStore(),
|
|
3894
4373
|
orders: createMemoryStore(),
|
|
4374
|
+
purchaseIntents: createMemoryStore(),
|
|
3895
4375
|
subscriptions: createMemoryStore(),
|
|
3896
4376
|
bookings: createMemoryStore(),
|
|
3897
4377
|
services: createMemoryStore(),
|
|
@@ -4041,9 +4521,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4041
4521
|
// injected into the HTML page served by the mock. Edit this file directly \u2014
|
|
4042
4522
|
// type-check via tsgo + the IframeToParentMessage shape from the SDK.
|
|
4043
4523
|
(function () {
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4524
|
+
const TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
|
|
4525
|
+
const DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
|
|
4526
|
+
const MSG = {
|
|
4047
4527
|
INIT: "init", SET_TOKEN: "set_token", SET_CART: "set_cart", GET_DATA: "get_data",
|
|
4048
4528
|
REFRESH_TOKEN: "refresh_token", LOGOUT: "logout",
|
|
4049
4529
|
PROCESS_CHECKOUT: "process_checkout", ABORT_CHECKOUT: "abort_checkout",
|
|
@@ -4056,12 +4536,36 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4056
4536
|
CHECKOUT_STATUS: "checkout_status", CHECKOUT_COMPLETE: "checkout_complete",
|
|
4057
4537
|
ORDER_TYPE_CHANGED: "order_type_changed", REQUEST_SUBMIT: "request_submit"
|
|
4058
4538
|
};
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4539
|
+
const PURCHASE_INTENT = {
|
|
4540
|
+
PATH: "/api/v1/purchase-intents",
|
|
4541
|
+
ORDER_CREATED: "order_created",
|
|
4542
|
+
CARD_POPUP_REQUIRED: "card_popup_required",
|
|
4543
|
+
AUTHORIZATION_REQUIRED: "authorization_required",
|
|
4544
|
+
ELIGIBLE: "eligible"
|
|
4545
|
+
};
|
|
4546
|
+
const CHECKOUT_STATUS = {
|
|
4547
|
+
PROCESSING: "processing",
|
|
4548
|
+
SUCCESS: "success",
|
|
4549
|
+
FAILED: "failed"
|
|
4550
|
+
};
|
|
4551
|
+
const CHECKOUT_ERROR = { FAILED: "CHECKOUT_FAILED" };
|
|
4552
|
+
const ORDER_STATUS = { CONFIRMED: "confirmed" };
|
|
4553
|
+
const TENDER = {
|
|
4554
|
+
CARD: "card",
|
|
4555
|
+
MOBILE_MONEY: "mobile_money",
|
|
4556
|
+
AUTHORITY_TRANSIENT: "transient",
|
|
4557
|
+
MTN: "mtn"
|
|
4558
|
+
};
|
|
4559
|
+
const PAYMENT_PROVIDER = { PAYSTACK: "paystack" };
|
|
4560
|
+
const SOURCE_KIND = { CART: "cart" };
|
|
4561
|
+
const AUTHORIZATION = { OTP: "otp" };
|
|
4562
|
+
const EXACT_ZERO_MONEY = /^[+-]?0+(?:\\.0+)?(?:[eE][+-]?\\d+)?$/;
|
|
4563
|
+
const qs = new URLSearchParams(location.search);
|
|
4564
|
+
const AUTO = qs.get("auto") === "true";
|
|
4565
|
+
const DEMO_OTP = qs.get("otp") || "123456";
|
|
4566
|
+
const ORIGIN = qs.get("origin") || "*";
|
|
4567
|
+
const NONCE = qs.get("nonce");
|
|
4568
|
+
const state = {
|
|
4065
4569
|
businessId: DEFAULT_BUSINESS,
|
|
4066
4570
|
token: null,
|
|
4067
4571
|
cart: null,
|
|
@@ -4072,15 +4576,33 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4072
4576
|
contact: null,
|
|
4073
4577
|
pendingOtp: null
|
|
4074
4578
|
};
|
|
4075
|
-
|
|
4076
|
-
|
|
4579
|
+
const content = document.getElementById("content");
|
|
4580
|
+
const title = document.getElementById("title");
|
|
4077
4581
|
|
|
4078
4582
|
function send(msg) {
|
|
4079
4583
|
if (NONCE) msg.nonce = NONCE;
|
|
4080
4584
|
parent.postMessage(msg, ORIGIN);
|
|
4081
4585
|
}
|
|
4586
|
+
function isExactlyZeroMoney(value) {
|
|
4587
|
+
return typeof value === "number"
|
|
4588
|
+
? Object.is(value, 0) || Object.is(value, -0)
|
|
4589
|
+
: EXACT_ZERO_MONEY.test(String(value).trim());
|
|
4590
|
+
}
|
|
4082
4591
|
function reportHeight() { send({ type: MSG.HEIGHT_CHANGE, height: document.documentElement.scrollHeight }); }
|
|
4083
4592
|
function ready() { send({ type: MSG.READY, height: document.documentElement.scrollHeight }); }
|
|
4593
|
+
function responseWithBody(response) {
|
|
4594
|
+
return response.json().then(function (body) {
|
|
4595
|
+
return { status: response.status, body: body };
|
|
4596
|
+
});
|
|
4597
|
+
}
|
|
4598
|
+
function assertSuccessfulResponse(response, fallbackMessage) {
|
|
4599
|
+
if (response.status >= 400) {
|
|
4600
|
+
throw new Error(
|
|
4601
|
+
(response.body && response.body.error && response.body.error.message) || fallbackMessage
|
|
4602
|
+
);
|
|
4603
|
+
}
|
|
4604
|
+
return response.body;
|
|
4605
|
+
}
|
|
4084
4606
|
|
|
4085
4607
|
function defaultAddress() {
|
|
4086
4608
|
return {
|
|
@@ -4090,9 +4612,20 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4090
4612
|
};
|
|
4091
4613
|
}
|
|
4092
4614
|
function defaultPayment(type) {
|
|
4093
|
-
if (type ===
|
|
4094
|
-
|
|
4095
|
-
|
|
4615
|
+
if (type === TENDER.CARD) {
|
|
4616
|
+
return {
|
|
4617
|
+
type: TENDER.CARD,
|
|
4618
|
+
provider: PAYMENT_PROVIDER.PAYSTACK,
|
|
4619
|
+
last_four: "4242",
|
|
4620
|
+
label: "Visa \u2022\u2022\u2022\u2022 4242"
|
|
4621
|
+
};
|
|
4622
|
+
}
|
|
4623
|
+
return {
|
|
4624
|
+
type: TENDER.MOBILE_MONEY,
|
|
4625
|
+
provider: TENDER.MTN,
|
|
4626
|
+
phone_number: "+233244000000",
|
|
4627
|
+
label: "MTN Mobile Money"
|
|
4628
|
+
};
|
|
4096
4629
|
}
|
|
4097
4630
|
|
|
4098
4631
|
function renderAuth() {
|
|
@@ -4117,12 +4650,12 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4117
4650
|
' <div class="option" data-key="lagos">5 Allen Ave, Ikeja, Lagos</div>' +
|
|
4118
4651
|
'</div>' +
|
|
4119
4652
|
'<button id="confirm">Use this address</button>';
|
|
4120
|
-
|
|
4653
|
+
const options = {
|
|
4121
4654
|
"default": defaultAddress(),
|
|
4122
4655
|
"east-legon": { street_address: "8 Ring Road", city: "Accra", region: "Greater Accra", country: "GH", latitude: 5.65, longitude: -0.16 },
|
|
4123
4656
|
"lagos": { street_address: "5 Allen Avenue", city: "Lagos", region: "Lagos", country: "NG", latitude: 6.5244, longitude: 3.3792 }
|
|
4124
4657
|
};
|
|
4125
|
-
|
|
4658
|
+
let picked = "default";
|
|
4126
4659
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
4127
4660
|
el.onclick = function () {
|
|
4128
4661
|
picked = el.getAttribute("data-key");
|
|
@@ -4145,10 +4678,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4145
4678
|
'<div class="options" id="pay-options">' +
|
|
4146
4679
|
' <div class="option selected" data-method="mobile_money">\u{1F4F1} MTN Mobile Money</div>' +
|
|
4147
4680
|
' <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
4681
|
'</div>' +
|
|
4150
4682
|
'<button id="confirm">Use this method</button>';
|
|
4151
|
-
|
|
4683
|
+
let picked = TENDER.MOBILE_MONEY;
|
|
4152
4684
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
4153
4685
|
el.onclick = function () {
|
|
4154
4686
|
picked = el.getAttribute("data-method");
|
|
@@ -4192,7 +4724,6 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4192
4724
|
'</div>' +
|
|
4193
4725
|
'<label>Payment method</label>' +
|
|
4194
4726
|
'<select id="pay-method">' +
|
|
4195
|
-
' <option value="cash">Cash</option>' +
|
|
4196
4727
|
' <option value="mobile_money">Mobile Money (MTN)</option>' +
|
|
4197
4728
|
' <option value="card">Card (Paystack)</option>' +
|
|
4198
4729
|
'</select>' +
|
|
@@ -4209,65 +4740,139 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4209
4740
|
}
|
|
4210
4741
|
|
|
4211
4742
|
function runCheckout() {
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4743
|
+
const statusEl = document.getElementById("status");
|
|
4744
|
+
const errEl = document.getElementById("err");
|
|
4745
|
+
const resultEl = document.getElementById("result");
|
|
4215
4746
|
if (errEl) errEl.textContent = "";
|
|
4216
4747
|
if (!state.cart) { errEl.textContent = "No cart provided"; return; }
|
|
4217
|
-
|
|
4748
|
+
const customer = {
|
|
4218
4749
|
name: document.getElementById("cust-name").value.trim() || "Customer",
|
|
4219
4750
|
email: document.getElementById("cust-email").value.trim() || "noemail@example.com",
|
|
4220
4751
|
phone: document.getElementById("cust-phone").value.trim() || "+233000000000",
|
|
4221
4752
|
save_details: false
|
|
4222
4753
|
};
|
|
4223
|
-
|
|
4224
|
-
|
|
4754
|
+
const paymentMethod = document.getElementById("pay-method").value;
|
|
4755
|
+
const orderType = document.getElementById("order-type").value;
|
|
4225
4756
|
statusEl.textContent = "Processing\u2026";
|
|
4226
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
4757
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.PROCESSING, context: {} });
|
|
4227
4758
|
|
|
4228
|
-
|
|
4759
|
+
const headers = { "content-type": "application/json" };
|
|
4229
4760
|
if (state.token) headers["x-session-token"] = state.token;
|
|
4230
4761
|
|
|
4231
|
-
|
|
4762
|
+
const preference = paymentMethod === TENDER.MOBILE_MONEY
|
|
4763
|
+
? {
|
|
4764
|
+
method_kind: TENDER.MOBILE_MONEY,
|
|
4765
|
+
authority: {
|
|
4766
|
+
authority_kind: TENDER.AUTHORITY_TRANSIENT,
|
|
4767
|
+
phone_number: customer.phone,
|
|
4768
|
+
network: TENDER.MTN
|
|
4769
|
+
}
|
|
4770
|
+
}
|
|
4771
|
+
: { method_kind: TENDER.CARD };
|
|
4772
|
+
const intentKey = "mock-element:" + state.cart.id;
|
|
4773
|
+
fetch(PURCHASE_INTENT.PATH, {
|
|
4232
4774
|
method: "POST",
|
|
4233
4775
|
headers: headers,
|
|
4234
4776
|
body: JSON.stringify({
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4777
|
+
client_intent_key: intentKey,
|
|
4778
|
+
source: { kind: SOURCE_KIND.CART, cart_id: state.cart.id },
|
|
4779
|
+
checkout: {
|
|
4780
|
+
customer: customer,
|
|
4781
|
+
order_type: orderType,
|
|
4782
|
+
address_info: state.address || {},
|
|
4783
|
+
tender_preference: preference
|
|
4784
|
+
}
|
|
4240
4785
|
})
|
|
4241
4786
|
})
|
|
4242
|
-
.then(
|
|
4787
|
+
.then(responseWithBody)
|
|
4243
4788
|
.then(function (resp) {
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4789
|
+
const intent = assertSuccessfulResponse(resp, "Intent creation failed");
|
|
4790
|
+
const terms = intent.current_terms;
|
|
4791
|
+
const option = terms && terms.payment_options && terms.payment_options.filter(function (candidate) {
|
|
4792
|
+
return candidate.status === PURCHASE_INTENT.ELIGIBLE && candidate.method_kind === preference.method_kind;
|
|
4793
|
+
})[0];
|
|
4794
|
+
if (!terms || (!option && !isExactlyZeroMoney(terms.money.rail_amount))) {
|
|
4795
|
+
throw new Error("No eligible payment option");
|
|
4796
|
+
}
|
|
4797
|
+
const confirmationPath = PURCHASE_INTENT.PATH + "/" + encodeURIComponent(intent.id) + "/confirm";
|
|
4798
|
+
const confirmationBody = JSON.stringify({
|
|
4799
|
+
accepted_intent_revision: terms.intent_revision,
|
|
4800
|
+
accepted_terms_version: terms.terms_version,
|
|
4801
|
+
accepted_due_now: terms.money.due_now,
|
|
4802
|
+
accepted_customer_debit_amount: option
|
|
4803
|
+
? option.customer_debit_amount
|
|
4804
|
+
: terms.money.rail_amount,
|
|
4805
|
+
currency: terms.money.currency,
|
|
4806
|
+
payment_option_id: option ? option.id : null,
|
|
4807
|
+
idempotency_key: "mock-confirm:" + intent.id
|
|
4808
|
+
});
|
|
4809
|
+
function confirmIntent() {
|
|
4810
|
+
return fetch(confirmationPath, {
|
|
4811
|
+
method: "POST",
|
|
4812
|
+
headers: headers,
|
|
4813
|
+
body: confirmationBody
|
|
4814
|
+
})
|
|
4815
|
+
.then(responseWithBody)
|
|
4816
|
+
.then(function (response) {
|
|
4817
|
+
return assertSuccessfulResponse(response, "Intent confirmation failed");
|
|
4818
|
+
});
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
return confirmIntent().then(function (outcome) {
|
|
4822
|
+
// This mock has no external provider window. Replaying the exact
|
|
4823
|
+
// idempotent confirmation models the provider-approved callback.
|
|
4824
|
+
return outcome.status === PURCHASE_INTENT.CARD_POPUP_REQUIRED
|
|
4825
|
+
? confirmIntent()
|
|
4826
|
+
: outcome;
|
|
4827
|
+
});
|
|
4828
|
+
})
|
|
4829
|
+
.then(function (outcome) {
|
|
4830
|
+
if (outcome.status !== PURCHASE_INTENT.AUTHORIZATION_REQUIRED) return outcome;
|
|
4831
|
+
return fetch(
|
|
4832
|
+
PURCHASE_INTENT.PATH + "/" + encodeURIComponent(outcome.intent_id) +
|
|
4833
|
+
"/attempts/" + encodeURIComponent(outcome.attempt_id) + "/actions",
|
|
4834
|
+
{
|
|
4835
|
+
method: "POST",
|
|
4836
|
+
headers: headers,
|
|
4837
|
+
body: JSON.stringify({ authorization: { type: AUTHORIZATION.OTP, value: DEMO_OTP } })
|
|
4838
|
+
}
|
|
4839
|
+
)
|
|
4840
|
+
.then(responseWithBody)
|
|
4841
|
+
.then(function (response) {
|
|
4842
|
+
return assertSuccessfulResponse(response, "Authorization failed");
|
|
4843
|
+
});
|
|
4844
|
+
})
|
|
4845
|
+
.then(function (outcome) {
|
|
4846
|
+
if (outcome.status !== PURCHASE_INTENT.ORDER_CREATED) {
|
|
4847
|
+
throw new Error("Checkout requires " + outcome.status.replace(/_/g, " "));
|
|
4250
4848
|
}
|
|
4251
|
-
|
|
4252
|
-
|
|
4849
|
+
send({
|
|
4850
|
+
type: MSG.CHECKOUT_STATUS,
|
|
4851
|
+
status: CHECKOUT_STATUS.SUCCESS,
|
|
4852
|
+
context: { order_id: outcome.order_id }
|
|
4853
|
+
});
|
|
4253
4854
|
send({
|
|
4254
4855
|
type: MSG.CHECKOUT_COMPLETE,
|
|
4255
4856
|
success: true,
|
|
4256
4857
|
order: {
|
|
4257
|
-
id:
|
|
4258
|
-
order_number:
|
|
4259
|
-
status:
|
|
4858
|
+
id: outcome.order_id,
|
|
4859
|
+
order_number: outcome.order_number || outcome.order_id,
|
|
4860
|
+
status: ORDER_STATUS.CONFIRMED,
|
|
4260
4861
|
total: (state.cart && state.cart.total_price) || "0.00",
|
|
4261
4862
|
currency: (state.cart && state.cart.currency) || "GHS"
|
|
4262
4863
|
}
|
|
4263
4864
|
});
|
|
4264
|
-
statusEl.textContent = "Success \u2014 order " +
|
|
4865
|
+
statusEl.textContent = "Success \u2014 order " + outcome.order_id;
|
|
4265
4866
|
resultEl.style.display = "block";
|
|
4266
|
-
resultEl.textContent = JSON.stringify(
|
|
4867
|
+
resultEl.textContent = JSON.stringify(outcome, null, 2);
|
|
4267
4868
|
})
|
|
4268
4869
|
.catch(function (e) {
|
|
4269
|
-
send({
|
|
4270
|
-
|
|
4870
|
+
send({
|
|
4871
|
+
type: MSG.CHECKOUT_COMPLETE,
|
|
4872
|
+
success: false,
|
|
4873
|
+
error: { code: CHECKOUT_ERROR.FAILED, message: String(e), recoverable: true }
|
|
4874
|
+
});
|
|
4875
|
+
statusEl.textContent = "Failed";
|
|
4271
4876
|
errEl.textContent = String(e);
|
|
4272
4877
|
});
|
|
4273
4878
|
}
|
|
@@ -4284,7 +4889,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4284
4889
|
break;
|
|
4285
4890
|
case MSG.SET_CART:
|
|
4286
4891
|
state.cart = msg.cart;
|
|
4287
|
-
|
|
4892
|
+
const s = document.getElementById("status");
|
|
4288
4893
|
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
4894
|
reportHeight();
|
|
4290
4895
|
break;
|
|
@@ -4308,7 +4913,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
4308
4913
|
}
|
|
4309
4914
|
break;
|
|
4310
4915
|
case MSG.ABORT_CHECKOUT:
|
|
4311
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
4916
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.FAILED, context: {} });
|
|
4312
4917
|
break;
|
|
4313
4918
|
}
|
|
4314
4919
|
}
|
|
@@ -4922,6 +5527,7 @@ function createAuthService(opts) {
|
|
|
4922
5527
|
token: id,
|
|
4923
5528
|
business_id: options.defaultBusinessId,
|
|
4924
5529
|
is_authenticated: authenticated,
|
|
5530
|
+
account_id: authenticated ? ids.raw("account") : void 0,
|
|
4925
5531
|
customer_id: authenticated ? ids.customer() : void 0,
|
|
4926
5532
|
customer_name: profile?.name,
|
|
4927
5533
|
customer_email: profile?.email,
|
|
@@ -5004,7 +5610,7 @@ function createAuthService(opts) {
|
|
|
5004
5610
|
if (!s) return { is_authenticated: false, user_type: "guest" };
|
|
5005
5611
|
return {
|
|
5006
5612
|
is_authenticated: s.is_authenticated,
|
|
5007
|
-
account_id: s.
|
|
5613
|
+
account_id: s.account_id,
|
|
5008
5614
|
user_type: s.is_authenticated ? "customer" : "guest",
|
|
5009
5615
|
session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
|
|
5010
5616
|
session: s
|
|
@@ -6302,88 +6908,10 @@ function createCartService(opts) {
|
|
|
6302
6908
|
};
|
|
6303
6909
|
}
|
|
6304
6910
|
|
|
6305
|
-
// src/mock/domain/
|
|
6306
|
-
function
|
|
6307
|
-
const { registry, clock,
|
|
6911
|
+
// src/mock/domain/payment.ts
|
|
6912
|
+
function createPaymentService(options) {
|
|
6913
|
+
const { registry, clock, bus } = options;
|
|
6308
6914
|
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
6915
|
authorize(body) {
|
|
6388
6916
|
if (!body.payment_id?.trim()) throw validation("payment_id required");
|
|
6389
6917
|
if (!body.authorization) throw validation("authorization required");
|
|
@@ -6394,7 +6922,7 @@ function createCheckoutService(opts) {
|
|
|
6394
6922
|
const updated = {
|
|
6395
6923
|
...order,
|
|
6396
6924
|
payment_state: "paid",
|
|
6397
|
-
status:
|
|
6925
|
+
status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
|
|
6398
6926
|
updated_at: clock.iso()
|
|
6399
6927
|
};
|
|
6400
6928
|
registry.orders.put(order.id, updated);
|
|
@@ -6405,10 +6933,544 @@ function createCheckoutService(opts) {
|
|
|
6405
6933
|
order_number: order.user_friendly_id,
|
|
6406
6934
|
payment_id: body.payment_id,
|
|
6407
6935
|
payment_reference: order.payment_reference,
|
|
6408
|
-
payment_status:
|
|
6936
|
+
payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
|
|
6409
6937
|
requires_authorization: false,
|
|
6410
|
-
next_action: { type:
|
|
6938
|
+
next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
|
|
6939
|
+
};
|
|
6940
|
+
}
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
|
|
6944
|
+
// src/mock/domain/purchase-intent.ts
|
|
6945
|
+
var INTENT_LIFETIME_MS = 15 * 60 * 1e3;
|
|
6946
|
+
var ZERO_MONEY = "0.00";
|
|
6947
|
+
var MOCK_PROTECTION = {
|
|
6948
|
+
status: CHECKOUT_PROTECTION_STATUS.PROTECTED,
|
|
6949
|
+
message: "Covered by Cimplify Protection"
|
|
6950
|
+
};
|
|
6951
|
+
var PURCHASE_INTENT_MOBILE_MONEY_NETWORKS = new Set(
|
|
6952
|
+
Object.values(PURCHASE_INTENT_MOBILE_MONEY_NETWORK)
|
|
6953
|
+
);
|
|
6954
|
+
var MOCK_PURCHASE_INTENT = {
|
|
6955
|
+
OWNER_KIND: { ACCOUNT: "account", STOREFRONT_SESSION: "storefront_session" },
|
|
6956
|
+
CART_STATUS: { ACTIVE: "active", CONVERTED: "converted" },
|
|
6957
|
+
ORDER_STATUS: { PENDING: "pending", CONFIRMED: "confirmed" },
|
|
6958
|
+
PAYMENT_STATE: { NOT_PAID: "not_paid", PAID: "paid" },
|
|
6959
|
+
EVENT: {
|
|
6960
|
+
ORDER_CREATED: "order.created",
|
|
6961
|
+
PAYMENT_CAPTURED: "order.payment.captured",
|
|
6962
|
+
ORDER_CONFIRMED: "order.confirmed"
|
|
6963
|
+
},
|
|
6964
|
+
ID_PREFIX: { INTENT: "pi", ATTEMPT: "attempt" },
|
|
6965
|
+
DEFAULT_LOCATION_ID: "mock-location",
|
|
6966
|
+
MOCK_PAYSTACK_PUBLIC_KEY: "pk_test_mock_paystack",
|
|
6967
|
+
COMPLIANCE: {
|
|
6968
|
+
POLICY_NAME: "mock-storefront-commerce"
|
|
6969
|
+
}
|
|
6970
|
+
};
|
|
6971
|
+
var DECIMAL_WIRE_VALUE = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
|
|
6972
|
+
function isExactlyZero(value) {
|
|
6973
|
+
if (typeof value === "number") return Object.is(value, 0) || Object.is(value, -0);
|
|
6974
|
+
const match2 = DECIMAL_WIRE_VALUE.exec(value.trim());
|
|
6975
|
+
return Boolean(match2 && [...match2[2], ...match2[3] ?? ""].every((digit) => digit === "0"));
|
|
6976
|
+
}
|
|
6977
|
+
function sameDecimal(left, right) {
|
|
6978
|
+
const canonical = (value) => {
|
|
6979
|
+
const wire = typeof value === "number" ? String(value) : value.trim();
|
|
6980
|
+
const match2 = DECIMAL_WIRE_VALUE.exec(wire);
|
|
6981
|
+
if (!match2) return null;
|
|
6982
|
+
let digits = `${match2[2]}${match2[3] ?? ""}`.replace(/^0+/, "");
|
|
6983
|
+
if (!digits) return "0";
|
|
6984
|
+
let scale = BigInt((match2[3] ?? "").length) - BigInt(match2[4] ?? "0");
|
|
6985
|
+
while (digits.endsWith("0")) {
|
|
6986
|
+
digits = digits.slice(0, -1);
|
|
6987
|
+
scale -= 1n;
|
|
6988
|
+
}
|
|
6989
|
+
const sign = match2[1] === "-" ? "-" : "";
|
|
6990
|
+
return `${sign}${digits}:${scale}`;
|
|
6991
|
+
};
|
|
6992
|
+
const normalizedLeft = canonical(left);
|
|
6993
|
+
return normalizedLeft !== null && normalizedLeft === canonical(right);
|
|
6994
|
+
}
|
|
6995
|
+
function wireMoney(value) {
|
|
6996
|
+
return typeof value === "string" ? value.trim() : String(value);
|
|
6997
|
+
}
|
|
6998
|
+
function decimal(value) {
|
|
6999
|
+
return wireMoney(value.toFixed(2));
|
|
7000
|
+
}
|
|
7001
|
+
function paymentOptions(intentId, currency, amount, expiresAt) {
|
|
7002
|
+
const normalizedCurrency = currencyCode(currency);
|
|
7003
|
+
const normalizedAmount = wireMoney(amount);
|
|
7004
|
+
return [
|
|
7005
|
+
{
|
|
7006
|
+
id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.CARD}`,
|
|
7007
|
+
rank: 0,
|
|
7008
|
+
method_kind: PURCHASE_INTENT_PAYMENT_METHOD.CARD,
|
|
7009
|
+
collection_mode: PAYMENT_COLLECTION_MODE.CREDENTIAL_CHARGE,
|
|
7010
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
7011
|
+
principal_amount: normalizedAmount,
|
|
7012
|
+
fee_amount: wireMoney(ZERO_MONEY),
|
|
7013
|
+
customer_debit_amount: normalizedAmount,
|
|
7014
|
+
currency: normalizedCurrency,
|
|
7015
|
+
protection: MOCK_PROTECTION,
|
|
7016
|
+
status: PAYMENT_OPTION_STATUS.ELIGIBLE,
|
|
7017
|
+
expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
|
|
7018
|
+
expires_at: expiresAt
|
|
7019
|
+
},
|
|
7020
|
+
{
|
|
7021
|
+
id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY}`,
|
|
7022
|
+
rank: 1,
|
|
7023
|
+
method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
|
|
7024
|
+
collection_mode: PAYMENT_COLLECTION_MODE.REQUEST_TO_PAY,
|
|
7025
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
7026
|
+
principal_amount: normalizedAmount,
|
|
7027
|
+
fee_amount: wireMoney(ZERO_MONEY),
|
|
7028
|
+
customer_debit_amount: normalizedAmount,
|
|
7029
|
+
currency: normalizedCurrency,
|
|
7030
|
+
protection: MOCK_PROTECTION,
|
|
7031
|
+
status: PAYMENT_OPTION_STATUS.ELIGIBLE,
|
|
7032
|
+
expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
|
|
7033
|
+
expires_at: expiresAt
|
|
7034
|
+
}
|
|
7035
|
+
];
|
|
7036
|
+
}
|
|
7037
|
+
function termsForCart(input) {
|
|
7038
|
+
const total = wireMoney(input.total);
|
|
7039
|
+
return {
|
|
7040
|
+
id: `${input.intentId}:terms:${input.version}`,
|
|
7041
|
+
intent_revision: input.revision,
|
|
7042
|
+
terms_version: input.version,
|
|
7043
|
+
terms_digest: `mock:${input.intentId}:${input.revision}:${input.version}`,
|
|
7044
|
+
money: {
|
|
7045
|
+
currency: currencyCode(input.currency),
|
|
7046
|
+
subtotal: wireMoney(input.subtotal),
|
|
7047
|
+
discount_total: wireMoney(input.discount),
|
|
7048
|
+
tax_total: wireMoney(input.tax),
|
|
7049
|
+
delivery_total: wireMoney(input.delivery),
|
|
7050
|
+
statutory_total: wireMoney(ZERO_MONEY),
|
|
7051
|
+
total_obligation: total,
|
|
7052
|
+
due_now: total,
|
|
7053
|
+
rail_amount: total,
|
|
7054
|
+
store_credit_consumed: wireMoney(ZERO_MONEY),
|
|
7055
|
+
voucher_credit_consumed: wireMoney(ZERO_MONEY)
|
|
7056
|
+
},
|
|
7057
|
+
material: {
|
|
7058
|
+
line_digest: `mock-lines:${input.intentId}`,
|
|
7059
|
+
destination_digest: `mock-destination:${input.intentId}`,
|
|
7060
|
+
delivery_promise_digest: `mock-delivery:${input.intentId}`,
|
|
7061
|
+
entitlement_digest: `mock-entitlement:${input.intentId}`,
|
|
7062
|
+
cancellation_terms_digest: `mock-cancellation:${input.intentId}`
|
|
7063
|
+
},
|
|
7064
|
+
compliance: {
|
|
7065
|
+
policy_name: MOCK_PURCHASE_INTENT.COMPLIANCE.POLICY_NAME,
|
|
7066
|
+
policy_version: 1,
|
|
7067
|
+
lane: CHECKOUT_COMPLIANCE_LANE.NATIVE,
|
|
7068
|
+
reason_code: CHECKOUT_COMPLIANCE_REASON.PHYSICAL_GOODS_NATIVE,
|
|
7069
|
+
evidence_digest: `mock-compliance:${input.intentId}`
|
|
7070
|
+
},
|
|
7071
|
+
payment_options: isExactlyZero(input.total) ? [] : paymentOptions(input.intentId, input.currency, input.total, input.validUntil),
|
|
7072
|
+
valid_until: input.validUntil,
|
|
7073
|
+
created_at: input.now
|
|
7074
|
+
};
|
|
7075
|
+
}
|
|
7076
|
+
function intentView(intent) {
|
|
7077
|
+
const {
|
|
7078
|
+
intent_owner: _owner,
|
|
7079
|
+
mint_request: _request,
|
|
7080
|
+
last_confirmation: _confirmation,
|
|
7081
|
+
...view
|
|
7082
|
+
} = intent;
|
|
7083
|
+
return view;
|
|
7084
|
+
}
|
|
7085
|
+
function purchasingPrincipal(session) {
|
|
7086
|
+
if (!session.is_authenticated) {
|
|
7087
|
+
return { kind: PURCHASING_PRINCIPAL_KIND.GUEST };
|
|
7088
|
+
}
|
|
7089
|
+
if (!session.account_id) throw validation("Authenticated mock session has no account authority");
|
|
7090
|
+
return {
|
|
7091
|
+
kind: PURCHASING_PRINCIPAL_KIND.ACCOUNT,
|
|
7092
|
+
account_id: session.account_id
|
|
7093
|
+
};
|
|
7094
|
+
}
|
|
7095
|
+
function assertSupportedTenderPreference(request) {
|
|
7096
|
+
if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) return;
|
|
7097
|
+
const checkout = request.checkout;
|
|
7098
|
+
if (!checkout) throw validation("Cart checkout input is required");
|
|
7099
|
+
const preference = checkout.tender_preference;
|
|
7100
|
+
if (preference?.method_kind !== PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY || preference.authority.authority_kind !== MOBILE_MONEY_TENDER_AUTHORITY_KIND.TRANSIENT) {
|
|
7101
|
+
return;
|
|
7102
|
+
}
|
|
7103
|
+
if (!PURCHASE_INTENT_MOBILE_MONEY_NETWORKS.has(preference.authority.network)) {
|
|
7104
|
+
throw validation("Mobile Money network is not available for PurchaseIntent checkout");
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
function intentOwner(session) {
|
|
7108
|
+
if (!session.is_authenticated) {
|
|
7109
|
+
return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.STOREFRONT_SESSION, id: session.id };
|
|
7110
|
+
}
|
|
7111
|
+
if (!session.account_id) throw validation("Authenticated mock session has no account authority");
|
|
7112
|
+
return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.ACCOUNT, id: session.account_id };
|
|
7113
|
+
}
|
|
7114
|
+
function createPurchaseIntentMockService(options) {
|
|
7115
|
+
const { registry, clock, ids, bus } = options;
|
|
7116
|
+
function stored(session, intentId) {
|
|
7117
|
+
const intent = registry.purchaseIntents.get(intentId);
|
|
7118
|
+
if (!intent) throw notFound(`Purchase intent ${intentId} not found`);
|
|
7119
|
+
const owner = intentOwner(session);
|
|
7120
|
+
if (intent.intent_owner.kind !== owner.kind || intent.intent_owner.id !== owner.id) {
|
|
7121
|
+
throw notFound(`Purchase intent ${intentId} not found`);
|
|
7122
|
+
}
|
|
7123
|
+
return intent;
|
|
7124
|
+
}
|
|
7125
|
+
function completeProviderOrder(intent, order, attemptId) {
|
|
7126
|
+
const updated = {
|
|
7127
|
+
...order,
|
|
7128
|
+
payment_state: MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID,
|
|
7129
|
+
status: MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED,
|
|
7130
|
+
updated_at: clock.iso()
|
|
7131
|
+
};
|
|
7132
|
+
registry.orders.put(order.id, updated);
|
|
7133
|
+
bus.emit(
|
|
7134
|
+
MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
|
|
7135
|
+
{ order_id: order.id, reference: order.payment_reference },
|
|
7136
|
+
order.business_id
|
|
7137
|
+
);
|
|
7138
|
+
bus.emit(MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED, { order_id: order.id }, order.business_id);
|
|
7139
|
+
const completed = {
|
|
7140
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
7141
|
+
intent_id: intent.id,
|
|
7142
|
+
attempt_id: attemptId,
|
|
7143
|
+
order_id: order.id,
|
|
7144
|
+
order_number: order.user_friendly_id,
|
|
7145
|
+
payment_id: order.payment_id
|
|
7146
|
+
};
|
|
7147
|
+
registry.purchaseIntents.put(intent.id, {
|
|
7148
|
+
...intent,
|
|
7149
|
+
updated_at: clock.iso(),
|
|
7150
|
+
last_confirmation: completed
|
|
7151
|
+
});
|
|
7152
|
+
return completed;
|
|
7153
|
+
}
|
|
7154
|
+
return {
|
|
7155
|
+
mint(session, request) {
|
|
7156
|
+
if (!request.client_intent_key.trim()) throw validation("client_intent_key required");
|
|
7157
|
+
if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
|
|
7158
|
+
throw validation("The mock currently prepares cart purchase intents only");
|
|
7159
|
+
}
|
|
7160
|
+
const checkoutInput = request.checkout;
|
|
7161
|
+
if (!checkoutInput) throw validation("Cart checkout input is required");
|
|
7162
|
+
assertSupportedTenderPreference(request);
|
|
7163
|
+
const cart = registry.carts.get(request.source.cart_id);
|
|
7164
|
+
if (!cart) throw notFound(`Cart ${request.source.cart_id} not found`);
|
|
7165
|
+
if (cart.items.length === 0) {
|
|
7166
|
+
throw validation("Cart is empty", { code: ErrorCode.CART_EMPTY });
|
|
7167
|
+
}
|
|
7168
|
+
const principal = purchasingPrincipal(session);
|
|
7169
|
+
const owner = intentOwner(session);
|
|
7170
|
+
const existing = registry.purchaseIntents.list(
|
|
7171
|
+
(candidate) => candidate.mint_request.client_intent_key === request.client_intent_key && candidate.intent_owner.kind === owner.kind && candidate.intent_owner.id === owner.id
|
|
7172
|
+
)[0];
|
|
7173
|
+
if (existing) return intentView(existing);
|
|
7174
|
+
const now = clock.iso();
|
|
7175
|
+
const expiresAt = new Date(clock.nowMs() + INTENT_LIFETIME_MS).toISOString();
|
|
7176
|
+
const intentId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.INTENT);
|
|
7177
|
+
const terms = termsForCart({
|
|
7178
|
+
intentId,
|
|
7179
|
+
revision: 1,
|
|
7180
|
+
version: 1,
|
|
7181
|
+
currency: cart.currency,
|
|
7182
|
+
subtotal: cart.subtotal,
|
|
7183
|
+
discount: cart.total_discounts,
|
|
7184
|
+
tax: cart.tax_amount,
|
|
7185
|
+
delivery: cart.delivery_fee,
|
|
7186
|
+
total: cart.total_price,
|
|
7187
|
+
now,
|
|
7188
|
+
validUntil: expiresAt
|
|
7189
|
+
});
|
|
7190
|
+
const intent = {
|
|
7191
|
+
id: intentId,
|
|
7192
|
+
seller_business_id: cart.business_id,
|
|
7193
|
+
principal,
|
|
7194
|
+
revision: 1,
|
|
7195
|
+
status: PURCHASE_INTENT_STATUS.OPEN,
|
|
7196
|
+
source: {
|
|
7197
|
+
kind: PURCHASE_INTENT_SOURCE_KIND.CART,
|
|
7198
|
+
source_id: cart.id,
|
|
7199
|
+
source_version: cart.updated_at,
|
|
7200
|
+
seller_business_id: cart.business_id,
|
|
7201
|
+
subject: {
|
|
7202
|
+
kind: PURCHASING_PRINCIPAL_KIND.BUSINESS,
|
|
7203
|
+
business_id: cart.business_id
|
|
7204
|
+
},
|
|
7205
|
+
binding_digest: `mock-cart:${cart.id}:${cart.updated_at}`
|
|
7206
|
+
},
|
|
7207
|
+
lines: cart.items.map((line, ordinal) => ({
|
|
7208
|
+
id: line.id,
|
|
7209
|
+
ordinal,
|
|
7210
|
+
sellable: {
|
|
7211
|
+
kind: line.variant_id ? PURCHASE_INTENT_SELLABLE_KIND.VARIANT : PURCHASE_INTENT_SELLABLE_KIND.PRODUCT,
|
|
7212
|
+
product_id: line.item_id,
|
|
7213
|
+
variant_id: line.variant_id,
|
|
7214
|
+
location_id: checkoutInput.location_id || MOCK_PURCHASE_INTENT.DEFAULT_LOCATION_ID
|
|
7215
|
+
},
|
|
7216
|
+
quantity: line.quantity,
|
|
7217
|
+
configuration: {
|
|
7218
|
+
kind: PURCHASE_INTENT_LINE_CONFIGURATION_KIND.SIMPLE,
|
|
7219
|
+
variant_id: line.variant_id,
|
|
7220
|
+
customer_inputs: line.customer_inputs
|
|
7221
|
+
}
|
|
7222
|
+
})),
|
|
7223
|
+
line_pricing: cart.items.map((line) => {
|
|
7224
|
+
const unitPrice = Number(line.price) + Number(line.add_ons_price);
|
|
7225
|
+
const lineSubtotal = unitPrice * line.quantity - Number(line.item_discount_amount);
|
|
7226
|
+
return {
|
|
7227
|
+
intent_line_id: line.id,
|
|
7228
|
+
quantity: line.quantity,
|
|
7229
|
+
unit_price: decimal(unitPrice),
|
|
7230
|
+
line_subtotal: decimal(lineSubtotal),
|
|
7231
|
+
currency: currencyCode(cart.currency)
|
|
7232
|
+
};
|
|
7233
|
+
}),
|
|
7234
|
+
current_terms: terms,
|
|
7235
|
+
expires_at: expiresAt,
|
|
7236
|
+
created_at: now,
|
|
7237
|
+
updated_at: now,
|
|
7238
|
+
intent_owner: owner,
|
|
7239
|
+
mint_request: request
|
|
7240
|
+
};
|
|
7241
|
+
registry.purchaseIntents.put(intentId, intent);
|
|
7242
|
+
return intentView(intent);
|
|
7243
|
+
},
|
|
7244
|
+
get(session, intentId) {
|
|
7245
|
+
return intentView(stored(session, intentId));
|
|
7246
|
+
},
|
|
7247
|
+
refresh(session, intentId, expectedRevision) {
|
|
7248
|
+
const intent = stored(session, intentId);
|
|
7249
|
+
if (intent.revision !== expectedRevision) {
|
|
7250
|
+
throw validation("expected_intent_revision does not match current intent revision");
|
|
7251
|
+
}
|
|
7252
|
+
if (!intent.current_terms) throw validation("Purchase intent has no current terms");
|
|
7253
|
+
const previous = intent.current_terms;
|
|
7254
|
+
const nextDelivery = Number(previous.money.delivery_total) + 1;
|
|
7255
|
+
const nextTotal = Number(previous.money.total_obligation) + 1;
|
|
7256
|
+
const now = clock.iso();
|
|
7257
|
+
const replacement = termsForCart({
|
|
7258
|
+
intentId,
|
|
7259
|
+
revision: intent.revision,
|
|
7260
|
+
version: previous.terms_version + 1,
|
|
7261
|
+
currency: previous.money.currency,
|
|
7262
|
+
subtotal: String(previous.money.subtotal),
|
|
7263
|
+
discount: String(previous.money.discount_total),
|
|
7264
|
+
tax: String(previous.money.tax_total),
|
|
7265
|
+
delivery: nextDelivery.toFixed(2),
|
|
7266
|
+
total: nextTotal.toFixed(2),
|
|
7267
|
+
now,
|
|
7268
|
+
validUntil: intent.expires_at
|
|
7269
|
+
});
|
|
7270
|
+
const updated = { ...intent, current_terms: replacement, updated_at: now };
|
|
7271
|
+
registry.purchaseIntents.put(intentId, updated);
|
|
7272
|
+
return intentView(updated);
|
|
7273
|
+
},
|
|
7274
|
+
confirm(session, intentId, request) {
|
|
7275
|
+
const intent = stored(session, intentId);
|
|
7276
|
+
if (intent.status === PURCHASE_INTENT_STATUS.CONVERTED && intent.last_confirmation) {
|
|
7277
|
+
const orderId2 = "order_id" in intent.last_confirmation ? intent.last_confirmation.order_id : void 0;
|
|
7278
|
+
const order2 = orderId2 ? registry.orders.get(orderId2) : void 0;
|
|
7279
|
+
if (order2?.payment_state === MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID) {
|
|
7280
|
+
return {
|
|
7281
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
7282
|
+
intent_id: intent.id,
|
|
7283
|
+
attempt_id: "attempt_id" in intent.last_confirmation ? intent.last_confirmation.attempt_id : ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT),
|
|
7284
|
+
order_id: order2.id
|
|
7285
|
+
};
|
|
7286
|
+
}
|
|
7287
|
+
if (order2?.payment_id && intent.last_confirmation.status === PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED) {
|
|
7288
|
+
return completeProviderOrder(intent, order2, intent.last_confirmation.attempt_id);
|
|
7289
|
+
}
|
|
7290
|
+
return intent.last_confirmation;
|
|
7291
|
+
}
|
|
7292
|
+
const terms = intent.current_terms;
|
|
7293
|
+
if (!terms) throw validation("Purchase intent has no current terms");
|
|
7294
|
+
const zeroRail = isExactlyZero(terms.money.rail_amount);
|
|
7295
|
+
const option = request.payment_option_id ? terms.payment_options.find(
|
|
7296
|
+
(candidate) => candidate.id === request.payment_option_id && candidate.status === PAYMENT_OPTION_STATUS.ELIGIBLE
|
|
7297
|
+
) : void 0;
|
|
7298
|
+
const acceptedBaseTermsChanged = !sameDecimal(request.accepted_due_now, terms.money.due_now) || request.currency !== terms.money.currency;
|
|
7299
|
+
if (request.accepted_intent_revision !== intent.revision || request.accepted_terms_version !== terms.terms_version || acceptedBaseTermsChanged) {
|
|
7300
|
+
const deltas = acceptedBaseTermsChanged ? [
|
|
7301
|
+
{
|
|
7302
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.MONEY,
|
|
7303
|
+
component: CHECKOUT_TERMS_COMPONENT.DUE_NOW,
|
|
7304
|
+
accepted: request.accepted_due_now,
|
|
7305
|
+
replacement: terms.money.due_now
|
|
7306
|
+
}
|
|
7307
|
+
] : [
|
|
7308
|
+
{
|
|
7309
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
|
|
7310
|
+
accepted_option_id: request.payment_option_id ?? null,
|
|
7311
|
+
accepted_customer_debit_amount: request.accepted_customer_debit_amount,
|
|
7312
|
+
replacement_option_id: option?.id ?? null,
|
|
7313
|
+
replacement_customer_debit_amount: option?.customer_debit_amount ?? null
|
|
7314
|
+
}
|
|
7315
|
+
];
|
|
7316
|
+
return {
|
|
7317
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
|
|
7318
|
+
intent_id: intent.id,
|
|
7319
|
+
replacement: terms,
|
|
7320
|
+
deltas
|
|
7321
|
+
};
|
|
7322
|
+
}
|
|
7323
|
+
if (!zeroRail && !option || zeroRail && request.payment_option_id != null) {
|
|
7324
|
+
return {
|
|
7325
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE,
|
|
7326
|
+
intent_id: intent.id,
|
|
7327
|
+
lines: intent.lines.map((line) => ({
|
|
7328
|
+
line_id: line.id,
|
|
7329
|
+
reason: CHECKOUT_INVALIDATION_REASON.PAYMENT_OPTION_CHANGED
|
|
7330
|
+
}))
|
|
7331
|
+
};
|
|
7332
|
+
}
|
|
7333
|
+
const acceptedCustomerDebitMatches = zeroRail ? isExactlyZero(request.accepted_customer_debit_amount) : Boolean(
|
|
7334
|
+
option && sameDecimal(request.accepted_customer_debit_amount, option.customer_debit_amount)
|
|
7335
|
+
);
|
|
7336
|
+
if (!acceptedCustomerDebitMatches) {
|
|
7337
|
+
return {
|
|
7338
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
|
|
7339
|
+
intent_id: intent.id,
|
|
7340
|
+
replacement: terms,
|
|
7341
|
+
deltas: [
|
|
7342
|
+
{
|
|
7343
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
|
|
7344
|
+
accepted_option_id: request.payment_option_id ?? null,
|
|
7345
|
+
accepted_customer_debit_amount: request.accepted_customer_debit_amount,
|
|
7346
|
+
replacement_option_id: option?.id ?? null,
|
|
7347
|
+
replacement_customer_debit_amount: option?.customer_debit_amount ?? null
|
|
7348
|
+
}
|
|
7349
|
+
]
|
|
7350
|
+
};
|
|
7351
|
+
}
|
|
7352
|
+
if (intent.mint_request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
|
|
7353
|
+
throw validation("The mock currently confirms cart purchase intents only");
|
|
7354
|
+
}
|
|
7355
|
+
const checkoutInput = intent.mint_request.checkout;
|
|
7356
|
+
if (!checkoutInput) throw validation("Cart checkout input is required");
|
|
7357
|
+
const cart = registry.carts.get(intent.mint_request.source.cart_id);
|
|
7358
|
+
if (!cart || cart.status !== MOCK_PURCHASE_INTENT.CART_STATUS.ACTIVE) {
|
|
7359
|
+
return { status: PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE, intent_id: intent.id };
|
|
7360
|
+
}
|
|
7361
|
+
const attemptId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT);
|
|
7362
|
+
const orderId = ids.order();
|
|
7363
|
+
const orderNumber = ids.orderNumber();
|
|
7364
|
+
const paymentId = zeroRail ? void 0 : ids.payment();
|
|
7365
|
+
const paymentReference = zeroRail ? void 0 : ids.payment();
|
|
7366
|
+
const needsProvider = option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD || option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY;
|
|
7367
|
+
const paid = zeroRail;
|
|
7368
|
+
const order = {
|
|
7369
|
+
id: orderId,
|
|
7370
|
+
business_id: cart.business_id,
|
|
7371
|
+
user_friendly_id: orderNumber,
|
|
7372
|
+
channel: cart.channel,
|
|
7373
|
+
status: paid ? MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED : MOCK_PURCHASE_INTENT.ORDER_STATUS.PENDING,
|
|
7374
|
+
payment_state: paid ? MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID : MOCK_PURCHASE_INTENT.PAYMENT_STATE.NOT_PAID,
|
|
7375
|
+
order_type: checkoutInput.order_type,
|
|
7376
|
+
customer_id: session.customer_id,
|
|
7377
|
+
customer_name: checkoutInput.customer.name,
|
|
7378
|
+
customer_email: checkoutInput.customer.email,
|
|
7379
|
+
customer_phone: checkoutInput.customer.phone,
|
|
7380
|
+
delivery_address: checkoutInput.address_info.street_address,
|
|
7381
|
+
delivery_fee: cart.delivery_fee,
|
|
7382
|
+
subtotal: cart.subtotal,
|
|
7383
|
+
tax_amount: cart.tax_amount,
|
|
7384
|
+
service_charge: cart.service_charge,
|
|
7385
|
+
total_discounts: cart.total_discounts,
|
|
7386
|
+
total_price: cart.total_price,
|
|
7387
|
+
currency: cart.currency,
|
|
7388
|
+
items: cart.items,
|
|
7389
|
+
applied_discount_ids: cart.applied_discount_ids,
|
|
7390
|
+
applied_discount_codes: cart.applied_discount_codes,
|
|
7391
|
+
payment_id: paymentId,
|
|
7392
|
+
payment_reference: paymentReference,
|
|
7393
|
+
metadata: checkoutInput.metadata,
|
|
7394
|
+
created_at: clock.iso(),
|
|
7395
|
+
updated_at: clock.iso()
|
|
6411
7396
|
};
|
|
7397
|
+
registry.orders.put(orderId, order);
|
|
7398
|
+
registry.carts.put(cart.id, {
|
|
7399
|
+
...cart,
|
|
7400
|
+
status: MOCK_PURCHASE_INTENT.CART_STATUS.CONVERTED,
|
|
7401
|
+
updated_at: clock.iso()
|
|
7402
|
+
});
|
|
7403
|
+
bus.emit(
|
|
7404
|
+
MOCK_PURCHASE_INTENT.EVENT.ORDER_CREATED,
|
|
7405
|
+
{ order_id: orderId, order_number: orderNumber },
|
|
7406
|
+
cart.business_id
|
|
7407
|
+
);
|
|
7408
|
+
if (paid) {
|
|
7409
|
+
if (paymentReference) {
|
|
7410
|
+
bus.emit(
|
|
7411
|
+
MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
|
|
7412
|
+
{ order_id: orderId, reference: paymentReference },
|
|
7413
|
+
cart.business_id
|
|
7414
|
+
);
|
|
7415
|
+
}
|
|
7416
|
+
bus.emit(
|
|
7417
|
+
MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED,
|
|
7418
|
+
{ order_id: orderId },
|
|
7419
|
+
cart.business_id
|
|
7420
|
+
);
|
|
7421
|
+
}
|
|
7422
|
+
let confirmation;
|
|
7423
|
+
if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY) {
|
|
7424
|
+
confirmation = {
|
|
7425
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED,
|
|
7426
|
+
intent_id: intent.id,
|
|
7427
|
+
attempt_id: attemptId,
|
|
7428
|
+
order_id: orderId,
|
|
7429
|
+
authorization_type: AUTHORIZATION_TYPE.OTP,
|
|
7430
|
+
display_text: "Approve on your mobile money app"
|
|
7431
|
+
};
|
|
7432
|
+
} else if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD) {
|
|
7433
|
+
confirmation = {
|
|
7434
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED,
|
|
7435
|
+
intent_id: intent.id,
|
|
7436
|
+
attempt_id: attemptId,
|
|
7437
|
+
order_id: orderId,
|
|
7438
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
7439
|
+
client_secret: `access_${paymentReference}`,
|
|
7440
|
+
public_key: MOCK_PURCHASE_INTENT.MOCK_PAYSTACK_PUBLIC_KEY
|
|
7441
|
+
};
|
|
7442
|
+
} else {
|
|
7443
|
+
confirmation = {
|
|
7444
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
7445
|
+
intent_id: intent.id,
|
|
7446
|
+
attempt_id: attemptId,
|
|
7447
|
+
order_id: orderId
|
|
7448
|
+
};
|
|
7449
|
+
}
|
|
7450
|
+
if (needsProvider && !paymentId) {
|
|
7451
|
+
throw validation("Provider-backed purchase intent has no payment authority");
|
|
7452
|
+
}
|
|
7453
|
+
registry.purchaseIntents.put(intent.id, {
|
|
7454
|
+
...intent,
|
|
7455
|
+
status: PURCHASE_INTENT_STATUS.CONVERTED,
|
|
7456
|
+
converted_order_id: orderId,
|
|
7457
|
+
updated_at: clock.iso(),
|
|
7458
|
+
last_confirmation: confirmation
|
|
7459
|
+
});
|
|
7460
|
+
return confirmation;
|
|
7461
|
+
},
|
|
7462
|
+
submitAction(session, intentId, attemptId, request) {
|
|
7463
|
+
const intent = stored(session, intentId);
|
|
7464
|
+
const previous = intent.last_confirmation;
|
|
7465
|
+
if (!previous || previous.status !== PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED || previous.attempt_id !== attemptId) {
|
|
7466
|
+
throw validation("Purchase-intent attempt is not awaiting authorization");
|
|
7467
|
+
}
|
|
7468
|
+
if (!request.authorization) throw validation("authorization required");
|
|
7469
|
+
const order = registry.orders.get(previous.order_id);
|
|
7470
|
+
if (!order || !order.payment_id) {
|
|
7471
|
+
throw notFound(`Purchase-intent attempt ${attemptId} not found`);
|
|
7472
|
+
}
|
|
7473
|
+
return completeProviderOrder(intent, order, attemptId);
|
|
6412
7474
|
}
|
|
6413
7475
|
};
|
|
6414
7476
|
}
|
|
@@ -11724,7 +12786,7 @@ var ID_PREFIX = {
|
|
|
11724
12786
|
product: "prod_",
|
|
11725
12787
|
service: "svc_"
|
|
11726
12788
|
};
|
|
11727
|
-
var
|
|
12789
|
+
var DEFAULT_CURRENCY2 = "GHS";
|
|
11728
12790
|
function slugify(value) {
|
|
11729
12791
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
11730
12792
|
}
|
|
@@ -11759,7 +12821,7 @@ function makeSeedContext(registry) {
|
|
|
11759
12821
|
const business = {
|
|
11760
12822
|
business_type: "other",
|
|
11761
12823
|
email: `hello@${handle}.test`,
|
|
11762
|
-
default_currency:
|
|
12824
|
+
default_currency: DEFAULT_CURRENCY2,
|
|
11763
12825
|
default_offers_table_service: false,
|
|
11764
12826
|
default_accepts_online_orders: true,
|
|
11765
12827
|
status: "active",
|
|
@@ -11938,7 +13000,8 @@ function createDeps(options = {}) {
|
|
|
11938
13000
|
const inventory = createInventoryService({ registry });
|
|
11939
13001
|
const cart = createCartService({ registry, clock, ids, bus, inventory });
|
|
11940
13002
|
const orders = createOrderService({ registry, clock, bus });
|
|
11941
|
-
const
|
|
13003
|
+
const payments = createPaymentService({ registry, clock, bus });
|
|
13004
|
+
const purchaseIntents = createPurchaseIntentMockService({ registry, clock, ids, bus });
|
|
11942
13005
|
const subscriptions = createSubscriptionService({ registry, clock, bus });
|
|
11943
13006
|
const scheduling = createSchedulingService({ registry, clock, bus });
|
|
11944
13007
|
const fx = createFxService({ registry, clock, ids });
|
|
@@ -11959,7 +13022,8 @@ function createDeps(options = {}) {
|
|
|
11959
13022
|
business,
|
|
11960
13023
|
catalogue,
|
|
11961
13024
|
cart,
|
|
11962
|
-
|
|
13025
|
+
payments,
|
|
13026
|
+
purchaseIntents,
|
|
11963
13027
|
orders,
|
|
11964
13028
|
subscriptions,
|
|
11965
13029
|
scheduling,
|