@cimplify/sdk 0.103.0 → 0.104.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-DHBwvh7U.d.ts → catalogue-BlQ85Uma.d.mts} +492 -748
- package/dist/{client-BfpNjJ_a.d.mts → catalogue-C8TqOj9K.d.ts} +492 -748
- package/dist/{payment-H5LoIvXU.d.mts → checkout-BqK9XUz5.d.mts} +4338 -1495
- package/dist/{payment-H5LoIvXU.d.ts → checkout-BqK9XUz5.d.ts} +4338 -1495
- package/dist/{chunk-GOHSXBKW.mjs → chunk-33VCYWVL.mjs} +25 -1
- package/dist/{chunk-C2JSNJUC.mjs → chunk-6G3ANKQA.mjs} +1080 -369
- package/dist/{chunk-QOWPGS7Z.js → chunk-BWEIMKLF.js} +572 -17
- package/dist/{chunk-JSWJ4XDE.js → chunk-DQEXY542.js} +1630 -824
- package/dist/{chunk-CNN7Z2IP.js → chunk-EV2EVOYS.js} +27 -0
- package/dist/{chunk-VIL6IQCG.js → chunk-HYFA67A7.js} +1084 -373
- package/dist/{chunk-H7ACMHM3.mjs → chunk-L5SNEU2O.mjs} +553 -16
- package/dist/{chunk-JG5CQNCB.js → chunk-RBNBSQI7.mjs} +221 -19
- package/dist/{chunk-VNPEYLB2.mjs → chunk-SFUJMS5E.js} +265 -2
- package/dist/{chunk-M4URVKHE.mjs → chunk-U54CWUUT.mjs} +11 -2
- package/dist/{chunk-GERXZQSX.mjs → chunk-UFOWPN3D.mjs} +967 -582
- package/dist/{chunk-J6PNHDHL.mjs → chunk-V4457OKB.mjs} +1580 -785
- package/dist/{chunk-R6OLQ33I.js → chunk-WXREIBFW.js} +1041 -654
- package/dist/{chunk-NNG6IFFX.js → chunk-XAUSDMMZ.js} +13 -3
- package/dist/{client-DpqRobY9.d.mts → client-DhXeiUrf.d.ts} +2 -2
- package/dist/{client-Dqg2mc-7.d.ts → client-mXiUrV4X.d.mts} +2 -2
- package/dist/{index-C8uDqLwJ.d.ts → index-B0AdZvrM.d.mts} +9 -4
- package/dist/{index-e2y6AK70.d.mts → index-CK8Nk5uQ.d.ts} +9 -4
- package/dist/index-DPWI5XcU.d.ts +8 -0
- package/dist/index-DVfUh-ci.d.mts +8 -0
- package/dist/index.d.mts +34 -15
- package/dist/index.d.ts +34 -15
- package/dist/index.js +306 -133
- package/dist/index.mjs +7 -6
- package/dist/mock/cli.mjs +1494 -395
- package/dist/mock/library.js +1220 -361
- package/dist/mock/library.mjs +1220 -361
- package/dist/mock/msw.js +1220 -361
- package/dist/mock/msw.mjs +1220 -361
- package/dist/{price-C2RqscMY.d.ts → price-6hQIIac9.d.ts} +1 -1
- package/dist/{price-BvePgDXe.d.mts → price-BUDv1rI7.d.mts} +1 -1
- package/dist/react.d.mts +10 -5
- package/dist/react.d.ts +10 -5
- package/dist/react.js +279 -225
- package/dist/react.mjs +140 -86
- package/dist/{server-DoXIpSd7.d.ts → server-DJ0mSs9i.d.mts} +33 -57
- package/dist/{server-C7FyLdrz.d.mts → server-DSTX76Ki.d.ts} +33 -57
- package/dist/server.d.mts +3 -4
- package/dist/server.d.ts +3 -4
- package/dist/server.js +12 -11
- package/dist/server.mjs +6 -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 +2217 -14
- package/dist/testing.d.ts +2217 -14
- package/dist/testing.js +173 -83
- package/dist/testing.mjs +28 -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/chat-widget.json +1 -1
- package/registry/order-history.json +1 -1
- package/registry/order-summary.json +1 -1
- package/dist/link-cuNVYc2s.d.mts +0 -297
- package/dist/link-cuNVYc2s.d.ts +0 -297
package/dist/mock/msw.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// ../node_modules/.bun/hono@4.
|
|
1
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/compose.js
|
|
2
2
|
var compose = (middleware, onError, onNotFound) => {
|
|
3
3
|
return (context, next) => {
|
|
4
4
|
let index = -1;
|
|
@@ -42,21 +42,46 @@ var compose = (middleware, onError, onNotFound) => {
|
|
|
42
42
|
};
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// ../node_modules/.bun/hono@4.
|
|
45
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request/constants.js
|
|
46
46
|
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
47
47
|
|
|
48
|
-
// ../node_modules/.bun/hono@4.
|
|
48
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/buffer.js
|
|
49
|
+
var bufferToFormData = (arrayBuffer, contentType) => {
|
|
50
|
+
const response = new Response(arrayBuffer, {
|
|
51
|
+
headers: {
|
|
52
|
+
// Normalize the media type (case-insensitive) while keeping parameters like the boundary
|
|
53
|
+
"Content-Type": contentType.replace(/^[^;]+/, (mediaType) => mediaType.toLowerCase())
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return response.formData();
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/body.js
|
|
60
|
+
var isRawRequest = (request) => "headers" in request;
|
|
49
61
|
var parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {
|
|
50
62
|
const { all = false, dot = false } = options;
|
|
51
|
-
const headers = request
|
|
63
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
52
64
|
const contentType = headers.get("Content-Type");
|
|
53
|
-
|
|
65
|
+
const mediaType = contentType?.split(";")[0].trim().toLowerCase();
|
|
66
|
+
if (mediaType === "multipart/form-data" || mediaType === "application/x-www-form-urlencoded") {
|
|
54
67
|
return parseFormData(request, { all, dot });
|
|
55
68
|
}
|
|
56
69
|
return {};
|
|
57
70
|
};
|
|
58
71
|
async function parseFormData(request, options) {
|
|
59
|
-
|
|
72
|
+
if (!isRawRequest(request) && request.bodyCache.formData) {
|
|
73
|
+
return convertFormDataToBodyData(
|
|
74
|
+
await request.bodyCache.formData,
|
|
75
|
+
options
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const headers = isRawRequest(request) ? request.headers : request.raw.headers;
|
|
79
|
+
const arrayBuffer = await request.arrayBuffer();
|
|
80
|
+
const formDataPromise = bufferToFormData(arrayBuffer, headers.get("Content-Type") || "");
|
|
81
|
+
if (!isRawRequest(request)) {
|
|
82
|
+
request.bodyCache.formData = formDataPromise;
|
|
83
|
+
}
|
|
84
|
+
const formData = await formDataPromise;
|
|
60
85
|
if (formData) {
|
|
61
86
|
return convertFormDataToBodyData(formData, options);
|
|
62
87
|
}
|
|
@@ -116,7 +141,7 @@ var handleParsingNestedValues = (form, key, value) => {
|
|
|
116
141
|
});
|
|
117
142
|
};
|
|
118
143
|
|
|
119
|
-
// ../node_modules/.bun/hono@4.
|
|
144
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/url.js
|
|
120
145
|
var splitPath = (path) => {
|
|
121
146
|
const paths = path.split("/");
|
|
122
147
|
if (paths[0] === "") {
|
|
@@ -222,13 +247,13 @@ var checkOptionalParameter = (path) => {
|
|
|
222
247
|
if (segment !== "" && !/\:/.test(segment)) {
|
|
223
248
|
basePath += "/" + segment;
|
|
224
249
|
} else if (/\:/.test(segment)) {
|
|
225
|
-
if (
|
|
250
|
+
if (segment.charCodeAt(segment.length - 1) === 63) {
|
|
226
251
|
if (results.length === 0 && basePath === "") {
|
|
227
252
|
results.push("/");
|
|
228
253
|
} else {
|
|
229
254
|
results.push(basePath);
|
|
230
255
|
}
|
|
231
|
-
const optionalSegment = segment.
|
|
256
|
+
const optionalSegment = segment.slice(0, -1);
|
|
232
257
|
basePath += "/" + optionalSegment;
|
|
233
258
|
results.push(basePath);
|
|
234
259
|
} else {
|
|
@@ -238,18 +263,16 @@ var checkOptionalParameter = (path) => {
|
|
|
238
263
|
});
|
|
239
264
|
return results.filter((v, i, a) => a.indexOf(v) === i);
|
|
240
265
|
};
|
|
266
|
+
var tryDecodeURIComponent = (str) => str.indexOf("%") !== -1 ? tryDecode(str, decodeURIComponent_) : str;
|
|
241
267
|
var _decodeURI = (value) => {
|
|
242
|
-
if (!/[%+]/.test(value)) {
|
|
243
|
-
return value;
|
|
244
|
-
}
|
|
245
268
|
if (value.indexOf("+") !== -1) {
|
|
246
269
|
value = value.replace(/\+/g, " ");
|
|
247
270
|
}
|
|
248
|
-
return
|
|
271
|
+
return tryDecodeURIComponent(value);
|
|
249
272
|
};
|
|
250
273
|
var _getQueryParam = (url, key, multiple) => {
|
|
251
274
|
let encoded;
|
|
252
|
-
if (!multiple && key &&
|
|
275
|
+
if (!multiple && key && key.indexOf("%") === -1 && key.indexOf("+") === -1) {
|
|
253
276
|
let keyIndex2 = url.indexOf("?", 8);
|
|
254
277
|
if (keyIndex2 === -1) {
|
|
255
278
|
return void 0;
|
|
@@ -273,7 +296,7 @@ var _getQueryParam = (url, key, multiple) => {
|
|
|
273
296
|
return void 0;
|
|
274
297
|
}
|
|
275
298
|
}
|
|
276
|
-
const results =
|
|
299
|
+
const results = /* @__PURE__ */ Object.create(null);
|
|
277
300
|
encoded ??= /[%+]/.test(url);
|
|
278
301
|
let keyIndex = url.indexOf("?", 8);
|
|
279
302
|
while (keyIndex !== -1) {
|
|
@@ -319,8 +342,7 @@ var getQueryParams = (url, key) => {
|
|
|
319
342
|
};
|
|
320
343
|
var decodeURIComponent_ = decodeURIComponent;
|
|
321
344
|
|
|
322
|
-
// ../node_modules/.bun/hono@4.
|
|
323
|
-
var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);
|
|
345
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/request.js
|
|
324
346
|
var HonoRequest = class {
|
|
325
347
|
/**
|
|
326
348
|
* `.raw` can get the raw Request object.
|
|
@@ -359,7 +381,6 @@ var HonoRequest = class {
|
|
|
359
381
|
this.raw = request;
|
|
360
382
|
this.path = path;
|
|
361
383
|
this.#matchResult = matchResult;
|
|
362
|
-
this.#validatedData = {};
|
|
363
384
|
}
|
|
364
385
|
param(key) {
|
|
365
386
|
return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();
|
|
@@ -367,7 +388,7 @@ var HonoRequest = class {
|
|
|
367
388
|
#getDecodedParam(key) {
|
|
368
389
|
const paramKey = this.#matchResult[0][this.routeIndex][1][key];
|
|
369
390
|
const param = this.#getParamValue(paramKey);
|
|
370
|
-
return param &&
|
|
391
|
+
return param && tryDecodeURIComponent(param);
|
|
371
392
|
}
|
|
372
393
|
#getAllDecodedParams() {
|
|
373
394
|
const decoded = {};
|
|
@@ -375,7 +396,7 @@ var HonoRequest = class {
|
|
|
375
396
|
for (const key of keys) {
|
|
376
397
|
const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);
|
|
377
398
|
if (value !== void 0) {
|
|
378
|
-
decoded[key] =
|
|
399
|
+
decoded[key] = tryDecodeURIComponent(value);
|
|
379
400
|
}
|
|
380
401
|
}
|
|
381
402
|
return decoded;
|
|
@@ -393,7 +414,7 @@ var HonoRequest = class {
|
|
|
393
414
|
if (name) {
|
|
394
415
|
return this.raw.headers.get(name) ?? void 0;
|
|
395
416
|
}
|
|
396
|
-
const headerData =
|
|
417
|
+
const headerData = /* @__PURE__ */ Object.create(null);
|
|
397
418
|
this.raw.headers.forEach((value, key) => {
|
|
398
419
|
headerData[key] = value;
|
|
399
420
|
});
|
|
@@ -408,8 +429,7 @@ var HonoRequest = class {
|
|
|
408
429
|
if (cachedBody) {
|
|
409
430
|
return cachedBody;
|
|
410
431
|
}
|
|
411
|
-
const anyCachedKey
|
|
412
|
-
if (anyCachedKey) {
|
|
432
|
+
for (const anyCachedKey in bodyCache) {
|
|
413
433
|
return bodyCache[anyCachedKey].then((body) => {
|
|
414
434
|
if (anyCachedKey === "json") {
|
|
415
435
|
body = JSON.stringify(body);
|
|
@@ -512,10 +532,10 @@ var HonoRequest = class {
|
|
|
512
532
|
* @param data - The validated data to add.
|
|
513
533
|
*/
|
|
514
534
|
addValidatedData(target, data) {
|
|
515
|
-
this.#validatedData[target] = data;
|
|
535
|
+
(this.#validatedData ??= {})[target] = data;
|
|
516
536
|
}
|
|
517
537
|
valid(target) {
|
|
518
|
-
return this.#validatedData[target];
|
|
538
|
+
return this.#validatedData?.[target];
|
|
519
539
|
}
|
|
520
540
|
/**
|
|
521
541
|
* `.url()` can get the request url strings.
|
|
@@ -602,7 +622,7 @@ var HonoRequest = class {
|
|
|
602
622
|
}
|
|
603
623
|
};
|
|
604
624
|
|
|
605
|
-
// ../node_modules/.bun/hono@4.
|
|
625
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/html.js
|
|
606
626
|
var HtmlEscapedCallbackPhase = {
|
|
607
627
|
Stringify: 1};
|
|
608
628
|
var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
|
|
@@ -633,7 +653,7 @@ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) =>
|
|
|
633
653
|
}
|
|
634
654
|
};
|
|
635
655
|
|
|
636
|
-
// ../node_modules/.bun/hono@4.
|
|
656
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/context.js
|
|
637
657
|
var TEXT_PLAIN = "text/plain; charset=UTF-8";
|
|
638
658
|
var setDefaultContentType = (contentType, headers) => {
|
|
639
659
|
return {
|
|
@@ -906,11 +926,11 @@ var Context = class {
|
|
|
906
926
|
return Object.fromEntries(this.#var);
|
|
907
927
|
}
|
|
908
928
|
#newResponse(data, arg, headers) {
|
|
909
|
-
|
|
910
|
-
if (typeof arg === "object" &&
|
|
911
|
-
|
|
912
|
-
for (const [key, value] of
|
|
913
|
-
if (key
|
|
929
|
+
let responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders;
|
|
930
|
+
if (typeof arg === "object" && arg.headers) {
|
|
931
|
+
responseHeaders ??= new Headers();
|
|
932
|
+
for (const [key, value] of new Headers(arg.headers)) {
|
|
933
|
+
if (key === "set-cookie") {
|
|
914
934
|
responseHeaders.append(key, value);
|
|
915
935
|
} else {
|
|
916
936
|
responseHeaders.set(key, value);
|
|
@@ -918,19 +938,34 @@ var Context = class {
|
|
|
918
938
|
}
|
|
919
939
|
}
|
|
920
940
|
if (headers) {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
941
|
+
if (!responseHeaders) {
|
|
942
|
+
let count = 0;
|
|
943
|
+
for (const k in headers) {
|
|
944
|
+
if (++count > 1 || typeof headers[k] !== "string") {
|
|
945
|
+
responseHeaders = new Headers();
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
if (responseHeaders) {
|
|
951
|
+
for (const k in headers) {
|
|
952
|
+
const v = headers[k];
|
|
953
|
+
if (typeof v === "string") {
|
|
954
|
+
responseHeaders.set(k, v);
|
|
955
|
+
} else {
|
|
956
|
+
responseHeaders.delete(k);
|
|
957
|
+
for (const v2 of v) {
|
|
958
|
+
responseHeaders.append(k, v2);
|
|
959
|
+
}
|
|
928
960
|
}
|
|
929
961
|
}
|
|
930
962
|
}
|
|
931
963
|
}
|
|
932
964
|
const status = typeof arg === "number" ? arg : arg?.status ?? this.#status;
|
|
933
|
-
return createResponseInstance(data, {
|
|
965
|
+
return createResponseInstance(data, {
|
|
966
|
+
status,
|
|
967
|
+
headers: responseHeaders ?? headers
|
|
968
|
+
});
|
|
934
969
|
}
|
|
935
970
|
newResponse = (...args) => this.#newResponse(...args);
|
|
936
971
|
/**
|
|
@@ -1040,18 +1075,18 @@ var Context = class {
|
|
|
1040
1075
|
};
|
|
1041
1076
|
};
|
|
1042
1077
|
|
|
1043
|
-
// ../node_modules/.bun/hono@4.
|
|
1078
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router.js
|
|
1044
1079
|
var METHOD_NAME_ALL = "ALL";
|
|
1045
1080
|
var METHOD_NAME_ALL_LOWERCASE = "all";
|
|
1046
|
-
var METHODS = ["get", "post", "put", "delete", "options", "patch"];
|
|
1081
|
+
var METHODS = ["get", "post", "put", "delete", "options", "patch", "query"];
|
|
1047
1082
|
var MESSAGE_MATCHER_IS_ALREADY_BUILT = "Can not add a route since the matcher is already built.";
|
|
1048
1083
|
var UnsupportedPathError = class extends Error {
|
|
1049
1084
|
};
|
|
1050
1085
|
|
|
1051
|
-
// ../node_modules/.bun/hono@4.
|
|
1086
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/utils/constants.js
|
|
1052
1087
|
var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
|
|
1053
1088
|
|
|
1054
|
-
// ../node_modules/.bun/hono@4.
|
|
1089
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono-base.js
|
|
1055
1090
|
var notFoundHandler = (c) => {
|
|
1056
1091
|
return c.text("404 Not Found", 404);
|
|
1057
1092
|
};
|
|
@@ -1070,6 +1105,7 @@ var Hono = class _Hono {
|
|
|
1070
1105
|
delete;
|
|
1071
1106
|
options;
|
|
1072
1107
|
patch;
|
|
1108
|
+
query;
|
|
1073
1109
|
all;
|
|
1074
1110
|
on;
|
|
1075
1111
|
use;
|
|
@@ -1369,8 +1405,8 @@ var Hono = class _Hono {
|
|
|
1369
1405
|
* @see {@link https://hono.dev/docs/api/hono#fetch}
|
|
1370
1406
|
*
|
|
1371
1407
|
* @param {Request} request - request Object of request
|
|
1372
|
-
* @param {Env}
|
|
1373
|
-
* @param {ExecutionContext} - context of execution
|
|
1408
|
+
* @param {Env} env - env Object
|
|
1409
|
+
* @param {ExecutionContext} executionCtx - context of execution
|
|
1374
1410
|
* @returns {Response | Promise<Response>} response of request
|
|
1375
1411
|
*
|
|
1376
1412
|
*/
|
|
@@ -1427,7 +1463,7 @@ var Hono = class _Hono {
|
|
|
1427
1463
|
};
|
|
1428
1464
|
};
|
|
1429
1465
|
|
|
1430
|
-
// ../node_modules/.bun/hono@4.
|
|
1466
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
1431
1467
|
var emptyParam = [];
|
|
1432
1468
|
function match(method, path) {
|
|
1433
1469
|
const matchers = this.buildAllMatchers();
|
|
@@ -1448,7 +1484,7 @@ function match(method, path) {
|
|
|
1448
1484
|
return match2(method, path);
|
|
1449
1485
|
}
|
|
1450
1486
|
|
|
1451
|
-
// ../node_modules/.bun/hono@4.
|
|
1487
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
1452
1488
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
1453
1489
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
1454
1490
|
var TAIL_WILDCARD_REG_EXP_STR = "(?:|/.*)";
|
|
@@ -1462,7 +1498,7 @@ function compareKey(a, b) {
|
|
|
1462
1498
|
return 1;
|
|
1463
1499
|
}
|
|
1464
1500
|
if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {
|
|
1465
|
-
return 1;
|
|
1501
|
+
return b === TAIL_WILDCARD_REG_EXP_STR ? -1 : 1;
|
|
1466
1502
|
} else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {
|
|
1467
1503
|
return -1;
|
|
1468
1504
|
}
|
|
@@ -1474,76 +1510,75 @@ function compareKey(a, b) {
|
|
|
1474
1510
|
return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;
|
|
1475
1511
|
}
|
|
1476
1512
|
var Node = class _Node {
|
|
1513
|
+
// handler index of a dynamic path, or -1 for a static path terminal
|
|
1477
1514
|
#index;
|
|
1478
1515
|
#varIndex;
|
|
1479
1516
|
#children = /* @__PURE__ */ Object.create(null);
|
|
1480
|
-
insert(tokens, index, paramMap, context,
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
}
|
|
1501
|
-
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
|
|
1502
|
-
if (/\((?!\?:)/.test(regexpStr)) {
|
|
1503
|
-
throw PATH_ERROR;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
node = this.#children[regexpStr];
|
|
1507
|
-
if (!node) {
|
|
1508
|
-
if (Object.keys(this.#children).some(
|
|
1509
|
-
(k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
|
|
1510
|
-
)) {
|
|
1511
|
-
throw PATH_ERROR;
|
|
1517
|
+
insert(tokens, index, paramMap, context, isStatic) {
|
|
1518
|
+
let node = this;
|
|
1519
|
+
for (let i = 0, len = tokens.length; i < len; i++) {
|
|
1520
|
+
const token = tokens[i];
|
|
1521
|
+
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(/^\:([^\{\}]+)(?:\{(.+)\})?$/);
|
|
1522
|
+
let nextNode;
|
|
1523
|
+
if (pattern) {
|
|
1524
|
+
const name = pattern[1];
|
|
1525
|
+
let regexpStr = pattern[2] || LABEL_REG_EXP_STR;
|
|
1526
|
+
if (name && pattern[2]) {
|
|
1527
|
+
if (regexpStr === ".*") {
|
|
1528
|
+
throw PATH_ERROR;
|
|
1529
|
+
}
|
|
1530
|
+
regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, "(?:");
|
|
1531
|
+
if (/\((?!\?:)/.test(regexpStr)) {
|
|
1532
|
+
throw PATH_ERROR;
|
|
1533
|
+
}
|
|
1534
|
+
if (regexpStr.length === 1 && regExpMetaChars.has(regexpStr)) {
|
|
1535
|
+
throw PATH_ERROR;
|
|
1536
|
+
}
|
|
1512
1537
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1538
|
+
nextNode = node.#children[regexpStr];
|
|
1539
|
+
if (!nextNode) {
|
|
1540
|
+
if (regexpStr !== ONLY_WILDCARD_REG_EXP_STR && regexpStr !== TAIL_WILDCARD_REG_EXP_STR) {
|
|
1541
|
+
for (const k in node.#children) {
|
|
1542
|
+
if (
|
|
1543
|
+
// a single-char pattern coexists with single-char literals as a literal does
|
|
1544
|
+
(regexpStr.length > 1 || k.length > 1) && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
|
|
1545
|
+
) {
|
|
1546
|
+
throw PATH_ERROR;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
nextNode = node.#children[regexpStr] = new _Node();
|
|
1515
1551
|
}
|
|
1516
|
-
node = this.#children[regexpStr] = new _Node();
|
|
1517
1552
|
if (name !== "") {
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
}
|
|
1521
|
-
if (!pathErrorCheckOnly && name !== "") {
|
|
1522
|
-
paramMap.push([name, node.#varIndex]);
|
|
1523
|
-
}
|
|
1524
|
-
} else {
|
|
1525
|
-
node = this.#children[token];
|
|
1526
|
-
if (!node) {
|
|
1527
|
-
if (Object.keys(this.#children).some(
|
|
1528
|
-
(k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
|
|
1529
|
-
)) {
|
|
1530
|
-
throw PATH_ERROR;
|
|
1553
|
+
nextNode.#varIndex ??= context.varIndex++;
|
|
1554
|
+
paramMap.push([name, nextNode.#varIndex]);
|
|
1531
1555
|
}
|
|
1532
|
-
|
|
1533
|
-
|
|
1556
|
+
} else {
|
|
1557
|
+
nextNode = node.#children[token];
|
|
1558
|
+
if (!nextNode) {
|
|
1559
|
+
for (const k in node.#children) {
|
|
1560
|
+
if (k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR) {
|
|
1561
|
+
throw PATH_ERROR;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
nextNode = node.#children[token] = new _Node();
|
|
1534
1565
|
}
|
|
1535
|
-
node = this.#children[token] = new _Node();
|
|
1536
1566
|
}
|
|
1567
|
+
node = nextNode;
|
|
1568
|
+
}
|
|
1569
|
+
if (node.#index !== void 0) {
|
|
1570
|
+
throw PATH_ERROR;
|
|
1537
1571
|
}
|
|
1538
|
-
node
|
|
1572
|
+
node.#index = isStatic ? -1 : index;
|
|
1539
1573
|
}
|
|
1540
1574
|
buildRegExpStr() {
|
|
1541
1575
|
const childKeys = Object.keys(this.#children).sort(compareKey);
|
|
1542
1576
|
const strList = childKeys.map((k) => {
|
|
1543
1577
|
const c = this.#children[k];
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1578
|
+
const childStr = c.buildRegExpStr();
|
|
1579
|
+
return childStr === "" ? "" : (typeof c.#varIndex === "number" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\${k}` : k) + childStr;
|
|
1580
|
+
}).filter(Boolean);
|
|
1581
|
+
if (typeof this.#index === "number" && this.#index !== -1) {
|
|
1547
1582
|
strList.unshift(`#${this.#index}`);
|
|
1548
1583
|
}
|
|
1549
1584
|
if (strList.length === 0) {
|
|
@@ -1556,16 +1591,24 @@ var Node = class _Node {
|
|
|
1556
1591
|
}
|
|
1557
1592
|
};
|
|
1558
1593
|
|
|
1559
|
-
// ../node_modules/.bun/hono@4.
|
|
1594
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
1560
1595
|
var Trie = class {
|
|
1561
1596
|
#context = { varIndex: 0 };
|
|
1562
1597
|
#root = new Node();
|
|
1563
|
-
|
|
1598
|
+
#index = 0;
|
|
1599
|
+
// dynamic path -> [handler index, param assoc]; static paths are not registered
|
|
1600
|
+
paths = /* @__PURE__ */ Object.create(null);
|
|
1601
|
+
insert(path, isStatic) {
|
|
1602
|
+
if (isStatic) {
|
|
1603
|
+
this.#root.insert(path.split(""), 0, [], this.#context, true);
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1564
1606
|
const paramAssoc = [];
|
|
1565
1607
|
const groups = [];
|
|
1608
|
+
let markedPath = path;
|
|
1566
1609
|
for (let i = 0; ; ) {
|
|
1567
1610
|
let replaced = false;
|
|
1568
|
-
|
|
1611
|
+
markedPath = markedPath.replace(/\{[^}]+\}/g, (m) => {
|
|
1569
1612
|
const mark = `@\\${i}`;
|
|
1570
1613
|
groups[i] = [mark, m];
|
|
1571
1614
|
i++;
|
|
@@ -1576,7 +1619,7 @@ var Trie = class {
|
|
|
1576
1619
|
break;
|
|
1577
1620
|
}
|
|
1578
1621
|
}
|
|
1579
|
-
const tokens =
|
|
1622
|
+
const tokens = markedPath.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
|
|
1580
1623
|
for (let i = groups.length - 1; i >= 0; i--) {
|
|
1581
1624
|
const [mark] = groups[i];
|
|
1582
1625
|
for (let j = tokens.length - 1; j >= 0; j--) {
|
|
@@ -1586,8 +1629,8 @@ var Trie = class {
|
|
|
1586
1629
|
}
|
|
1587
1630
|
}
|
|
1588
1631
|
}
|
|
1589
|
-
this.#root.insert(tokens, index, paramAssoc, this.#context,
|
|
1590
|
-
|
|
1632
|
+
this.#root.insert(tokens, this.#index, paramAssoc, this.#context, false);
|
|
1633
|
+
this.paths[path] = [this.#index++, paramAssoc];
|
|
1591
1634
|
}
|
|
1592
1635
|
buildRegExp() {
|
|
1593
1636
|
let regexp = this.#root.buildRegExpStr();
|
|
@@ -1612,8 +1655,7 @@ var Trie = class {
|
|
|
1612
1655
|
}
|
|
1613
1656
|
};
|
|
1614
1657
|
|
|
1615
|
-
// ../node_modules/.bun/hono@4.
|
|
1616
|
-
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1658
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1617
1659
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
1618
1660
|
function buildWildcardRegExp(path) {
|
|
1619
1661
|
return wildcardRegExpCache[path] ??= new RegExp(
|
|
@@ -1626,63 +1668,6 @@ function buildWildcardRegExp(path) {
|
|
|
1626
1668
|
function clearWildcardRegExpCache() {
|
|
1627
1669
|
wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
1628
1670
|
}
|
|
1629
|
-
function buildMatcherFromPreprocessedRoutes(routes) {
|
|
1630
|
-
const trie = new Trie();
|
|
1631
|
-
const handlerData = [];
|
|
1632
|
-
if (routes.length === 0) {
|
|
1633
|
-
return nullMatcher;
|
|
1634
|
-
}
|
|
1635
|
-
const routesWithStaticPathFlag = routes.map(
|
|
1636
|
-
(route) => [!/\*|\/:/.test(route[0]), ...route]
|
|
1637
|
-
).sort(
|
|
1638
|
-
([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length
|
|
1639
|
-
);
|
|
1640
|
-
const staticMap = /* @__PURE__ */ Object.create(null);
|
|
1641
|
-
for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
|
|
1642
|
-
const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];
|
|
1643
|
-
if (pathErrorCheckOnly) {
|
|
1644
|
-
staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
|
|
1645
|
-
} else {
|
|
1646
|
-
j++;
|
|
1647
|
-
}
|
|
1648
|
-
let paramAssoc;
|
|
1649
|
-
try {
|
|
1650
|
-
paramAssoc = trie.insert(path, j, pathErrorCheckOnly);
|
|
1651
|
-
} catch (e) {
|
|
1652
|
-
throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
|
|
1653
|
-
}
|
|
1654
|
-
if (pathErrorCheckOnly) {
|
|
1655
|
-
continue;
|
|
1656
|
-
}
|
|
1657
|
-
handlerData[j] = handlers.map(([h, paramCount]) => {
|
|
1658
|
-
const paramIndexMap = /* @__PURE__ */ Object.create(null);
|
|
1659
|
-
paramCount -= 1;
|
|
1660
|
-
for (; paramCount >= 0; paramCount--) {
|
|
1661
|
-
const [key, value] = paramAssoc[paramCount];
|
|
1662
|
-
paramIndexMap[key] = value;
|
|
1663
|
-
}
|
|
1664
|
-
return [h, paramIndexMap];
|
|
1665
|
-
});
|
|
1666
|
-
}
|
|
1667
|
-
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
1668
|
-
for (let i = 0, len = handlerData.length; i < len; i++) {
|
|
1669
|
-
for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
|
|
1670
|
-
const map = handlerData[i][j]?.[1];
|
|
1671
|
-
if (!map) {
|
|
1672
|
-
continue;
|
|
1673
|
-
}
|
|
1674
|
-
const keys = Object.keys(map);
|
|
1675
|
-
for (let k = 0, len3 = keys.length; k < len3; k++) {
|
|
1676
|
-
map[keys[k]] = paramReplacementMap[map[keys[k]]];
|
|
1677
|
-
}
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
const handlerMap = [];
|
|
1681
|
-
for (const i in indexReplacementMap) {
|
|
1682
|
-
handlerMap[i] = handlerData[indexReplacementMap[i]];
|
|
1683
|
-
}
|
|
1684
|
-
return [regexp, handlerMap, staticMap];
|
|
1685
|
-
}
|
|
1686
1671
|
function findMiddleware(middleware, path) {
|
|
1687
1672
|
if (!middleware) {
|
|
1688
1673
|
return void 0;
|
|
@@ -1698,9 +1683,18 @@ var RegExpRouter = class {
|
|
|
1698
1683
|
name = "RegExpRouter";
|
|
1699
1684
|
#middleware;
|
|
1700
1685
|
#routes;
|
|
1686
|
+
#tries;
|
|
1701
1687
|
constructor() {
|
|
1702
1688
|
this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
|
|
1703
1689
|
this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
|
|
1690
|
+
this.#tries = { [METHOD_NAME_ALL]: new Trie() };
|
|
1691
|
+
}
|
|
1692
|
+
#insertPath(method, path) {
|
|
1693
|
+
try {
|
|
1694
|
+
this.#tries[method].insert(path, !/\*|\/:/.test(path));
|
|
1695
|
+
} catch (e) {
|
|
1696
|
+
throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;
|
|
1697
|
+
}
|
|
1704
1698
|
}
|
|
1705
1699
|
add(method, path, handler) {
|
|
1706
1700
|
const middleware = this.#middleware;
|
|
@@ -1709,10 +1703,12 @@ var RegExpRouter = class {
|
|
|
1709
1703
|
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
1710
1704
|
}
|
|
1711
1705
|
if (!middleware[method]) {
|
|
1706
|
+
this.#tries[method] = new Trie();
|
|
1712
1707
|
[middleware, routes].forEach((handlerMap) => {
|
|
1713
1708
|
handlerMap[method] = /* @__PURE__ */ Object.create(null);
|
|
1714
1709
|
Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {
|
|
1715
1710
|
handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];
|
|
1711
|
+
this.#insertPath(method, p);
|
|
1716
1712
|
});
|
|
1717
1713
|
});
|
|
1718
1714
|
}
|
|
@@ -1722,13 +1718,12 @@ var RegExpRouter = class {
|
|
|
1722
1718
|
const paramCount = (path.match(/\/:/g) || []).length;
|
|
1723
1719
|
if (/\*$/.test(path)) {
|
|
1724
1720
|
const re = buildWildcardRegExp(path);
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
}
|
|
1721
|
+
Object.keys(middleware).forEach((m) => {
|
|
1722
|
+
if ((method === METHOD_NAME_ALL || method === m) && !middleware[m][path]) {
|
|
1723
|
+
this.#insertPath(m, path);
|
|
1724
|
+
middleware[m][path] = findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];
|
|
1725
|
+
}
|
|
1726
|
+
});
|
|
1732
1727
|
Object.keys(middleware).forEach((m) => {
|
|
1733
1728
|
if (method === METHOD_NAME_ALL || method === m) {
|
|
1734
1729
|
Object.keys(middleware[m]).forEach((p) => {
|
|
@@ -1750,9 +1745,12 @@ var RegExpRouter = class {
|
|
|
1750
1745
|
const path2 = paths[i];
|
|
1751
1746
|
Object.keys(routes).forEach((m) => {
|
|
1752
1747
|
if (method === METHOD_NAME_ALL || method === m) {
|
|
1753
|
-
routes[m][path2]
|
|
1754
|
-
|
|
1755
|
-
|
|
1748
|
+
if (!routes[m][path2]) {
|
|
1749
|
+
this.#insertPath(m, path2);
|
|
1750
|
+
routes[m][path2] = [
|
|
1751
|
+
...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []
|
|
1752
|
+
];
|
|
1753
|
+
}
|
|
1756
1754
|
routes[m][path2].push([handler, paramCount - len + i + 1]);
|
|
1757
1755
|
}
|
|
1758
1756
|
});
|
|
@@ -1764,33 +1762,58 @@ var RegExpRouter = class {
|
|
|
1764
1762
|
Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {
|
|
1765
1763
|
matchers[method] ||= this.#buildMatcher(method);
|
|
1766
1764
|
});
|
|
1767
|
-
this.#middleware = this.#routes = void 0;
|
|
1765
|
+
this.#middleware = this.#routes = this.#tries = void 0;
|
|
1768
1766
|
clearWildcardRegExpCache();
|
|
1769
1767
|
return matchers;
|
|
1770
1768
|
}
|
|
1771
1769
|
#buildMatcher(method) {
|
|
1772
|
-
const
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1770
|
+
const middleware = this.#middleware[method];
|
|
1771
|
+
const routes = this.#routes[method];
|
|
1772
|
+
const trie = this.#tries[method];
|
|
1773
|
+
const staticMap = /* @__PURE__ */ Object.create(null);
|
|
1774
|
+
const handlerData = [];
|
|
1775
|
+
[middleware, routes].forEach((r) => {
|
|
1776
|
+
for (const path in r) {
|
|
1777
|
+
const handlers = r[path];
|
|
1778
|
+
const pathData = trie.paths[path];
|
|
1779
|
+
if (!pathData) {
|
|
1780
|
+
staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
|
|
1781
|
+
continue;
|
|
1782
|
+
}
|
|
1783
|
+
const paramAssoc = pathData[1];
|
|
1784
|
+
handlerData[pathData[0]] = handlers.map(([h, paramCount]) => {
|
|
1785
|
+
const paramIndexMap = /* @__PURE__ */ Object.create(null);
|
|
1786
|
+
paramCount -= 1;
|
|
1787
|
+
for (; paramCount >= 0; paramCount--) {
|
|
1788
|
+
const [key, value] = paramAssoc[paramCount];
|
|
1789
|
+
paramIndexMap[key] = value;
|
|
1790
|
+
}
|
|
1791
|
+
return [h, paramIndexMap];
|
|
1792
|
+
});
|
|
1783
1793
|
}
|
|
1784
1794
|
});
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1795
|
+
const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();
|
|
1796
|
+
for (let i = 0, len = handlerData.length; i < len; i++) {
|
|
1797
|
+
for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {
|
|
1798
|
+
const map = handlerData[i][j]?.[1];
|
|
1799
|
+
if (!map) {
|
|
1800
|
+
continue;
|
|
1801
|
+
}
|
|
1802
|
+
const keys = Object.keys(map);
|
|
1803
|
+
for (let k = 0, len3 = keys.length; k < len3; k++) {
|
|
1804
|
+
map[keys[k]] = paramReplacementMap[map[keys[k]]];
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1789
1807
|
}
|
|
1808
|
+
const handlerMap = [];
|
|
1809
|
+
for (const i in indexReplacementMap) {
|
|
1810
|
+
handlerMap[i] = handlerData[indexReplacementMap[i]];
|
|
1811
|
+
}
|
|
1812
|
+
return [regexp, handlerMap, staticMap];
|
|
1790
1813
|
}
|
|
1791
1814
|
};
|
|
1792
1815
|
|
|
1793
|
-
// ../node_modules/.bun/hono@4.
|
|
1816
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/smart-router/router.js
|
|
1794
1817
|
var SmartRouter = class {
|
|
1795
1818
|
name = "SmartRouter";
|
|
1796
1819
|
#routers = [];
|
|
@@ -1845,7 +1868,7 @@ var SmartRouter = class {
|
|
|
1845
1868
|
}
|
|
1846
1869
|
};
|
|
1847
1870
|
|
|
1848
|
-
// ../node_modules/.bun/hono@4.
|
|
1871
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/node.js
|
|
1849
1872
|
var emptyParams = /* @__PURE__ */ Object.create(null);
|
|
1850
1873
|
var hasChildren = (children) => {
|
|
1851
1874
|
for (const _ in children) {
|
|
@@ -1979,9 +2002,18 @@ var Node2 = class _Node2 {
|
|
|
1979
2002
|
if (m) {
|
|
1980
2003
|
params[name] = m[0];
|
|
1981
2004
|
this.#pushHandlerSets(handlerSets, child, method, node.#params, params);
|
|
2005
|
+
if (m[0].length === restPathString.length && child.#children["*"]) {
|
|
2006
|
+
this.#pushHandlerSets(
|
|
2007
|
+
handlerSets,
|
|
2008
|
+
child.#children["*"],
|
|
2009
|
+
method,
|
|
2010
|
+
node.#params,
|
|
2011
|
+
params
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
1982
2014
|
if (hasChildren(child.#children)) {
|
|
1983
2015
|
child.#params = params;
|
|
1984
|
-
const componentCount = m[0].match(/\//)?.length ?? 0;
|
|
2016
|
+
const componentCount = m[0].match(/\//g)?.length ?? 0;
|
|
1985
2017
|
const targetCurNodes = curNodesQueue[componentCount] ||= [];
|
|
1986
2018
|
targetCurNodes.push(child);
|
|
1987
2019
|
}
|
|
@@ -2020,7 +2052,7 @@ var Node2 = class _Node2 {
|
|
|
2020
2052
|
}
|
|
2021
2053
|
};
|
|
2022
2054
|
|
|
2023
|
-
// ../node_modules/.bun/hono@4.
|
|
2055
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/router/trie-router/router.js
|
|
2024
2056
|
var TrieRouter = class {
|
|
2025
2057
|
name = "TrieRouter";
|
|
2026
2058
|
#node;
|
|
@@ -2042,7 +2074,7 @@ var TrieRouter = class {
|
|
|
2042
2074
|
}
|
|
2043
2075
|
};
|
|
2044
2076
|
|
|
2045
|
-
// ../node_modules/.bun/hono@4.
|
|
2077
|
+
// ../node_modules/.bun/hono@4.13.2/node_modules/hono/dist/hono.js
|
|
2046
2078
|
var Hono2 = class extends Hono {
|
|
2047
2079
|
/**
|
|
2048
2080
|
* Creates an instance of the Hono class.
|
|
@@ -2058,9 +2090,75 @@ var Hono2 = class extends Hono {
|
|
|
2058
2090
|
};
|
|
2059
2091
|
|
|
2060
2092
|
// src/types/common.ts
|
|
2093
|
+
var SUPPORTED_CURRENCY_CODES = /* @__PURE__ */ new Set([
|
|
2094
|
+
"USD",
|
|
2095
|
+
"EUR",
|
|
2096
|
+
"GBP",
|
|
2097
|
+
"JPY",
|
|
2098
|
+
"CNY",
|
|
2099
|
+
"CHF",
|
|
2100
|
+
"CAD",
|
|
2101
|
+
"AUD",
|
|
2102
|
+
"GHS",
|
|
2103
|
+
"NGN",
|
|
2104
|
+
"KES",
|
|
2105
|
+
"ZAR",
|
|
2106
|
+
"XOF",
|
|
2107
|
+
"XAF",
|
|
2108
|
+
"EGP",
|
|
2109
|
+
"TZS",
|
|
2110
|
+
"UGX",
|
|
2111
|
+
"RWF",
|
|
2112
|
+
"ETB",
|
|
2113
|
+
"ZMW",
|
|
2114
|
+
"BWP",
|
|
2115
|
+
"MUR",
|
|
2116
|
+
"NAD",
|
|
2117
|
+
"MWK",
|
|
2118
|
+
"AOA",
|
|
2119
|
+
"CDF",
|
|
2120
|
+
"GMD",
|
|
2121
|
+
"GNF",
|
|
2122
|
+
"LRD",
|
|
2123
|
+
"SLL",
|
|
2124
|
+
"MZN",
|
|
2125
|
+
"BIF",
|
|
2126
|
+
"INR",
|
|
2127
|
+
"BRL",
|
|
2128
|
+
"COP",
|
|
2129
|
+
"MXN",
|
|
2130
|
+
"KRW",
|
|
2131
|
+
"TRY",
|
|
2132
|
+
"THB",
|
|
2133
|
+
"MYR",
|
|
2134
|
+
"PHP",
|
|
2135
|
+
"IDR",
|
|
2136
|
+
"VND",
|
|
2137
|
+
"SGD",
|
|
2138
|
+
"HKD",
|
|
2139
|
+
"TWD",
|
|
2140
|
+
"AED",
|
|
2141
|
+
"SAR",
|
|
2142
|
+
"ILS"
|
|
2143
|
+
]);
|
|
2144
|
+
function isSupportedCurrency(code) {
|
|
2145
|
+
return SUPPORTED_CURRENCY_CODES.has(code);
|
|
2146
|
+
}
|
|
2147
|
+
var warnedCurrencies = /* @__PURE__ */ new Set();
|
|
2148
|
+
function currencyCode(value) {
|
|
2149
|
+
const normalized = value.trim().toUpperCase();
|
|
2150
|
+
if (!isSupportedCurrency(normalized) && !warnedCurrencies.has(normalized)) {
|
|
2151
|
+
warnedCurrencies.add(normalized);
|
|
2152
|
+
console.warn(
|
|
2153
|
+
`[Cimplify] Unrecognized currency code "${normalized}". It may not format correctly.`
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
return normalized;
|
|
2157
|
+
}
|
|
2061
2158
|
var ErrorCode = {
|
|
2062
2159
|
NOT_FOUND: "NOT_FOUND",
|
|
2063
|
-
VALIDATION_ERROR: "VALIDATION_ERROR"
|
|
2160
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
2161
|
+
CART_EMPTY: "CART_EMPTY"};
|
|
2064
2162
|
var DOCS_ERROR_BASE_URL = "https://docs.cimplify.io/reference/error-codes";
|
|
2065
2163
|
function docsUrlForCode(code) {
|
|
2066
2164
|
return `${DOCS_ERROR_BASE_URL}#${code.toLowerCase().replace(/_/g, "-")}`;
|
|
@@ -2098,6 +2196,8 @@ var ERROR_SUGGESTIONS = {
|
|
|
2098
2196
|
CARD_DECLINED: "Card was declined. Ask shopper to retry or switch payment method.",
|
|
2099
2197
|
INVALID_OTP: "Authorization code is invalid. Let shopper re-enter OTP/PIN and retry.",
|
|
2100
2198
|
OTP_EXPIRED: "Authorization code expired. Request a new OTP and re-submit authorization.",
|
|
2199
|
+
OTP_CHALLENGE_EXHAUSTED: "This verification challenge has no attempts left. Continue checkout without saved details or request a fresh challenge.",
|
|
2200
|
+
OTP_CHALLENGE_CONSUMED: "This verification challenge has already been used. Continue with the established session or request a fresh challenge.",
|
|
2101
2201
|
AUTHORIZATION_FAILED: "Additional payment authorization failed. Retry authorization or change payment method.",
|
|
2102
2202
|
PAYMENT_ACTION_NOT_COMPLETED: "Required payment action was not completed. Resume provider flow and poll for status.",
|
|
2103
2203
|
SLOT_UNAVAILABLE: "Selected schedule slot is unavailable. Refresh available slots and ask shopper to reselect.",
|
|
@@ -2116,6 +2216,10 @@ var ERROR_SUGGESTIONS = {
|
|
|
2116
2216
|
CHECKOUT_NOT_READY: "Checkout elements are still initializing. Wait for readiness before submit.",
|
|
2117
2217
|
CANCELLED: "Checkout was cancelled. Preserve cart state and allow shopper to retry.",
|
|
2118
2218
|
REQUEST_TIMEOUT: "Provider call timed out. Poll payment/order status before issuing another charge attempt.",
|
|
2219
|
+
CHECKOUT_AUTHORITY_REQUIRED: "Provide an exact cart or purchase-intent authority before starting checkout.",
|
|
2220
|
+
AUTH_CONFIG_REQUIRED: "Configure the OAuth client and redirect URI before mounting embedded authentication.",
|
|
2221
|
+
AUTH_CALLBACK_MISSING_TOKEN: "The authentication callback returned no access token. Restart sign-in before checkout.",
|
|
2222
|
+
AUTH_START_FAILED: "Embedded authentication could not start. Verify the OAuth configuration and retry.",
|
|
2119
2223
|
POPUP_BLOCKED: "Browser blocked provider popup. Ask shopper to enable popups and retry.",
|
|
2120
2224
|
FX_QUOTE_FAILED: "Failed to lock FX quote. Retry currency quote or fallback to base currency."
|
|
2121
2225
|
};
|
|
@@ -2159,7 +2263,7 @@ function authMiddleware(deps) {
|
|
|
2159
2263
|
await next();
|
|
2160
2264
|
};
|
|
2161
2265
|
}
|
|
2162
|
-
var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
|
|
2266
|
+
var IDEMPOTENT_PATH_RE = /\/(cart\/items|cart\/coupons|checkout|purchase-intents|payments\/authorization|auth\/(request-otp|verify-otp|profile|logout))(\/|$)/;
|
|
2163
2267
|
var IDEMPOTENCY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2164
2268
|
function idempotencyMiddleware(deps) {
|
|
2165
2269
|
return async (c, next) => {
|
|
@@ -2169,7 +2273,8 @@ function idempotencyMiddleware(deps) {
|
|
|
2169
2273
|
const key = c.req.header("idempotency-key") ?? c.req.header("Idempotency-Key");
|
|
2170
2274
|
if (!key || key.length > 200) return next();
|
|
2171
2275
|
const session = c.var.session;
|
|
2172
|
-
const
|
|
2276
|
+
const routeScope = c.req.path.includes("/purchase-intents") ? `:${c.req.path}` : "";
|
|
2277
|
+
const recordId = `${session.id}${routeScope}:${key}`;
|
|
2173
2278
|
const existing = deps.registry.idempotency.get(recordId);
|
|
2174
2279
|
if (existing) {
|
|
2175
2280
|
const expiresAt = Date.parse(existing.expires_at);
|
|
@@ -2247,8 +2352,89 @@ var PAYMENT_METHOD = {
|
|
|
2247
2352
|
MOBILE_MONEY: "mobile_money",
|
|
2248
2353
|
CARD: "card"
|
|
2249
2354
|
};
|
|
2355
|
+
var CHECKOUT_NEXT_ACTION = {
|
|
2356
|
+
NONE: "none"};
|
|
2357
|
+
var CHECKOUT_ORDER_RESULT_STATUS = {
|
|
2358
|
+
CONFIRMED: "confirmed"};
|
|
2359
|
+
var AUTHORIZATION_TYPE = {
|
|
2360
|
+
OTP: "otp"};
|
|
2361
|
+
var DEFAULT_CURRENCY = "GHS";
|
|
2362
|
+
|
|
2363
|
+
// src/types/purchase-intent.ts
|
|
2364
|
+
var PURCHASE_INTENT_SOURCE_KIND = {
|
|
2365
|
+
CART: "cart"};
|
|
2366
|
+
var PURCHASING_PRINCIPAL_KIND = {
|
|
2367
|
+
ACCOUNT: "account",
|
|
2368
|
+
BUSINESS: "business",
|
|
2369
|
+
GUEST: "guest"};
|
|
2370
|
+
var PURCHASE_INTENT_STATUS = {
|
|
2371
|
+
OPEN: "open",
|
|
2372
|
+
CONVERTED: "converted"};
|
|
2373
|
+
var PURCHASE_INTENT_SELLABLE_KIND = {
|
|
2374
|
+
PRODUCT: "product",
|
|
2375
|
+
VARIANT: "variant"
|
|
2376
|
+
};
|
|
2377
|
+
var PURCHASE_INTENT_LINE_CONFIGURATION_KIND = {
|
|
2378
|
+
SIMPLE: "simple"};
|
|
2379
|
+
var MOBILE_MONEY_TENDER_AUTHORITY_KIND = {
|
|
2380
|
+
TRANSIENT: "transient"};
|
|
2381
|
+
var PURCHASE_INTENT_MOBILE_MONEY_NETWORK = {
|
|
2382
|
+
MTN: "mtn",
|
|
2383
|
+
TELECEL: "telecel",
|
|
2384
|
+
AIRTEL: "airtel"
|
|
2385
|
+
};
|
|
2386
|
+
var CHECKOUT_COMPLIANCE_LANE = {
|
|
2387
|
+
NATIVE: "native"};
|
|
2388
|
+
var CHECKOUT_COMPLIANCE_REASON = {
|
|
2389
|
+
PHYSICAL_GOODS_NATIVE: "physical_goods_native"};
|
|
2390
|
+
var PURCHASE_INTENT_PAYMENT_METHOD = {
|
|
2391
|
+
CARD: "card",
|
|
2392
|
+
MOBILE_MONEY: "mobile_money"};
|
|
2393
|
+
var PAYMENT_COLLECTION_MODE = {
|
|
2394
|
+
REQUEST_TO_PAY: "request_to_pay",
|
|
2395
|
+
CREDENTIAL_CHARGE: "credential_charge"};
|
|
2396
|
+
var PURCHASE_INTENT_PAYMENT_PROVIDER = {
|
|
2397
|
+
PAYSTACK: "paystack"};
|
|
2398
|
+
var PAYMENT_OPTION_STATUS = {
|
|
2399
|
+
ELIGIBLE: "eligible"};
|
|
2400
|
+
var PAYMENT_OPTION_NEXT_ACTION = {
|
|
2401
|
+
PROVIDER_APPROVAL: "provider_approval"};
|
|
2402
|
+
var LEGACY_CHECKOUT_PAYMENT_STATUS = {
|
|
2403
|
+
SUCCEEDED: "succeeded"};
|
|
2404
|
+
var CHECKOUT_TERMS_COMPONENT = {
|
|
2405
|
+
DUE_NOW: "due_now"};
|
|
2406
|
+
var CHECKOUT_TERMS_DELTA_KIND = {
|
|
2407
|
+
MONEY: "money",
|
|
2408
|
+
PAYMENT_OPTION: "payment_option"
|
|
2409
|
+
};
|
|
2410
|
+
var CHECKOUT_INVALIDATION_REASON = {
|
|
2411
|
+
PAYMENT_OPTION_CHANGED: "payment_option_changed"
|
|
2412
|
+
};
|
|
2413
|
+
var PURCHASE_INTENT_CONFIRM_STATUS = {
|
|
2414
|
+
ORDER_CREATED: "order_created",
|
|
2415
|
+
CARD_POPUP_REQUIRED: "card_popup_required",
|
|
2416
|
+
AUTHORIZATION_REQUIRED: "authorization_required",
|
|
2417
|
+
TERMS_CHANGED: "terms_changed",
|
|
2418
|
+
SELECTION_UNAVAILABLE: "selection_unavailable",
|
|
2419
|
+
SOURCE_UNAVAILABLE: "source_unavailable"
|
|
2420
|
+
};
|
|
2250
2421
|
|
|
2251
2422
|
// src/mock/routes/v1.ts
|
|
2423
|
+
var PURCHASE_INTENT_ROUTE = {
|
|
2424
|
+
COLLECTION: "/purchase-intents",
|
|
2425
|
+
MEMBER: "/purchase-intents/:intent_id",
|
|
2426
|
+
REFRESH: "/purchase-intents/:intent_id/refresh",
|
|
2427
|
+
CONFIRM: "/purchase-intents/:intent_id/confirm",
|
|
2428
|
+
ACTION: "/purchase-intents/:intent_id/attempts/:attempt_id/actions"
|
|
2429
|
+
};
|
|
2430
|
+
var PURCHASE_INTENT_ROUTE_PARAM = {
|
|
2431
|
+
INTENT_ID: "intent_id",
|
|
2432
|
+
ATTEMPT_ID: "attempt_id"
|
|
2433
|
+
};
|
|
2434
|
+
var PAYMENT_ROUTE = {
|
|
2435
|
+
COLLECTION_OPTIONS: "/payments/collection-options",
|
|
2436
|
+
AUTHORIZATION: "/payments/authorization"
|
|
2437
|
+
};
|
|
2252
2438
|
function buildV1(deps) {
|
|
2253
2439
|
const app = new Hono2();
|
|
2254
2440
|
app.use("*", chaosMiddleware(deps));
|
|
@@ -2510,6 +2696,36 @@ function buildV1(deps) {
|
|
|
2510
2696
|
"/catalogue/quotes/:quote_id/refresh",
|
|
2511
2697
|
(c) => tryRoute(c, () => deps.catalogue.refreshQuote(c.req.param("quote_id")))
|
|
2512
2698
|
);
|
|
2699
|
+
app.get("/reviews/summary", (c) => {
|
|
2700
|
+
const productId = c.req.query("product_id");
|
|
2701
|
+
if (productId !== void 0 && (productId.length === 0 || productId.trim() !== productId)) {
|
|
2702
|
+
return fail(c, validation("product_id must be a non-empty canonical identifier"));
|
|
2703
|
+
}
|
|
2704
|
+
const response = {
|
|
2705
|
+
data: {
|
|
2706
|
+
ratings: {
|
|
2707
|
+
product_id: productId ?? null,
|
|
2708
|
+
review_count: 0,
|
|
2709
|
+
rating_sum: 0,
|
|
2710
|
+
average_rating: null,
|
|
2711
|
+
distribution: { one: 0, two: 0, three: 0, four: 0, five: 0 }
|
|
2712
|
+
},
|
|
2713
|
+
trust_chips: {
|
|
2714
|
+
on_time_delivery_percent: null,
|
|
2715
|
+
delivery_sample_size: 0,
|
|
2716
|
+
average_first_response_seconds: null,
|
|
2717
|
+
support_sample_size: 0
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
};
|
|
2721
|
+
return c.json(response);
|
|
2722
|
+
});
|
|
2723
|
+
app.get("/reviews", (c) => {
|
|
2724
|
+
const response = {
|
|
2725
|
+
data: { items: [], next_cursor: null }
|
|
2726
|
+
};
|
|
2727
|
+
return c.json(response);
|
|
2728
|
+
});
|
|
2513
2729
|
app.get("/cart", (c) => tryRoute(c, () => deps.cart.toUICart(deps.cart.get(c.var.session))));
|
|
2514
2730
|
app.delete("/cart", (c) => tryRoute(c, () => deps.cart.clear(c.var.session)));
|
|
2515
2731
|
app.get("/cart/items", (c) => tryRoute(c, () => deps.cart.getItems(c.var.session)));
|
|
@@ -2591,28 +2807,81 @@ function buildV1(deps) {
|
|
|
2591
2807
|
"/subscriptions/:subscription_id/skip",
|
|
2592
2808
|
(c) => tryRoute(c, () => deps.subscriptions.skipNext(c.req.param("subscription_id")))
|
|
2593
2809
|
);
|
|
2594
|
-
app.get(
|
|
2810
|
+
app.get(PAYMENT_ROUTE.COLLECTION_OPTIONS, async (c) => {
|
|
2595
2811
|
const currency = c.req.query("currency")?.trim().toUpperCase();
|
|
2596
2812
|
if (!currency || !/^[A-Z]{3}$/.test(currency)) {
|
|
2597
2813
|
return fail(c, validation("currency must be a canonical three-letter code"));
|
|
2598
2814
|
}
|
|
2599
2815
|
return tryRoute(c, () => ({
|
|
2600
2816
|
presentment_currency: currency,
|
|
2601
|
-
options: currency ===
|
|
2817
|
+
options: currency === DEFAULT_CURRENCY ? [
|
|
2602
2818
|
{ method: PAYMENT_METHOD.CARD, network: null },
|
|
2603
|
-
{
|
|
2604
|
-
|
|
2605
|
-
|
|
2819
|
+
{
|
|
2820
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
2821
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.MTN
|
|
2822
|
+
},
|
|
2823
|
+
{
|
|
2824
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
2825
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.TELECEL
|
|
2826
|
+
},
|
|
2827
|
+
{
|
|
2828
|
+
method: PAYMENT_METHOD.MOBILE_MONEY,
|
|
2829
|
+
network: PURCHASE_INTENT_MOBILE_MONEY_NETWORK.AIRTEL
|
|
2830
|
+
}
|
|
2606
2831
|
] : currency === "USD" ? [{ method: PAYMENT_METHOD.CARD, network: null }] : []
|
|
2607
2832
|
}));
|
|
2608
2833
|
});
|
|
2609
|
-
app.post(
|
|
2834
|
+
app.post(PURCHASE_INTENT_ROUTE.COLLECTION, async (c) => {
|
|
2835
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2836
|
+
return tryRoute(c, () => deps.purchaseIntents.mint(c.var.session, body));
|
|
2837
|
+
});
|
|
2838
|
+
app.get(
|
|
2839
|
+
PURCHASE_INTENT_ROUTE.MEMBER,
|
|
2840
|
+
(c) => tryRoute(
|
|
2841
|
+
c,
|
|
2842
|
+
() => deps.purchaseIntents.get(
|
|
2843
|
+
c.var.session,
|
|
2844
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID)
|
|
2845
|
+
)
|
|
2846
|
+
)
|
|
2847
|
+
);
|
|
2848
|
+
app.post(PURCHASE_INTENT_ROUTE.REFRESH, async (c) => {
|
|
2849
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2850
|
+
return tryRoute(
|
|
2851
|
+
c,
|
|
2852
|
+
() => deps.purchaseIntents.refresh(
|
|
2853
|
+
c.var.session,
|
|
2854
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
2855
|
+
body.expected_intent_revision
|
|
2856
|
+
)
|
|
2857
|
+
);
|
|
2858
|
+
});
|
|
2859
|
+
app.post(PURCHASE_INTENT_ROUTE.CONFIRM, async (c) => {
|
|
2860
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2861
|
+
return tryRoute(
|
|
2862
|
+
c,
|
|
2863
|
+
() => deps.purchaseIntents.confirm(
|
|
2864
|
+
c.var.session,
|
|
2865
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
2866
|
+
body
|
|
2867
|
+
)
|
|
2868
|
+
);
|
|
2869
|
+
});
|
|
2870
|
+
app.post(PURCHASE_INTENT_ROUTE.ACTION, async (c) => {
|
|
2610
2871
|
const body = await c.req.json().catch(() => ({}));
|
|
2611
|
-
return tryRoute(
|
|
2872
|
+
return tryRoute(
|
|
2873
|
+
c,
|
|
2874
|
+
() => deps.purchaseIntents.submitAction(
|
|
2875
|
+
c.var.session,
|
|
2876
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.INTENT_ID),
|
|
2877
|
+
c.req.param(PURCHASE_INTENT_ROUTE_PARAM.ATTEMPT_ID),
|
|
2878
|
+
body
|
|
2879
|
+
)
|
|
2880
|
+
);
|
|
2612
2881
|
});
|
|
2613
|
-
app.post(
|
|
2882
|
+
app.post(PAYMENT_ROUTE.AUTHORIZATION, async (c) => {
|
|
2614
2883
|
const body = await c.req.json().catch(() => ({}));
|
|
2615
|
-
return tryRoute(c, () => deps.
|
|
2884
|
+
return tryRoute(c, () => deps.payments.authorize(body));
|
|
2616
2885
|
});
|
|
2617
2886
|
app.get("/auth/status", (c) => tryRoute(c, () => deps.auth.status(c.var.session.token)));
|
|
2618
2887
|
app.post("/auth/request-otp", async (c) => {
|
|
@@ -2918,6 +3187,7 @@ function createStateRegistry() {
|
|
|
2918
3187
|
collections: createMemoryStore(),
|
|
2919
3188
|
carts: createMemoryStore(),
|
|
2920
3189
|
orders: createMemoryStore(),
|
|
3190
|
+
purchaseIntents: createMemoryStore(),
|
|
2921
3191
|
subscriptions: createMemoryStore(),
|
|
2922
3192
|
bookings: createMemoryStore(),
|
|
2923
3193
|
services: createMemoryStore(),
|
|
@@ -3067,9 +3337,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3067
3337
|
// injected into the HTML page served by the mock. Edit this file directly \u2014
|
|
3068
3338
|
// type-check via tsgo + the IframeToParentMessage shape from the SDK.
|
|
3069
3339
|
(function () {
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3340
|
+
const TYPE = window.__CIMPLIFY_MOCK_ELEMENT__;
|
|
3341
|
+
const DEFAULT_BUSINESS = window.__CIMPLIFY_MOCK_BUSINESS__;
|
|
3342
|
+
const MSG = {
|
|
3073
3343
|
INIT: "init", SET_TOKEN: "set_token", SET_CART: "set_cart", GET_DATA: "get_data",
|
|
3074
3344
|
REFRESH_TOKEN: "refresh_token", LOGOUT: "logout",
|
|
3075
3345
|
PROCESS_CHECKOUT: "process_checkout", ABORT_CHECKOUT: "abort_checkout",
|
|
@@ -3082,12 +3352,36 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3082
3352
|
CHECKOUT_STATUS: "checkout_status", CHECKOUT_COMPLETE: "checkout_complete",
|
|
3083
3353
|
ORDER_TYPE_CHANGED: "order_type_changed", REQUEST_SUBMIT: "request_submit"
|
|
3084
3354
|
};
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3355
|
+
const PURCHASE_INTENT = {
|
|
3356
|
+
PATH: "/api/v1/purchase-intents",
|
|
3357
|
+
ORDER_CREATED: "order_created",
|
|
3358
|
+
CARD_POPUP_REQUIRED: "card_popup_required",
|
|
3359
|
+
AUTHORIZATION_REQUIRED: "authorization_required",
|
|
3360
|
+
ELIGIBLE: "eligible"
|
|
3361
|
+
};
|
|
3362
|
+
const CHECKOUT_STATUS = {
|
|
3363
|
+
PROCESSING: "processing",
|
|
3364
|
+
SUCCESS: "success",
|
|
3365
|
+
FAILED: "failed"
|
|
3366
|
+
};
|
|
3367
|
+
const CHECKOUT_ERROR = { FAILED: "CHECKOUT_FAILED" };
|
|
3368
|
+
const ORDER_STATUS = { CONFIRMED: "confirmed" };
|
|
3369
|
+
const TENDER = {
|
|
3370
|
+
CARD: "card",
|
|
3371
|
+
MOBILE_MONEY: "mobile_money",
|
|
3372
|
+
AUTHORITY_TRANSIENT: "transient",
|
|
3373
|
+
MTN: "mtn"
|
|
3374
|
+
};
|
|
3375
|
+
const PAYMENT_PROVIDER = { PAYSTACK: "paystack" };
|
|
3376
|
+
const SOURCE_KIND = { CART: "cart" };
|
|
3377
|
+
const AUTHORIZATION = { OTP: "otp" };
|
|
3378
|
+
const EXACT_ZERO_MONEY = /^[+-]?0+(?:\\.0+)?(?:[eE][+-]?\\d+)?$/;
|
|
3379
|
+
const qs = new URLSearchParams(location.search);
|
|
3380
|
+
const AUTO = qs.get("auto") === "true";
|
|
3381
|
+
const DEMO_OTP = qs.get("otp") || "123456";
|
|
3382
|
+
const ORIGIN = qs.get("origin") || "*";
|
|
3383
|
+
const NONCE = qs.get("nonce");
|
|
3384
|
+
const state = {
|
|
3091
3385
|
businessId: DEFAULT_BUSINESS,
|
|
3092
3386
|
token: null,
|
|
3093
3387
|
cart: null,
|
|
@@ -3098,15 +3392,33 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3098
3392
|
contact: null,
|
|
3099
3393
|
pendingOtp: null
|
|
3100
3394
|
};
|
|
3101
|
-
|
|
3102
|
-
|
|
3395
|
+
const content = document.getElementById("content");
|
|
3396
|
+
const title = document.getElementById("title");
|
|
3103
3397
|
|
|
3104
3398
|
function send(msg) {
|
|
3105
3399
|
if (NONCE) msg.nonce = NONCE;
|
|
3106
3400
|
parent.postMessage(msg, ORIGIN);
|
|
3107
3401
|
}
|
|
3402
|
+
function isExactlyZeroMoney(value) {
|
|
3403
|
+
return typeof value === "number"
|
|
3404
|
+
? Object.is(value, 0) || Object.is(value, -0)
|
|
3405
|
+
: EXACT_ZERO_MONEY.test(String(value).trim());
|
|
3406
|
+
}
|
|
3108
3407
|
function reportHeight() { send({ type: MSG.HEIGHT_CHANGE, height: document.documentElement.scrollHeight }); }
|
|
3109
3408
|
function ready() { send({ type: MSG.READY, height: document.documentElement.scrollHeight }); }
|
|
3409
|
+
function responseWithBody(response) {
|
|
3410
|
+
return response.json().then(function (body) {
|
|
3411
|
+
return { status: response.status, body: body };
|
|
3412
|
+
});
|
|
3413
|
+
}
|
|
3414
|
+
function assertSuccessfulResponse(response, fallbackMessage) {
|
|
3415
|
+
if (response.status >= 400) {
|
|
3416
|
+
throw new Error(
|
|
3417
|
+
(response.body && response.body.error && response.body.error.message) || fallbackMessage
|
|
3418
|
+
);
|
|
3419
|
+
}
|
|
3420
|
+
return response.body;
|
|
3421
|
+
}
|
|
3110
3422
|
|
|
3111
3423
|
function defaultAddress() {
|
|
3112
3424
|
return {
|
|
@@ -3116,9 +3428,20 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3116
3428
|
};
|
|
3117
3429
|
}
|
|
3118
3430
|
function defaultPayment(type) {
|
|
3119
|
-
if (type ===
|
|
3120
|
-
|
|
3121
|
-
|
|
3431
|
+
if (type === TENDER.CARD) {
|
|
3432
|
+
return {
|
|
3433
|
+
type: TENDER.CARD,
|
|
3434
|
+
provider: PAYMENT_PROVIDER.PAYSTACK,
|
|
3435
|
+
last_four: "4242",
|
|
3436
|
+
label: "Visa \u2022\u2022\u2022\u2022 4242"
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
return {
|
|
3440
|
+
type: TENDER.MOBILE_MONEY,
|
|
3441
|
+
provider: TENDER.MTN,
|
|
3442
|
+
phone_number: "+233244000000",
|
|
3443
|
+
label: "MTN Mobile Money"
|
|
3444
|
+
};
|
|
3122
3445
|
}
|
|
3123
3446
|
|
|
3124
3447
|
function renderAuth() {
|
|
@@ -3143,12 +3466,12 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3143
3466
|
' <div class="option" data-key="lagos">5 Allen Ave, Ikeja, Lagos</div>' +
|
|
3144
3467
|
'</div>' +
|
|
3145
3468
|
'<button id="confirm">Use this address</button>';
|
|
3146
|
-
|
|
3469
|
+
const options = {
|
|
3147
3470
|
"default": defaultAddress(),
|
|
3148
3471
|
"east-legon": { street_address: "8 Ring Road", city: "Accra", region: "Greater Accra", country: "GH", latitude: 5.65, longitude: -0.16 },
|
|
3149
3472
|
"lagos": { street_address: "5 Allen Avenue", city: "Lagos", region: "Lagos", country: "NG", latitude: 6.5244, longitude: 3.3792 }
|
|
3150
3473
|
};
|
|
3151
|
-
|
|
3474
|
+
let picked = "default";
|
|
3152
3475
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
3153
3476
|
el.onclick = function () {
|
|
3154
3477
|
picked = el.getAttribute("data-key");
|
|
@@ -3171,10 +3494,9 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3171
3494
|
'<div class="options" id="pay-options">' +
|
|
3172
3495
|
' <div class="option selected" data-method="mobile_money">\u{1F4F1} MTN Mobile Money</div>' +
|
|
3173
3496
|
' <div class="option" data-method="card">\u{1F4B3} Card (Paystack)</div>' +
|
|
3174
|
-
' <div class="option" data-method="cash">\u{1F4B5} Cash on delivery</div>' +
|
|
3175
3497
|
'</div>' +
|
|
3176
3498
|
'<button id="confirm">Use this method</button>';
|
|
3177
|
-
|
|
3499
|
+
let picked = TENDER.MOBILE_MONEY;
|
|
3178
3500
|
Array.prototype.forEach.call(document.querySelectorAll(".option"), function (el) {
|
|
3179
3501
|
el.onclick = function () {
|
|
3180
3502
|
picked = el.getAttribute("data-method");
|
|
@@ -3218,7 +3540,6 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3218
3540
|
'</div>' +
|
|
3219
3541
|
'<label>Payment method</label>' +
|
|
3220
3542
|
'<select id="pay-method">' +
|
|
3221
|
-
' <option value="cash">Cash</option>' +
|
|
3222
3543
|
' <option value="mobile_money">Mobile Money (MTN)</option>' +
|
|
3223
3544
|
' <option value="card">Card (Paystack)</option>' +
|
|
3224
3545
|
'</select>' +
|
|
@@ -3235,65 +3556,139 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3235
3556
|
}
|
|
3236
3557
|
|
|
3237
3558
|
function runCheckout() {
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3559
|
+
const statusEl = document.getElementById("status");
|
|
3560
|
+
const errEl = document.getElementById("err");
|
|
3561
|
+
const resultEl = document.getElementById("result");
|
|
3241
3562
|
if (errEl) errEl.textContent = "";
|
|
3242
3563
|
if (!state.cart) { errEl.textContent = "No cart provided"; return; }
|
|
3243
|
-
|
|
3564
|
+
const customer = {
|
|
3244
3565
|
name: document.getElementById("cust-name").value.trim() || "Customer",
|
|
3245
3566
|
email: document.getElementById("cust-email").value.trim() || "noemail@example.com",
|
|
3246
3567
|
phone: document.getElementById("cust-phone").value.trim() || "+233000000000",
|
|
3247
3568
|
save_details: false
|
|
3248
3569
|
};
|
|
3249
|
-
|
|
3250
|
-
|
|
3570
|
+
const paymentMethod = document.getElementById("pay-method").value;
|
|
3571
|
+
const orderType = document.getElementById("order-type").value;
|
|
3251
3572
|
statusEl.textContent = "Processing\u2026";
|
|
3252
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
3573
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.PROCESSING, context: {} });
|
|
3253
3574
|
|
|
3254
|
-
|
|
3575
|
+
const headers = { "content-type": "application/json" };
|
|
3255
3576
|
if (state.token) headers["x-session-token"] = state.token;
|
|
3256
3577
|
|
|
3257
|
-
|
|
3578
|
+
const preference = paymentMethod === TENDER.MOBILE_MONEY
|
|
3579
|
+
? {
|
|
3580
|
+
method_kind: TENDER.MOBILE_MONEY,
|
|
3581
|
+
authority: {
|
|
3582
|
+
authority_kind: TENDER.AUTHORITY_TRANSIENT,
|
|
3583
|
+
phone_number: customer.phone,
|
|
3584
|
+
network: TENDER.MTN
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
: { method_kind: TENDER.CARD };
|
|
3588
|
+
const intentKey = "mock-element:" + state.cart.id;
|
|
3589
|
+
fetch(PURCHASE_INTENT.PATH, {
|
|
3258
3590
|
method: "POST",
|
|
3259
3591
|
headers: headers,
|
|
3260
3592
|
body: JSON.stringify({
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3593
|
+
client_intent_key: intentKey,
|
|
3594
|
+
source: { kind: SOURCE_KIND.CART, cart_id: state.cart.id },
|
|
3595
|
+
checkout: {
|
|
3596
|
+
customer: customer,
|
|
3597
|
+
order_type: orderType,
|
|
3598
|
+
address_info: state.address || {},
|
|
3599
|
+
tender_preference: preference
|
|
3600
|
+
}
|
|
3266
3601
|
})
|
|
3267
3602
|
})
|
|
3268
|
-
.then(
|
|
3603
|
+
.then(responseWithBody)
|
|
3269
3604
|
.then(function (resp) {
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3605
|
+
const intent = assertSuccessfulResponse(resp, "Intent creation failed");
|
|
3606
|
+
const terms = intent.current_terms;
|
|
3607
|
+
const option = terms && terms.payment_options && terms.payment_options.filter(function (candidate) {
|
|
3608
|
+
return candidate.status === PURCHASE_INTENT.ELIGIBLE && candidate.method_kind === preference.method_kind;
|
|
3609
|
+
})[0];
|
|
3610
|
+
if (!terms || (!option && !isExactlyZeroMoney(terms.money.rail_amount))) {
|
|
3611
|
+
throw new Error("No eligible payment option");
|
|
3612
|
+
}
|
|
3613
|
+
const confirmationPath = PURCHASE_INTENT.PATH + "/" + encodeURIComponent(intent.id) + "/confirm";
|
|
3614
|
+
const confirmationBody = JSON.stringify({
|
|
3615
|
+
accepted_intent_revision: terms.intent_revision,
|
|
3616
|
+
accepted_terms_version: terms.terms_version,
|
|
3617
|
+
accepted_due_now: terms.money.due_now,
|
|
3618
|
+
accepted_customer_debit_amount: option
|
|
3619
|
+
? option.customer_debit_amount
|
|
3620
|
+
: terms.money.rail_amount,
|
|
3621
|
+
currency: terms.money.currency,
|
|
3622
|
+
payment_option_id: option ? option.id : null,
|
|
3623
|
+
idempotency_key: "mock-confirm:" + intent.id
|
|
3624
|
+
});
|
|
3625
|
+
function confirmIntent() {
|
|
3626
|
+
return fetch(confirmationPath, {
|
|
3627
|
+
method: "POST",
|
|
3628
|
+
headers: headers,
|
|
3629
|
+
body: confirmationBody
|
|
3630
|
+
})
|
|
3631
|
+
.then(responseWithBody)
|
|
3632
|
+
.then(function (response) {
|
|
3633
|
+
return assertSuccessfulResponse(response, "Intent confirmation failed");
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
return confirmIntent().then(function (outcome) {
|
|
3638
|
+
// This mock has no external provider window. Replaying the exact
|
|
3639
|
+
// idempotent confirmation models the provider-approved callback.
|
|
3640
|
+
return outcome.status === PURCHASE_INTENT.CARD_POPUP_REQUIRED
|
|
3641
|
+
? confirmIntent()
|
|
3642
|
+
: outcome;
|
|
3643
|
+
});
|
|
3644
|
+
})
|
|
3645
|
+
.then(function (outcome) {
|
|
3646
|
+
if (outcome.status !== PURCHASE_INTENT.AUTHORIZATION_REQUIRED) return outcome;
|
|
3647
|
+
return fetch(
|
|
3648
|
+
PURCHASE_INTENT.PATH + "/" + encodeURIComponent(outcome.intent_id) +
|
|
3649
|
+
"/attempts/" + encodeURIComponent(outcome.attempt_id) + "/actions",
|
|
3650
|
+
{
|
|
3651
|
+
method: "POST",
|
|
3652
|
+
headers: headers,
|
|
3653
|
+
body: JSON.stringify({ authorization: { type: AUTHORIZATION.OTP, value: DEMO_OTP } })
|
|
3654
|
+
}
|
|
3655
|
+
)
|
|
3656
|
+
.then(responseWithBody)
|
|
3657
|
+
.then(function (response) {
|
|
3658
|
+
return assertSuccessfulResponse(response, "Authorization failed");
|
|
3659
|
+
});
|
|
3660
|
+
})
|
|
3661
|
+
.then(function (outcome) {
|
|
3662
|
+
if (outcome.status !== PURCHASE_INTENT.ORDER_CREATED) {
|
|
3663
|
+
throw new Error("Checkout requires " + outcome.status.replace(/_/g, " "));
|
|
3276
3664
|
}
|
|
3277
|
-
|
|
3278
|
-
|
|
3665
|
+
send({
|
|
3666
|
+
type: MSG.CHECKOUT_STATUS,
|
|
3667
|
+
status: CHECKOUT_STATUS.SUCCESS,
|
|
3668
|
+
context: { order_id: outcome.order_id }
|
|
3669
|
+
});
|
|
3279
3670
|
send({
|
|
3280
3671
|
type: MSG.CHECKOUT_COMPLETE,
|
|
3281
3672
|
success: true,
|
|
3282
3673
|
order: {
|
|
3283
|
-
id:
|
|
3284
|
-
order_number:
|
|
3285
|
-
status:
|
|
3674
|
+
id: outcome.order_id,
|
|
3675
|
+
order_number: outcome.order_number || outcome.order_id,
|
|
3676
|
+
status: ORDER_STATUS.CONFIRMED,
|
|
3286
3677
|
total: (state.cart && state.cart.total_price) || "0.00",
|
|
3287
3678
|
currency: (state.cart && state.cart.currency) || "GHS"
|
|
3288
3679
|
}
|
|
3289
3680
|
});
|
|
3290
|
-
statusEl.textContent = "Success \u2014 order " +
|
|
3681
|
+
statusEl.textContent = "Success \u2014 order " + outcome.order_id;
|
|
3291
3682
|
resultEl.style.display = "block";
|
|
3292
|
-
resultEl.textContent = JSON.stringify(
|
|
3683
|
+
resultEl.textContent = JSON.stringify(outcome, null, 2);
|
|
3293
3684
|
})
|
|
3294
3685
|
.catch(function (e) {
|
|
3295
|
-
send({
|
|
3296
|
-
|
|
3686
|
+
send({
|
|
3687
|
+
type: MSG.CHECKOUT_COMPLETE,
|
|
3688
|
+
success: false,
|
|
3689
|
+
error: { code: CHECKOUT_ERROR.FAILED, message: String(e), recoverable: true }
|
|
3690
|
+
});
|
|
3691
|
+
statusEl.textContent = "Failed";
|
|
3297
3692
|
errEl.textContent = String(e);
|
|
3298
3693
|
});
|
|
3299
3694
|
}
|
|
@@ -3310,7 +3705,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3310
3705
|
break;
|
|
3311
3706
|
case MSG.SET_CART:
|
|
3312
3707
|
state.cart = msg.cart;
|
|
3313
|
-
|
|
3708
|
+
const s = document.getElementById("status");
|
|
3314
3709
|
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");
|
|
3315
3710
|
reportHeight();
|
|
3316
3711
|
break;
|
|
@@ -3334,7 +3729,7 @@ var ELEMENT_HANDLER_JS = `/* eslint-disable */
|
|
|
3334
3729
|
}
|
|
3335
3730
|
break;
|
|
3336
3731
|
case MSG.ABORT_CHECKOUT:
|
|
3337
|
-
send({ type: MSG.CHECKOUT_STATUS, status:
|
|
3732
|
+
send({ type: MSG.CHECKOUT_STATUS, status: CHECKOUT_STATUS.FAILED, context: {} });
|
|
3338
3733
|
break;
|
|
3339
3734
|
}
|
|
3340
3735
|
}
|
|
@@ -3405,9 +3800,10 @@ function buildLink(deps) {
|
|
|
3405
3800
|
app.post("/auth/request-otp", async (c) => {
|
|
3406
3801
|
const body = await c.req.json().catch(() => ({}));
|
|
3407
3802
|
return tryRoute(c, () => {
|
|
3408
|
-
deps.auth.requestOtp(body.contact ?? "");
|
|
3803
|
+
const challenge = deps.auth.requestOtp(body.contact ?? "");
|
|
3409
3804
|
return {
|
|
3410
3805
|
success: true,
|
|
3806
|
+
challenge_id: challenge.otp_id,
|
|
3411
3807
|
message: `OTP sent to ${body.contact ?? "contact"}`,
|
|
3412
3808
|
expires_in: 300,
|
|
3413
3809
|
is_new_account: false
|
|
@@ -3417,7 +3813,11 @@ function buildLink(deps) {
|
|
|
3417
3813
|
app.post("/auth/verify-otp", async (c) => {
|
|
3418
3814
|
const body = await c.req.json().catch(() => ({}));
|
|
3419
3815
|
return tryRoute(c, () => {
|
|
3420
|
-
const session = deps.auth.verifyOtp(
|
|
3816
|
+
const session = deps.auth.verifyOtp(
|
|
3817
|
+
body.contact ?? "",
|
|
3818
|
+
body.otp_code ?? "",
|
|
3819
|
+
body.challenge_id
|
|
3820
|
+
);
|
|
3421
3821
|
c.header("x-session-token", session.token);
|
|
3422
3822
|
const customer = deps.link.ensureCustomer(session.customer_id ?? session.id, {
|
|
3423
3823
|
email: body.contact?.includes("@") ? body.contact : void 0,
|
|
@@ -3511,10 +3911,7 @@ function buildLink(deps) {
|
|
|
3511
3911
|
};
|
|
3512
3912
|
});
|
|
3513
3913
|
});
|
|
3514
|
-
app.get(
|
|
3515
|
-
"/preferences",
|
|
3516
|
-
(c) => tryRoute(c, () => deps.link.getPreferences(customerId(c)))
|
|
3517
|
-
);
|
|
3914
|
+
app.get("/preferences", (c) => tryRoute(c, () => deps.link.getPreferences(customerId(c))));
|
|
3518
3915
|
app.post("/preferences", async (c) => {
|
|
3519
3916
|
const body = await c.req.json().catch(() => ({}));
|
|
3520
3917
|
return tryRoute(c, () => {
|
|
@@ -3946,6 +4343,7 @@ function createAuthService(opts) {
|
|
|
3946
4343
|
token: id,
|
|
3947
4344
|
business_id: options.defaultBusinessId,
|
|
3948
4345
|
is_authenticated: authenticated,
|
|
4346
|
+
account_id: authenticated ? ids.raw("account") : void 0,
|
|
3949
4347
|
customer_id: authenticated ? ids.customer() : void 0,
|
|
3950
4348
|
customer_name: profile?.name,
|
|
3951
4349
|
customer_email: profile?.email,
|
|
@@ -3991,9 +4389,16 @@ function createAuthService(opts) {
|
|
|
3991
4389
|
code_hint: options.mode === "permissive" ? code : void 0
|
|
3992
4390
|
};
|
|
3993
4391
|
},
|
|
3994
|
-
verifyOtp(contact, code) {
|
|
3995
|
-
|
|
3996
|
-
|
|
4392
|
+
verifyOtp(contact, code, challengeId) {
|
|
4393
|
+
let otp = challengeId ? registry.otps.get(challengeId) : void 0;
|
|
4394
|
+
if (!challengeId) {
|
|
4395
|
+
const matching = registry.otps.list((o) => o.contact === contact);
|
|
4396
|
+
otp = matching[matching.length - 1];
|
|
4397
|
+
}
|
|
4398
|
+
if (challengeId) {
|
|
4399
|
+
if (!otp) throw new Error("No OTP challenge found");
|
|
4400
|
+
if (otp.contact !== contact) throw new Error("OTP challenge does not match contact");
|
|
4401
|
+
}
|
|
3997
4402
|
if (options.mode === "strict") {
|
|
3998
4403
|
if (!otp) throw new Error("No OTP requested");
|
|
3999
4404
|
if (otp.code !== code && code !== options.defaultOtp) throw new Error("Invalid OTP");
|
|
@@ -4021,7 +4426,7 @@ function createAuthService(opts) {
|
|
|
4021
4426
|
if (!s) return { is_authenticated: false, user_type: "guest" };
|
|
4022
4427
|
return {
|
|
4023
4428
|
is_authenticated: s.is_authenticated,
|
|
4024
|
-
account_id: s.
|
|
4429
|
+
account_id: s.account_id,
|
|
4025
4430
|
user_type: s.is_authenticated ? "customer" : "guest",
|
|
4026
4431
|
session_expires_at: s.expires_at ? Math.floor(new Date(s.expires_at).getTime() / 1e3) : void 0,
|
|
4027
4432
|
session: s
|
|
@@ -5319,88 +5724,10 @@ function createCartService(opts) {
|
|
|
5319
5724
|
};
|
|
5320
5725
|
}
|
|
5321
5726
|
|
|
5322
|
-
// src/mock/domain/
|
|
5323
|
-
function
|
|
5324
|
-
const { registry, clock,
|
|
5727
|
+
// src/mock/domain/payment.ts
|
|
5728
|
+
function createPaymentService(options) {
|
|
5729
|
+
const { registry, clock, bus } = options;
|
|
5325
5730
|
return {
|
|
5326
|
-
process(session, body) {
|
|
5327
|
-
if (!body.cart_id) throw validation("cart_id required");
|
|
5328
|
-
const c = registry.carts.get(body.cart_id);
|
|
5329
|
-
if (!c) throw notFound(`Cart ${body.cart_id} not found`);
|
|
5330
|
-
if (c.items.length === 0) throw validation("Cart is empty", { code: "CART_EMPTY" });
|
|
5331
|
-
const orderId = ids.order();
|
|
5332
|
-
const orderNumber = ids.orderNumber();
|
|
5333
|
-
const paymentId = ids.payment();
|
|
5334
|
-
const reference = ids.payment();
|
|
5335
|
-
const requiresAuth = body.payment_method === "mobile_money";
|
|
5336
|
-
const isCard = body.payment_method === "card" || body.payment_method === "stripe";
|
|
5337
|
-
const cardProvider = body.payment_method === "stripe" || body.pay_currency?.toUpperCase() === "USD" ? "stripe" : "paystack";
|
|
5338
|
-
const order = {
|
|
5339
|
-
id: orderId,
|
|
5340
|
-
business_id: c.business_id,
|
|
5341
|
-
user_friendly_id: orderNumber,
|
|
5342
|
-
channel: c.channel,
|
|
5343
|
-
status: requiresAuth ? "pending" : "confirmed",
|
|
5344
|
-
payment_state: requiresAuth ? "not_paid" : "paid",
|
|
5345
|
-
order_type: body.order_type,
|
|
5346
|
-
customer_id: session.customer_id,
|
|
5347
|
-
customer_name: body.customer.name,
|
|
5348
|
-
customer_email: body.customer.email,
|
|
5349
|
-
customer_phone: body.customer.phone,
|
|
5350
|
-
delivery_address: body.address_info?.street_address,
|
|
5351
|
-
delivery_fee: c.delivery_fee,
|
|
5352
|
-
subtotal: c.subtotal,
|
|
5353
|
-
tax_amount: c.tax_amount,
|
|
5354
|
-
service_charge: c.service_charge,
|
|
5355
|
-
total_discounts: c.total_discounts,
|
|
5356
|
-
total_price: c.total_price,
|
|
5357
|
-
currency: c.currency,
|
|
5358
|
-
items: c.items,
|
|
5359
|
-
applied_discount_ids: c.applied_discount_ids,
|
|
5360
|
-
applied_discount_codes: c.applied_discount_codes,
|
|
5361
|
-
payment_id: paymentId,
|
|
5362
|
-
payment_reference: reference,
|
|
5363
|
-
metadata: body.metadata,
|
|
5364
|
-
created_at: clock.iso(),
|
|
5365
|
-
updated_at: clock.iso()
|
|
5366
|
-
};
|
|
5367
|
-
registry.orders.put(orderId, order);
|
|
5368
|
-
registry.carts.put(c.id, { ...c, status: "converted", updated_at: clock.iso() });
|
|
5369
|
-
bus.emit("order.created", { order_id: orderId, order_number: orderNumber }, c.business_id);
|
|
5370
|
-
if (!requiresAuth) {
|
|
5371
|
-
bus.emit("order.payment.captured", { order_id: orderId, reference }, c.business_id);
|
|
5372
|
-
bus.emit("order.confirmed", { order_id: orderId }, c.business_id);
|
|
5373
|
-
}
|
|
5374
|
-
const billToken = `bt_${reference.slice(-12)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
5375
|
-
const result = {
|
|
5376
|
-
order_id: orderId,
|
|
5377
|
-
order_number: orderNumber,
|
|
5378
|
-
payment_id: paymentId,
|
|
5379
|
-
payment_status: requiresAuth || isCard ? "pending" : "succeeded",
|
|
5380
|
-
payment_reference: reference,
|
|
5381
|
-
requires_authorization: requiresAuth,
|
|
5382
|
-
authorization_type: requiresAuth ? "otp" : void 0,
|
|
5383
|
-
next_action: requiresAuth ? {
|
|
5384
|
-
type: "authorization",
|
|
5385
|
-
authorization_type: "otp",
|
|
5386
|
-
display_text: "Approve on your mobile money app"
|
|
5387
|
-
} : { type: "none" },
|
|
5388
|
-
provider: isCard ? cardProvider : body.payment_method,
|
|
5389
|
-
bill_token: billToken
|
|
5390
|
-
};
|
|
5391
|
-
if (isCard) {
|
|
5392
|
-
const proc = globalThis.process;
|
|
5393
|
-
result.client_secret = cardProvider === "stripe" ? `pi_${orderId.slice(-12)}_secret_${Math.random().toString(36).slice(2, 18)}` : `access_${reference}`;
|
|
5394
|
-
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";
|
|
5395
|
-
result.next_action = {
|
|
5396
|
-
type: "card_popup",
|
|
5397
|
-
provider: cardProvider,
|
|
5398
|
-
client_secret: result.client_secret,
|
|
5399
|
-
public_key: result.public_key
|
|
5400
|
-
};
|
|
5401
|
-
}
|
|
5402
|
-
return result;
|
|
5403
|
-
},
|
|
5404
5731
|
authorize(body) {
|
|
5405
5732
|
if (!body.payment_id?.trim()) throw validation("payment_id required");
|
|
5406
5733
|
if (!body.authorization) throw validation("authorization required");
|
|
@@ -5411,7 +5738,7 @@ function createCheckoutService(opts) {
|
|
|
5411
5738
|
const updated = {
|
|
5412
5739
|
...order,
|
|
5413
5740
|
payment_state: "paid",
|
|
5414
|
-
status:
|
|
5741
|
+
status: CHECKOUT_ORDER_RESULT_STATUS.CONFIRMED,
|
|
5415
5742
|
updated_at: clock.iso()
|
|
5416
5743
|
};
|
|
5417
5744
|
registry.orders.put(order.id, updated);
|
|
@@ -5422,10 +5749,538 @@ function createCheckoutService(opts) {
|
|
|
5422
5749
|
order_number: order.user_friendly_id,
|
|
5423
5750
|
payment_id: body.payment_id,
|
|
5424
5751
|
payment_reference: order.payment_reference,
|
|
5425
|
-
payment_status:
|
|
5752
|
+
payment_status: LEGACY_CHECKOUT_PAYMENT_STATUS.SUCCEEDED,
|
|
5426
5753
|
requires_authorization: false,
|
|
5427
|
-
next_action: { type:
|
|
5754
|
+
next_action: { type: CHECKOUT_NEXT_ACTION.NONE }
|
|
5755
|
+
};
|
|
5756
|
+
}
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
// src/mock/domain/purchase-intent.ts
|
|
5761
|
+
var INTENT_LIFETIME_MS = 15 * 60 * 1e3;
|
|
5762
|
+
var ZERO_MONEY = "0.00";
|
|
5763
|
+
var PURCHASE_INTENT_MOBILE_MONEY_NETWORKS = new Set(
|
|
5764
|
+
Object.values(PURCHASE_INTENT_MOBILE_MONEY_NETWORK)
|
|
5765
|
+
);
|
|
5766
|
+
var MOCK_PURCHASE_INTENT = {
|
|
5767
|
+
OWNER_KIND: { ACCOUNT: "account", STOREFRONT_SESSION: "storefront_session" },
|
|
5768
|
+
CART_STATUS: { ACTIVE: "active", CONVERTED: "converted" },
|
|
5769
|
+
ORDER_STATUS: { PENDING: "pending", CONFIRMED: "confirmed" },
|
|
5770
|
+
PAYMENT_STATE: { NOT_PAID: "not_paid", PAID: "paid" },
|
|
5771
|
+
EVENT: {
|
|
5772
|
+
ORDER_CREATED: "order.created",
|
|
5773
|
+
PAYMENT_CAPTURED: "order.payment.captured",
|
|
5774
|
+
ORDER_CONFIRMED: "order.confirmed"
|
|
5775
|
+
},
|
|
5776
|
+
ID_PREFIX: { INTENT: "pi", ATTEMPT: "attempt" },
|
|
5777
|
+
DEFAULT_LOCATION_ID: "mock-location",
|
|
5778
|
+
MOCK_PAYSTACK_PUBLIC_KEY: "pk_test_mock_paystack",
|
|
5779
|
+
COMPLIANCE: {
|
|
5780
|
+
POLICY_NAME: "mock-storefront-commerce"
|
|
5781
|
+
}
|
|
5782
|
+
};
|
|
5783
|
+
var DECIMAL_WIRE_VALUE = /^([+-]?)(\d+)(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/;
|
|
5784
|
+
function isExactlyZero(value) {
|
|
5785
|
+
if (typeof value === "number") return Object.is(value, 0) || Object.is(value, -0);
|
|
5786
|
+
const match2 = DECIMAL_WIRE_VALUE.exec(value.trim());
|
|
5787
|
+
return Boolean(match2 && [...match2[2], ...match2[3] ?? ""].every((digit) => digit === "0"));
|
|
5788
|
+
}
|
|
5789
|
+
function sameDecimal(left, right) {
|
|
5790
|
+
const canonical = (value) => {
|
|
5791
|
+
const wire = typeof value === "number" ? String(value) : value.trim();
|
|
5792
|
+
const match2 = DECIMAL_WIRE_VALUE.exec(wire);
|
|
5793
|
+
if (!match2) return null;
|
|
5794
|
+
let digits = `${match2[2]}${match2[3] ?? ""}`.replace(/^0+/, "");
|
|
5795
|
+
if (!digits) return "0";
|
|
5796
|
+
let scale = BigInt((match2[3] ?? "").length) - BigInt(match2[4] ?? "0");
|
|
5797
|
+
while (digits.endsWith("0")) {
|
|
5798
|
+
digits = digits.slice(0, -1);
|
|
5799
|
+
scale -= 1n;
|
|
5800
|
+
}
|
|
5801
|
+
const sign = match2[1] === "-" ? "-" : "";
|
|
5802
|
+
return `${sign}${digits}:${scale}`;
|
|
5803
|
+
};
|
|
5804
|
+
const normalizedLeft = canonical(left);
|
|
5805
|
+
return normalizedLeft !== null && normalizedLeft === canonical(right);
|
|
5806
|
+
}
|
|
5807
|
+
function wireMoney(value) {
|
|
5808
|
+
return typeof value === "string" ? value.trim() : String(value);
|
|
5809
|
+
}
|
|
5810
|
+
function decimal(value) {
|
|
5811
|
+
return wireMoney(value.toFixed(2));
|
|
5812
|
+
}
|
|
5813
|
+
function paymentOptions(intentId, currency, amount, expiresAt) {
|
|
5814
|
+
const normalizedCurrency = currencyCode(currency);
|
|
5815
|
+
const normalizedAmount = wireMoney(amount);
|
|
5816
|
+
return [
|
|
5817
|
+
{
|
|
5818
|
+
id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.CARD}`,
|
|
5819
|
+
rank: 0,
|
|
5820
|
+
method_kind: PURCHASE_INTENT_PAYMENT_METHOD.CARD,
|
|
5821
|
+
collection_mode: PAYMENT_COLLECTION_MODE.CREDENTIAL_CHARGE,
|
|
5822
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
5823
|
+
principal_amount: normalizedAmount,
|
|
5824
|
+
fee_amount: wireMoney(ZERO_MONEY),
|
|
5825
|
+
customer_debit_amount: normalizedAmount,
|
|
5826
|
+
currency: normalizedCurrency,
|
|
5827
|
+
status: PAYMENT_OPTION_STATUS.ELIGIBLE,
|
|
5828
|
+
expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
|
|
5829
|
+
expires_at: expiresAt
|
|
5830
|
+
},
|
|
5831
|
+
{
|
|
5832
|
+
id: `${intentId}:${PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY}`,
|
|
5833
|
+
rank: 1,
|
|
5834
|
+
method_kind: PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY,
|
|
5835
|
+
collection_mode: PAYMENT_COLLECTION_MODE.REQUEST_TO_PAY,
|
|
5836
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
5837
|
+
principal_amount: normalizedAmount,
|
|
5838
|
+
fee_amount: wireMoney(ZERO_MONEY),
|
|
5839
|
+
customer_debit_amount: normalizedAmount,
|
|
5840
|
+
currency: normalizedCurrency,
|
|
5841
|
+
status: PAYMENT_OPTION_STATUS.ELIGIBLE,
|
|
5842
|
+
expected_next_action: PAYMENT_OPTION_NEXT_ACTION.PROVIDER_APPROVAL,
|
|
5843
|
+
expires_at: expiresAt
|
|
5844
|
+
}
|
|
5845
|
+
];
|
|
5846
|
+
}
|
|
5847
|
+
function termsForCart(input) {
|
|
5848
|
+
const total = wireMoney(input.total);
|
|
5849
|
+
return {
|
|
5850
|
+
id: `${input.intentId}:terms:${input.version}`,
|
|
5851
|
+
intent_revision: input.revision,
|
|
5852
|
+
terms_version: input.version,
|
|
5853
|
+
terms_digest: `mock:${input.intentId}:${input.revision}:${input.version}`,
|
|
5854
|
+
money: {
|
|
5855
|
+
currency: currencyCode(input.currency),
|
|
5856
|
+
subtotal: wireMoney(input.subtotal),
|
|
5857
|
+
discount_total: wireMoney(input.discount),
|
|
5858
|
+
tax_total: wireMoney(input.tax),
|
|
5859
|
+
delivery_total: wireMoney(input.delivery),
|
|
5860
|
+
statutory_total: wireMoney(ZERO_MONEY),
|
|
5861
|
+
total_obligation: total,
|
|
5862
|
+
due_now: total,
|
|
5863
|
+
rail_amount: total,
|
|
5864
|
+
store_credit_consumed: wireMoney(ZERO_MONEY),
|
|
5865
|
+
voucher_credit_consumed: wireMoney(ZERO_MONEY)
|
|
5866
|
+
},
|
|
5867
|
+
material: {
|
|
5868
|
+
line_digest: `mock-lines:${input.intentId}`,
|
|
5869
|
+
destination_digest: `mock-destination:${input.intentId}`,
|
|
5870
|
+
delivery_promise_digest: `mock-delivery:${input.intentId}`,
|
|
5871
|
+
entitlement_digest: `mock-entitlement:${input.intentId}`,
|
|
5872
|
+
cancellation_terms_digest: `mock-cancellation:${input.intentId}`
|
|
5873
|
+
},
|
|
5874
|
+
compliance: {
|
|
5875
|
+
policy_name: MOCK_PURCHASE_INTENT.COMPLIANCE.POLICY_NAME,
|
|
5876
|
+
policy_version: 1,
|
|
5877
|
+
lane: CHECKOUT_COMPLIANCE_LANE.NATIVE,
|
|
5878
|
+
reason_code: CHECKOUT_COMPLIANCE_REASON.PHYSICAL_GOODS_NATIVE,
|
|
5879
|
+
evidence_digest: `mock-compliance:${input.intentId}`
|
|
5880
|
+
},
|
|
5881
|
+
payment_options: isExactlyZero(input.total) ? [] : paymentOptions(input.intentId, input.currency, input.total, input.validUntil),
|
|
5882
|
+
valid_until: input.validUntil,
|
|
5883
|
+
created_at: input.now
|
|
5884
|
+
};
|
|
5885
|
+
}
|
|
5886
|
+
function intentView(intent) {
|
|
5887
|
+
const {
|
|
5888
|
+
intent_owner: _owner,
|
|
5889
|
+
mint_request: _request,
|
|
5890
|
+
last_confirmation: _confirmation,
|
|
5891
|
+
...view
|
|
5892
|
+
} = intent;
|
|
5893
|
+
return view;
|
|
5894
|
+
}
|
|
5895
|
+
function purchasingPrincipal(session) {
|
|
5896
|
+
if (!session.is_authenticated) {
|
|
5897
|
+
return { kind: PURCHASING_PRINCIPAL_KIND.GUEST };
|
|
5898
|
+
}
|
|
5899
|
+
if (!session.account_id) throw validation("Authenticated mock session has no account authority");
|
|
5900
|
+
return {
|
|
5901
|
+
kind: PURCHASING_PRINCIPAL_KIND.ACCOUNT,
|
|
5902
|
+
account_id: session.account_id
|
|
5903
|
+
};
|
|
5904
|
+
}
|
|
5905
|
+
function assertSupportedTenderPreference(request) {
|
|
5906
|
+
if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) return;
|
|
5907
|
+
const checkout = request.checkout;
|
|
5908
|
+
if (!checkout) throw validation("Cart checkout input is required");
|
|
5909
|
+
const preference = checkout.tender_preference;
|
|
5910
|
+
if (preference?.method_kind !== PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY || preference.authority.authority_kind !== MOBILE_MONEY_TENDER_AUTHORITY_KIND.TRANSIENT) {
|
|
5911
|
+
return;
|
|
5912
|
+
}
|
|
5913
|
+
if (!PURCHASE_INTENT_MOBILE_MONEY_NETWORKS.has(preference.authority.network)) {
|
|
5914
|
+
throw validation("Mobile Money network is not available for PurchaseIntent checkout");
|
|
5915
|
+
}
|
|
5916
|
+
}
|
|
5917
|
+
function intentOwner(session) {
|
|
5918
|
+
if (!session.is_authenticated) {
|
|
5919
|
+
return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.STOREFRONT_SESSION, id: session.id };
|
|
5920
|
+
}
|
|
5921
|
+
if (!session.account_id) throw validation("Authenticated mock session has no account authority");
|
|
5922
|
+
return { kind: MOCK_PURCHASE_INTENT.OWNER_KIND.ACCOUNT, id: session.account_id };
|
|
5923
|
+
}
|
|
5924
|
+
function createPurchaseIntentMockService(options) {
|
|
5925
|
+
const { registry, clock, ids, bus } = options;
|
|
5926
|
+
function stored(session, intentId) {
|
|
5927
|
+
const intent = registry.purchaseIntents.get(intentId);
|
|
5928
|
+
if (!intent) throw notFound(`Purchase intent ${intentId} not found`);
|
|
5929
|
+
const owner = intentOwner(session);
|
|
5930
|
+
if (intent.intent_owner.kind !== owner.kind || intent.intent_owner.id !== owner.id) {
|
|
5931
|
+
throw notFound(`Purchase intent ${intentId} not found`);
|
|
5932
|
+
}
|
|
5933
|
+
return intent;
|
|
5934
|
+
}
|
|
5935
|
+
function completeProviderOrder(intent, order, attemptId) {
|
|
5936
|
+
const updated = {
|
|
5937
|
+
...order,
|
|
5938
|
+
payment_state: MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID,
|
|
5939
|
+
status: MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED,
|
|
5940
|
+
updated_at: clock.iso()
|
|
5941
|
+
};
|
|
5942
|
+
registry.orders.put(order.id, updated);
|
|
5943
|
+
bus.emit(
|
|
5944
|
+
MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
|
|
5945
|
+
{ order_id: order.id, reference: order.payment_reference },
|
|
5946
|
+
order.business_id
|
|
5947
|
+
);
|
|
5948
|
+
bus.emit(MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED, { order_id: order.id }, order.business_id);
|
|
5949
|
+
const completed = {
|
|
5950
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
5951
|
+
intent_id: intent.id,
|
|
5952
|
+
attempt_id: attemptId,
|
|
5953
|
+
order_id: order.id,
|
|
5954
|
+
order_number: order.user_friendly_id,
|
|
5955
|
+
payment_id: order.payment_id
|
|
5956
|
+
};
|
|
5957
|
+
registry.purchaseIntents.put(intent.id, {
|
|
5958
|
+
...intent,
|
|
5959
|
+
updated_at: clock.iso(),
|
|
5960
|
+
last_confirmation: completed
|
|
5961
|
+
});
|
|
5962
|
+
return completed;
|
|
5963
|
+
}
|
|
5964
|
+
return {
|
|
5965
|
+
mint(session, request) {
|
|
5966
|
+
if (!request.client_intent_key.trim()) throw validation("client_intent_key required");
|
|
5967
|
+
if (request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
|
|
5968
|
+
throw validation("The mock currently prepares cart purchase intents only");
|
|
5969
|
+
}
|
|
5970
|
+
const checkoutInput = request.checkout;
|
|
5971
|
+
if (!checkoutInput) throw validation("Cart checkout input is required");
|
|
5972
|
+
assertSupportedTenderPreference(request);
|
|
5973
|
+
const cart = registry.carts.get(request.source.cart_id);
|
|
5974
|
+
if (!cart) throw notFound(`Cart ${request.source.cart_id} not found`);
|
|
5975
|
+
if (cart.items.length === 0) {
|
|
5976
|
+
throw validation("Cart is empty", { code: ErrorCode.CART_EMPTY });
|
|
5977
|
+
}
|
|
5978
|
+
const principal = purchasingPrincipal(session);
|
|
5979
|
+
const owner = intentOwner(session);
|
|
5980
|
+
const existing = registry.purchaseIntents.list(
|
|
5981
|
+
(candidate) => candidate.mint_request.client_intent_key === request.client_intent_key && candidate.intent_owner.kind === owner.kind && candidate.intent_owner.id === owner.id
|
|
5982
|
+
)[0];
|
|
5983
|
+
if (existing) return intentView(existing);
|
|
5984
|
+
const now = clock.iso();
|
|
5985
|
+
const expiresAt = new Date(clock.nowMs() + INTENT_LIFETIME_MS).toISOString();
|
|
5986
|
+
const intentId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.INTENT);
|
|
5987
|
+
const terms = termsForCart({
|
|
5988
|
+
intentId,
|
|
5989
|
+
revision: 1,
|
|
5990
|
+
version: 1,
|
|
5991
|
+
currency: cart.currency,
|
|
5992
|
+
subtotal: cart.subtotal,
|
|
5993
|
+
discount: cart.total_discounts,
|
|
5994
|
+
tax: cart.tax_amount,
|
|
5995
|
+
delivery: cart.delivery_fee,
|
|
5996
|
+
total: cart.total_price,
|
|
5997
|
+
now,
|
|
5998
|
+
validUntil: expiresAt
|
|
5999
|
+
});
|
|
6000
|
+
const intent = {
|
|
6001
|
+
id: intentId,
|
|
6002
|
+
seller_business_id: cart.business_id,
|
|
6003
|
+
principal,
|
|
6004
|
+
revision: 1,
|
|
6005
|
+
status: PURCHASE_INTENT_STATUS.OPEN,
|
|
6006
|
+
source: {
|
|
6007
|
+
kind: PURCHASE_INTENT_SOURCE_KIND.CART,
|
|
6008
|
+
source_id: cart.id,
|
|
6009
|
+
source_version: cart.updated_at,
|
|
6010
|
+
seller_business_id: cart.business_id,
|
|
6011
|
+
subject: {
|
|
6012
|
+
kind: PURCHASING_PRINCIPAL_KIND.BUSINESS,
|
|
6013
|
+
business_id: cart.business_id
|
|
6014
|
+
},
|
|
6015
|
+
binding_digest: `mock-cart:${cart.id}:${cart.updated_at}`
|
|
6016
|
+
},
|
|
6017
|
+
lines: cart.items.map((line, ordinal) => ({
|
|
6018
|
+
id: line.id,
|
|
6019
|
+
ordinal,
|
|
6020
|
+
sellable: {
|
|
6021
|
+
kind: line.variant_id ? PURCHASE_INTENT_SELLABLE_KIND.VARIANT : PURCHASE_INTENT_SELLABLE_KIND.PRODUCT,
|
|
6022
|
+
product_id: line.item_id,
|
|
6023
|
+
variant_id: line.variant_id,
|
|
6024
|
+
location_id: checkoutInput.location_id || MOCK_PURCHASE_INTENT.DEFAULT_LOCATION_ID
|
|
6025
|
+
},
|
|
6026
|
+
quantity: line.quantity,
|
|
6027
|
+
configuration: {
|
|
6028
|
+
kind: PURCHASE_INTENT_LINE_CONFIGURATION_KIND.SIMPLE,
|
|
6029
|
+
variant_id: line.variant_id,
|
|
6030
|
+
customer_inputs: line.customer_inputs
|
|
6031
|
+
}
|
|
6032
|
+
})),
|
|
6033
|
+
line_pricing: cart.items.map((line) => {
|
|
6034
|
+
const unitPrice = Number(line.price) + Number(line.add_ons_price);
|
|
6035
|
+
const lineSubtotal = unitPrice * line.quantity - Number(line.item_discount_amount);
|
|
6036
|
+
return {
|
|
6037
|
+
intent_line_id: line.id,
|
|
6038
|
+
quantity: line.quantity,
|
|
6039
|
+
unit_price: decimal(unitPrice),
|
|
6040
|
+
line_subtotal: decimal(lineSubtotal),
|
|
6041
|
+
currency: currencyCode(cart.currency)
|
|
6042
|
+
};
|
|
6043
|
+
}),
|
|
6044
|
+
current_terms: terms,
|
|
6045
|
+
expires_at: expiresAt,
|
|
6046
|
+
created_at: now,
|
|
6047
|
+
updated_at: now,
|
|
6048
|
+
intent_owner: owner,
|
|
6049
|
+
mint_request: request
|
|
5428
6050
|
};
|
|
6051
|
+
registry.purchaseIntents.put(intentId, intent);
|
|
6052
|
+
return intentView(intent);
|
|
6053
|
+
},
|
|
6054
|
+
get(session, intentId) {
|
|
6055
|
+
return intentView(stored(session, intentId));
|
|
6056
|
+
},
|
|
6057
|
+
refresh(session, intentId, expectedRevision) {
|
|
6058
|
+
const intent = stored(session, intentId);
|
|
6059
|
+
if (intent.revision !== expectedRevision) {
|
|
6060
|
+
throw validation("expected_intent_revision does not match current intent revision");
|
|
6061
|
+
}
|
|
6062
|
+
if (!intent.current_terms) throw validation("Purchase intent has no current terms");
|
|
6063
|
+
const previous = intent.current_terms;
|
|
6064
|
+
const nextDelivery = Number(previous.money.delivery_total) + 1;
|
|
6065
|
+
const nextTotal = Number(previous.money.total_obligation) + 1;
|
|
6066
|
+
const now = clock.iso();
|
|
6067
|
+
const replacement = termsForCart({
|
|
6068
|
+
intentId,
|
|
6069
|
+
revision: intent.revision,
|
|
6070
|
+
version: previous.terms_version + 1,
|
|
6071
|
+
currency: previous.money.currency,
|
|
6072
|
+
subtotal: String(previous.money.subtotal),
|
|
6073
|
+
discount: String(previous.money.discount_total),
|
|
6074
|
+
tax: String(previous.money.tax_total),
|
|
6075
|
+
delivery: nextDelivery.toFixed(2),
|
|
6076
|
+
total: nextTotal.toFixed(2),
|
|
6077
|
+
now,
|
|
6078
|
+
validUntil: intent.expires_at
|
|
6079
|
+
});
|
|
6080
|
+
const updated = { ...intent, current_terms: replacement, updated_at: now };
|
|
6081
|
+
registry.purchaseIntents.put(intentId, updated);
|
|
6082
|
+
return intentView(updated);
|
|
6083
|
+
},
|
|
6084
|
+
confirm(session, intentId, request) {
|
|
6085
|
+
const intent = stored(session, intentId);
|
|
6086
|
+
if (intent.status === PURCHASE_INTENT_STATUS.CONVERTED && intent.last_confirmation) {
|
|
6087
|
+
const orderId2 = "order_id" in intent.last_confirmation ? intent.last_confirmation.order_id : void 0;
|
|
6088
|
+
const order2 = orderId2 ? registry.orders.get(orderId2) : void 0;
|
|
6089
|
+
if (order2?.payment_state === MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID) {
|
|
6090
|
+
return {
|
|
6091
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
6092
|
+
intent_id: intent.id,
|
|
6093
|
+
attempt_id: "attempt_id" in intent.last_confirmation ? intent.last_confirmation.attempt_id : ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT),
|
|
6094
|
+
order_id: order2.id
|
|
6095
|
+
};
|
|
6096
|
+
}
|
|
6097
|
+
if (order2?.payment_id && intent.last_confirmation.status === PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED) {
|
|
6098
|
+
return completeProviderOrder(intent, order2, intent.last_confirmation.attempt_id);
|
|
6099
|
+
}
|
|
6100
|
+
return intent.last_confirmation;
|
|
6101
|
+
}
|
|
6102
|
+
const terms = intent.current_terms;
|
|
6103
|
+
if (!terms) throw validation("Purchase intent has no current terms");
|
|
6104
|
+
const zeroRail = isExactlyZero(terms.money.rail_amount);
|
|
6105
|
+
const option = request.payment_option_id ? terms.payment_options.find(
|
|
6106
|
+
(candidate) => candidate.id === request.payment_option_id && candidate.status === PAYMENT_OPTION_STATUS.ELIGIBLE
|
|
6107
|
+
) : void 0;
|
|
6108
|
+
const acceptedBaseTermsChanged = !sameDecimal(request.accepted_due_now, terms.money.due_now) || request.currency !== terms.money.currency;
|
|
6109
|
+
if (request.accepted_intent_revision !== intent.revision || request.accepted_terms_version !== terms.terms_version || acceptedBaseTermsChanged) {
|
|
6110
|
+
const deltas = acceptedBaseTermsChanged ? [
|
|
6111
|
+
{
|
|
6112
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.MONEY,
|
|
6113
|
+
component: CHECKOUT_TERMS_COMPONENT.DUE_NOW,
|
|
6114
|
+
accepted: request.accepted_due_now,
|
|
6115
|
+
replacement: terms.money.due_now
|
|
6116
|
+
}
|
|
6117
|
+
] : [
|
|
6118
|
+
{
|
|
6119
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
|
|
6120
|
+
accepted_option_id: request.payment_option_id ?? null,
|
|
6121
|
+
accepted_customer_debit_amount: request.accepted_customer_debit_amount,
|
|
6122
|
+
replacement_option_id: option?.id ?? null,
|
|
6123
|
+
replacement_customer_debit_amount: option?.customer_debit_amount ?? null
|
|
6124
|
+
}
|
|
6125
|
+
];
|
|
6126
|
+
return {
|
|
6127
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
|
|
6128
|
+
intent_id: intent.id,
|
|
6129
|
+
replacement: terms,
|
|
6130
|
+
deltas
|
|
6131
|
+
};
|
|
6132
|
+
}
|
|
6133
|
+
if (!zeroRail && !option || zeroRail && request.payment_option_id != null) {
|
|
6134
|
+
return {
|
|
6135
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.SELECTION_UNAVAILABLE,
|
|
6136
|
+
intent_id: intent.id,
|
|
6137
|
+
lines: intent.lines.map((line) => ({
|
|
6138
|
+
line_id: line.id,
|
|
6139
|
+
reason: CHECKOUT_INVALIDATION_REASON.PAYMENT_OPTION_CHANGED
|
|
6140
|
+
}))
|
|
6141
|
+
};
|
|
6142
|
+
}
|
|
6143
|
+
const acceptedCustomerDebitMatches = zeroRail ? isExactlyZero(request.accepted_customer_debit_amount) : Boolean(
|
|
6144
|
+
option && sameDecimal(request.accepted_customer_debit_amount, option.customer_debit_amount)
|
|
6145
|
+
);
|
|
6146
|
+
if (!acceptedCustomerDebitMatches) {
|
|
6147
|
+
return {
|
|
6148
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.TERMS_CHANGED,
|
|
6149
|
+
intent_id: intent.id,
|
|
6150
|
+
replacement: terms,
|
|
6151
|
+
deltas: [
|
|
6152
|
+
{
|
|
6153
|
+
kind: CHECKOUT_TERMS_DELTA_KIND.PAYMENT_OPTION,
|
|
6154
|
+
accepted_option_id: request.payment_option_id ?? null,
|
|
6155
|
+
accepted_customer_debit_amount: request.accepted_customer_debit_amount,
|
|
6156
|
+
replacement_option_id: option?.id ?? null,
|
|
6157
|
+
replacement_customer_debit_amount: option?.customer_debit_amount ?? null
|
|
6158
|
+
}
|
|
6159
|
+
]
|
|
6160
|
+
};
|
|
6161
|
+
}
|
|
6162
|
+
if (intent.mint_request.source.kind !== PURCHASE_INTENT_SOURCE_KIND.CART) {
|
|
6163
|
+
throw validation("The mock currently confirms cart purchase intents only");
|
|
6164
|
+
}
|
|
6165
|
+
const checkoutInput = intent.mint_request.checkout;
|
|
6166
|
+
if (!checkoutInput) throw validation("Cart checkout input is required");
|
|
6167
|
+
const cart = registry.carts.get(intent.mint_request.source.cart_id);
|
|
6168
|
+
if (!cart || cart.status !== MOCK_PURCHASE_INTENT.CART_STATUS.ACTIVE) {
|
|
6169
|
+
return { status: PURCHASE_INTENT_CONFIRM_STATUS.SOURCE_UNAVAILABLE, intent_id: intent.id };
|
|
6170
|
+
}
|
|
6171
|
+
const attemptId = ids.raw(MOCK_PURCHASE_INTENT.ID_PREFIX.ATTEMPT);
|
|
6172
|
+
const orderId = ids.order();
|
|
6173
|
+
const orderNumber = ids.orderNumber();
|
|
6174
|
+
const paymentId = zeroRail ? void 0 : ids.payment();
|
|
6175
|
+
const paymentReference = zeroRail ? void 0 : ids.payment();
|
|
6176
|
+
const needsProvider = option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD || option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY;
|
|
6177
|
+
const paid = zeroRail;
|
|
6178
|
+
const order = {
|
|
6179
|
+
id: orderId,
|
|
6180
|
+
business_id: cart.business_id,
|
|
6181
|
+
user_friendly_id: orderNumber,
|
|
6182
|
+
channel: cart.channel,
|
|
6183
|
+
status: paid ? MOCK_PURCHASE_INTENT.ORDER_STATUS.CONFIRMED : MOCK_PURCHASE_INTENT.ORDER_STATUS.PENDING,
|
|
6184
|
+
payment_state: paid ? MOCK_PURCHASE_INTENT.PAYMENT_STATE.PAID : MOCK_PURCHASE_INTENT.PAYMENT_STATE.NOT_PAID,
|
|
6185
|
+
order_type: checkoutInput.order_type,
|
|
6186
|
+
customer_id: session.customer_id,
|
|
6187
|
+
customer_name: checkoutInput.customer.name,
|
|
6188
|
+
customer_email: checkoutInput.customer.email,
|
|
6189
|
+
customer_phone: checkoutInput.customer.phone,
|
|
6190
|
+
delivery_address: checkoutInput.address_info.street_address,
|
|
6191
|
+
delivery_fee: cart.delivery_fee,
|
|
6192
|
+
subtotal: cart.subtotal,
|
|
6193
|
+
tax_amount: cart.tax_amount,
|
|
6194
|
+
service_charge: cart.service_charge,
|
|
6195
|
+
total_discounts: cart.total_discounts,
|
|
6196
|
+
total_price: cart.total_price,
|
|
6197
|
+
currency: cart.currency,
|
|
6198
|
+
items: cart.items,
|
|
6199
|
+
applied_discount_ids: cart.applied_discount_ids,
|
|
6200
|
+
applied_discount_codes: cart.applied_discount_codes,
|
|
6201
|
+
payment_id: paymentId,
|
|
6202
|
+
payment_reference: paymentReference,
|
|
6203
|
+
metadata: checkoutInput.metadata,
|
|
6204
|
+
created_at: clock.iso(),
|
|
6205
|
+
updated_at: clock.iso()
|
|
6206
|
+
};
|
|
6207
|
+
registry.orders.put(orderId, order);
|
|
6208
|
+
registry.carts.put(cart.id, {
|
|
6209
|
+
...cart,
|
|
6210
|
+
status: MOCK_PURCHASE_INTENT.CART_STATUS.CONVERTED,
|
|
6211
|
+
updated_at: clock.iso()
|
|
6212
|
+
});
|
|
6213
|
+
bus.emit(
|
|
6214
|
+
MOCK_PURCHASE_INTENT.EVENT.ORDER_CREATED,
|
|
6215
|
+
{ order_id: orderId, order_number: orderNumber },
|
|
6216
|
+
cart.business_id
|
|
6217
|
+
);
|
|
6218
|
+
if (paid) {
|
|
6219
|
+
if (paymentReference) {
|
|
6220
|
+
bus.emit(
|
|
6221
|
+
MOCK_PURCHASE_INTENT.EVENT.PAYMENT_CAPTURED,
|
|
6222
|
+
{ order_id: orderId, reference: paymentReference },
|
|
6223
|
+
cart.business_id
|
|
6224
|
+
);
|
|
6225
|
+
}
|
|
6226
|
+
bus.emit(
|
|
6227
|
+
MOCK_PURCHASE_INTENT.EVENT.ORDER_CONFIRMED,
|
|
6228
|
+
{ order_id: orderId },
|
|
6229
|
+
cart.business_id
|
|
6230
|
+
);
|
|
6231
|
+
}
|
|
6232
|
+
let confirmation;
|
|
6233
|
+
if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.MOBILE_MONEY) {
|
|
6234
|
+
confirmation = {
|
|
6235
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED,
|
|
6236
|
+
intent_id: intent.id,
|
|
6237
|
+
attempt_id: attemptId,
|
|
6238
|
+
order_id: orderId,
|
|
6239
|
+
authorization_type: AUTHORIZATION_TYPE.OTP,
|
|
6240
|
+
display_text: "Approve on your mobile money app"
|
|
6241
|
+
};
|
|
6242
|
+
} else if (option?.method_kind === PURCHASE_INTENT_PAYMENT_METHOD.CARD) {
|
|
6243
|
+
confirmation = {
|
|
6244
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.CARD_POPUP_REQUIRED,
|
|
6245
|
+
intent_id: intent.id,
|
|
6246
|
+
attempt_id: attemptId,
|
|
6247
|
+
order_id: orderId,
|
|
6248
|
+
provider: PURCHASE_INTENT_PAYMENT_PROVIDER.PAYSTACK,
|
|
6249
|
+
client_secret: `access_${paymentReference}`,
|
|
6250
|
+
public_key: MOCK_PURCHASE_INTENT.MOCK_PAYSTACK_PUBLIC_KEY
|
|
6251
|
+
};
|
|
6252
|
+
} else {
|
|
6253
|
+
confirmation = {
|
|
6254
|
+
status: PURCHASE_INTENT_CONFIRM_STATUS.ORDER_CREATED,
|
|
6255
|
+
intent_id: intent.id,
|
|
6256
|
+
attempt_id: attemptId,
|
|
6257
|
+
order_id: orderId
|
|
6258
|
+
};
|
|
6259
|
+
}
|
|
6260
|
+
if (needsProvider && !paymentId) {
|
|
6261
|
+
throw validation("Provider-backed purchase intent has no payment authority");
|
|
6262
|
+
}
|
|
6263
|
+
registry.purchaseIntents.put(intent.id, {
|
|
6264
|
+
...intent,
|
|
6265
|
+
status: PURCHASE_INTENT_STATUS.CONVERTED,
|
|
6266
|
+
converted_order_id: orderId,
|
|
6267
|
+
updated_at: clock.iso(),
|
|
6268
|
+
last_confirmation: confirmation
|
|
6269
|
+
});
|
|
6270
|
+
return confirmation;
|
|
6271
|
+
},
|
|
6272
|
+
submitAction(session, intentId, attemptId, request) {
|
|
6273
|
+
const intent = stored(session, intentId);
|
|
6274
|
+
const previous = intent.last_confirmation;
|
|
6275
|
+
if (!previous || previous.status !== PURCHASE_INTENT_CONFIRM_STATUS.AUTHORIZATION_REQUIRED || previous.attempt_id !== attemptId) {
|
|
6276
|
+
throw validation("Purchase-intent attempt is not awaiting authorization");
|
|
6277
|
+
}
|
|
6278
|
+
if (!request.authorization) throw validation("authorization required");
|
|
6279
|
+
const order = registry.orders.get(previous.order_id);
|
|
6280
|
+
if (!order || !order.payment_id) {
|
|
6281
|
+
throw notFound(`Purchase-intent attempt ${attemptId} not found`);
|
|
6282
|
+
}
|
|
6283
|
+
return completeProviderOrder(intent, order, attemptId);
|
|
5429
6284
|
}
|
|
5430
6285
|
};
|
|
5431
6286
|
}
|
|
@@ -5808,6 +6663,7 @@ function createDeliveryService() {
|
|
|
5808
6663
|
rate_id: "dlvrt_mock_standard",
|
|
5809
6664
|
name: "Standard",
|
|
5810
6665
|
fee: standardFree ? money(0) : money(15),
|
|
6666
|
+
fee_status: "priced",
|
|
5811
6667
|
currency: "GHS",
|
|
5812
6668
|
free_delivery_applied: standardFree,
|
|
5813
6669
|
free_over_amount: money(200),
|
|
@@ -5821,6 +6677,7 @@ function createDeliveryService() {
|
|
|
5821
6677
|
name: "Express",
|
|
5822
6678
|
description: "Delivered within the hour",
|
|
5823
6679
|
fee: money(25),
|
|
6680
|
+
fee_status: "priced",
|
|
5824
6681
|
currency: "GHS",
|
|
5825
6682
|
free_delivery_applied: false,
|
|
5826
6683
|
eta_min_minutes: 30,
|
|
@@ -10739,7 +11596,7 @@ var ID_PREFIX = {
|
|
|
10739
11596
|
product: "prod_",
|
|
10740
11597
|
service: "svc_"
|
|
10741
11598
|
};
|
|
10742
|
-
var
|
|
11599
|
+
var DEFAULT_CURRENCY2 = "GHS";
|
|
10743
11600
|
function slugify(value) {
|
|
10744
11601
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
10745
11602
|
}
|
|
@@ -10774,7 +11631,7 @@ function makeSeedContext(registry) {
|
|
|
10774
11631
|
const business = {
|
|
10775
11632
|
business_type: "other",
|
|
10776
11633
|
email: `hello@${handle}.test`,
|
|
10777
|
-
default_currency:
|
|
11634
|
+
default_currency: DEFAULT_CURRENCY2,
|
|
10778
11635
|
default_offers_table_service: false,
|
|
10779
11636
|
default_accepts_online_orders: true,
|
|
10780
11637
|
status: "active",
|
|
@@ -10953,7 +11810,8 @@ function createDeps(options = {}) {
|
|
|
10953
11810
|
const inventory = createInventoryService({ registry });
|
|
10954
11811
|
const cart = createCartService({ registry, clock, ids, bus, inventory });
|
|
10955
11812
|
const orders = createOrderService({ registry, clock, bus });
|
|
10956
|
-
const
|
|
11813
|
+
const payments = createPaymentService({ registry, clock, bus });
|
|
11814
|
+
const purchaseIntents = createPurchaseIntentMockService({ registry, clock, ids, bus });
|
|
10957
11815
|
const subscriptions = createSubscriptionService({ registry, clock, bus });
|
|
10958
11816
|
const scheduling = createSchedulingService({ registry, clock, bus });
|
|
10959
11817
|
const fx = createFxService({ registry, clock, ids });
|
|
@@ -10974,7 +11832,8 @@ function createDeps(options = {}) {
|
|
|
10974
11832
|
business,
|
|
10975
11833
|
catalogue,
|
|
10976
11834
|
cart,
|
|
10977
|
-
|
|
11835
|
+
payments,
|
|
11836
|
+
purchaseIntents,
|
|
10978
11837
|
orders,
|
|
10979
11838
|
subscriptions,
|
|
10980
11839
|
scheduling,
|