@porulle/core 0.9.0 → 0.10.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.
Files changed (113) hide show
  1. package/dist/config/defaults.js +1 -1
  2. package/dist/config/types.d.ts +4 -3
  3. package/dist/config/types.d.ts.map +1 -1
  4. package/dist/generated/plugin-manifest.d.ts +3 -0
  5. package/dist/generated/plugin-manifest.d.ts.map +1 -1
  6. package/dist/generated/plugin-manifest.js +1 -0
  7. package/dist/index.d.ts +5 -2
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2 -0
  10. package/dist/interfaces/rest/router.d.ts.map +1 -1
  11. package/dist/interfaces/rest/router.js +2 -0
  12. package/dist/interfaces/rest/routes/checkout.d.ts.map +1 -1
  13. package/dist/interfaces/rest/routes/checkout.js +1 -0
  14. package/dist/interfaces/rest/schemas/catalog.d.ts +391 -0
  15. package/dist/interfaces/rest/schemas/catalog.d.ts.map +1 -1
  16. package/dist/interfaces/rest/schemas/responses.d.ts +51 -0
  17. package/dist/interfaces/rest/schemas/responses.d.ts.map +1 -1
  18. package/dist/interfaces/rest/schemas/search.d.ts +17 -0
  19. package/dist/interfaces/rest/schemas/search.d.ts.map +1 -1
  20. package/dist/kernel/database/adapter.d.ts +8 -0
  21. package/dist/kernel/database/adapter.d.ts.map +1 -1
  22. package/dist/kernel/database/migrate.d.ts +1 -1
  23. package/dist/kernel/database/migrate.d.ts.map +1 -1
  24. package/dist/kernel/database/migrate.js +5 -2
  25. package/dist/kernel/database/scoped-db.d.ts.map +1 -1
  26. package/dist/kernel/database/scoped-db.js +21 -23
  27. package/dist/kernel/errors.d.ts +12 -0
  28. package/dist/kernel/errors.d.ts.map +1 -1
  29. package/dist/kernel/errors.js +16 -0
  30. package/dist/kernel/jobs/adapter.d.ts +27 -8
  31. package/dist/kernel/jobs/adapter.d.ts.map +1 -1
  32. package/dist/kernel/jobs/adapter.js +1 -2
  33. package/dist/kernel/jobs/drizzle-adapter.d.ts +13 -3
  34. package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
  35. package/dist/kernel/jobs/drizzle-adapter.js +46 -5
  36. package/dist/kernel/jobs/runner.d.ts +2 -1
  37. package/dist/kernel/jobs/runner.d.ts.map +1 -1
  38. package/dist/kernel/jobs/runner.js +100 -15
  39. package/dist/kernel/jobs/types.d.ts +13 -0
  40. package/dist/kernel/jobs/types.d.ts.map +1 -1
  41. package/dist/kernel/plugin/manifest.d.ts +2 -0
  42. package/dist/kernel/plugin/manifest.d.ts.map +1 -1
  43. package/dist/kernel/plugin/manifest.js +9 -0
  44. package/dist/modules/catalog/entity-service.d.ts +1 -0
  45. package/dist/modules/catalog/entity-service.d.ts.map +1 -1
  46. package/dist/modules/catalog/entity-service.js +15 -1
  47. package/dist/modules/catalog/repository/index.d.ts +2 -2
  48. package/dist/modules/catalog/repository/index.d.ts.map +1 -1
  49. package/dist/modules/catalog/repository/index.js +9 -1
  50. package/dist/modules/catalog/schema.d.ts +51 -0
  51. package/dist/modules/catalog/schema.d.ts.map +1 -1
  52. package/dist/modules/catalog/schema.js +12 -1
  53. package/dist/modules/catalog/schemas.d.ts +3 -1
  54. package/dist/modules/catalog/schemas.d.ts.map +1 -1
  55. package/dist/modules/channels/adapter.d.ts +117 -0
  56. package/dist/modules/channels/adapter.d.ts.map +1 -0
  57. package/dist/modules/channels/adapter.js +3 -0
  58. package/dist/modules/inventory/repository/index.js +4 -4
  59. package/dist/modules/inventory/service.d.ts.map +1 -1
  60. package/dist/modules/inventory/service.js +7 -3
  61. package/dist/modules/orders/service.d.ts.map +1 -1
  62. package/dist/modules/orders/service.js +7 -4
  63. package/dist/modules/orders/stale-order-cleanup.d.ts.map +1 -1
  64. package/dist/modules/orders/stale-order-cleanup.js +25 -16
  65. package/dist/modules/promotions/repository/index.d.ts +1 -1
  66. package/dist/modules/promotions/repository/index.d.ts.map +1 -1
  67. package/dist/modules/promotions/repository/index.js +6 -3
  68. package/dist/modules/promotions/service.d.ts +1 -0
  69. package/dist/modules/promotions/service.d.ts.map +1 -1
  70. package/dist/modules/promotions/service.js +1 -1
  71. package/dist/modules/webhooks/repository/index.d.ts +1 -1
  72. package/dist/modules/webhooks/repository/index.d.ts.map +1 -1
  73. package/dist/modules/webhooks/repository/index.js +16 -10
  74. package/dist/runtime/commerce.d.ts.map +1 -1
  75. package/dist/runtime/commerce.js +9 -0
  76. package/dist/runtime/kernel.d.ts.map +1 -1
  77. package/dist/runtime/kernel.js +14 -2
  78. package/dist/runtime/server.d.ts +1 -4
  79. package/dist/runtime/server.d.ts.map +1 -1
  80. package/dist/runtime/server.js +16 -34
  81. package/package.json +3 -3
  82. package/src/config/defaults.ts +1 -1
  83. package/src/config/types.ts +7 -3
  84. package/src/generated/plugin-capabilities.d.ts +1 -0
  85. package/src/generated/plugin-manifest.ts +1 -0
  86. package/src/generated/plugin-repositories.d.ts +1 -0
  87. package/src/index.ts +31 -1
  88. package/src/interfaces/rest/router.ts +2 -0
  89. package/src/interfaces/rest/routes/checkout.ts +1 -0
  90. package/src/kernel/database/adapter.ts +8 -0
  91. package/src/kernel/database/migrate.ts +8 -2
  92. package/src/kernel/database/scoped-db.ts +25 -26
  93. package/src/kernel/errors.ts +20 -0
  94. package/src/kernel/jobs/adapter.ts +39 -8
  95. package/src/kernel/jobs/drizzle-adapter.ts +72 -9
  96. package/src/kernel/jobs/runner.ts +194 -72
  97. package/src/kernel/jobs/types.ts +25 -0
  98. package/src/kernel/plugin/manifest.ts +12 -0
  99. package/src/modules/catalog/entity-service.ts +9 -1
  100. package/src/modules/catalog/repository/index.ts +13 -3
  101. package/src/modules/catalog/schema.ts +33 -18
  102. package/src/modules/catalog/schemas.ts +3 -1
  103. package/src/modules/channels/adapter.ts +139 -0
  104. package/src/modules/inventory/repository/index.ts +4 -4
  105. package/src/modules/inventory/service.ts +12 -2
  106. package/src/modules/orders/service.ts +8 -12
  107. package/src/modules/orders/stale-order-cleanup.ts +35 -24
  108. package/src/modules/promotions/repository/index.ts +6 -2
  109. package/src/modules/promotions/service.ts +2 -0
  110. package/src/modules/webhooks/repository/index.ts +16 -14
  111. package/src/runtime/commerce.ts +10 -0
  112. package/src/runtime/kernel.ts +16 -2
  113. package/src/runtime/server.ts +22 -42
