pg-workflows 0.6.0 → 0.6.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.
- package/dist/index.cjs +237 -167
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +237 -167
- package/dist/index.js.map +5 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -271,15 +271,23 @@ async function runMigrations(db) {
|
|
|
271
271
|
job_id varchar(256)
|
|
272
272
|
);
|
|
273
273
|
`, []);
|
|
274
|
-
await db.executeSql(`
|
|
275
|
-
CREATE INDEX workflow_runs_workflow_id_idx ON workflow_runs USING btree (workflow_id);
|
|
276
|
-
`, []);
|
|
277
274
|
await db.executeSql(`
|
|
278
275
|
CREATE INDEX workflow_runs_created_at_idx ON workflow_runs USING btree (created_at);
|
|
276
|
+
CREATE INDEX workflow_runs_resource_id_created_at_idx ON workflow_runs USING btree (resource_id, created_at DESC);
|
|
277
|
+
CREATE INDEX workflow_runs_status_created_at_idx ON workflow_runs USING btree (status, created_at DESC);
|
|
278
|
+
CREATE INDEX workflow_runs_workflow_id_created_at_idx ON workflow_runs USING btree (workflow_id, created_at DESC);
|
|
279
|
+
CREATE INDEX workflow_runs_resource_id_workflow_id_created_at_idx ON workflow_runs USING btree (resource_id, workflow_id, created_at DESC);
|
|
279
280
|
`, []);
|
|
281
|
+
} else {
|
|
280
282
|
await db.executeSql(`
|
|
281
|
-
|
|
282
|
-
|
|
283
|
+
DROP INDEX IF EXISTS workflow_runs_workflow_id_idx;
|
|
284
|
+
DROP INDEX IF EXISTS workflow_runs_resource_id_idx;
|
|
285
|
+
CREATE INDEX IF NOT EXISTS workflow_runs_created_at_idx ON workflow_runs USING btree (created_at);
|
|
286
|
+
CREATE INDEX IF NOT EXISTS workflow_runs_resource_id_created_at_idx ON workflow_runs USING btree (resource_id, created_at DESC);
|
|
287
|
+
CREATE INDEX IF NOT EXISTS workflow_runs_status_created_at_idx ON workflow_runs USING btree (status, created_at DESC);
|
|
288
|
+
CREATE INDEX IF NOT EXISTS workflow_runs_workflow_id_created_at_idx ON workflow_runs USING btree (workflow_id, created_at DESC);
|
|
289
|
+
CREATE INDEX IF NOT EXISTS workflow_runs_resource_id_workflow_id_created_at_idx ON workflow_runs USING btree (resource_id, workflow_id, created_at DESC);
|
|
290
|
+
`, []);
|
|
283
291
|
}
|
|
284
292
|
}
|
|
285
293
|
|
|
@@ -317,8 +325,7 @@ async function insertWorkflowRun({
|
|
|
317
325
|
status,
|
|
318
326
|
input,
|
|
319
327
|
maxRetries,
|
|
320
|
-
timeoutAt
|
|
321
|
-
timeline
|
|
328
|
+
timeoutAt
|
|
322
329
|
}, db) {
|
|
323
330
|
const runId = generateKSUID("run");
|
|
324
331
|
const now = new Date;
|
|
@@ -348,7 +355,7 @@ async function insertWorkflowRun({
|
|
|
348
355
|
timeoutAt,
|
|
349
356
|
now,
|
|
350
357
|
now,
|
|
351
|
-
|
|
358
|
+
"{}",
|
|
352
359
|
0
|
|
353
360
|
]);
|
|
354
361
|
const insertedRun = result.rows[0];
|
|
@@ -376,7 +383,8 @@ async function getWorkflowRun({
|
|
|
376
383
|
async function updateWorkflowRun({
|
|
377
384
|
runId,
|
|
378
385
|
resourceId,
|
|
379
|
-
data
|
|
386
|
+
data,
|
|
387
|
+
expectedStatuses
|
|
380
388
|
}, db) {
|
|
381
389
|
const now = new Date;
|
|
382
390
|
const updates = ["updated_at = $1"];
|
|
@@ -432,10 +440,20 @@ async function updateWorkflowRun({
|
|
|
432
440
|
values.push(data.jobId);
|
|
433
441
|
paramIndex++;
|
|
434
442
|
}
|
|
435
|
-
const whereClause = resourceId ? `WHERE id = $${paramIndex} AND resource_id = $${paramIndex + 1}` : `WHERE id = $${paramIndex}`;
|
|
436
443
|
values.push(runId);
|
|
444
|
+
const idParam = paramIndex;
|
|
445
|
+
paramIndex++;
|
|
437
446
|
if (resourceId) {
|
|
438
447
|
values.push(resourceId);
|
|
448
|
+
paramIndex++;
|
|
449
|
+
}
|
|
450
|
+
if (expectedStatuses && expectedStatuses.length > 0) {
|
|
451
|
+
values.push(expectedStatuses);
|
|
452
|
+
paramIndex++;
|
|
453
|
+
}
|
|
454
|
+
let whereClause = resourceId ? `WHERE id = $${idParam} AND resource_id = $${idParam + 1}` : `WHERE id = $${idParam}`;
|
|
455
|
+
if (expectedStatuses && expectedStatuses.length > 0) {
|
|
456
|
+
whereClause += ` AND status = ANY($${paramIndex - 1})`;
|
|
439
457
|
}
|
|
440
458
|
const query = `
|
|
441
459
|
UPDATE workflow_runs
|
|
@@ -476,20 +494,28 @@ async function getWorkflowRuns({
|
|
|
476
494
|
values.push(workflowId);
|
|
477
495
|
paramIndex++;
|
|
478
496
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
497
|
+
const cursorIds = [startingAfter, endingBefore].filter(Boolean);
|
|
498
|
+
if (cursorIds.length > 0) {
|
|
499
|
+
const cursorResult = await db.executeSql("SELECT id, created_at FROM workflow_runs WHERE id = ANY($1)", [cursorIds]);
|
|
500
|
+
const cursorMap = new Map;
|
|
501
|
+
for (const row of cursorResult.rows) {
|
|
502
|
+
cursorMap.set(row.id, typeof row.created_at === "string" ? new Date(row.created_at) : row.created_at);
|
|
485
503
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
504
|
+
if (startingAfter) {
|
|
505
|
+
const cursor = cursorMap.get(startingAfter);
|
|
506
|
+
if (cursor) {
|
|
507
|
+
conditions.push(`created_at < $${paramIndex}`);
|
|
508
|
+
values.push(cursor);
|
|
509
|
+
paramIndex++;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (endingBefore) {
|
|
513
|
+
const cursor = cursorMap.get(endingBefore);
|
|
514
|
+
if (cursor) {
|
|
515
|
+
conditions.push(`created_at > $${paramIndex}`);
|
|
516
|
+
values.push(cursor);
|
|
517
|
+
paramIndex++;
|
|
518
|
+
}
|
|
493
519
|
}
|
|
494
520
|
}
|
|
495
521
|
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
@@ -686,8 +712,7 @@ class WorkflowEngine {
|
|
|
686
712
|
status: "running" /* RUNNING */,
|
|
687
713
|
input,
|
|
688
714
|
maxRetries: options?.retries ?? workflow2.retries ?? 0,
|
|
689
|
-
timeoutAt
|
|
690
|
-
timeline: undefined
|
|
715
|
+
timeoutAt
|
|
691
716
|
}, _db);
|
|
692
717
|
const job = {
|
|
693
718
|
runId: insertedRun.id,
|
|
@@ -718,7 +743,8 @@ class WorkflowEngine {
|
|
|
718
743
|
data: {
|
|
719
744
|
status: "paused" /* PAUSED */,
|
|
720
745
|
pausedAt: new Date
|
|
721
|
-
}
|
|
746
|
+
},
|
|
747
|
+
expectedStatuses: ["running" /* RUNNING */, "pending" /* PENDING */]
|
|
722
748
|
});
|
|
723
749
|
this.logger.log("Paused workflow run", {
|
|
724
750
|
runId,
|
|
@@ -732,6 +758,10 @@ class WorkflowEngine {
|
|
|
732
758
|
options
|
|
733
759
|
}) {
|
|
734
760
|
await this.checkIfHasStarted();
|
|
761
|
+
const current = await this.getRun({ runId, resourceId });
|
|
762
|
+
if (current.status !== "paused" /* PAUSED */) {
|
|
763
|
+
throw new WorkflowEngineError(`Cannot resume workflow run in '${current.status}' status, must be 'paused'`, current.workflowId, runId);
|
|
764
|
+
}
|
|
735
765
|
return this.triggerEvent({
|
|
736
766
|
runId,
|
|
737
767
|
resourceId,
|
|
@@ -751,8 +781,7 @@ class WorkflowEngine {
|
|
|
751
781
|
return run;
|
|
752
782
|
}
|
|
753
783
|
const stepId = run.currentStepId;
|
|
754
|
-
const
|
|
755
|
-
const waitForStep = waitForStepEntry && typeof waitForStepEntry === "object" && "waitFor" in waitForStepEntry ? waitForStepEntry : null;
|
|
784
|
+
const waitForStep = this.getWaitForStepEntry(run.timeline, stepId);
|
|
756
785
|
if (!waitForStep) {
|
|
757
786
|
return run;
|
|
758
787
|
}
|
|
@@ -761,18 +790,21 @@ class WorkflowEngine {
|
|
|
761
790
|
return this.resumeWorkflow({ runId, resourceId });
|
|
762
791
|
}
|
|
763
792
|
if (skipOutput && timeoutEvent) {
|
|
764
|
-
await this.
|
|
765
|
-
runId,
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
793
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
794
|
+
const freshRun = await this.getRun({ runId, resourceId }, { exclusiveLock: true, db });
|
|
795
|
+
return this.updateRun({
|
|
796
|
+
runId,
|
|
797
|
+
resourceId,
|
|
798
|
+
data: {
|
|
799
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
800
|
+
[stepId]: {
|
|
801
|
+
output: data ?? {},
|
|
802
|
+
timestamp: new Date
|
|
803
|
+
}
|
|
804
|
+
})
|
|
805
|
+
}
|
|
806
|
+
}, { db });
|
|
807
|
+
}, this.pool);
|
|
776
808
|
return this.triggerEvent({ runId, resourceId, eventName: timeoutEvent });
|
|
777
809
|
}
|
|
778
810
|
if (eventName) {
|
|
@@ -793,7 +825,8 @@ class WorkflowEngine {
|
|
|
793
825
|
resourceId,
|
|
794
826
|
data: {
|
|
795
827
|
status: "cancelled" /* CANCELLED */
|
|
796
|
-
}
|
|
828
|
+
},
|
|
829
|
+
expectedStatuses: ["pending" /* PENDING */, "running" /* RUNNING */, "paused" /* PAUSED */]
|
|
797
830
|
});
|
|
798
831
|
this.logger.log(`cancelled workflow run with id ${runId}`);
|
|
799
832
|
return run;
|
|
@@ -818,7 +851,7 @@ class WorkflowEngine {
|
|
|
818
851
|
data
|
|
819
852
|
}
|
|
820
853
|
};
|
|
821
|
-
this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
854
|
+
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
822
855
|
expireInSeconds: options?.expireInSeconds ?? defaultExpireInSeconds
|
|
823
856
|
});
|
|
824
857
|
this.logger.log(`event ${eventName} sent for workflow run with id ${runId}`);
|
|
@@ -834,10 +867,17 @@ class WorkflowEngine {
|
|
|
834
867
|
async updateRun({
|
|
835
868
|
runId,
|
|
836
869
|
resourceId,
|
|
837
|
-
data
|
|
870
|
+
data,
|
|
871
|
+
expectedStatuses
|
|
838
872
|
}, { db } = {}) {
|
|
839
|
-
const run = await updateWorkflowRun({ runId, resourceId, data }, db ?? this.db);
|
|
873
|
+
const run = await updateWorkflowRun({ runId, resourceId, data, expectedStatuses }, db ?? this.db);
|
|
840
874
|
if (!run) {
|
|
875
|
+
if (expectedStatuses) {
|
|
876
|
+
const current = await getWorkflowRun({ runId, resourceId }, { db: db ?? this.db });
|
|
877
|
+
if (current) {
|
|
878
|
+
throw new WorkflowEngineError(`Cannot update workflow run in '${current.status}' status, expected: ${expectedStatuses.join(", ")}`, current.workflowId, runId);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
841
881
|
throw new WorkflowRunNotFoundError(runId);
|
|
842
882
|
}
|
|
843
883
|
return run;
|
|
@@ -920,36 +960,40 @@ class WorkflowEngine {
|
|
|
920
960
|
throw new WorkflowEngineError("Missing current step id", workflowId, runId);
|
|
921
961
|
}
|
|
922
962
|
if (run.status === "paused" /* PAUSED */) {
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
const
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
963
|
+
run = await withPostgresTransaction(this.db, async (db) => {
|
|
964
|
+
const lockedRun = await this.getRun({ runId, resourceId: scopedResourceId }, { exclusiveLock: true, db });
|
|
965
|
+
if (lockedRun.status !== "paused" /* PAUSED */) {
|
|
966
|
+
return lockedRun;
|
|
967
|
+
}
|
|
968
|
+
const waitForStep = this.getWaitForStepEntry(lockedRun.timeline, lockedRun.currentStepId);
|
|
969
|
+
const currentStep = this.getCachedStepEntry(lockedRun.timeline, lockedRun.currentStepId);
|
|
970
|
+
const waitFor = waitForStep?.waitFor;
|
|
971
|
+
const hasCurrentStepOutput = currentStep?.output !== undefined;
|
|
972
|
+
const eventMatches = waitFor && event?.name && (event.name === waitFor.eventName || event.name === waitFor.timeoutEvent) && !hasCurrentStepOutput;
|
|
973
|
+
if (eventMatches) {
|
|
974
|
+
const isTimeout = event?.name === waitFor?.timeoutEvent;
|
|
975
|
+
const skipOutput = waitFor?.skipOutput;
|
|
976
|
+
return this.updateRun({
|
|
977
|
+
runId,
|
|
978
|
+
resourceId: scopedResourceId,
|
|
979
|
+
data: {
|
|
980
|
+
status: "running" /* RUNNING */,
|
|
981
|
+
pausedAt: null,
|
|
982
|
+
resumedAt: new Date,
|
|
983
|
+
jobId: job?.id,
|
|
984
|
+
...skipOutput ? {} : {
|
|
985
|
+
timeline: import_es_toolkit.merge(lockedRun.timeline, {
|
|
986
|
+
[lockedRun.currentStepId]: {
|
|
987
|
+
output: event?.data ?? {},
|
|
988
|
+
...isTimeout ? { timedOut: true } : {},
|
|
989
|
+
timestamp: new Date
|
|
990
|
+
}
|
|
991
|
+
})
|
|
992
|
+
}
|
|
948
993
|
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
run = await this.updateRun({
|
|
994
|
+
}, { db });
|
|
995
|
+
}
|
|
996
|
+
return this.updateRun({
|
|
953
997
|
runId,
|
|
954
998
|
resourceId: scopedResourceId,
|
|
955
999
|
data: {
|
|
@@ -958,8 +1002,8 @@ class WorkflowEngine {
|
|
|
958
1002
|
resumedAt: new Date,
|
|
959
1003
|
jobId: job?.id
|
|
960
1004
|
}
|
|
961
|
-
});
|
|
962
|
-
}
|
|
1005
|
+
}, { db });
|
|
1006
|
+
}, this.pool);
|
|
963
1007
|
}
|
|
964
1008
|
const baseStep = {
|
|
965
1009
|
run: async (stepId, handler) => {
|
|
@@ -1089,6 +1133,10 @@ class WorkflowEngine {
|
|
|
1089
1133
|
const stepEntry = timeline[stepId];
|
|
1090
1134
|
return stepEntry && typeof stepEntry === "object" && "output" in stepEntry ? stepEntry : null;
|
|
1091
1135
|
}
|
|
1136
|
+
getWaitForStepEntry(timeline, stepId) {
|
|
1137
|
+
const entry = timeline[`${stepId}-wait-for`];
|
|
1138
|
+
return entry && typeof entry === "object" && "waitFor" in entry ? entry : null;
|
|
1139
|
+
}
|
|
1092
1140
|
async runStep({
|
|
1093
1141
|
stepId,
|
|
1094
1142
|
run,
|
|
@@ -1130,7 +1178,7 @@ class WorkflowEngine {
|
|
|
1130
1178
|
runId: run.id,
|
|
1131
1179
|
resourceId: run.resourceId ?? undefined,
|
|
1132
1180
|
data: {
|
|
1133
|
-
timeline: import_es_toolkit.merge(
|
|
1181
|
+
timeline: import_es_toolkit.merge(persistedRun.timeline, {
|
|
1134
1182
|
[stepId]: {
|
|
1135
1183
|
output,
|
|
1136
1184
|
timestamp: new Date
|
|
@@ -1174,64 +1222,46 @@ ${error.stack}` : String(error)
|
|
|
1174
1222
|
if (cached?.output !== undefined) {
|
|
1175
1223
|
return cached.timedOut ? undefined : cached.output;
|
|
1176
1224
|
}
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
if (typeof fastForwardConfig === "object" && fastForwardConfig !== null && !Array.isArray(fastForwardConfig)) {
|
|
1182
|
-
const mockData = fastForwardConfig[stepId];
|
|
1183
|
-
if (mockData !== undefined) {
|
|
1184
|
-
output = mockData;
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
run = await this.updateRun({
|
|
1225
|
+
const timeoutEvent = timeoutDate ? `__timeout_${stepId}` : undefined;
|
|
1226
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
1227
|
+
const freshRun = await this.getRun({ runId: run.id, resourceId: run.resourceId ?? undefined }, { exclusiveLock: true, db });
|
|
1228
|
+
return this.updateRun({
|
|
1189
1229
|
runId: run.id,
|
|
1190
1230
|
resourceId: run.resourceId ?? undefined,
|
|
1191
1231
|
data: {
|
|
1232
|
+
status: "paused" /* PAUSED */,
|
|
1192
1233
|
currentStepId: stepId,
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1234
|
+
pausedAt: new Date,
|
|
1235
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
1236
|
+
[`${stepId}-wait-for`]: {
|
|
1237
|
+
waitFor: { eventName, timeoutEvent },
|
|
1196
1238
|
timestamp: new Date
|
|
1197
1239
|
}
|
|
1198
1240
|
})
|
|
1199
1241
|
}
|
|
1200
|
-
});
|
|
1201
|
-
|
|
1202
|
-
runId: run.id,
|
|
1203
|
-
workflowId: run.workflowId
|
|
1204
|
-
});
|
|
1205
|
-
return output;
|
|
1206
|
-
}
|
|
1207
|
-
const timeoutEvent = timeoutDate ? `__timeout_${stepId}` : undefined;
|
|
1208
|
-
await this.updateRun({
|
|
1209
|
-
runId: run.id,
|
|
1210
|
-
resourceId: run.resourceId ?? undefined,
|
|
1211
|
-
data: {
|
|
1212
|
-
status: "paused" /* PAUSED */,
|
|
1213
|
-
currentStepId: stepId,
|
|
1214
|
-
pausedAt: new Date,
|
|
1215
|
-
timeline: import_es_toolkit.merge(run.timeline, {
|
|
1216
|
-
[`${stepId}-wait-for`]: {
|
|
1217
|
-
waitFor: { eventName, timeoutEvent },
|
|
1218
|
-
timestamp: new Date
|
|
1219
|
-
}
|
|
1220
|
-
})
|
|
1221
|
-
}
|
|
1222
|
-
});
|
|
1242
|
+
}, { db });
|
|
1243
|
+
}, this.pool);
|
|
1223
1244
|
if (timeoutDate && timeoutEvent) {
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1245
|
+
try {
|
|
1246
|
+
const job = {
|
|
1247
|
+
runId: run.id,
|
|
1248
|
+
resourceId: run.resourceId ?? undefined,
|
|
1249
|
+
workflowId: run.workflowId,
|
|
1250
|
+
input: run.input,
|
|
1251
|
+
event: { name: timeoutEvent, data: { date: timeoutDate.toISOString() } }
|
|
1252
|
+
};
|
|
1253
|
+
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, job, {
|
|
1254
|
+
startAfter: timeoutDate.getTime() <= Date.now() ? new Date : timeoutDate,
|
|
1255
|
+
expireInSeconds: defaultExpireInSeconds
|
|
1256
|
+
});
|
|
1257
|
+
} catch (error) {
|
|
1258
|
+
await this.updateRun({
|
|
1259
|
+
runId: run.id,
|
|
1260
|
+
resourceId: run.resourceId ?? undefined,
|
|
1261
|
+
data: { status: "running" /* RUNNING */, pausedAt: null }
|
|
1262
|
+
});
|
|
1263
|
+
throw error;
|
|
1264
|
+
}
|
|
1235
1265
|
}
|
|
1236
1266
|
this.logger.log(`Step ${stepId} waiting${eventName ? ` for event "${eventName}"` : ""}${timeoutDate ? ` until ${timeoutDate.toISOString()}` : ""}`, { runId: run.id, workflowId: run.workflowId });
|
|
1237
1267
|
}
|
|
@@ -1256,59 +1286,99 @@ ${error.stack}` : String(error)
|
|
|
1256
1286
|
const pollStateEntry = persistedRun.timeline[`${stepId}-poll`];
|
|
1257
1287
|
const startedAt = pollStateEntry && typeof pollStateEntry === "object" && "startedAt" in pollStateEntry ? new Date(pollStateEntry.startedAt) : new Date;
|
|
1258
1288
|
if (timeoutMs !== undefined && Date.now() >= startedAt.getTime() + timeoutMs) {
|
|
1259
|
-
await this.
|
|
1289
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
1290
|
+
const freshRun = await this.getRun({ runId: run.id, resourceId: run.resourceId ?? undefined }, { exclusiveLock: true, db });
|
|
1291
|
+
return this.updateRun({
|
|
1292
|
+
runId: run.id,
|
|
1293
|
+
resourceId: run.resourceId ?? undefined,
|
|
1294
|
+
data: {
|
|
1295
|
+
currentStepId: stepId,
|
|
1296
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
1297
|
+
[stepId]: { output: {}, timedOut: true, timestamp: new Date }
|
|
1298
|
+
})
|
|
1299
|
+
}
|
|
1300
|
+
}, { db });
|
|
1301
|
+
}, this.pool);
|
|
1302
|
+
return { timedOut: true };
|
|
1303
|
+
}
|
|
1304
|
+
let result;
|
|
1305
|
+
try {
|
|
1306
|
+
result = await conditionFn();
|
|
1307
|
+
} catch (error) {
|
|
1308
|
+
this.logger.error(`Poll conditionFn for step ${stepId} threw an error, treating as non-match and continuing to poll`, error, { runId: run.id, workflowId: run.workflowId });
|
|
1309
|
+
if (timeoutMs !== undefined && Date.now() >= startedAt.getTime() + timeoutMs) {
|
|
1310
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
1311
|
+
const freshRun = await this.getRun({ runId: run.id, resourceId: run.resourceId ?? undefined }, { exclusiveLock: true, db });
|
|
1312
|
+
return this.updateRun({
|
|
1313
|
+
runId: run.id,
|
|
1314
|
+
resourceId: run.resourceId ?? undefined,
|
|
1315
|
+
data: {
|
|
1316
|
+
currentStepId: stepId,
|
|
1317
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
1318
|
+
[stepId]: { output: {}, timedOut: true, timestamp: new Date }
|
|
1319
|
+
})
|
|
1320
|
+
}
|
|
1321
|
+
}, { db });
|
|
1322
|
+
}, this.pool);
|
|
1323
|
+
return { timedOut: true };
|
|
1324
|
+
}
|
|
1325
|
+
result = false;
|
|
1326
|
+
}
|
|
1327
|
+
if (result !== false) {
|
|
1328
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
1329
|
+
const freshRun = await this.getRun({ runId: run.id, resourceId: run.resourceId ?? undefined }, { exclusiveLock: true, db });
|
|
1330
|
+
return this.updateRun({
|
|
1331
|
+
runId: run.id,
|
|
1332
|
+
resourceId: run.resourceId ?? undefined,
|
|
1333
|
+
data: {
|
|
1334
|
+
currentStepId: stepId,
|
|
1335
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
1336
|
+
[stepId]: { output: result, timestamp: new Date }
|
|
1337
|
+
})
|
|
1338
|
+
}
|
|
1339
|
+
}, { db });
|
|
1340
|
+
}, this.pool);
|
|
1341
|
+
return { timedOut: false, data: result };
|
|
1342
|
+
}
|
|
1343
|
+
const pollEvent = `__poll_${stepId}`;
|
|
1344
|
+
await withPostgresTransaction(this.db, async (db) => {
|
|
1345
|
+
const freshRun = await this.getRun({ runId: run.id, resourceId: run.resourceId ?? undefined }, { exclusiveLock: true, db });
|
|
1346
|
+
return this.updateRun({
|
|
1260
1347
|
runId: run.id,
|
|
1261
1348
|
resourceId: run.resourceId ?? undefined,
|
|
1262
1349
|
data: {
|
|
1350
|
+
status: "paused" /* PAUSED */,
|
|
1263
1351
|
currentStepId: stepId,
|
|
1264
|
-
|
|
1265
|
-
|
|
1352
|
+
pausedAt: new Date,
|
|
1353
|
+
timeline: import_es_toolkit.merge(freshRun.timeline, {
|
|
1354
|
+
[`${stepId}-poll`]: { startedAt: startedAt.toISOString() },
|
|
1355
|
+
[`${stepId}-wait-for`]: {
|
|
1356
|
+
waitFor: { timeoutEvent: pollEvent, skipOutput: true },
|
|
1357
|
+
timestamp: new Date
|
|
1358
|
+
}
|
|
1266
1359
|
})
|
|
1267
1360
|
}
|
|
1361
|
+
}, { db });
|
|
1362
|
+
}, this.pool);
|
|
1363
|
+
try {
|
|
1364
|
+
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, {
|
|
1365
|
+
runId: run.id,
|
|
1366
|
+
resourceId: run.resourceId ?? undefined,
|
|
1367
|
+
workflowId: run.workflowId,
|
|
1368
|
+
input: run.input,
|
|
1369
|
+
event: { name: pollEvent, data: {} }
|
|
1370
|
+
}, {
|
|
1371
|
+
startAfter: new Date(Date.now() + intervalMs),
|
|
1372
|
+
expireInSeconds: defaultExpireInSeconds
|
|
1268
1373
|
});
|
|
1269
|
-
|
|
1270
|
-
}
|
|
1271
|
-
const result = await conditionFn();
|
|
1272
|
-
if (result !== false) {
|
|
1374
|
+
} catch (error) {
|
|
1273
1375
|
await this.updateRun({
|
|
1274
1376
|
runId: run.id,
|
|
1275
1377
|
resourceId: run.resourceId ?? undefined,
|
|
1276
|
-
data: {
|
|
1277
|
-
currentStepId: stepId,
|
|
1278
|
-
timeline: import_es_toolkit.merge(persistedRun.timeline, {
|
|
1279
|
-
[stepId]: { output: result, timestamp: new Date }
|
|
1280
|
-
})
|
|
1281
|
-
}
|
|
1378
|
+
data: { status: "running" /* RUNNING */, pausedAt: null }
|
|
1282
1379
|
});
|
|
1283
|
-
|
|
1380
|
+
throw error;
|
|
1284
1381
|
}
|
|
1285
|
-
const pollEvent = `__poll_${stepId}`;
|
|
1286
|
-
await this.updateRun({
|
|
1287
|
-
runId: run.id,
|
|
1288
|
-
resourceId: run.resourceId ?? undefined,
|
|
1289
|
-
data: {
|
|
1290
|
-
status: "paused" /* PAUSED */,
|
|
1291
|
-
currentStepId: stepId,
|
|
1292
|
-
pausedAt: new Date,
|
|
1293
|
-
timeline: import_es_toolkit.merge(persistedRun.timeline, {
|
|
1294
|
-
[`${stepId}-poll`]: { startedAt: startedAt.toISOString() },
|
|
1295
|
-
[`${stepId}-wait-for`]: {
|
|
1296
|
-
waitFor: { timeoutEvent: pollEvent, skipOutput: true },
|
|
1297
|
-
timestamp: new Date
|
|
1298
|
-
}
|
|
1299
|
-
})
|
|
1300
|
-
}
|
|
1301
|
-
});
|
|
1302
|
-
await this.boss.send(WORKFLOW_RUN_QUEUE_NAME, {
|
|
1303
|
-
runId: run.id,
|
|
1304
|
-
resourceId: run.resourceId ?? undefined,
|
|
1305
|
-
workflowId: run.workflowId,
|
|
1306
|
-
input: run.input,
|
|
1307
|
-
event: { name: pollEvent, data: {} }
|
|
1308
|
-
}, {
|
|
1309
|
-
startAfter: new Date(Date.now() + intervalMs),
|
|
1310
|
-
expireInSeconds: defaultExpireInSeconds
|
|
1311
|
-
});
|
|
1312
1382
|
this.logger.log(`Step ${stepId} polling every ${intervalMs}ms...`, {
|
|
1313
1383
|
runId: run.id,
|
|
1314
1384
|
workflowId: run.workflowId
|
|
@@ -1353,5 +1423,5 @@ ${error.stack}` : String(error)
|
|
|
1353
1423
|
}
|
|
1354
1424
|
}
|
|
1355
1425
|
|
|
1356
|
-
//# debugId=
|
|
1426
|
+
//# debugId=12BE08AB4C2E4D0564756E2164756E21
|
|
1357
1427
|
//# sourceMappingURL=index.js.map
|
package/dist/index.d.cts
CHANGED
|
@@ -236,10 +236,11 @@ declare class WorkflowEngine {
|
|
|
236
236
|
exclusiveLock?: boolean;
|
|
237
237
|
db?: Db;
|
|
238
238
|
}): Promise<WorkflowRun>;
|
|
239
|
-
updateRun({ runId, resourceId, data }: {
|
|
239
|
+
updateRun({ runId, resourceId, data, expectedStatuses }: {
|
|
240
240
|
runId: string;
|
|
241
241
|
resourceId?: string;
|
|
242
242
|
data: Partial<WorkflowRun>;
|
|
243
|
+
expectedStatuses?: string[];
|
|
243
244
|
}, { db }?: {
|
|
244
245
|
db?: Db;
|
|
245
246
|
}): Promise<WorkflowRun>;
|
|
@@ -256,6 +257,7 @@ declare class WorkflowEngine {
|
|
|
256
257
|
private resolveScopedResourceId;
|
|
257
258
|
private handleWorkflowRun;
|
|
258
259
|
private getCachedStepEntry;
|
|
260
|
+
private getWaitForStepEntry;
|
|
259
261
|
private runStep;
|
|
260
262
|
private waitStep;
|
|
261
263
|
private pollStep;
|
package/dist/index.d.ts
CHANGED
|
@@ -236,10 +236,11 @@ declare class WorkflowEngine {
|
|
|
236
236
|
exclusiveLock?: boolean;
|
|
237
237
|
db?: Db;
|
|
238
238
|
}): Promise<WorkflowRun>;
|
|
239
|
-
updateRun({ runId, resourceId, data }: {
|
|
239
|
+
updateRun({ runId, resourceId, data, expectedStatuses }: {
|
|
240
240
|
runId: string;
|
|
241
241
|
resourceId?: string;
|
|
242
242
|
data: Partial<WorkflowRun>;
|
|
243
|
+
expectedStatuses?: string[];
|
|
243
244
|
}, { db }?: {
|
|
244
245
|
db?: Db;
|
|
245
246
|
}): Promise<WorkflowRun>;
|
|
@@ -256,6 +257,7 @@ declare class WorkflowEngine {
|
|
|
256
257
|
private resolveScopedResourceId;
|
|
257
258
|
private handleWorkflowRun;
|
|
258
259
|
private getCachedStepEntry;
|
|
260
|
+
private getWaitForStepEntry;
|
|
259
261
|
private runStep;
|
|
260
262
|
private waitStep;
|
|
261
263
|
private pollStep;
|