@porulle/core 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/dist/auth/middleware.d.ts.map +1 -1
  2. package/dist/auth/middleware.js +26 -7
  3. package/dist/auth/setup.d.ts.map +1 -1
  4. package/dist/auth/setup.js +7 -5
  5. package/dist/config/defaults.d.ts.map +1 -1
  6. package/dist/config/defaults.js +3 -1
  7. package/dist/config/types.d.ts +13 -3
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/generated/plugin-manifest.d.ts +3 -0
  10. package/dist/generated/plugin-manifest.d.ts.map +1 -1
  11. package/dist/generated/plugin-manifest.js +1 -0
  12. package/dist/hooks/checkout-completion.d.ts.map +1 -1
  13. package/dist/hooks/checkout-completion.js +2 -2
  14. package/dist/hooks/checkout.d.ts.map +1 -1
  15. package/dist/hooks/checkout.js +1 -1
  16. package/dist/index.d.ts +5 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -0
  19. package/dist/interfaces/rest/router.d.ts.map +1 -1
  20. package/dist/interfaces/rest/router.js +2 -0
  21. package/dist/interfaces/rest/routes/admin/staff.d.ts.map +1 -1
  22. package/dist/interfaces/rest/routes/admin/staff.js +37 -3
  23. package/dist/interfaces/rest/routes/checkout.d.ts +9 -0
  24. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  25. package/dist/interfaces/rest/routes/checkout.js +82 -22
  26. package/dist/interfaces/rest/routes/inventory.d.ts.map +1 -1
  27. package/dist/interfaces/rest/routes/inventory.js +13 -0
  28. package/dist/interfaces/rest/routes/orders.d.ts.map +1 -1
  29. package/dist/interfaces/rest/routes/orders.js +13 -0
  30. package/dist/interfaces/rest/schemas/catalog.d.ts +391 -0
  31. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  32. package/dist/interfaces/rest/schemas/orders.js +2 -2
  33. package/dist/interfaces/rest/schemas/responses.d.ts +85 -0
  34. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  35. package/dist/interfaces/rest/schemas/search.d.ts +17 -0
  36. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  37. package/dist/kernel/database/scoped-db.d.ts +2 -1
  38. package/dist/kernel/database/scoped-db.d.ts.map +1 -1
  39. package/dist/kernel/database/scoped-db.js +74 -15
  40. package/dist/kernel/errors.d.ts +12 -0
  41. package/dist/kernel/errors.d.ts.map +1 -1
  42. package/dist/kernel/errors.js +16 -0
  43. package/dist/kernel/jobs/adapter.d.ts +27 -8
  44. package/dist/kernel/jobs/adapter.d.ts.map +1 -1
  45. package/dist/kernel/jobs/adapter.js +1 -2
  46. package/dist/kernel/jobs/drizzle-adapter.d.ts +13 -3
  47. package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
  48. package/dist/kernel/jobs/drizzle-adapter.js +46 -5
  49. package/dist/kernel/jobs/runner.d.ts +2 -1
  50. package/dist/kernel/jobs/runner.d.ts.map +1 -1
  51. package/dist/kernel/jobs/runner.js +100 -15
  52. package/dist/kernel/jobs/types.d.ts +13 -0
  53. package/dist/kernel/jobs/types.d.ts.map +1 -1
  54. package/dist/kernel/plugin/manifest.d.ts +5 -0
  55. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  56. package/dist/kernel/plugin/manifest.js +9 -0
  57. package/dist/modules/analytics/drizzle-adapter.d.ts.map +1 -1
  58. package/dist/modules/analytics/drizzle-adapter.js +16 -3
  59. package/dist/modules/cart/service.d.ts.map +1 -1
  60. package/dist/modules/cart/service.js +3 -2
  61. package/dist/modules/catalog/entity-service.d.ts +1 -0
  62. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  63. package/dist/modules/catalog/entity-service.js +49 -3
  64. package/dist/modules/catalog/repository/index.d.ts +2 -2
  65. package/dist/modules/catalog/repository/index.d.ts.map +1 -1
  66. package/dist/modules/catalog/repository/index.js +9 -1
  67. package/dist/modules/catalog/schema.d.ts +51 -0
  68. package/dist/modules/catalog/schema.d.ts.map +1 -1
  69. package/dist/modules/catalog/schema.js +12 -1
  70. package/dist/modules/catalog/schemas.d.ts +3 -1
  71. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  72. package/dist/modules/channels/adapter.d.ts +117 -0
  73. package/dist/modules/channels/adapter.d.ts.map +1 -0
  74. package/dist/modules/channels/adapter.js +3 -0
  75. package/dist/modules/customers/service.d.ts.map +1 -1
  76. package/dist/modules/customers/service.js +6 -0
  77. package/dist/modules/inventory/repository/index.js +4 -4
  78. package/dist/modules/inventory/service.d.ts.map +1 -1
  79. package/dist/modules/inventory/service.js +7 -3
  80. package/dist/modules/orders/schema.d.ts +17 -0
  81. package/dist/modules/orders/schema.d.ts.map +1 -1
  82. package/dist/modules/orders/schema.js +4 -0
  83. package/dist/modules/orders/service.d.ts +16 -2
  84. package/dist/modules/orders/service.d.ts.map +1 -1
  85. package/dist/modules/orders/service.js +226 -26
  86. package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
  87. package/dist/modules/orders/stale-order-cleanup.js +37 -16
  88. package/dist/modules/pricing/service.d.ts.map +1 -1
  89. package/dist/modules/pricing/service.js +5 -5
  90. package/dist/modules/promotions/repository/index.d.ts +1 -1
  91. package/dist/modules/promotions/repository/index.d.ts.map +1 -1
  92. package/dist/modules/promotions/repository/index.js +6 -3
  93. package/dist/modules/promotions/service.d.ts +1 -0
  94. package/dist/modules/promotions/service.d.ts.map +1 -1
  95. package/dist/modules/promotions/service.js +1 -1
  96. package/dist/modules/webhooks/repository/index.d.ts +1 -1
  97. package/dist/modules/webhooks/repository/index.d.ts.map +1 -1
  98. package/dist/modules/webhooks/repository/index.js +16 -10
  99. package/dist/runtime/kernel.d.ts.map +1 -1
  100. package/dist/runtime/kernel.js +14 -2
  101. package/dist/runtime/server.d.ts +1 -4
  102. package/dist/runtime/server.d.ts.map +1 -1
  103. package/dist/runtime/server.js +16 -34
  104. package/dist/test-utils/create-test-config.d.ts.map +1 -1
  105. package/dist/test-utils/create-test-config.js +2 -0
  106. package/dist/test-utils/order-test-helpers.d.ts +12 -0
  107. package/dist/test-utils/order-test-helpers.d.ts.map +1 -0
  108. package/dist/test-utils/order-test-helpers.js +18 -0
  109. package/dist/test-utils/rest-api-test-utils.d.ts.map +1 -1
  110. package/dist/test-utils/rest-api-test-utils.js +1 -0
  111. package/dist/test-utils/test-actors.js +1 -1
  112. package/dist/testing.d.ts +1 -0
  113. package/dist/testing.d.ts.map +1 -1
  114. package/dist/testing.js +1 -0
  115. package/package.json +1 -1
  116. package/src/auth/middleware.ts +26 -7
  117. package/src/auth/setup.ts +10 -4
  118. package/src/config/defaults.ts +3 -1
  119. package/src/config/types.ts +16 -3
  120. package/src/generated/plugin-capabilities.d.ts +1 -0
  121. package/src/generated/plugin-manifest.ts +1 -0
  122. package/src/generated/plugin-repositories.d.ts +1 -0
  123. package/src/hooks/checkout-completion.ts +22 -16
  124. package/src/hooks/checkout.ts +11 -8
  125. package/src/index.ts +31 -1
  126. package/src/interfaces/rest/router.ts +2 -0
  127. package/src/interfaces/rest/routes/admin/staff.ts +45 -3
  128. package/src/interfaces/rest/routes/checkout.ts +94 -28
  129. package/src/interfaces/rest/routes/inventory.ts +14 -0
  130. package/src/interfaces/rest/routes/orders.ts +11 -0
  131. package/src/interfaces/rest/schemas/orders.ts +2 -2
  132. package/src/kernel/database/scoped-db.ts +83 -15
  133. package/src/kernel/errors.ts +20 -0
  134. package/src/kernel/jobs/adapter.ts +39 -8
  135. package/src/kernel/jobs/drizzle-adapter.ts +72 -9
  136. package/src/kernel/jobs/runner.ts +194 -72
  137. package/src/kernel/jobs/types.ts +25 -0
  138. package/src/kernel/plugin/manifest.ts +15 -0
  139. package/src/modules/analytics/drizzle-adapter.ts +19 -3
  140. package/src/modules/cart/service.ts +3 -2
  141. package/src/modules/catalog/entity-service.ts +37 -2
  142. package/src/modules/catalog/repository/index.ts +13 -3
  143. package/src/modules/catalog/schema.ts +33 -18
  144. package/src/modules/catalog/schemas.ts +3 -1
  145. package/src/modules/channels/adapter.ts +139 -0
  146. package/src/modules/customers/service.ts +6 -0
  147. package/src/modules/inventory/repository/index.ts +4 -4
  148. package/src/modules/inventory/service.ts +12 -2
  149. package/src/modules/orders/schema.ts +4 -0
  150. package/src/modules/orders/service.ts +404 -64
  151. package/src/modules/orders/stale-order-cleanup.ts +52 -27
  152. package/src/modules/pricing/service.ts +5 -6
  153. package/src/modules/promotions/repository/index.ts +6 -2
  154. package/src/modules/promotions/service.ts +2 -0
  155. package/src/modules/webhooks/repository/index.ts +16 -14
  156. package/src/runtime/kernel.ts +16 -2
  157. package/src/runtime/server.ts +22 -42
  158. package/src/test-utils/create-test-config.ts +2 -0
  159. package/src/test-utils/order-test-helpers.ts +26 -0
  160. package/src/test-utils/rest-api-test-utils.ts +1 -0
  161. package/src/test-utils/test-actors.ts +1 -1
  162. package/src/testing.ts +1 -0
