pg-workflows 0.8.3 → 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.
@@ -78,11 +78,19 @@ var import_pg_boss = require("pg-boss");
78
78
  // src/constants.ts
79
79
  var PAUSE_EVENT_NAME = "__internal_pause";
80
80
  var WORKFLOW_RUN_QUEUE_NAME = "workflow-run";
81
+ var WORKFLOW_RUN_DLQ_QUEUE_NAME = "workflow_run_dlq";
81
82
  var DEFAULT_PGBOSS_SCHEMA = "pgboss_v12_pgworkflow";
83
+ var MAX_WORKFLOW_ID_LENGTH = 256;
84
+ var MAX_RESOURCE_ID_LENGTH = 256;
85
+ var INVOKE_CHILD_WORKFLOW_TIMELINE_SUFFIX = "invoke-child-workflow";
86
+ var WAIT_FOR_TIMELINE_SUFFIX = "wait-for";
87
+ var invokeChildWorkflowTimelineKey = (stepId) => `${stepId}-${INVOKE_CHILD_WORKFLOW_TIMELINE_SUFFIX}`;
88
+ var waitForTimelineKey = (stepId) => `${stepId}-${WAIT_FOR_TIMELINE_SUFFIX}`;
89
+ var isInvokeChildWorkflowTimelineEntry = (entry) => !!entry && typeof entry === "object" && ("invokeChildWorkflow" in entry);
82
90
 
83
91
  // src/db/migration.ts
84
92
  var MIGRATION_LOCK_ID = 738291645;
