@porulle/core 0.10.5 → 0.10.6
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/interfaces/rest/routes/checkout.d.ts.map +1 -1
- package/dist/interfaces/rest/routes/checkout.js +12 -1
- package/dist/kernel/hooks/executor.d.ts.map +1 -1
- package/dist/kernel/hooks/executor.js +18 -1
- package/dist/runtime/server.d.ts.map +1 -1
- package/dist/runtime/server.js +8 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/src/interfaces/rest/routes/checkout.ts +12 -1
- package/src/kernel/hooks/executor.ts +19 -1
- package/src/runtime/server.ts +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/rest/routes/checkout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAoBzD,OAAO,EAAE,KAAK,MAAM,EAAwC,MAAM,aAAa,CAAC;AAKhF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,WAAW,CAAC,EAC1C,KAAK,EAAE,KAAK,GAAG,IAAI,EACnB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC7B,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmC7B;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,gCA2U5C"}
|
|
@@ -162,6 +162,7 @@ export function checkoutRoutes(kernel) {
|
|
|
162
162
|
origin: "rest",
|
|
163
163
|
database: { db: kernel.database.db },
|
|
164
164
|
});
|
|
165
|
+
let pipelineStage = "validate-and-calculate";
|
|
165
166
|
try {
|
|
166
167
|
// Phase 1: DB transaction for validation — releases connection immediately after
|
|
167
168
|
const validated = await kernel.database.transaction(async (_tx) => {
|
|
@@ -170,11 +171,13 @@ export function checkoutRoutes(kernel) {
|
|
|
170
171
|
});
|
|
171
172
|
// Phase 2: Payment authorization — NO DB connection held while calling Stripe/etc.
|
|
172
173
|
// If Stripe takes 5s, the DB connection pool is not affected.
|
|
174
|
+
pipelineStage = "authorize-payment";
|
|
173
175
|
context.tx = null;
|
|
174
176
|
const processed = await runBeforeHooks(paymentHooks, validated, "create", context);
|
|
175
177
|
// SEC-07: resolve the order's customer server-side. A self-service actor
|
|
176
178
|
// can only attribute the order to its own profile; a client-supplied
|
|
177
179
|
// foreign customerId is ignored unless the actor is staff.
|
|
180
|
+
pipelineStage = "resolve-customer";
|
|
178
181
|
const customerUuid = await resolveCheckoutCustomerUuid(kernel.services.customers, actor, processed.customerId);
|
|
179
182
|
const orderPayload = {
|
|
180
183
|
...(body.idempotencyKey !== undefined
|
|
@@ -225,6 +228,7 @@ export function checkoutRoutes(kernel) {
|
|
|
225
228
|
// Checkout is a trusted, already-server-priced pipeline (resolveCurrentPrices
|
|
226
229
|
// + promotions/tax) and reserves stock in its own after-hooks — so it hands
|
|
227
230
|
// the order primitive precomputed totals rather than re-deriving them.
|
|
231
|
+
pipelineStage = "create-order";
|
|
228
232
|
const order = await kernel.services.orders.create(orderPayload, actor, undefined, {
|
|
229
233
|
trustedPricing: true,
|
|
230
234
|
});
|
|
@@ -232,6 +236,7 @@ export function checkoutRoutes(kernel) {
|
|
|
232
236
|
return c.json(mapErrorToResponse(order.error), mapErrorToStatus(order.error));
|
|
233
237
|
}
|
|
234
238
|
if (order.ok && (processed.appliedPromotions?.length ?? 0) > 0) {
|
|
239
|
+
pipelineStage = "record-promotion-usage";
|
|
235
240
|
await kernel.services.promotions.recordUsage({
|
|
236
241
|
promotions: processed.appliedPromotions ?? [],
|
|
237
242
|
organizationId: order.value.organizationId,
|
|
@@ -242,6 +247,7 @@ export function checkoutRoutes(kernel) {
|
|
|
242
247
|
});
|
|
243
248
|
}
|
|
244
249
|
if (order.ok) {
|
|
250
|
+
pipelineStage = "report-tax-transaction";
|
|
245
251
|
await kernel.services.tax.reportTransaction({
|
|
246
252
|
transactionId: order.value.id,
|
|
247
253
|
transactionDate: new Date(),
|
|
@@ -271,7 +277,9 @@ export function checkoutRoutes(kernel) {
|
|
|
271
277
|
}
|
|
272
278
|
// Stash paymentMethodId for completeCheckout compensation chain
|
|
273
279
|
context.context.paymentMethodId = processed.paymentMethodId;
|
|
280
|
+
pipelineStage = "complete-checkout";
|
|
274
281
|
const afterReport = await runAfterHooks(afterHooks, null, order.value, "create", context);
|
|
282
|
+
pipelineStage = "mark-cart-checked-out";
|
|
275
283
|
await kernel.services.cart.markAsCheckedOut(body.cartId, actor);
|
|
276
284
|
return c.json({
|
|
277
285
|
data: {
|
|
@@ -297,7 +305,10 @@ export function checkoutRoutes(kernel) {
|
|
|
297
305
|
});
|
|
298
306
|
});
|
|
299
307
|
// Always log the real error — hidden errors in checkout are unacceptable
|
|
300
|
-
const
|
|
308
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
309
|
+
const realMessage = rawMessage === "[object ErrorEvent]"
|
|
310
|
+
? `Checkout stage "${pipelineStage}" failed: ${rawMessage}`
|
|
311
|
+
: rawMessage;
|
|
301
312
|
const realStack = error instanceof Error ? error.stack : undefined;
|
|
302
313
|
console.error("[checkout] Pipeline failed:", { message: realMessage, stack: realStack, code: error?.code });
|
|
303
314
|
const message = isCommerceError(error)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/kernel/hooks/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAKzE;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/kernel/hooks/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEpF,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAKzE;AAqCD,wBAAsB,cAAc,CAAC,CAAC,EACpC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,EACtB,IAAI,EAAE,CAAC,EACP,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,aAAa,CAAC,CAAC,EACnC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,EACrB,YAAY,EAAE,CAAC,GAAG,IAAI,EACtB,eAAe,EAAE,CAAC,EAClB,SAAS,EAAE,aAAa,EACxB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
|
|
@@ -13,6 +13,23 @@ function withTimeout(promiseOrValue, timeoutMs, hookName) {
|
|
|
13
13
|
promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
+
function actionableHookError(error, hookName) {
|
|
17
|
+
if (error instanceof Error && error.message && error.message !== "[object ErrorEvent]") {
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
20
|
+
const candidate = error;
|
|
21
|
+
const nested = candidate?.error ?? candidate?.cause;
|
|
22
|
+
const nestedMessage = nested instanceof Error
|
|
23
|
+
? nested.message
|
|
24
|
+
: nested && typeof nested === "object" && typeof nested.message === "string"
|
|
25
|
+
? String(nested.message)
|
|
26
|
+
: undefined;
|
|
27
|
+
const directMessage = typeof candidate?.message === "string" && candidate.message !== "[object ErrorEvent]"
|
|
28
|
+
? candidate.message
|
|
29
|
+
: undefined;
|
|
30
|
+
const message = nestedMessage || directMessage || String(error);
|
|
31
|
+
return new Error(`Before-hook "${hookName}" failed: ${message}`, { cause: error });
|
|
32
|
+
}
|
|
16
33
|
export async function runBeforeHooks(hooks, data, operation, context) {
|
|
17
34
|
let current = data;
|
|
18
35
|
for (const hook of hooks) {
|
|
@@ -25,7 +42,7 @@ export async function runBeforeHooks(hooks, data, operation, context) {
|
|
|
25
42
|
error: error instanceof Error ? error.message : String(error),
|
|
26
43
|
requestId: context.requestId,
|
|
27
44
|
});
|
|
28
|
-
throw error; //
|
|
45
|
+
throw actionableHookError(error, hookName); // Before hooks MUST succeed with actionable context.
|
|
29
46
|
}
|
|
30
47
|
}
|
|
31
48
|
return current;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/runtime/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAOzD,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItE,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAcvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAyVxB,MAAM,UAAU,MAAM;GA2FtD"}
|
package/dist/runtime/server.js
CHANGED
|
@@ -22,7 +22,15 @@ import { mapErrorToResponse } from "../kernel/error-mapper.js";
|
|
|
22
22
|
* Exposed for direct unit testing — see test/server-edge-runtime.test.ts.
|
|
23
23
|
*/
|
|
24
24
|
export function isNodeRuntime() {
|
|
25
|
+
const workerUserAgent = typeof navigator !== "undefined"
|
|
26
|
+
? navigator.userAgent
|
|
27
|
+
: undefined;
|
|
28
|
+
const hasWorkersSocketPair = "WebSocketPair" in globalThis;
|
|
29
|
+
if (workerUserAgent === "Cloudflare-Workers" || hasWorkersSocketPair) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
25
32
|
return (typeof process !== "undefined" &&
|
|
33
|
+
typeof process.versions?.node === "string" &&
|
|
26
34
|
typeof process.on === "function" &&
|
|
27
35
|
typeof process.exit === "function");
|
|
28
36
|
}
|