@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.
@@ -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,gCAgU5C"}
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 realMessage = error instanceof Error ? error.message : String(error);
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;AAmBD,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"}
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; // Re-throw beforeHooks MUST succeed
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,CAMvC;AAeD;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc;;;;;sBAyVxB,MAAM,UAAU,MAAM;GA2FtD"}
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"}
@@ -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
  }