pg-workflows 0.13.2 → 0.15.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.
- package/README.md +2 -0
- package/dist/client.entry.cjs +189 -64
- package/dist/client.entry.d.cts +43 -1
- package/dist/client.entry.d.ts +43 -1
- package/dist/client.entry.js +4 -2
- package/dist/client.entry.js.map +10 -9
- package/dist/index.cjs +209 -70
- package/dist/index.d.cts +44 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +26 -8
- package/dist/index.js.map +11 -10
- package/dist/shared/{chunk-hjycwrsq.js → chunk-n0f7hd40.js} +158 -36
- package/dist/shared/chunk-n0f7hd40.js.map +17 -0
- package/package.json +9 -3
- package/dist/shared/chunk-hjycwrsq.js.map +0 -16
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,15 @@ type WorkflowRun = {
|
|
|
18
18
|
timeoutAt: Date | null;
|
|
19
19
|
retryCount: number;
|
|
20
20
|
maxRetries: number;
|
|
21
|
+
/** Resolved scheduling priority (pg-boss integer; higher runs first). */
|
|
22
|
+
priority: number;
|
|
23
|
+
/**
|
|
24
|
+
* When true, this run participates in the per-workflow-ID uniqueness
|
|
25
|
+
* constraint: no other pending or running singleton run of the same
|
|
26
|
+
* workflow ID may exist at the same time. Paused, completed, failed, and
|
|
27
|
+
* cancelled runs release the slot.
|
|
28
|
+
*/
|
|
29
|
+
singleton: boolean;
|
|
21
30
|
jobId: string | null;
|
|
22
31
|
idempotencyKey: string | null;
|
|
23
32
|
parentRunId: string | null;
|
|
@@ -35,6 +44,18 @@ type DurationObject = {
|
|
|
35
44
|
seconds?: number;
|
|
36
45
|
};
|
|
37
46
|
type Duration = string | DurationObject;
|
|
47
|
+
/**
|
|
48
|
+
* Named priority levels mapped to pg-boss integer priorities. Higher numbers
|
|
49
|
+
* are fetched first (pg-boss orders by `priority DESC`), and `normal = 0`
|
|
50
|
+
* matches pg-boss's own default. The ±100 spacing leaves room for numeric
|
|
51
|
+
* tuning between tiers via the escape hatch.
|
|
52
|
+
*/
|
|
53
|
+
declare const PRIORITY_LEVELS: {
|
|
54
|
+
readonly high: 100;
|
|
55
|
+
readonly normal: 0;
|
|
56
|
+
readonly low: -100;
|
|
57
|
+
};
|
|
58
|
+
type WorkflowPriority = keyof typeof PRIORITY_LEVELS | number;
|
|
38
59
|
type Schedule = string | Exclude<Duration, string>;
|
|
39
60
|
declare enum WorkflowStatus {
|
|
40
61
|
PENDING = "pending",
|
|
@@ -61,11 +82,25 @@ type StartWorkflowOptions = {
|
|
|
61
82
|
retries?: number;
|
|
62
83
|
expireInSeconds?: number;
|
|
63
84
|
idempotencyKey?: string;
|
|
85
|
+
priority?: WorkflowPriority;
|
|
86
|
+
/**
|
|
87
|
+
* Client-side counterpart of `workflow(..., { singleton: true })`.
|
|
88
|
+
* The engine reads this from the registered definition; pass it here when
|
|
89
|
+
* starting from `WorkflowClient` so the uniqueness constraint applies.
|
|
90
|
+
*/
|
|
91
|
+
singleton?: boolean;
|
|
64
92
|
};
|
|
65
93
|
type WorkflowOptions<I extends InputParameters> = {
|
|
66
94
|
timeout?: number;
|
|
67
95
|
retries?: number;
|
|
68
96
|
inputSchema?: I;
|
|
97
|
+
priority?: WorkflowPriority;
|
|
98
|
+
/**
|
|
99
|
+
* When true, at most one pending or running run of this workflow ID may exist.
|
|
100
|
+
* A second `startWorkflow` throws `WorkflowRunInProgressError` until the
|
|
101
|
+
* current run pauses, completes, fails, or is cancelled.
|
|
102
|
+
*/
|
|
103
|
+
singleton?: boolean;
|
|
69
104
|
/**
|
|
70
105
|
* Recurring schedule. Accepts a cron expression (`'0 9 * * 1-5'`),
|
|
71
106
|
* a duration string (`'5m'`, `'1 hour'`), or a `DurationObject`.
|
|
@@ -173,6 +208,8 @@ type WorkflowDefinition<TInput extends InputParameters = InputParameters> = {
|
|
|
173
208
|
inputSchema?: TInput;
|
|
174
209
|
timeout?: number;
|
|
175
210
|
retries?: number;
|
|
211
|
+
priority?: WorkflowPriority;
|
|
212
|
+
singleton?: boolean;
|
|
176
213
|
schedule?: Schedule;
|
|
177
214
|
timezone?: string;
|
|
178
215
|
plugins?: WorkflowPlugin[];
|
|
@@ -199,6 +236,7 @@ interface WorkflowFactory<TStepExt = object> {
|
|
|
199
236
|
TOutput = unknown
|
|
200
237
|
>(id: string, options?: {
|
|
201
238
|
inputSchema?: TInput;
|
|
239
|
+
singleton?: boolean;
|
|
202
240
|
}): WorkflowRef<TInput, TOutput>;
|
|
203
241
|
}
|
|
204
242
|
/**
|
|
@@ -215,6 +253,7 @@ interface WorkflowRef<
|
|
|
215
253
|
(handler: (context: WorkflowContext<TInput, StepBaseContext>) => Promise<unknown>, options?: Omit<WorkflowOptions<TInput>, "inputSchema">): WorkflowDefinition<TInput>;
|
|
216
254
|
readonly id: string;
|
|
217
255
|
readonly inputSchema?: TInput;
|
|
256
|
+
readonly singleton?: boolean;
|
|
218
257
|
}
|
|
219
258
|
type WorkflowRunProgress = WorkflowRun & {
|
|
220
259
|
completionPercentage: number;
|
|
@@ -321,6 +360,7 @@ declare function createWorkflowRef<
|
|
|
321
360
|
TInput extends InputParameters = InputParameters
|
|
322
361
|
>(id: string, options?: {
|
|
323
362
|
inputSchema?: TInput;
|
|
363
|
+
singleton?: boolean;
|
|
324
364
|
}): WorkflowRef<TInput, TOutput>;
|
|
325
365
|
declare const workflow: WorkflowFactory;
|
|
326
366
|
import pg2 from "pg";
|
|
@@ -494,6 +534,9 @@ declare class WorkflowEngineError extends Error {
|
|
|
494
534
|
declare class WorkflowRunNotFoundError extends WorkflowEngineError {
|
|
495
535
|
constructor(runId?: string, workflowId?: string);
|
|
496
536
|
}
|
|
537
|
+
declare class WorkflowRunInProgressError extends WorkflowEngineError {
|
|
538
|
+
constructor(workflowId: string, runId?: string);
|
|
539
|
+
}
|
|
497
540
|
import { AttributeValue, Tracer } from "@opentelemetry/api";
|
|
498
541
|
type OtelPluginOptions = {
|
|
499
542
|
/** Tracer to use. Defaults to `trace.getTracer('pg-workflows')`. */
|
|
@@ -504,4 +547,4 @@ type OtelPluginOptions = {
|
|
|
504
547
|
attributes?: (context: WorkflowContext) => Record<string, AttributeValue>;
|
|
505
548
|
};
|
|
506
549
|
declare function otelPlugin(options?: OtelPluginOptions): WorkflowPlugin<StepBaseContext, object>;
|
|
507
|
-
export { workflow, otelPlugin, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunNotFoundError, WorkflowRun, WorkflowRef, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepBaseContext, StartWorkflowOptions, ScheduleContext, Schedule, OtelPluginOptions, InputParameters, InferInputParameters, Duration };
|
|
550
|
+
export { workflow, otelPlugin, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunNotFoundError, WorkflowRunInProgressError, WorkflowRun, WorkflowRef, WorkflowPriority, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepBaseContext, StartWorkflowOptions, ScheduleContext, Schedule, OtelPluginOptions, InputParameters, InferInputParameters, Duration };
|
package/dist/index.js
CHANGED
|
@@ -6,8 +6,10 @@ import {
|
|
|
6
6
|
WORKFLOW_RUN_QUEUE_NAME,
|
|
7
7
|
WorkflowClient,
|
|
8
8
|
WorkflowEngineError,
|
|
9
|
+
WorkflowRunInProgressError,
|
|
9
10
|
WorkflowRunNotFoundError,
|
|
10
11
|
WorkflowStatus,
|
|
12
|
+
assertSingletonSlotAvailable,
|
|
11
13
|
createWorkflowRef,
|
|
12
14
|
getWorkflowLastRun,
|
|
13
15
|
getWorkflowRun,
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
insertWorkflowRun,
|
|
16
18
|
invokeChildWorkflowTimelineKey,
|
|
17
19
|
isInvokeChildWorkflowTimelineEntry,
|
|
20
|
+
resolvePriority,
|
|
18
21
|
runMigrations,
|
|
19
22
|
scheduleQueueNameFor,
|
|
20
23
|
updateWorkflowRun,
|
|
@@ -23,7 +26,7 @@ import {
|
|
|
23
26
|
waitForTimelineKey,
|
|
24
27
|
withPostgresTransaction,
|
|
25
28
|
workflow
|
|
26
|
-
} from "./shared/chunk-
|
|
29
|
+
} from "./shared/chunk-n0f7hd40.js";
|
|
27
30
|
// src/engine.ts
|
|
28
31
|
import { merge } from "es-toolkit";
|
|
29
32
|
import pg from "pg";
|
|
@@ -224,11 +227,10 @@ class WorkflowEngine {
|
|
|
224
227
|
const db = {
|
|
225
228
|
executeSql: (text, values) => this.pool.query(text, values)
|
|
226
229
|
};
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
230
|
+
this.boss = boss ?? new PgBoss({ db, schema: DEFAULT_PGBOSS_SCHEMA });
|
|
231
|
+
this.boss.on("error", (error) => {
|
|
232
|
+
this.logger.error("pg-boss error", error);
|
|
233
|
+
});
|
|
232
234
|
this.db = this.boss.getDb();
|
|
233
235
|
}
|
|
234
236
|
async start(asEngine = true, {
|
|
@@ -282,6 +284,10 @@ class WorkflowEngine {
|
|
|
282
284
|
try {
|
|
283
285
|
await this.createWorkflowRun({ workflowId, input: {}, scheduledAt });
|
|
284
286
|
} catch (error) {
|
|
287
|
+
if (error instanceof WorkflowRunInProgressError) {
|
|
288
|
+
this.logger.log(`Skipping schedule fire for "${workflowId}": a run is already in progress`, { workflowId, runId: error.runId });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
285
291
|
this.logger.error(`Schedule fire failed to start a run for workflow "${workflowId}"`, error instanceof Error ? error : new Error(String(error)), { workflowId });
|
|
286
292
|
throw error;
|
|
287
293
|
}
|
|
@@ -394,6 +400,7 @@ class WorkflowEngine {
|
|
|
394
400
|
parentRunId,
|
|
395
401
|
parentStepId,
|
|
396
402
|
parentResourceId,
|
|
403
|
+
parentPriority,
|
|
397
404
|
scheduledAt,
|
|
398
405
|
enqueue = true,
|
|
399
406
|
db
|
|
@@ -424,12 +431,14 @@ class WorkflowEngine {
|
|
|
424
431
|
status: "running" /* RUNNING */,
|
|
425
432
|
input,
|
|
426
433
|
maxRetries: options?.retries ?? workflow2.retries ?? 0,
|
|
434
|
+
priority: resolvePriority(options?.priority, workflow2.priority, parentPriority),
|
|
427
435
|
timeoutAt,
|
|
428
436
|
idempotencyKey,
|
|
429
437
|
parentRunId,
|
|
430
438
|
parentStepId,
|
|
431
439
|
parentResourceId,
|
|
432
|
-
scheduledAt
|
|
440
|
+
scheduledAt,
|
|
441
|
+
singleton: workflow2.singleton === true || options?.singleton === true
|
|
433
442
|
}, targetDb);
|
|
434
443
|
const insertAndEnqueue = async (targetDb) => {
|
|
435
444
|
const result = await insertRun(targetDb);
|
|
@@ -451,6 +460,7 @@ class WorkflowEngine {
|
|
|
451
460
|
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
452
461
|
startAfter: new Date,
|
|
453
462
|
expireInSeconds: options?.expireInSeconds ?? defaultExpireInSeconds,
|
|
463
|
+
priority: run.priority,
|
|
454
464
|
...retrySendOptions(run.maxRetries),
|
|
455
465
|
...db ? { db } : {}
|
|
456
466
|
});
|
|
@@ -587,6 +597,9 @@ class WorkflowEngine {
|
|
|
587
597
|
}) {
|
|
588
598
|
await this.checkIfHasStarted();
|
|
589
599
|
const run = await this.getRun({ runId, resourceId });
|
|
600
|
+
if (run.singleton) {
|
|
601
|
+
await assertSingletonSlotAvailable({ workflowId: run.workflowId, exceptRunId: run.id }, this.db);
|
|
602
|
+
}
|
|
590
603
|
const jobResourceId = resourceId ?? run.resourceId ?? undefined;
|
|
591
604
|
const job = {
|
|
592
605
|
runId: run.id,
|
|
@@ -600,6 +613,7 @@ class WorkflowEngine {
|
|
|
600
613
|
};
|
|
601
614
|
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
602
615
|
expireInSeconds: options?.expireInSeconds ?? defaultExpireInSeconds,
|
|
616
|
+
priority: run.priority,
|
|
603
617
|
...retrySendOptions(run.maxRetries)
|
|
604
618
|
});
|
|
605
619
|
this.logger.log(`event ${eventName} sent for workflow run with id ${runId}`);
|
|
@@ -1009,6 +1023,7 @@ class WorkflowEngine {
|
|
|
1009
1023
|
parentRunId: run.id,
|
|
1010
1024
|
parentStepId: stepId,
|
|
1011
1025
|
parentResourceId: run.resourceId ?? undefined,
|
|
1026
|
+
parentPriority: run.priority,
|
|
1012
1027
|
enqueue: true,
|
|
1013
1028
|
db
|
|
1014
1029
|
});
|
|
@@ -1207,6 +1222,7 @@ ${error.stack}` : String(error)
|
|
|
1207
1222
|
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
1208
1223
|
startAfter: timeoutDate.getTime() <= Date.now() ? new Date : timeoutDate,
|
|
1209
1224
|
expireInSeconds: defaultExpireInSeconds,
|
|
1225
|
+
priority: run.priority,
|
|
1210
1226
|
...retrySendOptions(run.maxRetries)
|
|
1211
1227
|
});
|
|
1212
1228
|
} catch (error) {
|
|
@@ -1325,6 +1341,7 @@ ${error.stack}` : String(error)
|
|
|
1325
1341
|
}, {
|
|
1326
1342
|
startAfter: new Date(Date.now() + intervalMs),
|
|
1327
1343
|
expireInSeconds: defaultExpireInSeconds,
|
|
1344
|
+
priority: run.priority,
|
|
1328
1345
|
...retrySendOptions(run.maxRetries)
|
|
1329
1346
|
});
|
|
1330
1347
|
} catch (error) {
|
|
@@ -1561,10 +1578,11 @@ export {
|
|
|
1561
1578
|
createWorkflowRef,
|
|
1562
1579
|
WorkflowStatus,
|
|
1563
1580
|
WorkflowRunNotFoundError,
|
|
1581
|
+
WorkflowRunInProgressError,
|
|
1564
1582
|
WorkflowEngineError,
|
|
1565
1583
|
WorkflowEngine,
|
|
1566
1584
|
WorkflowClient
|
|
1567
1585
|
};
|
|
1568
1586
|
|
|
1569
|
-
//# debugId=
|
|
1587
|
+
//# debugId=4EA8624FFA9819BE64756E2164756E21
|
|
1570
1588
|
//# sourceMappingURL=index.js.map
|