@@ -14,6 +14,7 @@ function context(actor, services, database, tx = null) {
14
14
  tx,
15
15
  logger: createLogger("orders"),
16
16
  services,
17
+ ...(services.jobs ? { jobs: services.jobs } : {}),
17
18
  context: { moduleName: "orders" },
18
19
  database: { db: database.db },
19
20
  });
@@ -425,8 +426,9 @@ export class OrderService {
425
426
  }, actor, ctx);
426
427
  if (!releaseResult.ok) {
427
428
  const err = toCommerceError(releaseResult.error);
428
- if (!(err instanceof CommerceValidationError &&
429
- err.message === "No inventory record found for this entity.")) {
429
+ // Tolerate a missing inventory record (nothing to release); surface
430
+ // any other inventory error. Check the typed code, not the message.
431
+ if (err.code !== "INVENTORY_RECORD_NOT_FOUND") {
430
432
  return Err(err);
431
433
  }
432
434
  }
@@ -495,8 +497,9 @@ export class OrderService {
495
497
  }, actor, ctx);
496
498
  if (!releaseResult.ok) {
497
499
  const err = toCommerceError(releaseResult.error);
498
- if (!(err instanceof CommerceValidationError &&
499
- err.message === "No inventory record found for this entity.")) {
500
+ // Tolerate a missing inventory record (nothing to release); surface
501
+ // any other inventory error. Check the typed code, not the message.
502
+ if (err.code !== "INVENTORY_RECORD_NOT_FOUND") {
500
503
  return Err(err);
501
504
  }
502
505
  }
@@ -1 +1 @@
1
- {"version":3,"file":"stale-order-cleanup.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/stale-order-cleanup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAejE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAChD;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3B;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAkD/C,CAAC"}
1
+ {"version":3,"file":"stale-order-cleanup.d.ts","sourceRoot":"","sources":["../../../src/modules/orders/stale-order-cleanup.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAejE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAChD;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3B;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CA6D/C,CAAC"}
@@ -36,25 +36,34 @@ export const staleOrderCleanupTask = {
36
36
  async handler({ input, ctx }) {
37
37
  const thresholdHours = input.thresholdHours ?? 48;
38
38
  const cutoff = new Date(Date.now() - thresholdHours * 60 * 60 * 1000);
39
- // Find stale pending orders
40
- const staleOrders = await ctx.db
41
- .select()
42
- .from(orders)
43
- .where(and(eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
44
39
  const cancelledIds = [];
45
40
  // Cancel each stale order via the service (triggers inventory release + payment refund)
46
41
  const orderService = ctx.services.orders;
47
- for (const order of staleOrders) {
48
- try {
49
- await orderService.cancel(order.id, systemActorForOrg(order.organizationId), `Auto-cancelled: pending for >${thresholdHours}h`);
50
- cancelledIds.push(order.id);
51
- ctx.logger.info(`Stale order ${order.orderNumber} auto-cancelled`, {
52
- orderId: order.id,
53
- placedAt: order.placedAt,
54
- });
55
- }
56
- catch (error) {
57
- ctx.logger.error(`Failed to cancel stale order ${order.orderNumber}`, { error });
42
+ // This is a cross-org system sweep, but the row reads stay org-scoped: first
43
+ // enumerate the orgs that have stale orders (id-only projection), then read
44
+ // each org's stale orders under an explicit organizationId predicate. No
45
+ // query returns another tenant's order rows.
46
+ const staleOrgs = await ctx.db
47
+ .selectDistinct({ organizationId: orders.organizationId })
48
+ .from(orders)
49
+ .where(and(eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
50
+ for (const { organizationId } of staleOrgs) {
51
+ const staleOrders = await ctx.db
52
+ .select()
53
+ .from(orders)
54
+ .where(and(eq(orders.organizationId, organizationId), eq(orders.status, "pending"), lt(orders.placedAt, cutoff)));
55
+ for (const order of staleOrders) {
56
+ try {
57
+ await orderService.cancel(order.id, systemActorForOrg(order.organizationId), `Auto-cancelled: pending for >${thresholdHours}h`);
58
+ cancelledIds.push(order.id);
59
+ ctx.logger.info(`Stale order ${order.orderNumber} auto-cancelled`, {
60
+ orderId: order.id,
61
+ placedAt: order.placedAt,
62
+ });
63
+ }
64
+ catch (error) {
65
+ ctx.logger.error(`Failed to cancel stale order ${order.orderNumber}`, { error });
66
+ }
58
67
  }
59
68
  }
60
69
  return {
@@ -28,7 +28,7 @@ export declare class PromotionsRepository {
28
28
  findUsageById(id: string, ctx?: TxContext): Promise<PromotionUsage | undefined>;
29
29
  findUsagesByPromotionId(promotionId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
30
30
  findUsagesByCustomerId(customerId: string, ctx?: TxContext): Promise<PromotionUsage[]>;
31
- createUsage(data: PromotionUsageInsert, ctx?: TxContext): Promise<PromotionUsage>;
31
+ createUsage(data: PromotionUsageInsert, orgId: string, ctx?: TxContext): Promise<PromotionUsage>;
32
32
  countUsages(promotionId: string, ctx?: TxContext): Promise<number>;
33
33
  countUsagesByCustomer(promotionId: string, customerId: string, ctx?: TxContext): Promise<number>;
34
34
  isUsageLimitReached(orgId: string, promotionId: string, ctx?: TxContext): Promise<boolean>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/promotions/repository/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/promotions/repository/index.ts"],"names":[],"mappings":"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"}
@@ -112,16 +112,19 @@ export class PromotionsRepository {
112
112
  .from(promotionUsages)
113
113
  .where(eq(promotionUsages.customerId, customerId));
114
114
  }
115
- async createUsage(data, ctx) {
115
+ async createUsage(data, orgId, ctx) {
116
116
  const db = this.getDb(ctx);
117
117
  // Atomic guard: use INSERT ... SELECT WHERE count < limit to prevent
118
118
  // race conditions between concurrent checkouts using the same coupon.
119
119
  // A plain count-then-insert has a TOCTOU gap; this single statement
120
120
  // ensures the insert only succeeds if the limit has not been reached.
121
+ // Both reads are scoped by organizationId: these are raw/lock queries that
122
+ // bypass the scoped-db proxy, so a promotionId from another tenant must
123
+ // resolve to no row rather than reading/locking a foreign promotion.
121
124
  const promo = await db
122
125
  .select({ usageLimitTotal: promotions.usageLimitTotal })
123
126
  .from(promotions)
124
- .where(eq(promotions.id, data.promotionId));
127
+ .where(and(eq(promotions.id, data.promotionId), eq(promotions.organizationId, orgId)));
125
128
  const limit = promo[0]?.usageLimitTotal;
126
129
  if (limit != null) {
127
130
  // Atomic guard: lock the promotion row and check usage count in the
@@ -131,7 +134,7 @@ export class PromotionsRepository {
131
134
  // SELECT ... FOR UPDATE acquires a row-level lock that serializes
132
135
  // concurrent callers. If the caller is already inside a transaction
133
136
  // (ctx.tx), the lock is held until that transaction commits.
134
- await db.execute(sql `SELECT id FROM promotions WHERE id = ${data.promotionId} FOR UPDATE`);
137
+ await db.execute(sql `SELECT id FROM promotions WHERE id = ${data.promotionId} AND organization_id = ${orgId} FOR UPDATE`);
135
138
  const currentCount = await this.countUsages(data.promotionId, ctx);
136
139
  if (currentCount >= limit) {
137
140
  throw new Error(`Promotion usage limit reached (${currentCount}/${limit})`);
@@ -85,6 +85,7 @@ export declare class PromotionService {
85
85
  applyPromotions(context: PromotionEvaluationContext, ctx?: TxContext): Promise<Result<PromotionApplicationResult>>;
86
86
  recordUsage(input: {
87
87
  promotions: AppliedPromotion[];
88
+ organizationId: string;
88
89
  customerId?: string;
89
90
  orderId?: string;
90
91
  }, ctx?: TxContext): Promise<Result<PromotionUsage[]>>;
@@ -1 +1 @@
1
- {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/modules/promotions/service.ts"],"names":[],"mappings":"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"}
@@ -302,7 +302,7 @@ export class PromotionService {
302
302
  promotionId: applied.promotionId,
303
303
  customerId: input.customerId ?? null,
304
304
  orderId: input.orderId ?? null,
305
- }, ctx);
305
+ }, input.organizationId, ctx);
306
306
  usages.push(usage);
307
307
  }
308
308
  return Ok(usages);
@@ -29,7 +29,7 @@ export declare class WebhooksRepository {
29
29
  limit?: number;
30
30
  }, ctx?: TxContext): Promise<WebhookDelivery[]>;
31
31
  findPendingDeliveries(ctx?: TxContext): Promise<WebhookDelivery[]>;
32
- findFailedDeliveries(endpointId?: string, ctx?: TxContext): Promise<WebhookDelivery[]>;
32
+ findFailedDeliveries(orgId: string, endpointId?: string, ctx?: TxContext): Promise<WebhookDelivery[]>;
33
33
  createDelivery(data: WebhookDeliveryInsert, ctx?: TxContext): Promise<WebhookDelivery>;
34
34
  updateDelivery(id: string, data: Partial<Omit<WebhookDeliveryInsert, "id">>, ctx?: TxContext): Promise<WebhookDelivery | undefined>;
35
35
  markDelivered(id: string, statusCode: number, ctx?: TxContext): Promise<WebhookDelivery | undefined>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/webhooks/repository/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/webhooks/repository/index.ts"],"names":[],"mappings":"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"}
@@ -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":"commerce.d.ts","sourceRoot":"","sources":["../../src/runtime/commerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAI1C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAwB,MAAM,wBAAwB,CAAC;AAErG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,GAAG,EAAE,gBAAgB,CAAC;IAEtB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IAEf,mDAAmD;IACnD,EAAE,EAAE,OAAO,CAAC;IAEZ,yDAAyD;IACzD,IAAI,EAAE,YAAY,CAAC;IAEnB,aAAa;IACb,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,gBAAgB,CAAC;CACtE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CAoC3B"}
1
+ {"version":3,"file":"commerce.d.ts","sourceRoot":"","sources":["../../src/runtime/commerce.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAK1C,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAkB,KAAK,gBAAgB,EAAwB,MAAM,wBAAwB,CAAC;AAErG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,GAAG,EAAE,gBAAgB,CAAC;IAEtB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IAEf,mDAAmD;IACnD,EAAE,EAAE,OAAO,CAAC;IAEZ,yDAAyD;IACzD,IAAI,EAAE,YAAY,CAAC;IAEnB,aAAa;IACb,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB,CAAC;IAE1C;;;;;;;;;;OAUG;IACH,eAAe,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,gBAAgB,CAAC;CACtE;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA6C3B"}
@@ -1,4 +1,5 @@
1
1
  import { createKernel } from "./kernel.js";
2
+ import { pushSchema } from "../kernel/database/migrate.js";
2
3
  import { ensureDefaultOrg, setBootDefaultOrgId } from "../auth/org.js";
3
4
  import { setBootStrictOrgResolution } from "../auth/strict-org-resolution.js";
4
5
  import { createAuth } from "../auth/setup.js";
@@ -17,6 +18,14 @@ import { createLocalAPI } from "../kernel/local-api.js";
17
18
  */
18
19
  export async function createCommerce(config) {
19
20
  const kernel = createKernel(config);
21
+ // Zero-migration adapters (PGlite) push core schema at construction, but a
22
+ // plugin's own tables live in `config.customSchemas`, which only exist after
23
+ // plugins run in `defineConfig` — i.e. after the adapter was constructed.
24
+ // Create them now so plugin routes have their tables. Guarded on both signals
25
+ // so plugin-less stores and migration-managed adapters (Postgres) are untouched.
26
+ if (config.databaseAdapter?.autoMigrate && config.customSchemas?.length) {
27
+ await pushSchema(kernel.database.db, config);
28
+ }
20
29
  setBootStrictOrgResolution(config.auth?.strictOrgResolution === true);
21
30
  // Register the config-driven org ID so resolveOrgId() can use it
22
31
  // without requiring every service to have config access.
@@ -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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -56,8 +56,8 @@
56
56
  "eslint": "^9.39.1",
57
57
  "typescript": "5.9.2",
58
58
  "vitest": "^3.2.4",
59
- "@porulle/typescript-config": "0.1.0",
60
- "@porulle/eslint-config": "0.1.0"
59
+ "@porulle/eslint-config": "0.1.0",
60
+ "@porulle/typescript-config": "0.1.0"
61
61
  },
62
62
  "publishConfig": {
63
63
  "access": "public"
@@ -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",
@@ -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[];
@@ -353,8 +356,9 @@ export interface CommerceConfig {
353
356
  analytics?: AnalyticsConfig;
354
357
  search?: SearchConfig;
355
358
  jobs?: {
356
- adapter?: JobsAdapter;
359
+ adapter?: ExecutionEngine;
357
360
  tasks?: TaskDefinition[];
361
+ processingOrder?: JobProcessingOrder;
358
362
  autorun?: {
359
363
  enabled: boolean;
360
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>;
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
  }
@@ -286,6 +286,7 @@ export function checkoutRoutes(kernel: Kernel) {
286
286
  if (order.ok && (processed.appliedPromotions?.length ?? 0) > 0) {
287
287
  await kernel.services.promotions.recordUsage({
288
288
  promotions: processed.appliedPromotions ?? [],
289
+ organizationId: order.value.organizationId,
289
290
  orderId: order.value.id,
290
291
  ...(customerUuid !== undefined
291
292
  ? { customerId: customerUuid }
@@ -10,6 +10,14 @@ export interface DatabaseAdapter<TDatabase = unknown, TTransaction = unknown> {
10
10
  provider: string;
11
11
  db: TDatabase;
12
12
  transaction<T>(fn: (tx: TTransaction) => Promise<T>): Promise<T>;
13
+ /**
14
+ * Zero-migration adapters (e.g. PGlite) set this so the runtime pushes any
15
+ * plugin-declared tables (`config.customSchemas`) at boot. Plugin schemas
16
+ * only exist after plugins run in `defineConfig` — i.e. after this adapter was
17
+ * constructed — so the adapter cannot create them itself. Omit/false when the
18
+ * consumer manages migrations (e.g. production Postgres via drizzle-kit).
19
+ */
20
+ autoMigrate?: boolean;
13
21
  }
14
22
 
15
23
  export interface DatabaseConnectionFactoryInput {
@@ -103,7 +103,10 @@ export function getSchemaFiles(): string[] {
103
103
  * Drizzle instance bound to your database. `drizzle-kit` must be available
104
104
  * (it is the standard schema tool); a clear error is thrown if it is not.
105
105
  */
106
- export async function pushSchema(drizzleInstance: unknown): Promise<void> {
106
+ export async function pushSchema(
107
+ drizzleInstance: unknown,
108
+ config?: CommerceConfig,
109
+ ): Promise<void> {
107
110
  const require = createRequire(import.meta.url);
108
111
  let drizzleKit: {
109
112
  pushSchema(
@@ -118,7 +121,10 @@ export async function pushSchema(drizzleInstance: unknown): Promise<void> {
118
121
  "pushSchema() requires `drizzle-kit` to be installed. Add it to your project: bun add -d drizzle-kit",
119
122
  );
120
123
  }
124
+ // With a config, push the merged core + plugin schema so plugin-declared
125
+ // tables are created too; without one, push core only (backward compatible).
126
+ const schemaToApply = config ? buildSchema(config) : getSchema();
121
127
  // drizzle-kit needs the native driver result shape; unwrap a normalized db.
122
- const { apply } = await drizzleKit.pushSchema(getSchema(), unwrapDb(drizzleInstance));
128
+ const { apply } = await drizzleKit.pushSchema(schemaToApply, unwrapDb(drizzleInstance));
123
129
  await apply();
124
130
  }