85
- var CURRENT_SCHEMA_VERSION = 2;
93
+ var CURRENT_SCHEMA_VERSION = 4;
86
94
  async function runMigrations(db) {
87
95
  if (await isSchemaUpToDate(db)) {
88
96
  return;
@@ -95,8 +103,8 @@ async function runMigrations(db) {
95
103
  id varchar(32) PRIMARY KEY NOT NULL,
96
104
  created_at timestamp with time zone DEFAULT now() NOT NULL,
97
105
  updated_at timestamp with time zone DEFAULT now() NOT NULL,
98
- resource_id varchar(32),
99
- workflow_id varchar(32) NOT NULL,
106
+ resource_id varchar(256),
107
+ workflow_id varchar(256) NOT NULL,
100
108
  status text DEFAULT 'pending' NOT NULL,
101
109
  input jsonb NOT NULL,
102
110
  output jsonb,
@@ -136,6 +144,15 @@ async function runMigrations(db) {
136
144
  CREATE UNIQUE INDEX IF NOT EXISTS workflow_runs_idempotency_key_idx ON workflow_runs (idempotency_key) WHERE idempotency_key IS NOT NULL
137
145
  `);
138
146
  }
147
+ if (currentVersion < 3) {
148
+ commands.push("ALTER TABLE workflow_runs ALTER COLUMN resource_id TYPE varchar(256)");
149
+ commands.push("ALTER TABLE workflow_runs ALTER COLUMN workflow_id TYPE varchar(256)");
150
+ }
151
+ if (currentVersion < 4) {
152
+ commands.push("ALTER TABLE workflow_runs ADD COLUMN IF NOT EXISTS parent_run_id varchar(32)");
153
+ commands.push("ALTER TABLE workflow_runs ADD COLUMN IF NOT EXISTS parent_step_id varchar(256)");
154
+ commands.push("ALTER TABLE workflow_runs ADD COLUMN IF NOT EXISTS parent_resource_id varchar(256)");
155
+ }
139
156
  if (currentVersion === 0) {
140
157
  commands.push(`INSERT INTO workflow_schema_version (version) VALUES (${CURRENT_SCHEMA_VERSION})`);
141
158
  } else {
@@ -198,7 +215,10 @@ function mapRowToWorkflowRun(row) {
198
215
  retryCount: row.retry_count,
199
216
  maxRetries: row.max_retries,
200
217
  jobId: row.job_id,
201
- idempotencyKey: row.idempotency_key
218
+ idempotencyKey: row.idempotency_key,
219
+ parentRunId: row.parent_run_id,
220
+ parentStepId: row.parent_step_id,
221
+ parentResourceId: row.parent_resource_id
202
222
  };
203
223
  }
204
224
  async function insertWorkflowRun({
@@ -209,7 +229,10 @@ async function insertWorkflowRun({
209
229
  input,
210
230
  maxRetries,
211
231
  timeoutAt,
212
- idempotencyKey
232
+ idempotencyKey,
233
+ parentRunId,
234
+ parentStepId,
235
+ parentResourceId
213
236
  }, db) {
214
237
  const runId = generateKSUID("run");
215
238
  const now = new Date;
@@ -226,9 +249,12 @@ async function insertWorkflowRun({
226
249
  updated_at,
227
250
  timeline,
228
251
  retry_count,
229
- idempotency_key
252
+ idempotency_key,
253
+ parent_run_id,
254
+ parent_step_id,
255
+ parent_resource_id
230
256
  )
231
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
257
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
232
258
  ON CONFLICT (idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING
233
259
  RETURNING *`, [
234
260
  runId,
@@ -243,7 +269,10 @@ async function insertWorkflowRun({
243
269
  now,
244
270
  "{}",
245
271
  0,
246
- idempotencyKey ?? null
272
+ idempotencyKey ?? null,
273
+ parentRunId ?? null,
274
+ parentStepId ?? null,
275
+ parentResourceId ?? null
247
276
  ]);
248
277
  if (result.rows[0]) {
249
278
  return { run: mapRowToWorkflowRun(result.rows[0]), created: true };
@@ -460,6 +489,17 @@ async function withPostgresTransaction(db, callback, pool) {
460
489
  }
461
490
 
462
491
  // src/error.ts
492
+ function validateWorkflowId(workflowId) {
493
+ if (workflowId.length > MAX_WORKFLOW_ID_LENGTH) {
494
+ throw new WorkflowEngineError(`workflowId exceeds maximum length of ${MAX_WORKFLOW_ID_LENGTH} characters (got ${workflowId.length})`, workflowId);
495
+ }
496
+ }
497
+ function validateResourceId(resourceId) {
498
+ if (resourceId != null && resourceId.length > MAX_RESOURCE_ID_LENGTH) {
499
+ throw new WorkflowEngineError(`resourceId exceeds maximum length of ${MAX_RESOURCE_ID_LENGTH} characters (got ${resourceId.length})`);
500
+ }
501
+ }
502
+
463
503
  class WorkflowEngineError extends Error {
464
504
  workflowId;
465
505
  runId;
@@ -503,6 +543,7 @@ var StepType;
503
543
  StepType2["WAIT_UNTIL"] = "waitUntil";
504
544
  StepType2["DELAY"] = "delay";
505
545
  StepType2["POLL"] = "poll";
546
+ StepType2["INVOKE_CHILD_WORKFLOW"] = "invokeChildWorkflow";
506
547
  })(StepType ||= {});
507
548
 
508
549
  // src/client.ts
@@ -587,6 +628,8 @@ class WorkflowClient {
587
628
  idempotencyKey = params.idempotencyKey;
588
629
  options = params.options;
589
630
  }
631
+ validateWorkflowId(workflowId);
632
+ validateResourceId(resourceId);
590
633
  const run = await withPostgresTransaction(this.db, async (_db) => {
591
634
  const timeoutAt = options?.timeout ? new Date(Date.now() + options.timeout) : null;
592
635
  const { run: insertedRun, created } = await insertWorkflowRun({
@@ -608,7 +651,8 @@ class WorkflowClient {
608
651
  };
609
652
  await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
610
653
  startAfter: new Date,
611
- expireInSeconds: options?.expireInSeconds ?? defaultExpireInSeconds
654
+ expireInSeconds: options?.expireInSeconds ?? defaultExpireInSeconds,
655
+ db: _db
612
656
  });
613
657
  }
614
658
  return insertedRun;
@@ -671,6 +715,11 @@ class WorkflowClient {
671
715
  if (current.status !== "paused" /* PAUSED */) {
672
716
  throw new WorkflowEngineError(`Cannot resume workflow run in '${current.status}' status, must be 'paused'`, current.workflowId, runId);
673
717
  }
718
+ const currentStepId = current.currentStepId;
719
+ const currentStepTimelineEntry = current.timeline[invokeChildWorkflowTimelineKey(currentStepId)];
720
+ if (isInvokeChildWorkflowTimelineEntry(currentStepTimelineEntry)) {
721
+ return current;
722
+ }
674
723
  return this.triggerEvent({
675
724
  runId,
676
725
  resourceId,
@@ -689,8 +738,12 @@ class WorkflowClient {
689
738
  if (run.status !== "paused" /* PAUSED */) {
690
739
  return run;
691
740
  }
692
- const stepId = run.currentStepId;
693
- const waitForEntry = run.timeline[`${stepId}-wait-for`];
741
+ const currentStepId = run.currentStepId;
742
+ const currentStepTimelineEntry = run.timeline[invokeChildWorkflowTimelineKey(currentStepId)];
743
+ if (isInvokeChildWorkflowTimelineEntry(currentStepTimelineEntry)) {
744
+ return run;
745
+ }
746
+ const waitForEntry = run.timeline[waitForTimelineKey(currentStepId)];
694
747
  if (!waitForEntry || typeof waitForEntry !== "object" || !("waitFor" in waitForEntry)) {
695
748
  return run;
696
749
  }
@@ -708,7 +761,7 @@ class WorkflowClient {
708
761
  resourceId,
709
762
  data: {
710
763
  timeline: import_es_toolkit.merge(freshRun.timeline, {
711
- [stepId]: {
764
+ [currentStepId]: {
712
765
  output: data ?? {},
713
766
  timestamp: new Date
714
767
  }
@@ -780,6 +833,9 @@ class WorkflowClient {
780
833
  workflowId
781
834
  }) {
782
835
  await this.ensureStarted();
836
+ if (workflowId)
837
+ validateWorkflowId(workflowId);
838
+ validateResourceId(resourceId);
783
839
  return getWorkflowRuns({
784
840
  resourceId,
785
841
  startingAfter,
@@ -805,7 +861,10 @@ function createWorkflowRef(id, options) {
805
861
  retries: defineOptions?.retries
806
862
  });
807
863
  Object.defineProperty(ref, "id", { value: id, enumerable: true });
808
- Object.defineProperty(ref, "inputSchema", { value: options?.inputSchema, enumerable: true });
864
+ Object.defineProperty(ref, "inputSchema", {
865
+ value: options?.inputSchema,
866
+ enumerable: true
867
+ });
809
868
  return ref;
810
869
  }
811
870
  function createWorkflowFactory(plugins = []) {
@@ -826,5 +885,5 @@ function createWorkflowFactory(plugins = []) {
826
885
  }
827
886
  var workflow = createWorkflowFactory();
828
887
 
829
- //# debugId=3DE4F376EEE27CBD64756E2164756E21
888
+ //# debugId=0055BEB6EF31F80764756E2164756E21
830
889
  //# sourceMappingURL=client.entry.js.map
@@ -20,6 +20,9 @@ type WorkflowRun = {
20
20
  maxRetries: number;
21
21
  jobId: string | null;
22
22
  idempotencyKey: string | null;
23
+ parentRunId: string | null;
24
+ parentStepId: string | null;
25
+ parentResourceId: string | null;
23
26
  };
24
27
  import { StandardSchemaV1 } from "@standard-schema/spec";
25
28
  type DurationObject = {
@@ -40,6 +43,13 @@ declare enum WorkflowStatus {
40
43
  }
41
44
  type InputParameters = StandardSchemaV1;
42
45
  type InferInputParameters<P extends InputParameters> = StandardSchemaV1.InferOutput<P>;
46
+ type WorkflowRunOptions = {
47
+ resourceId?: string;
48
+ timeout?: number;
49
+ retries?: number;
50
+ expireInSeconds?: number;
51
+ idempotencyKey?: string;
52
+ };
43
53
  type WorkflowOptions<I extends InputParameters> = {
44
54
  timeout?: number;
45
55
  retries?: number;
@@ -79,6 +89,23 @@ type StepBaseContext = {
79
89
  } | {
80
90
  timedOut: true;
81
91
  }>;
92
+ /**
93
+ * Invoke a child workflow from inside the current workflow and pause until
94
+ * the child run reaches a terminal state.
95
+ */
96
+ invokeChildWorkflow: {
97
+ <
98
+ TInput extends InputParameters,
99
+ TOutput = unknown
100
+ >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: WorkflowRunOptions): Promise<TOutput>;
101
+ <TOutput = unknown>(stepId: string, params: {
102
+ workflowId: string;
103
+ input: unknown;
104
+ resourceId?: string;
105
+ idempotencyKey?: string;
106
+ options?: WorkflowRunOptions;
107
+ }): Promise<TOutput>;
108
+ };
82
109
  };
83
110
  /**
84
111
  * Plugin that extends the workflow step API with extra methods.
@@ -119,7 +146,10 @@ type WorkflowDefinition<TInput extends InputParameters = InputParameters> = {
119
146
  *
120
147
  * Callable: pass a handler to create a full WorkflowDefinition.
121
148
  */
122
- interface WorkflowRef<TInput extends InputParameters = InputParameters> {
149
+ interface WorkflowRef<
150
+ TInput extends InputParameters = InputParameters,
151
+ TOutput = unknown
152
+ > {
123
153
  (handler: (context: WorkflowContext<TInput, StepBaseContext>) => Promise<unknown>, options?: Omit<WorkflowOptions<TInput>, "inputSchema">): WorkflowDefinition<TInput>;
124
154
  readonly id: string;
125
155
  readonly inputSchema?: TInput;
@@ -149,13 +179,7 @@ type WorkflowClientOptions = {
149
179
  connectionString: string;
150
180
  pool?: never;
151
181
  });
152
- type StartWorkflowOptions = {
153
- resourceId?: string;
154
- timeout?: number;
155
- retries?: number;
156
- expireInSeconds?: number;
157
- idempotencyKey?: string;
158
- };
182
+ type StartWorkflowOptions = WorkflowRunOptions;
159
183
  declare class WorkflowClient {
160
184
  private boss;
161
185
  private db;
@@ -231,7 +255,10 @@ declare class WorkflowClient {
231
255
  * Create a lightweight workflow reference.
232
256
  * Safe to import from `pg-workflows/client` - no engine or handler code.
233
257
  */
234
- declare function createWorkflowRef<TInput extends InputParameters = InputParameters>(id: string, options?: {
258
+ declare function createWorkflowRef<
259
+ TOutput = unknown,
260
+ TInput extends InputParameters = InputParameters
261
+ >(id: string, options?: {
235
262
  inputSchema?: TInput;
236
- }): WorkflowRef<TInput>;
263
+ }): WorkflowRef<TInput, TOutput>;
237
264
  export { createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRun, WorkflowRef, WorkflowLogger, WorkflowClientOptions, WorkflowClient, StartWorkflowOptions, InputParameters, InferInputParameters };
@@ -20,6 +20,9 @@ type WorkflowRun = {
20
20
  maxRetries: number;
21
21
  jobId: string | null;
22
22
  idempotencyKey: string | null;
23
+ parentRunId: string | null;
24
+ parentStepId: string | null;
25
+ parentResourceId: string | null;
23
26
  };
24
27
  import { StandardSchemaV1 } from "@standard-schema/spec";
25
28
  type DurationObject = {
@@ -40,6 +43,13 @@ declare enum WorkflowStatus {
40
43
  }
41
44
  type InputParameters = StandardSchemaV1;
42
45
  type InferInputParameters<P extends InputParameters> = StandardSchemaV1.InferOutput<P>;
46
+ type WorkflowRunOptions = {
47
+ resourceId?: string;
48
+ timeout?: number;
49
+ retries?: number;
50
+ expireInSeconds?: number;
51
+ idempotencyKey?: string;
52
+ };
43
53
  type WorkflowOptions<I extends InputParameters> = {
44
54
  timeout?: number;
45
55
  retries?: number;
@@ -79,6 +89,23 @@ type StepBaseContext = {
79
89
  } | {
80
90
  timedOut: true;
81
91
  }>;
92
+ /**
93
+ * Invoke a child workflow from inside the current workflow and pause until
94
+ * the child run reaches a terminal state.
95
+ */
96
+ invokeChildWorkflow: {
97
+ <
98
+ TInput extends InputParameters,
99
+ TOutput = unknown
100
+ >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: WorkflowRunOptions): Promise<TOutput>;
101
+ <TOutput = unknown>(stepId: string, params: {
102
+ workflowId: string;
103
+ input: unknown;
104
+ resourceId?: string;
105
+ idempotencyKey?: string;
106
+ options?: WorkflowRunOptions;
107
+ }): Promise<TOutput>;
108
+ };
82
109
  };
83
110
  /**
84
111
  * Plugin that extends the workflow step API with extra methods.
@@ -119,7 +146,10 @@ type WorkflowDefinition<TInput extends InputParameters = InputParameters> = {
119
146
  *
120
147
  * Callable: pass a handler to create a full WorkflowDefinition.
121
148
  */
122
- interface WorkflowRef<TInput extends InputParameters = InputParameters> {
149
+ interface WorkflowRef<
150
+ TInput extends InputParameters = InputParameters,
151
+ TOutput = unknown
152
+ > {
123
153
  (handler: (context: WorkflowContext<TInput, StepBaseContext>) => Promise<unknown>, options?: Omit<WorkflowOptions<TInput>, "inputSchema">): WorkflowDefinition<TInput>;
124
154
  readonly id: string;
125
155
  readonly inputSchema?: TInput;
@@ -149,13 +179,7 @@ type WorkflowClientOptions = {
149
179
  connectionString: string;
150
180
  pool?: never;
151
181
  });
152
- type StartWorkflowOptions = {
153
- resourceId?: string;
154
- timeout?: number;
155
- retries?: number;
156
- expireInSeconds?: number;
157
- idempotencyKey?: string;
158
- };
182
+ type StartWorkflowOptions = WorkflowRunOptions;
159
183
  declare class WorkflowClient {
160
184
  private boss;
161
185
  private db;
@@ -231,7 +255,10 @@ declare class WorkflowClient {
231
255
  * Create a lightweight workflow reference.
232
256
  * Safe to import from `pg-workflows/client` - no engine or handler code.
233
257
  */
234
- declare function createWorkflowRef<TInput extends InputParameters = InputParameters>(id: string, options?: {
258
+ declare function createWorkflowRef<
259
+ TOutput = unknown,
260
+ TInput extends InputParameters = InputParameters
261
+ >(id: string, options?: {
235
262
  inputSchema?: TInput;
236
- }): WorkflowRef<TInput>;
263
+ }): WorkflowRef<TInput, TOutput>;
237
264
  export { createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRun, WorkflowRef, WorkflowLogger, WorkflowClientOptions, WorkflowClient, StartWorkflowOptions, InputParameters, InferInputParameters };
@@ -2,7 +2,7 @@ import {
2
2
  WorkflowClient,
3
3
  WorkflowStatus,
4
4
  createWorkflowRef
5
- } from "./shared/chunk-2xy8z3xp.js";
5
+ } from "./shared/chunk-nygamc7b.js";
6
6
  export {
7
7
  createWorkflowRef,
8
8
  WorkflowStatus,