@@ -1,4 +1,4 @@
1
- import { eq, and, lte, isNull, or, desc, sql } from "drizzle-orm";
1
+ import { eq, and, lte, isNull, or, desc, sql, inArray } from "drizzle-orm";
2
2
  import { webhookEndpoints, webhookDeliveries } from "../schema.js";
3
3
  /**
4
4
  * WebhooksRepository provides type-safe database operations for webhooks.
@@ -113,19 +113,25 @@ export class WebhooksRepository {
113
113
  .where(and(isNull(webhookDeliveries.deliveredAt), isNull(webhookDeliveries.failedAt), or(isNull(webhookDeliveries.nextRetryAt), lte(webhookDeliveries.nextRetryAt, now))))
114
114
  .orderBy(webhookDeliveries.createdAt);
115
115
  }
116
- async findFailedDeliveries(endpointId, ctx) {
116
+ async findFailedDeliveries(orgId, endpointId, ctx) {
117
117
  const db = this.getDb(ctx);
118
- if (endpointId) {
119
- return db
120
- .select()
121
- .from(webhookDeliveries)
122
- .where(and(eq(webhookDeliveries.endpointId, endpointId), sql `${webhookDeliveries.failedAt} IS NOT NULL`))
123
- .orderBy(desc(webhookDeliveries.failedAt));
124
- }
118
+ // webhook_deliveries has no organizationId column — org lives on the parent
119
+ // endpoint. Scope failed deliveries to endpoints owned by orgId so this can
120
+ // never read another tenant's deliveries.
121
+ const orgEndpointIds = db
122
+ .select({ id: webhookEndpoints.id })
123
+ .from(webhookEndpoints)
124
+ .where(eq(webhookEndpoints.organizationId, orgId));
125
+ const conditions = [
126
+ sql `${webhookDeliveries.failedAt} IS NOT NULL`,
127
+ inArray(webhookDeliveries.endpointId, orgEndpointIds),
128
+ ];
129
+ if (endpointId)
130
+ conditions.push(eq(webhookDeliveries.endpointId, endpointId));
125
131
  return db
126
132
  .select()
127
133
  .from(webhookDeliveries)
128
- .where(sql `${webhookDeliveries.failedAt} IS NOT NULL`)
134
+ .where(and(...conditions))
129
135
  .orderBy(desc(webhookDeliveries.failedAt));
130
136
  }
131
137
  async createDelivery(data, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAoBzD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA6H3D"}
1
+ {"version":3,"file":"kernel.d.ts","sourceRoot":"","sources":["../../src/runtime/kernel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAqBzD,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,sBAAsB,EAC5B,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;AAC/C,OAAO,EACL,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,qBAAqB,CAAC;AAE7B,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CA0I3D"}
@@ -44,8 +44,20 @@ export function createKernel(config) {
44
44
  serviceContainer.database = database;
45
45
  const db = database.db;
46
46
  const jobsTaskMap = new Map((config.jobs?.tasks ?? []).map((t) => [t.slug, t]));
47
- const jobsAdapter = new DrizzleJobsAdapter(db, jobsTaskMap);
48
- serviceContainer.jobs = jobsAdapter;
47
+ const jobsEngine = config.jobs?.adapter ?? new DrizzleJobsAdapter(db);
48
+ const jobLogger = {
49
+ info: (message, data) => logger.info(message, data),
50
+ warn: (message, data) => logger.warn(message, data),
51
+ error: (message, data) => logger.error(message, data),
52
+ };
53
+ jobsEngine.register({
54
+ tasks: jobsTaskMap,
55
+ context: { logger: jobLogger, db, services: serviceContainer },
56
+ ...(config.jobs?.processingOrder !== undefined
57
+ ? { processingOrder: config.jobs.processingOrder }
58
+ : {}),
59
+ });
60
+ serviceContainer.jobs = jobsEngine;
49
61
  const topoGraph = kernelModulesForTopoSort();
50
62
  const order = topoSortModules(topoGraph);
51
63
  const moduleKeys = Object.keys(KERNEL_ALL_MODULES);
@@ -32,10 +32,7 @@ export declare function createServer(config: CommerceConfig): Promise<{
32
32
  kernel: import("./kernel-types.js").Kernel;
33
33
  logger: Logger;
34
34
  commerce: CommerceInstance;
35
- runJobs: (queue?: string, limit?: number) => Promise<{
36
- processed: number;
37
- failed: number;
38
- }>;
35
+ runJobs: (queue?: string, limit?: number) => Promise<import("../kernel/jobs/adapter.js").RunJobsResult>;
39
36
  }>;
40
37
  export {};
41
38
  //# sourceMappingURL=server.d.ts.map
@@ -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;AAExD,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;;;;;sBAuXxB,MAAM,UAAU,MAAM;;;;GAgFtD"}
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"}
@@ -345,41 +345,15 @@ export async function createServer(config) {
345
345
  // 1. autorun: in-process polling (long-running servers)
346
346
  // 2. GET /api/jobs/run: cron endpoint (serverless — Vercel, Cloudflare)
347
347
  // 3. runPendingJobs() export (custom worker process)
348
- const { runPendingJobs } = await import("../kernel/jobs/runner.js");
349
- const { runStaleJobReaper, getJobReapThresholdMs, getJobsReaperIntervalMs } = await import("../kernel/jobs/reaper.js");
350
- const taskMap = new Map();
351
- for (const task of config.jobs?.tasks ?? []) {
352
- const t = task;
353
- taskMap.set(t.slug ?? t.name ?? "", task);
354
- }
355
- const jobLogger = {
356
- info: (msg, data) => logger.info(data != null ? { data } : {}, msg),
357
- warn: (msg, data) => logger.warn(data != null ? { data } : {}, msg),
358
- error: (msg, data) => logger.error(data != null ? { data } : {}, msg),
359
- };
360
- let lastStaleJobReaperAt = 0;
361
- const maybeRunStaleJobReaper = async () => {
362
- const interval = getJobsReaperIntervalMs();
363
- const now = Date.now();
364
- if (now - lastStaleJobReaperAt < interval)
365
- return;
366
- lastStaleJobReaperAt = now;
367
- try {
368
- await runStaleJobReaper(kernel.database.db, getJobReapThresholdMs(), jobLogger);
369
- }
370
- catch (err) {
371
- logger.error({ err }, "Stale job reaper failed");
372
- }
373
- };
348
+ const jobsEngine = kernel.services
349
+ .jobs;
374
350
  const runJobs = async (queue, limit) => {
375
- await maybeRunStaleJobReaper();
376
- return runPendingJobs({
377
- db: kernel.database.db,
378
- tasks: taskMap,
351
+ if (jobsEngine.execution.mode !== "pull") {
352
+ throw new Error("The configured push execution engine is driven by its native runtime and cannot be polled with runJobs().");
353
+ }
354
+ return jobsEngine.execution.run({
379
355
  queue: queue ?? "default",
380
356
  limit: limit ?? 10,
381
- logger: jobLogger,
382
- services: kernel.services,
383
357
  });
384
358
  };
385
359
  // Strategy 1: Built-in cron endpoint for serverless deployments.
@@ -405,6 +379,9 @@ export async function createServer(config) {
405
379
  // Strategy 2: In-process polling for long-running servers (ECS, Cloud Run, Docker).
406
380
  // Enable via config.jobs.autorun.enabled = true
407
381
  if (config.jobs?.autorun?.enabled) {
382
+ if (jobsEngine.execution.mode !== "pull") {
383
+ throw new Error("jobs.autorun is only valid for pull execution engines. Serve the configured push engine through its native runtime.");
384
+ }
408
385
  const intervalMs = config.jobs.autorun.intervalMs ?? 10_000;
409
386
  const jobInterval = setInterval(async () => {
410
387
  try {
@@ -420,8 +397,13 @@ export async function createServer(config) {
420
397
  logger.info({ intervalMs }, "Job queue autorun started (in-process polling)");
421
398
  }
422
399
  else {
423
- logger.info("Job queue autorun disabled. Use GET /api/jobs/run for serverless cron, " +
424
- "or set config.jobs.autorun.enabled = true for in-process polling.");
400
+ if (jobsEngine.execution.mode === "push") {
401
+ logger.info("Push job engine registered. Serve it through its native runtime.");
402
+ }
403
+ else {
404
+ logger.info("Job queue autorun disabled. Use GET /api/jobs/run for serverless cron, " +
405
+ "or set config.jobs.autorun.enabled = true for in-process polling.");
406
+ }
425
407
  }
426
408
  const dispatchFetch = app.fetch.bind(app);
427
409
  app.fetch = ((input, env, executionCtx) => {
@@ -1 +1 @@
1
- {"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,CA+GzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAUnE"}
1
+ {"version":3,"file":"create-test-config.d.ts","sourceRoot":"","sources":["../../src/test-utils/create-test-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAgDzD,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC,cAAc,CAAC,CAiHzB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAUnE"}
@@ -53,6 +53,7 @@ export async function createTestConfig(overrides = {}) {
53
53
  provider: "postgresql",
54
54
  },
55
55
  auth: {
56
+ allowTestActor: true,
56
57
  defaultOrganizationId: "org_default",
57
58
  requireEmailVerification: false,
58
59
  apiKeys: { enabled: true, defaultPermissions: ["catalog:read"] },
@@ -70,6 +71,7 @@ export async function createTestConfig(overrides = {}) {
70
71
  "orders:create",
71
72
  "orders:read",
72
73
  "orders:update",
74
+ "orders:manage",
73
75
  "cart:create",
74
76
  "cart:update",
75
77
  "customers:update:self",
@@ -0,0 +1,12 @@
1
+ import type { Kernel } from "../runtime/kernel.js";
2
+ /**
3
+ * Mark an order as paid (captured) for tests.
4
+ *
5
+ * Refund and return flows require a paid order — a refund moves collected money,
6
+ * and the order primitive rejects refunds on unpaid/pending orders. There is no
7
+ * public "set captured amount" service (capture() needs a live payment intent),
8
+ * so tests set the column here through a single typed helper rather than
9
+ * scattering raw SQL / `as any` casts across test files.
10
+ */
11
+ export declare function markOrderPaidForTest(kernel: Kernel, orderId: string, amountCaptured: number, paymentIntentId?: string): Promise<void>;
12
+ //# sourceMappingURL=order-test-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order-test-helpers.d.ts","sourceRoot":"","sources":["../../src/test-utils/order-test-helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAGnD;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAMf"}
@@ -0,0 +1,18 @@
1
+ import { eq } from "drizzle-orm";
2
+ import { orders } from "../modules/orders/schema.js";
3
+ /**
4
+ * Mark an order as paid (captured) for tests.
5
+ *
6
+ * Refund and return flows require a paid order — a refund moves collected money,
7
+ * and the order primitive rejects refunds on unpaid/pending orders. There is no
8
+ * public "set captured amount" service (capture() needs a live payment intent),
9
+ * so tests set the column here through a single typed helper rather than
10
+ * scattering raw SQL / `as any` casts across test files.
11
+ */
12
+ export async function markOrderPaidForTest(kernel, orderId, amountCaptured, paymentIntentId) {
13
+ const db = kernel.database.db;
14
+ await db
15
+ .update(orders)
16
+ .set({ amountCaptured, ...(paymentIntentId ? { paymentIntentId } : {}) })
17
+ .where(eq(orders.id, orderId));
18
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAmCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
1
+ {"version":3,"file":"rest-api-test-utils.d.ts","sourceRoot":"","sources":["../../src/test-utils/rest-api-test-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAOpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,KAAK,SAAS,GAAG;IACf,SAAS,EAAE;QACT,IAAI,EAAE,YAAY,CAAC;QACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;KACrB,CAAC;CACH,CAAC;AAiDF;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,GAAE,OAAO,CAAC,cAAc,CAAM,GACtC,OAAO,CAAC;IACT,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC,CAqDD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAEnF;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,KAoCvB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,KAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,KASzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,WAwBA;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,qBAYF"}
@@ -130,6 +130,7 @@ export const testActor = {
130
130
  "orders:create",
131
131
  "orders:read",
132
132
  "orders:update",
133
+ "orders:manage",
133
134
  "cart:create",
134
135
  "cart:update",
135
136
  "cart:read",
@@ -28,7 +28,7 @@ export const testStaffActor = {
28
28
  role: "staff",
29
29
  permissions: [
30
30
  "catalog:read", "catalog:create", "catalog:update",
31
- "inventory:adjust", "orders:read", "orders:create", "orders:update",
31
+ "inventory:adjust", "orders:read", "orders:create", "orders:update", "orders:manage",
32
32
  ],
33
33
  };
34
34
  /** Customer with minimal read/write-own permissions. */
package/dist/testing.d.ts CHANGED
@@ -11,5 +11,6 @@ export { createRepositoryTestHarness } from "./test-utils/create-repository-test
11
11
  export { createPluginTestApp, type PluginTestApp, type TestAppEnv } from "./test-utils/create-plugin-test-app.js";
12
12
  export { TEST_ORG_ID, testAdminActor, testStaffActor, testCustomerActor, testNoPermActor, jsonHeaders, } from "./test-utils/test-actors.js";
13
13
  export { beforeHook, afterHook } from "./test-utils/typed-hooks.js";
14
+ export { markOrderPaidForTest } from "./test-utils/order-test-helpers.js";
14
15
  export type { Actor } from "./auth/types.js";
15
16
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAClH,OAAO,EACL,WAAW,EACX,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAClE,WAAW,GACZ,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAGpE,YAAY,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AACrF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,wCAAwC,CAAC;AAClH,OAAO,EACL,WAAW,EACX,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,eAAe,EAClE,WAAW,GACZ,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAG1E,YAAY,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/testing.js CHANGED
@@ -11,3 +11,4 @@ export { createRepositoryTestHarness } from "./test-utils/create-repository-test
11
11
  export { createPluginTestApp } from "./test-utils/create-plugin-test-app.js";
12
12
  export { TEST_ORG_ID, testAdminActor, testStaffActor, testCustomerActor, testNoPermActor, jsonHeaders, } from "./test-utils/test-actors.js";
13
13
  export { beforeHook, afterHook } from "./test-utils/typed-hooks.js";
14
+ export { markOrderPaidForTest } from "./test-utils/order-test-helpers.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -43,10 +43,10 @@ export function authMiddleware(
43
43
  // Resolve the default org from config, falling back to deprecated constant
44
44
  const defaultOrgId = config.auth?.defaultOrganizationId ?? DEFAULT_ORG_ID;
45
45
 
46
- // Test-only actor injection: NODE_ENV === "test" honors x-test-actor for
47
- // route-level integration tests that bypass the Better Auth session
48
- // round-trip. Production/non-test environments ignore this header entirely.
49
- if (process.env.NODE_ENV === "test") {
46
+ // Test-only actor injection: requires NODE_ENV === "test" AND an explicit
47
+ // config opt-in so staging/preview deployments left as NODE_ENV=test do not
48
+ // silently become a full auth bypass.
49
+ if (process.env.NODE_ENV === "test" && config.auth?.allowTestActor) {
50
50
  const testActorHeader = c.req.header("x-test-actor");
51
51
  if (testActorHeader) {
52
52
  try {
@@ -172,10 +172,29 @@ export function authMiddleware(
172
172
  if (result?.valid && result.key) {
173
173
  const apiKey = result.key as Record<string, unknown>;
174
174
 
175
- // v1.5+ renamed userId → referenceId on the apikey table.
176
- const userId = (apiKey.referenceId ?? "") as string;
177
175
  const name = (apiKey.name ?? "API Key") as string;
178
- const orgId = (apiKey.organizationId ?? defaultOrgId) as string;
176
+ // Read the org (and operator identity) from key metadata when present.
177
+ // POS shift keys carry { organizationId, operatorId } in metadata so the
178
+ // operator is scoped to their store WITHOUT any org membership/role
179
+ // (SEC-16 / R-01). Other keys fall back to the key's organizationId.
180
+ const rawMeta = apiKey.metadata;
181
+ const meta =
182
+ rawMeta && typeof rawMeta === "object"
183
+ ? (rawMeta as Record<string, unknown>)
184
+ : typeof rawMeta === "string"
185
+ ? (() => {
186
+ try {
187
+ return JSON.parse(rawMeta) as Record<string, unknown>;
188
+ } catch {
189
+ return null;
190
+ }
191
+ })()
192
+ : null;
193
+ const metaOrg = typeof meta?.organizationId === "string" ? meta.organizationId : undefined;
194
+ const orgId = (metaOrg ?? apiKey.organizationId ?? defaultOrgId) as string;
195
+ const userId = ((typeof meta?.operatorId === "string" ? meta.operatorId : undefined)
196
+ ?? apiKey.referenceId
197
+ ?? "") as string;
179
198
 
180
199
  // Better Auth stores permissions as Record<string, string[]>
181
200
  // (e.g. {"catalog":["read","create"]}). Flatten to the
package/src/auth/setup.ts CHANGED
@@ -7,13 +7,13 @@ import type { CommerceConfig } from "../config/types.js";
7
7
  import type { DatabaseAdapter } from "../kernel/database/adapter.js";
8
8
  import * as authSchema from "./auth-schema.js";
9
9
 
10
- type BetterAuthDbProvider = "pg" | "mysql" | "sqlite";
10
+ type BetterAuthDbProvider = "pg";
11
11
 
12
12
  function resolveAuthDbProvider(provider: string): BetterAuthDbProvider {
13
13
  if (provider === "postgres" || provider === "postgresql" || provider === "pg") return "pg";
14
- if (provider === "mysql") return "mysql";
15
- if (provider === "sqlite") return "sqlite";
16
- throw new Error(`Unsupported auth database provider "${provider}".`);
14
+ throw new Error(
15
+ `Unsupported auth database provider "${provider}". Only PostgreSQL is supported.`,
16
+ );
17
17
  }
18
18
 
19
19
  interface AuthEmailPayload {
@@ -91,6 +91,12 @@ export function createAuth(
91
91
  }
92
92
  : {}),
93
93
  ...(scope.keyExpiration ? { keyExpiration: scope.keyExpiration } : {}),
94
+ ...("references" in scope && scope.references
95
+ ? { references: scope.references as "user" | "organization" }
96
+ : {}),
97
+ ...(scope.enableMetadata || scope.references === "organization"
98
+ ? { enableMetadata: true }
99
+ : {}),
94
100
  }));
95
101
  plugins.push(apiKey(apiKeyConfigs));
96
102
  } else if (config.auth?.apiKeys?.enabled) {
@@ -17,7 +17,7 @@ export const defaultConfig: Partial<CommerceConfig> = {
17
17
  },
18
18
  roles: {
19
19
  owner: { permissions: ["*:*"] },
20
- admin: { permissions: ["*:*", "compensation:admin"] },
20
+ admin: { permissions: ["*:*", "catalog:sync", "compensation:admin"] },
21
21
  manager: {
22
22
  permissions: [
23
23
  "catalog:create",
@@ -27,7 +27,9 @@ export const defaultConfig: Partial<CommerceConfig> = {
27
27
  "inventory:read",
28
28
  "inventory:adjust",
29
29
  "orders:read",
30
+ "orders:create",
30
31
  "orders:update",
32
+ "orders:manage",
31
33
  "cart:create",
32
34
  "cart:update",
33
35
  "cart:manage",
@@ -8,8 +8,11 @@ import type { AuthInstance } from "../auth/setup.js";
8
8
  import type { DatabaseAdapter } from "../kernel/database/adapter.js";
9
9
  import type { TaxAdapter } from "../modules/tax/adapter.js";
10
10
  import type { SearchAdapter } from "../modules/search/adapter.js";
11
- import type { JobsAdapter } from "../kernel/jobs/adapter.js";
12
- import type { TaskDefinition } from "../kernel/jobs/types.js";
11
+ import type { ExecutionEngine } from "../kernel/jobs/adapter.js";
12
+ import type {
13
+ JobProcessingOrder,
14
+ TaskDefinition,
15
+ } from "../kernel/jobs/types.js";
13
16
 
14
17
  export interface RoleDefinition {
15
18
  permissions: string[];
@@ -113,6 +116,10 @@ export interface ApiKeyScopeDefinition {
113
116
  minExpiresIn?: number;
114
117
  maxExpiresIn?: number;
115
118
  };
119
+ /** Better Auth ownership model — POS shift keys bind to an organization. */
120
+ references?: "user" | "organization";
121
+ /** Allow metadata on keys minted under this scope (e.g. operatorId on POS shift keys). */
122
+ enableMetadata?: boolean;
116
123
  }
117
124
 
118
125
  export interface AuthConfig {
@@ -214,6 +221,11 @@ export interface AuthConfig {
214
221
  * Appended to the core plugin list (organization, bearer, jwt, etc.).
215
222
  */
216
223
  extraAuthPlugins?: unknown[];
224
+ /**
225
+ * When true (and NODE_ENV === "test"), the `x-test-actor` header may inject
226
+ * a trusted actor for integration tests. Default false.
227
+ */
228
+ allowTestActor?: boolean;
217
229
  }
218
230
 
219
231
  export interface CartConfig {
@@ -344,8 +356,9 @@ export interface CommerceConfig {
344
356
  analytics?: AnalyticsConfig;
345
357
  search?: SearchConfig;
346
358
  jobs?: {
347
- adapter?: JobsAdapter;
359
+ adapter?: ExecutionEngine;
348
360
  tasks?: TaskDefinition[];
361
+ processingOrder?: JobProcessingOrder;
349
362
  autorun?: {
350
363
  enabled: boolean;
351
364
  intervalMs?: number;
@@ -3,6 +3,7 @@
3
3
 
4
4
  export interface PluginCapabilityRegistryShape {
5
5
  "appointments": Record<string, unknown>;
6
+ "channel-connector": Record<string, unknown>;
6
7
  "gift-cards": Record<string, unknown>;
7
8
  "layaway": Record<string, unknown>;
8
9
  "loyalty": Record<string, unknown>;
@@ -4,6 +4,7 @@
4
4
 
5
5
  export const resolvedPluginManifest = [
6
6
  { id: "appointments", directory: "plugin-appointments" },
7
+ { id: "channel-connector", directory: "plugin-channel-connector" },
7
8
  { id: "gift-cards", directory: "plugin-gift-cards" },
8
9
  { id: "layaway", directory: "plugin-layaway" },
9
10
  { id: "loyalty", directory: "plugin-loyalty" },
@@ -3,6 +3,7 @@
3
3
 
4
4
  export interface PluginRepositoryRegistryShape {
5
5
  "appointments": Record<string, unknown>;
6
+ "channel-connector": Record<string, unknown>;
6
7
  "gift-cards": Record<string, unknown>;
7
8
  "layaway": Record<string, unknown>;
8
9
  "loyalty": Record<string, unknown>;
@@ -13,14 +13,14 @@ interface InventoryServiceLike {
13
13
  quantity: number;
14
14
  orderId: string;
15
15
  performedBy: string;
16
- }): Promise<{ ok: boolean; error?: { message: string } }>;
16
+ }, actor?: unknown): Promise<{ ok: boolean; error?: { message: string } }>;
17
17
  release(input: {
18
18
  entityId: string;
19
19
  variantId?: string;
20
20
  quantity: number;
21
21
  orderId: string;
22
22
  performedBy: string;
23
- }): Promise<unknown>;
23
+ }, actor?: unknown): Promise<unknown>;
24
24
  }
25
25
 
26
26
  interface PaymentsServiceLike {
@@ -80,13 +80,16 @@ export const reserveInventoryStep: Step<
80
80
  const performedBy = ctx.hook.actor?.userId ?? "system";
81
81
 
82
82
  for (const item of data.lineItems) {
83
- const result = await inventory.reserve({
84
- entityId: item.entityId,
85
- ...(item.variantId != null ? { variantId: item.variantId } : {}),
86
- quantity: item.quantity,
87
- orderId: data.checkoutId,
88
- performedBy,
89
- });
83
+ const result = await inventory.reserve(
84
+ {
85
+ entityId: item.entityId,
86
+ ...(item.variantId != null ? { variantId: item.variantId } : {}),
87
+ quantity: item.quantity,
88
+ orderId: data.checkoutId,
89
+ performedBy,
90
+ },
91
+ ctx.hook.actor,
92
+ );
90
93
 
91
94
  if (!result.ok) {
92
95
  return Err(
@@ -112,13 +115,16 @@ export const reserveInventoryStep: Step<
112
115
  const performedBy = ctx.hook.actor?.userId ?? "system";
113
116
 
114
117
  for (const r of reservations) {
115
- await inventory.release({
116
- entityId: r.entityId,
117
- ...(r.variantId != null ? { variantId: r.variantId } : {}),
118
- quantity: r.quantity,
119
- orderId: r.orderId,
120
- performedBy,
121
- });
118
+ await inventory.release(
119
+ {
120
+ entityId: r.entityId,
121
+ ...(r.variantId != null ? { variantId: r.variantId } : {}),
122
+ quantity: r.quantity,
123
+ orderId: r.orderId,
124
+ performedBy,
125
+ },
126
+ ctx.hook.actor,
127
+ );
122
128
  }
123
129
  },
124
130
  };
@@ -566,16 +566,19 @@ export const reserveInventory: AfterHook<OrderResult> = async ({ result, context
566
566
  quantity: number;
567
567
  orderId: string;
568
568
  performedBy: string;
569
- }): Promise<unknown>;
569
+ }, actor?: unknown): Promise<unknown>;
570
570
  };
571
571
  for (const lineItem of result.lineItems ?? []) {
572
- await inventory.reserve({
573
- entityId: lineItem.entityId,
574
- ...(lineItem.variantId != null ? { variantId: lineItem.variantId } : {}),
575
- quantity: lineItem.quantity,
576
- orderId: result.id,
577
- performedBy: context.actor?.userId ?? "system",
578
- });
572
+ await inventory.reserve(
573
+ {
574
+ entityId: lineItem.entityId,
575
+ ...(lineItem.variantId != null ? { variantId: lineItem.variantId } : {}),
576
+ quantity: lineItem.quantity,
577
+ orderId: result.id,
578
+ performedBy: context.actor?.userId ?? "system",
579
+ },
580
+ context.actor,
581
+ );
579
582
  }
580
583
  };
581
584
 
package/src/index.ts CHANGED
@@ -32,6 +32,7 @@ export type { Kernel } from "./runtime/kernel.js";
32
32
 
33
33
  export type { Actor } from "./auth/types.js";
34
34
  export { resolveOrgId, ensureDefaultOrg, DEFAULT_ORG_ID } from "./auth/org.js";
35
+ export { createSystemActor } from "./auth/system-actor.js";
35
36
  export { OrganizationService } from "./modules/organization/service.js";
36
37
  export { createScopedDb } from "./kernel/database/scoped-db.js";
37
38
  export type { ScopedOrganizationId } from "./kernel/database/scoped-db.js";
@@ -61,7 +62,15 @@ export type {
61
62
  export { runBeforeHooks, runAfterHooks } from "./kernel/hooks/executor.js";
62
63
  export { createHookContext } from "./kernel/hooks/create-context.js";
63
64
  export type { CreateHookContextArgs } from "./kernel/hooks/create-context.js";
64
- export type { JobsAdapter, EnqueueOptions } from "./kernel/jobs/adapter.js";
65
+ export type {
66
+ JobsAdapter,
67
+ EnqueueOptions,
68
+ ExecutionDriver,
69
+ ExecutionEngine,
70
+ ExecutionEngineSetup,
71
+ RunJobsOptions,
72
+ RunJobsResult,
73
+ } from "./kernel/jobs/adapter.js";
65
74
  export { NullJobsAdapter } from "./kernel/jobs/adapter.js";
66
75
  export { DrizzleJobsAdapter } from "./kernel/jobs/drizzle-adapter.js";
67
76
  export { runPendingJobs } from "./kernel/jobs/runner.js";
@@ -71,6 +80,9 @@ export type {
71
80
  TaskContext,
72
81
  TaskJobMeta,
73
82
  TaskRetryConfig,
83
+ JobProcessingOrder,
84
+ JobProcessingOrderField,
85
+ JobProcessingOrderRecord,
74
86
  } from "./kernel/jobs/types.js";
75
87
  export { BUILTIN_JOB_TASK_SLUGS } from "./kernel/jobs/types.js";
76
88
  export {
@@ -127,6 +139,24 @@ export type {
127
139
  PaymentRefund,
128
140
  PaymentWebhookEvent,
129
141
  } from "./modules/payments/adapter.js";
142
+ export { defineChannelConnector } from "./modules/channels/adapter.js";
143
+ export type {
144
+ ChannelCatalogItem,
145
+ ChannelCatalogPage,
146
+ ChannelCatalogVariant,
147
+ ChannelConnector,
148
+ ChannelConnectorCapabilities,
149
+ ChannelInventoryLevel,
150
+ ChannelOrderLine,
151
+ ChannelOrderSlice,
152
+ ChannelConnectorError,
153
+ ChannelOrderStatus,
154
+ ChannelPushOrderResult,
155
+ ChannelRefundResult,
156
+ ChannelReservation,
157
+ ChannelStore,
158
+ ChannelWebhookEvent,
159
+ } from "./modules/channels/adapter.js";
130
160
  export type { StorageAdapter } from "./modules/media/adapter.js";
131
161
  export { noopStorageAdapter } from "./modules/media/noop-adapter.js";
132
162
  export type {
@@ -240,6 +240,8 @@ class RouteChain {
240
240
 
241
241
  const result = await fn(handlerCtx);
242
242
 
243
+ if (result instanceof Response) return result;
244
+
243
245
  if (result === undefined || result === null) {
244
246
  return ctx.json({ data: null }, successStatus);
245
247
  }