@porulle/core 0.17.0 → 0.19.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 (36) hide show
  1. package/dist/index.d.ts +3 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +2 -1
  4. package/dist/interfaces/rest/router.d.ts +21 -0
  5. package/dist/interfaces/rest/router.d.ts.map +1 -1
  6. package/dist/interfaces/rest/router.js +22 -1
  7. package/dist/interfaces/rest/schemas/admin-jobs.d.ts +2 -2
  8. package/dist/interfaces/rest/schemas/responses.d.ts +4 -4
  9. package/dist/jobs.d.ts +5 -0
  10. package/dist/jobs.d.ts.map +1 -0
  11. package/dist/jobs.js +2 -0
  12. package/dist/kernel/jobs/adapter.d.ts +13 -1
  13. package/dist/kernel/jobs/adapter.d.ts.map +1 -1
  14. package/dist/kernel/jobs/drizzle-adapter.d.ts +9 -1
  15. package/dist/kernel/jobs/drizzle-adapter.d.ts.map +1 -1
  16. package/dist/kernel/jobs/drizzle-adapter.js +32 -1
  17. package/dist/kernel/jobs/runner.d.ts.map +1 -1
  18. package/dist/kernel/jobs/runner.js +6 -2
  19. package/dist/kernel/jobs/schema.d.ts +2 -2
  20. package/dist/kernel/jobs/schema.js +1 -1
  21. package/dist/kernel/jobs/step.d.ts +8 -0
  22. package/dist/kernel/jobs/step.d.ts.map +1 -0
  23. package/dist/kernel/jobs/step.js +24 -0
  24. package/dist/kernel/jobs/types.d.ts +27 -0
  25. package/dist/kernel/jobs/types.d.ts.map +1 -1
  26. package/dist/kernel/jobs/types.js +9 -0
  27. package/package.json +7 -1
  28. package/src/index.ts +4 -1
  29. package/src/interfaces/rest/router.ts +23 -1
  30. package/src/jobs.ts +18 -0
  31. package/src/kernel/jobs/adapter.ts +12 -0
  32. package/src/kernel/jobs/drizzle-adapter.ts +43 -2
  33. package/src/kernel/jobs/runner.ts +6 -2
  34. package/src/kernel/jobs/schema.ts +1 -1
  35. package/src/kernel/jobs/step.ts +27 -0
  36. package/src/kernel/jobs/types.ts +43 -0
package/dist/index.d.ts CHANGED
@@ -34,8 +34,9 @@ export { NullJobsAdapter } from "./kernel/jobs/adapter.js";
34
34
  export { DrizzleJobsAdapter } from "./kernel/jobs/drizzle-adapter.js";
35
35
  export { runPendingJobs } from "./kernel/jobs/runner.js";
36
36
  export type { RunPendingJobsArgs } from "./kernel/jobs/runner.js";
37
- export type { TaskDefinition, TaskContext, TaskJobMeta, TaskRetryConfig, JobProcessingOrder, JobProcessingOrderField, JobProcessingOrderRecord, } from "./kernel/jobs/types.js";
38
- export { BUILTIN_JOB_TASK_SLUGS } from "./kernel/jobs/types.js";
37
+ export type { TaskDefinition, TaskContext, TaskStep, TaskJobMeta, TaskRetryConfig, JobInstanceStatus, JobProcessingOrder, JobProcessingOrderField, JobProcessingOrderRecord, } from "./kernel/jobs/types.js";
38
+ export { BUILTIN_JOB_TASK_SLUGS, TaskNonRetryableError } from "./kernel/jobs/types.js";
39
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
39
40
  export { staleJobReaperTask, runStaleJobReaper, getJobReapThresholdMs, getJobsReaperIntervalMs, } from "./kernel/jobs/reaper.js";
40
41
  export { createLocalAPI, LocalAPI } from "./kernel/local-api.js";
41
42
  export type { CommerceLocalAPI, LocalAPIOptions } from "./kernel/local-api.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -22,7 +22,8 @@ export { createHookContext } from "./kernel/hooks/create-context.js";
22
22
  export { NullJobsAdapter } from "./kernel/jobs/adapter.js";
23
23
  export { DrizzleJobsAdapter } from "./kernel/jobs/drizzle-adapter.js";
24
24
  export { runPendingJobs } from "./kernel/jobs/runner.js";
25
- export { BUILTIN_JOB_TASK_SLUGS } from "./kernel/jobs/types.js";
25
+ export { BUILTIN_JOB_TASK_SLUGS, TaskNonRetryableError } from "./kernel/jobs/types.js";
26
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
26
27
  export { staleJobReaperTask, runStaleJobReaper, getJobReapThresholdMs, getJobsReaperIntervalMs, } from "./kernel/jobs/reaper.js";
27
28
  export { createLocalAPI, LocalAPI } from "./kernel/local-api.js";
28
29
  export { createCommerce } from "./runtime/commerce.js";
@@ -67,6 +67,7 @@ declare class RouteChain {
67
67
  private _input;
68
68
  private _query;
69
69
  private _params;
70
+ private _output;
70
71
  private _requireAuth;
71
72
  private _requiredPermission;
72
73
  constructor(method: string, fullPath: string, tag: string, routesList: PluginRouteRegistration[], pluginCtx?: {
@@ -84,6 +85,26 @@ declare class RouteChain {
84
85
  query(schema: z.ZodType): this;
85
86
  /** Override the auto-detected path parameter schema. */
86
87
  params(schema: z.ZodType): this;
88
+ /**
89
+ * Declare the shape carried inside the `{ data }` envelope on success.
90
+ *
91
+ * Without it the route documents its payload as `any`, and every generated
92
+ * client sees `data?: unknown` — so a consumer has to hand-maintain a
93
+ * parallel schema, which is the drift the generated SDK exists to prevent.
94
+ *
95
+ * Documentation only: the handler's return value is not validated against
96
+ * this schema. Validating would mean either stripping unknown keys — silently
97
+ * changing what goes over the wire for anyone who adopts it — or throwing at
98
+ * runtime, turning an OpenAPI annotation into a new failure mode. A stale
99
+ * annotation is the cheaper wrong.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * vendors.get("/{id}").summary("Get").output(VendorSchema)
104
+ * .handler(async ({ params, services }) => services.vendor.get(params.id));
105
+ * ```
106
+ */
107
+ output(schema: z.ZodType): this;
87
108
  /**
88
109
  * Require authentication. The handler's `actor` is guaranteed non-null.
89
110
  * Returns 401 if the request has no authenticated actor.
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/interfaces/rest/router.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../src/interfaces/rest/router.ts"],"names":[],"mappings":"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"}
@@ -56,6 +56,7 @@ class RouteChain {
56
56
  _input;
57
57
  _query;
58
58
  _params;
59
+ _output;
59
60
  _requireAuth = false;
60
61
  _requiredPermission;
61
62
  constructor(method, fullPath, tag, routesList, pluginCtx) {
@@ -79,6 +80,26 @@ class RouteChain {
79
80
  query(schema) { this._query = schema; return this; }
80
81
  /** Override the auto-detected path parameter schema. */
81
82
  params(schema) { this._params = schema; return this; }
83
+ /**
84
+ * Declare the shape carried inside the `{ data }` envelope on success.
85
+ *
86
+ * Without it the route documents its payload as `any`, and every generated
87
+ * client sees `data?: unknown` — so a consumer has to hand-maintain a
88
+ * parallel schema, which is the drift the generated SDK exists to prevent.
89
+ *
90
+ * Documentation only: the handler's return value is not validated against
91
+ * this schema. Validating would mean either stripping unknown keys — silently
92
+ * changing what goes over the wire for anyone who adopts it — or throwing at
93
+ * runtime, turning an OpenAPI annotation into a new failure mode. A stale
94
+ * annotation is the cheaper wrong.
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * vendors.get("/{id}").summary("Get").output(VendorSchema)
99
+ * .handler(async ({ params, services }) => services.vendor.get(params.id));
100
+ * ```
101
+ */
102
+ output(schema) { this._output = schema; return this; }
82
103
  /**
83
104
  * Require authentication. The handler's `actor` is guaranteed non-null.
84
105
  * Returns 401 if the request has no authenticated actor.
@@ -126,7 +147,7 @@ class RouteChain {
126
147
  ...(this._description ? { description: this._description } : {}),
127
148
  ...(Object.keys(request).length > 0 ? { request } : {}),
128
149
  responses: {
129
- [status]: wrapJson(z.object({ data: z.any() })),
150
+ [status]: wrapJson(z.object({ data: this._output ?? z.any() })),
130
151
  ...errorResponses,
131
152
  },
132
153
  });
@@ -122,14 +122,14 @@ export declare const listFailedJobsRoute: {
122
122
  tableName: "commerce_jobs";
123
123
  dataType: "string";
124
124
  columnType: "PgText";
125
- data: "pending" | "processing" | "succeeded" | "failed";
125
+ data: "pending" | "processing" | "succeeded" | "failed" | "cancelled";
126
126
  driverParam: string;
127
127
  notNull: true;
128
128
  hasDefault: true;
129
129
  isPrimaryKey: false;
130
130
  isAutoincrement: false;
131
131
  hasRuntimeDefault: false;
132
- enumValues: ["pending", "processing", "succeeded", "failed"];
132
+ enumValues: ["pending", "processing", "succeeded", "failed", "cancelled"];
133
133
  baseColumn: never;
134
134
  identity: undefined;
135
135
  generated: undefined;
@@ -2245,14 +2245,14 @@ export declare const JobSchema: import("drizzle-zod").BuildSchema<"select", {
2245
2245
  tableName: "commerce_jobs";
2246
2246
  dataType: "string";
2247
2247
  columnType: "PgText";
2248
- data: "pending" | "processing" | "succeeded" | "failed";
2248
+ data: "pending" | "processing" | "succeeded" | "failed" | "cancelled";
2249
2249
  driverParam: string;
2250
2250
  notNull: true;
2251
2251
  hasDefault: true;
2252
2252
  isPrimaryKey: false;
2253
2253
  isAutoincrement: false;
2254
2254
  hasRuntimeDefault: false;
2255
- enumValues: ["pending", "processing", "succeeded", "failed"];
2255
+ enumValues: ["pending", "processing", "succeeded", "failed", "cancelled"];
2256
2256
  baseColumn: never;
2257
2257
  identity: undefined;
2258
2258
  generated: undefined;
@@ -4280,14 +4280,14 @@ export declare const JobListResponse: z.ZodObject<{
4280
4280
  tableName: "commerce_jobs";
4281
4281
  dataType: "string";
4282
4282
  columnType: "PgText";
4283
- data: "pending" | "processing" | "succeeded" | "failed";
4283
+ data: "pending" | "processing" | "succeeded" | "failed" | "cancelled";
4284
4284
  driverParam: string;
4285
4285
  notNull: true;
4286
4286
  hasDefault: true;
4287
4287
  isPrimaryKey: false;
4288
4288
  isAutoincrement: false;
4289
4289
  hasRuntimeDefault: false;
4290
- enumValues: ["pending", "processing", "succeeded", "failed"];
4290
+ enumValues: ["pending", "processing", "succeeded", "failed", "cancelled"];
4291
4291
  baseColumn: never;
4292
4292
  identity: undefined;
4293
4293
  generated: undefined;
package/dist/jobs.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export type { JobInstanceStatus, TaskContext, TaskDefinition, TaskJobMeta, TaskRetryConfig, TaskStep, } from "./kernel/jobs/types.js";
2
+ export { TaskNonRetryableError } from "./kernel/jobs/types.js";
3
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
4
+ export type { EnqueueOptions, ExecutionEngine, ExecutionEngineSetup, JobsAdapter, } from "./kernel/jobs/adapter.js";
5
+ //# sourceMappingURL=jobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,WAAW,EACX,eAAe,EACf,QAAQ,GACT,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EACV,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,WAAW,GACZ,MAAM,0BAA0B,CAAC"}
package/dist/jobs.js ADDED
@@ -0,0 +1,2 @@
1
+ export { TaskNonRetryableError } from "./kernel/jobs/types.js";
2
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
@@ -1,7 +1,14 @@
1
- import type { JobProcessingOrder, TaskContext, TaskDefinition } from "./types.js";
1
+ import type { JobInstanceStatus, JobProcessingOrder, TaskContext, TaskDefinition } from "./types.js";
2
2
  /** Enqueue-only surface exposed to hooks and services. */
3
3
  export interface JobsAdapter {
4
4
  enqueue(taskSlug: string, input: Record<string, unknown>, options: EnqueueOptions): Promise<string>;
5
+ /** Not every engine can report instance status; absent where it cannot. */
6
+ status?(jobId: string): Promise<{
7
+ status: JobInstanceStatus;
8
+ error?: string;
9
+ }>;
10
+ /** Not every engine can cancel an in-flight instance; absent where it cannot. */
11
+ cancel?(jobId: string): Promise<void>;
5
12
  }
6
13
  export interface RunJobsOptions {
7
14
  queue?: string;
@@ -34,6 +41,11 @@ export interface EnqueueOptions {
34
41
  delayMs?: number;
35
42
  concurrencyKey?: string;
36
43
  supersedes?: boolean;
44
+ /** Used verbatim as the job id — the `commerce_jobs` row id on the drizzle
45
+ * engine, the instance id on Cloudflare Workflows — so a caller can address the
46
+ * job it created ("one generation, one instance"). Must be a UUID on drizzle.
47
+ * The pg-boss, Inngest and Trigger engines choose their own ids and ignore it. */
48
+ jobId?: string;
37
49
  }
38
50
  /**
39
51
  * No-op enqueue surface used by isolated hook contexts.
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,OAAO,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACvD,GACD;IACE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEN,uEAAuE;AACvE,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,WAAW;IAC3C,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC;CAGnB"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,2EAA2E;IAC3E,MAAM,CAAC,CACL,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1D,iFAAiF;IACjF,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,OAAO,EAAE,WAAW,CAAC;IACrB,eAAe,CAAC,EAAE,kBAAkB,CAAC;CACtC;AAED,MAAM,MAAM,eAAe,GACvB;IACE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACvD,GACD;IACE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEN,uEAAuE;AACvE,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;sFAGkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,WAAW;IAC3C,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,CAAC;CAGnB"}
@@ -1,5 +1,5 @@
1
1
  import type { DrizzleDatabase } from "../database/drizzle-db.js";
2
- import type { TaskDefinition } from "./types.js";
2
+ import type { JobInstanceStatus, TaskDefinition } from "./types.js";
3
3
  import type { EnqueueOptions, ExecutionEngine, ExecutionEngineSetup, RunJobsOptions } from "./adapter.js";
4
4
  /**
5
5
  * PostgreSQL-backed job queue adapter using the application's own database.
@@ -21,5 +21,13 @@ export declare class DrizzleJobsAdapter implements ExecutionEngine {
21
21
  constructor(db: DrizzleDatabase, tasks?: Map<string, TaskDefinition>);
22
22
  register(setup: ExecutionEngineSetup): void;
23
23
  enqueue(taskSlug: string, input: Record<string, unknown>, options: EnqueueOptions): Promise<string>;
24
+ status(jobId: string): Promise<{
25
+ status: JobInstanceStatus;
26
+ error?: string;
27
+ }>;
28
+ /** Cancels a job that has not started yet; a job already `processing` (or
29
+ * finished) cannot be interrupted from here and is left untouched. The row is
30
+ * kept so `status()` reports it as `terminated`. */
31
+ cancel(jobId: string): Promise<void>;
24
32
  }
25
33
  //# sourceMappingURL=drizzle-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle-adapter.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/drizzle-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,cAAc,CAAC;AAUtB;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IA4CtD,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IA5Cf,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,oBAAoB,CAAK;IAEjC,QAAQ,CAAC,SAAS;;wBAEK,cAAc;;;;MAmCnC;gBAGQ,EAAE,EAAE,eAAe,EACnB,KAAK,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa;IAGxD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAKrC,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC;CA+CnB"}
1
+ {"version":3,"file":"drizzle-adapter.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/drizzle-adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,cAAc,CAAC;AAUtB;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IA4CtD,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IA5Cf,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,oBAAoB,CAAK;IAEjC,QAAQ,CAAC,SAAS;;wBAEK,cAAc;;;;MAmCnC;gBAGQ,EAAE,EAAE,eAAe,EACnB,KAAK,GAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAa;IAGxD,QAAQ,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAKrC,OAAO,CACX,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC;IAiDZ,MAAM,CACV,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAezD;;wDAEoD;IAC9C,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ3C"}
@@ -1,5 +1,5 @@
1
1
  import { eq, and } from "drizzle-orm";
2
- import { OrgResolutionError } from "../errors.js";
2
+ import { OrgResolutionError, CommerceNotFoundError } from "../errors.js";
3
3
  import { commerceJobs } from "./schema.js";
4
4
  import { runPendingJobs } from "./runner.js";
5
5
  import { getJobReapThresholdMs, getJobsReaperIntervalMs, runStaleJobReaper, } from "./reaper.js";
@@ -71,6 +71,7 @@ export class DrizzleJobsAdapter {
71
71
  const rows = await this.db
72
72
  .insert(commerceJobs)
73
73
  .values({
74
+ ...(options.jobId ? { id: options.jobId } : {}),
74
75
  organizationId,
75
76
  taskSlug,
76
77
  input,
@@ -84,4 +85,34 @@ export class DrizzleJobsAdapter {
84
85
  .returning({ id: commerceJobs.id });
85
86
  return rows[0].id;
86
87
  }
88
+ async status(jobId) {
89
+ const [row] = await this.db
90
+ .select()
91
+ .from(commerceJobs)
92
+ .where(eq(commerceJobs.id, jobId))
93
+ .limit(1);
94
+ if (!row) {
95
+ throw new CommerceNotFoundError(`No job found for id ${jobId}`);
96
+ }
97
+ return {
98
+ status: DRIZZLE_JOB_STATUS_MAP[row.status],
99
+ ...(row.error !== null ? { error: row.error } : {}),
100
+ };
101
+ }
102
+ /** Cancels a job that has not started yet; a job already `processing` (or
103
+ * finished) cannot be interrupted from here and is left untouched. The row is
104
+ * kept so `status()` reports it as `terminated`. */
105
+ async cancel(jobId) {
106
+ await this.db
107
+ .update(commerceJobs)
108
+ .set({ status: "cancelled" })
109
+ .where(and(eq(commerceJobs.id, jobId), eq(commerceJobs.status, "pending")));
110
+ }
87
111
  }
112
+ const DRIZZLE_JOB_STATUS_MAP = {
113
+ pending: "queued",
114
+ processing: "running",
115
+ succeeded: "complete",
116
+ failed: "errored",
117
+ cancelled: "terminated",
118
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGrE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,kBAAkB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AA4CD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAkNhD"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAKrE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,kBAAkB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AA4CD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoNhD"}
@@ -1,4 +1,6 @@
1
1
  import { eq, and, sql } from "drizzle-orm";
2
+ import { TaskNonRetryableError } from "./types.js";
3
+ import { createPassThroughTaskStep } from "./step.js";
2
4
  import { commerceJobs } from "./schema.js";
3
5
  function isExclusiveTask(task) {
4
6
  return Boolean(task?.concurrency && task.concurrency.exclusive !== false);
@@ -115,6 +117,7 @@ export async function runPendingJobs(args) {
115
117
  return jobsToRun.sort(compareByProcessingOrder(processingOrder));
116
118
  });
117
119
  // Phase 2: Execute each claimed job outside the claim transaction
120
+ const step = createPassThroughTaskStep();
118
121
  const outcomes = await Promise.all(claimed.map(async (job) => {
119
122
  const task = tasks.get(job.taskSlug);
120
123
  if (!task) {
@@ -136,7 +139,7 @@ export async function runPendingJobs(args) {
136
139
  try {
137
140
  const result = await task.handler({
138
141
  input: job.input,
139
- ctx: { logger, db, services },
142
+ ctx: { logger, db, services, step },
140
143
  job: {
141
144
  attemptNumber: job.attempts + 1,
142
145
  maxAttempts: job.maxAttempts,
@@ -162,7 +165,8 @@ export async function runPendingJobs(args) {
162
165
  });
163
166
  const attempts = job.attempts + 1;
164
167
  const maxAttempts = job.maxAttempts;
165
- if (attempts >= maxAttempts) {
168
+ const nonRetryable = err instanceof TaskNonRetryableError;
169
+ if (nonRetryable || attempts >= maxAttempts) {
166
170
  await db
167
171
  .update(commerceJobs)
168
172
  .set({
@@ -109,14 +109,14 @@ export declare const commerceJobs: import("drizzle-orm/pg-core").PgTableWithColu
109
109
  tableName: "commerce_jobs";
110
110
  dataType: "string";
111
111
  columnType: "PgText";
112
- data: "pending" | "processing" | "succeeded" | "failed";
112
+ data: "pending" | "processing" | "succeeded" | "failed" | "cancelled";
113
113
  driverParam: string;
114
114
  notNull: true;
115
115
  hasDefault: true;
116
116
  isPrimaryKey: false;
117
117
  isAutoincrement: false;
118
118
  hasRuntimeDefault: false;
119
- enumValues: ["pending", "processing", "succeeded", "failed"];
119
+ enumValues: ["pending", "processing", "succeeded", "failed", "cancelled"];
120
120
  baseColumn: never;
121
121
  identity: undefined;
122
122
  generated: undefined;
@@ -10,7 +10,7 @@ export const commerceJobs = pgTable("commerce_jobs", {
10
10
  input: jsonb("input").notNull().default("{}"),
11
11
  output: jsonb("output"),
12
12
  status: text("status", {
13
- enum: ["pending", "processing", "succeeded", "failed"],
13
+ enum: ["pending", "processing", "succeeded", "failed", "cancelled"],
14
14
  })
15
15
  .notNull()
16
16
  .default("pending"),
@@ -0,0 +1,8 @@
1
+ import type { TaskStep } from "./types.js";
2
+ /**
3
+ * Pass-through `TaskStep` for pull-based engines with no durable step primitive:
4
+ * `do` runs `fn` immediately, `sleep` waits in-process. Lets a handler written
5
+ * against `ctx.step` run unchanged on the drizzle engine.
6
+ */
7
+ export declare function createPassThroughTaskStep(): TaskStep;
8
+ //# sourceMappingURL=step.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"step.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/step.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,QAAQ,CAkBpD"}
@@ -0,0 +1,24 @@
1
+ import { TaskNonRetryableError } from "./types.js";
2
+ /**
3
+ * Pass-through `TaskStep` for pull-based engines with no durable step primitive:
4
+ * `do` runs `fn` immediately, `sleep` waits in-process. Lets a handler written
5
+ * against `ctx.step` run unchanged on the drizzle engine.
6
+ */
7
+ export function createPassThroughTaskStep() {
8
+ return {
9
+ async do(_name, fn, options) {
10
+ try {
11
+ return await fn();
12
+ }
13
+ catch (error) {
14
+ if (options?.nonRetryable && !(error instanceof TaskNonRetryableError)) {
15
+ throw new TaskNonRetryableError(error instanceof Error ? error.message : String(error));
16
+ }
17
+ throw error;
18
+ }
19
+ },
20
+ async sleep(_name, ms) {
21
+ await new Promise((resolve) => setTimeout(resolve, ms));
22
+ },
23
+ };
24
+ }
@@ -8,7 +8,27 @@ export interface TaskContext {
8
8
  logger: Logger;
9
9
  db: DrizzleDatabase;
10
10
  services: ServiceContainer;
11
+ /** Durable per-phase execution, backed by the Workflow step on Cloudflare and a
12
+ * pass-through implementation on the drizzle engine. Absent on engines (pg-boss,
13
+ * Inngest, Trigger) that have not wired one; a handler that ignores it runs as before. */
14
+ step?: TaskStep;
11
15
  }
16
+ /** Durable, idempotent execution of one phase of a task handler. */
17
+ export interface TaskStep {
18
+ do<T>(name: string, fn: () => Promise<T>, options?: {
19
+ retries?: TaskRetryConfig;
20
+ timeout?: number;
21
+ nonRetryable?: boolean;
22
+ }): Promise<T>;
23
+ sleep(name: string, ms: number): Promise<void>;
24
+ }
25
+ /** Thrown by a task handler (or a step's callback) to signal the failure must not be
26
+ * retried — the Cloudflare engine maps it to `NonRetryableError`; the drizzle runner
27
+ * fails the job immediately regardless of remaining attempts. */
28
+ export declare class TaskNonRetryableError extends Error {
29
+ constructor(message: string);
30
+ }
31
+ export type JobInstanceStatus = "queued" | "running" | "waiting" | "complete" | "errored" | "terminated";
12
32
  export interface TaskRetryConfig {
13
33
  attempts: number;
14
34
  backoff?: {
@@ -44,6 +64,13 @@ export interface TaskDefinition<TInput extends Record<string, unknown> = Record<
44
64
  output: TOutput;
45
65
  }>;
46
66
  retries?: TaskRetryConfig;
67
+ /** The handler drives its own durable phases through `ctx.step` and keeps every
68
+ * side effect inside one. A push engine with a step primitive (Cloudflare) then
69
+ * runs the handler directly — each `ctx.step.do` is a real step with its own
70
+ * retries and `retries` above is only the per-step default — instead of
71
+ * wrapping the whole handler in one retried unit; `job.attemptNumber` is then
72
+ * always 1, because retries happen per step. Pull engines ignore it. */
73
+ durableSteps?: boolean;
47
74
  concurrency?: {
48
75
  key: (input: TInput) => string;
49
76
  exclusive?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,eAAO,MAAM,sBAAsB;;;CAGzB,CAAC;AAEX,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,eAAe,CAAC;IACpB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,aAAa,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,uBAAuB,GAC/B,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,CAAC;AAEf,MAAM,MAAM,kBAAkB,GAC1B;IACE,KAAK,EAAE,uBAAuB,CAAC;IAC/B,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC5B,GACD,CAAC,CACC,IAAI,EAAE,wBAAwB,EAC9B,KAAK,EAAE,wBAAwB,KAC5B,MAAM,CAAC,CAAC;AAEjB,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc,CAC7B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,IAAI,EAAE;QACd,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,WAAW,CAAC;QACjB,GAAG,CAAC,EAAE,WAAW,CAAC;KACnB,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,WAAW,CAAC,EAAE;QACZ,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;QAC/B,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,UAAU,CAAC,EAAE,OAAO,CAAC;KACtB,CAAC;CACH"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/kernel/jobs/types.ts"],"names":[],"mappings":"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"}
@@ -2,3 +2,12 @@ export const BUILTIN_JOB_TASK_SLUGS = {
2
2
  webhookDeliver: "webhooks/deliver",
3
3
  staleJobReaper: "jobs/reap-stale",
4
4
  };
5
+ /** Thrown by a task handler (or a step's callback) to signal the failure must not be
6
+ * retried — the Cloudflare engine maps it to `NonRetryableError`; the drizzle runner
7
+ * fails the job immediately regardless of remaining attempts. */
8
+ export class TaskNonRetryableError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "TaskNonRetryableError";
12
+ }
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porulle/core",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -33,6 +33,12 @@
33
33
  "bun": "./src/drizzle.ts",
34
34
  "import": "./dist/drizzle.js",
35
35
  "require": "./dist/drizzle.js"
36
+ },
37
+ "./jobs": {
38
+ "types": "./src/jobs.ts",
39
+ "bun": "./src/jobs.ts",
40
+ "import": "./dist/jobs.js",
41
+ "require": "./dist/jobs.js"
36
42
  }
37
43
  },
38
44
  "dependencies": {
package/src/index.ts CHANGED
@@ -84,13 +84,16 @@ export type { RunPendingJobsArgs } from "./kernel/jobs/runner.js";
84
84
  export type {
85
85
  TaskDefinition,
86
86
  TaskContext,
87
+ TaskStep,
87
88
  TaskJobMeta,
88
89
  TaskRetryConfig,
90
+ JobInstanceStatus,
89
91
  JobProcessingOrder,
90
92
  JobProcessingOrderField,
91
93
  JobProcessingOrderRecord,
92
94
  } from "./kernel/jobs/types.js";
93
- export { BUILTIN_JOB_TASK_SLUGS } from "./kernel/jobs/types.js";
95
+ export { BUILTIN_JOB_TASK_SLUGS, TaskNonRetryableError } from "./kernel/jobs/types.js";
96
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
94
97
  export {
95
98
  staleJobReaperTask,
96
99
  runStaleJobReaper,
@@ -85,6 +85,7 @@ class RouteChain {
85
85
  private _input: z.ZodType | undefined;
86
86
  private _query: z.ZodType | undefined;
87
87
  private _params: z.ZodType | undefined;
88
+ private _output: z.ZodType | undefined;
88
89
  private _requireAuth = false;
89
90
  private _requiredPermission: string | undefined;
90
91
  constructor(
@@ -117,6 +118,27 @@ class RouteChain {
117
118
  /** Override the auto-detected path parameter schema. */
118
119
  params(schema: z.ZodType) { this._params = schema; return this; }
119
120
 
121
+ /**
122
+ * Declare the shape carried inside the `{ data }` envelope on success.
123
+ *
124
+ * Without it the route documents its payload as `any`, and every generated
125
+ * client sees `data?: unknown` — so a consumer has to hand-maintain a
126
+ * parallel schema, which is the drift the generated SDK exists to prevent.
127
+ *
128
+ * Documentation only: the handler's return value is not validated against
129
+ * this schema. Validating would mean either stripping unknown keys — silently
130
+ * changing what goes over the wire for anyone who adopts it — or throwing at
131
+ * runtime, turning an OpenAPI annotation into a new failure mode. A stale
132
+ * annotation is the cheaper wrong.
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * vendors.get("/{id}").summary("Get").output(VendorSchema)
137
+ * .handler(async ({ params, services }) => services.vendor.get(params.id));
138
+ * ```
139
+ */
140
+ output(schema: z.ZodType) { this._output = schema; return this; }
141
+
120
142
  /**
121
143
  * Require authentication. The handler's `actor` is guaranteed non-null.
122
144
  * Returns 401 if the request has no authenticated actor.
@@ -165,7 +187,7 @@ class RouteChain {
165
187
  ...(this._description ? { description: this._description } : {}),
166
188
  ...(Object.keys(request).length > 0 ? { request } : {}),
167
189
  responses: {
168
- [status]: wrapJson(z.object({ data: z.any() })),
190
+ [status]: wrapJson(z.object({ data: this._output ?? z.any() })),
169
191
  ...errorResponses,
170
192
  },
171
193
  });
package/src/jobs.ts ADDED
@@ -0,0 +1,18 @@
1
+ // Leaf entry for execution engines that run outside Node (Cloudflare Workers):
2
+ // only the job types and the two runtime helpers, none of the server runtime.
3
+ export type {
4
+ JobInstanceStatus,
5
+ TaskContext,
6
+ TaskDefinition,
7
+ TaskJobMeta,
8
+ TaskRetryConfig,
9
+ TaskStep,
10
+ } from "./kernel/jobs/types.js";
11
+ export { TaskNonRetryableError } from "./kernel/jobs/types.js";
12
+ export { createPassThroughTaskStep } from "./kernel/jobs/step.js";
13
+ export type {
14
+ EnqueueOptions,
15
+ ExecutionEngine,
16
+ ExecutionEngineSetup,
17
+ JobsAdapter,
18
+ } from "./kernel/jobs/adapter.js";
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ JobInstanceStatus,
2
3
  JobProcessingOrder,
3
4
  TaskContext,
4
5
  TaskDefinition,
@@ -11,6 +12,12 @@ export interface JobsAdapter {
11
12
  input: Record<string, unknown>,
12
13
  options: EnqueueOptions,
13
14
  ): Promise<string>;
15
+ /** Not every engine can report instance status; absent where it cannot. */
16
+ status?(
17
+ jobId: string,
18
+ ): Promise<{ status: JobInstanceStatus; error?: string }>;
19
+ /** Not every engine can cancel an in-flight instance; absent where it cannot. */
20
+ cancel?(jobId: string): Promise<void>;
14
21
  }
15
22
 
16
23
  export interface RunJobsOptions {
@@ -51,6 +58,11 @@ export interface EnqueueOptions {
51
58
  delayMs?: number;
52
59
  concurrencyKey?: string;
53
60
  supersedes?: boolean;
61
+ /** Used verbatim as the job id — the `commerce_jobs` row id on the drizzle
62
+ * engine, the instance id on Cloudflare Workflows — so a caller can address the
63
+ * job it created ("one generation, one instance"). Must be a UUID on drizzle.
64
+ * The pg-boss, Inngest and Trigger engines choose their own ids and ignore it. */
65
+ jobId?: string;
54
66
  }
55
67
 
56
68
  /**
@@ -1,13 +1,13 @@
1
1
  import { eq, and } from "drizzle-orm";
2
2
  import type { DrizzleDatabase } from "../database/drizzle-db.js";
3
- import type { TaskDefinition } from "./types.js";
3
+ import type { JobInstanceStatus, TaskDefinition } from "./types.js";
4
4
  import type {
5
5
  EnqueueOptions,
6
6
  ExecutionEngine,
7
7
  ExecutionEngineSetup,
8
8
  RunJobsOptions,
9
9
  } from "./adapter.js";
10
- import { OrgResolutionError } from "../errors.js";
10
+ import { OrgResolutionError, CommerceNotFoundError } from "../errors.js";
11
11
  import { commerceJobs } from "./schema.js";
12
12
  import { runPendingJobs } from "./runner.js";
13
13
  import {
@@ -111,6 +111,7 @@ export class DrizzleJobsAdapter implements ExecutionEngine {
111
111
  const rows = await this.db
112
112
  .insert(commerceJobs)
113
113
  .values({
114
+ ...(options.jobId ? { id: options.jobId } : {}),
114
115
  organizationId,
115
116
  taskSlug,
116
117
  input,
@@ -125,4 +126,44 @@ export class DrizzleJobsAdapter implements ExecutionEngine {
125
126
 
126
127
  return rows[0]!.id;
127
128
  }
129
+
130
+ async status(
131
+ jobId: string,
132
+ ): Promise<{ status: JobInstanceStatus; error?: string }> {
133
+ const [row] = await this.db
134
+ .select()
135
+ .from(commerceJobs)
136
+ .where(eq(commerceJobs.id, jobId))
137
+ .limit(1);
138
+ if (!row) {
139
+ throw new CommerceNotFoundError(`No job found for id ${jobId}`);
140
+ }
141
+ return {
142
+ status: DRIZZLE_JOB_STATUS_MAP[row.status],
143
+ ...(row.error !== null ? { error: row.error } : {}),
144
+ };
145
+ }
146
+
147
+ /** Cancels a job that has not started yet; a job already `processing` (or
148
+ * finished) cannot be interrupted from here and is left untouched. The row is
149
+ * kept so `status()` reports it as `terminated`. */
150
+ async cancel(jobId: string): Promise<void> {
151
+ await this.db
152
+ .update(commerceJobs)
153
+ .set({ status: "cancelled" })
154
+ .where(
155
+ and(eq(commerceJobs.id, jobId), eq(commerceJobs.status, "pending")),
156
+ );
157
+ }
128
158
  }
159
+
160
+ const DRIZZLE_JOB_STATUS_MAP: Record<
161
+ "pending" | "processing" | "succeeded" | "failed" | "cancelled",
162
+ JobInstanceStatus
163
+ > = {
164
+ pending: "queued",
165
+ processing: "running",
166
+ succeeded: "complete",
167
+ failed: "errored",
168
+ cancelled: "terminated",
169
+ };
@@ -2,6 +2,8 @@ import { eq, and, sql } from "drizzle-orm";
2
2
  import type { DrizzleDatabase } from "../database/drizzle-db.js";
3
3
  import type { Logger, ServiceContainer } from "../hooks/types.js";
4
4
  import type { JobProcessingOrder, TaskDefinition } from "./types.js";
5
+ import { TaskNonRetryableError } from "./types.js";
6
+ import { createPassThroughTaskStep } from "./step.js";
5
7
  import { commerceJobs } from "./schema.js";
6
8
 
7
9
  export interface RunPendingJobsArgs {
@@ -180,6 +182,7 @@ export async function runPendingJobs(
180
182
  });
181
183
 
182
184
  // Phase 2: Execute each claimed job outside the claim transaction
185
+ const step = createPassThroughTaskStep();
183
186
  const outcomes = await Promise.all(
184
187
  claimed.map(async (job) => {
185
188
  const task = tasks.get(job.taskSlug);
@@ -207,7 +210,7 @@ export async function runPendingJobs(
207
210
  try {
208
211
  const result = await task.handler({
209
212
  input: job.input as Record<string, unknown>,
210
- ctx: { logger, db, services },
213
+ ctx: { logger, db, services, step },
211
214
  job: {
212
215
  attemptNumber: job.attempts + 1,
213
216
  maxAttempts: job.maxAttempts,
@@ -234,8 +237,9 @@ export async function runPendingJobs(
234
237
  });
235
238
  const attempts = job.attempts + 1;
236
239
  const maxAttempts = job.maxAttempts;
240
+ const nonRetryable = err instanceof TaskNonRetryableError;
237
241
 
238
- if (attempts >= maxAttempts) {
242
+ if (nonRetryable || attempts >= maxAttempts) {
239
243
  await db
240
244
  .update(commerceJobs)
241
245
  .set({
@@ -19,7 +19,7 @@ export const commerceJobs = pgTable("commerce_jobs", {
19
19
  input: jsonb("input").notNull().default("{}"),
20
20
  output: jsonb("output"),
21
21
  status: text("status", {
22
- enum: ["pending", "processing", "succeeded", "failed"],
22
+ enum: ["pending", "processing", "succeeded", "failed", "cancelled"],
23
23
  })
24
24
  .notNull()
25
25
  .default("pending"),
@@ -0,0 +1,27 @@
1
+ import { TaskNonRetryableError } from "./types.js";
2
+ import type { TaskStep } from "./types.js";
3
+
4
+ /**
5
+ * Pass-through `TaskStep` for pull-based engines with no durable step primitive:
6
+ * `do` runs `fn` immediately, `sleep` waits in-process. Lets a handler written
7
+ * against `ctx.step` run unchanged on the drizzle engine.
8
+ */
9
+ export function createPassThroughTaskStep(): TaskStep {
10
+ return {
11
+ async do(_name, fn, options) {
12
+ try {
13
+ return await fn();
14
+ } catch (error) {
15
+ if (options?.nonRetryable && !(error instanceof TaskNonRetryableError)) {
16
+ throw new TaskNonRetryableError(
17
+ error instanceof Error ? error.message : String(error),
18
+ );
19
+ }
20
+ throw error;
21
+ }
22
+ },
23
+ async sleep(_name, ms) {
24
+ await new Promise<void>((resolve) => setTimeout(resolve, ms));
25
+ },
26
+ };
27
+ }
@@ -10,8 +10,44 @@ export interface TaskContext {
10
10
  logger: Logger;
11
11
  db: DrizzleDatabase;
12
12
  services: ServiceContainer;
13
+ /** Durable per-phase execution, backed by the Workflow step on Cloudflare and a
14
+ * pass-through implementation on the drizzle engine. Absent on engines (pg-boss,
15
+ * Inngest, Trigger) that have not wired one; a handler that ignores it runs as before. */
16
+ step?: TaskStep;
13
17
  }
14
18
 
19
+ /** Durable, idempotent execution of one phase of a task handler. */
20
+ export interface TaskStep {
21
+ do<T>(
22
+ name: string,
23
+ fn: () => Promise<T>,
24
+ options?: {
25
+ retries?: TaskRetryConfig;
26
+ timeout?: number;
27
+ nonRetryable?: boolean;
28
+ },
29
+ ): Promise<T>;
30
+ sleep(name: string, ms: number): Promise<void>;
31
+ }
32
+
33
+ /** Thrown by a task handler (or a step's callback) to signal the failure must not be
34
+ * retried — the Cloudflare engine maps it to `NonRetryableError`; the drizzle runner
35
+ * fails the job immediately regardless of remaining attempts. */
36
+ export class TaskNonRetryableError extends Error {
37
+ constructor(message: string) {
38
+ super(message);
39
+ this.name = "TaskNonRetryableError";
40
+ }
41
+ }
42
+
43
+ export type JobInstanceStatus =
44
+ | "queued"
45
+ | "running"
46
+ | "waiting"
47
+ | "complete"
48
+ | "errored"
49
+ | "terminated";
50
+
15
51
  export interface TaskRetryConfig {
16
52
  attempts: number;
17
53
  backoff?: { type: "fixed" | "exponential"; delay: number };
@@ -59,6 +95,13 @@ export interface TaskDefinition<
59
95
  job?: TaskJobMeta;
60
96
  }) => Promise<{ output: TOutput }>;
61
97
  retries?: TaskRetryConfig;
98
+ /** The handler drives its own durable phases through `ctx.step` and keeps every
99
+ * side effect inside one. A push engine with a step primitive (Cloudflare) then
100
+ * runs the handler directly — each `ctx.step.do` is a real step with its own
101
+ * retries and `retries` above is only the per-step default — instead of
102
+ * wrapping the whole handler in one retried unit; `job.attemptNumber` is then
103
+ * always 1, because retries happen per step. Pull engines ignore it. */
104
+ durableSteps?: boolean;
62
105
  concurrency?: {
63
106
  key: (input: TInput) => string;
64
107
  exclusive?: boolean;