pg-workflows 0.7.0 → 0.8.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 +83 -765
- package/dist/client.entry.cjs +822 -0
- package/dist/client.entry.d.cts +227 -0
- package/dist/client.entry.d.ts +227 -0
- package/dist/client.entry.js +13 -0
- package/dist/client.entry.js.map +16 -0
- package/dist/index.cjs +710 -356
- package/dist/index.d.cts +123 -11
- package/dist/index.d.ts +123 -11
- package/dist/index.js +75 -450
- package/dist/index.js.map +12 -10
- package/dist/shared/chunk-8n9chg7z.js +753 -0
- package/dist/shared/chunk-8n9chg7z.js.map +16 -0
- package/package.json +11 -1
package/dist/index.js
CHANGED
|
@@ -1,50 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PGBOSS_SCHEMA,
|
|
3
|
+
PAUSE_EVENT_NAME,
|
|
4
|
+
StepType,
|
|
5
|
+
WORKFLOW_RUN_QUEUE_NAME,
|
|
6
|
+
WorkflowClient,
|
|
7
|
+
WorkflowEngineError,
|
|
8
|
+
WorkflowRunNotFoundError,
|
|
9
|
+
WorkflowStatus,
|
|
10
|
+
createWorkflowRef,
|
|
11
|
+
getWorkflowRun,
|
|
12
|
+
getWorkflowRuns,
|
|
13
|
+
insertWorkflowRun,
|
|
14
|
+
runMigrations,
|
|
15
|
+
updateWorkflowRun,
|
|
16
|
+
withPostgresTransaction,
|
|
17
|
+
workflow
|
|
18
|
+
} from "./shared/chunk-8n9chg7z.js";
|
|
18
19
|
// src/duration.ts
|
|
19
20
|
import parse from "parse-duration";
|
|
20
|
-
|
|
21
|
-
// src/error.ts
|
|
22
|
-
class WorkflowEngineError extends Error {
|
|
23
|
-
workflowId;
|
|
24
|
-
runId;
|
|
25
|
-
cause;
|
|
26
|
-
issues;
|
|
27
|
-
constructor(message, workflowId, runId, cause = undefined, issues) {
|
|
28
|
-
super(message);
|
|
29
|
-
this.workflowId = workflowId;
|
|
30
|
-
this.runId = runId;
|
|
31
|
-
this.cause = cause;
|
|
32
|
-
this.issues = issues;
|
|
33
|
-
this.name = "WorkflowEngineError";
|
|
34
|
-
if (Error.captureStackTrace) {
|
|
35
|
-
Error.captureStackTrace(this, WorkflowEngineError);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
class WorkflowRunNotFoundError extends WorkflowEngineError {
|
|
41
|
-
constructor(runId, workflowId) {
|
|
42
|
-
super("Workflow run not found", workflowId, runId);
|
|
43
|
-
this.name = "WorkflowRunNotFoundError";
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// src/duration.ts
|
|
48
21
|
var MS_PER_SECOND = 1000;
|
|
49
22
|
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
50
23
|
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
@@ -75,28 +48,6 @@ import { PgBoss } from "pg-boss";
|
|
|
75
48
|
|
|
76
49
|
// src/ast-parser.ts
|
|
77
50
|
import * as ts from "typescript";
|
|
78
|
-
|
|
79
|
-
// src/types.ts
|
|
80
|
-
var WorkflowStatus;
|
|
81
|
-
((WorkflowStatus2) => {
|
|
82
|
-
WorkflowStatus2["PENDING"] = "pending";
|
|
83
|
-
WorkflowStatus2["RUNNING"] = "running";
|
|
84
|
-
WorkflowStatus2["PAUSED"] = "paused";
|
|
85
|
-
WorkflowStatus2["COMPLETED"] = "completed";
|
|
86
|
-
WorkflowStatus2["FAILED"] = "failed";
|
|
87
|
-
WorkflowStatus2["CANCELLED"] = "cancelled";
|
|
88
|
-
})(WorkflowStatus ||= {});
|
|
89
|
-
var StepType;
|
|
90
|
-
((StepType2) => {
|
|
91
|
-
StepType2["PAUSE"] = "pause";
|
|
92
|
-
StepType2["RUN"] = "run";
|
|
93
|
-
StepType2["WAIT_FOR"] = "waitFor";
|
|
94
|
-
StepType2["WAIT_UNTIL"] = "waitUntil";
|
|
95
|
-
StepType2["DELAY"] = "delay";
|
|
96
|
-
StepType2["POLL"] = "poll";
|
|
97
|
-
})(StepType ||= {});
|
|
98
|
-
|
|
99
|
-
// src/ast-parser.ts
|
|
100
51
|
function parseWorkflowHandler(handler) {
|
|
101
52
|
const handlerSource = handler.toString();
|
|
102
53
|
const sourceFile = ts.createSourceFile("handler.ts", handlerSource, ts.ScriptTarget.Latest, true);
|
|
@@ -165,353 +116,8 @@ function parseWorkflowHandler(handler) {
|
|
|
165
116
|
return { steps: Array.from(steps.values()) };
|
|
166
117
|
}
|
|
167
118
|
|
|
168
|
-
// src/db/migration.ts
|
|
169
|
-
async function runMigrations(db) {
|
|
170
|
-
const tableExistsResult = await db.executeSql(`
|
|
171
|
-
SELECT EXISTS (
|
|
172
|
-
SELECT FROM information_schema.tables
|
|
173
|
-
WHERE table_schema = 'public'
|
|
174
|
-
AND table_name = 'workflow_runs'
|
|
175
|
-
);
|
|
176
|
-
`, []);
|
|
177
|
-
if (!tableExistsResult.rows[0]?.exists) {
|
|
178
|
-
await db.executeSql(`
|
|
179
|
-
CREATE TABLE workflow_runs (
|
|
180
|
-
id varchar(32) PRIMARY KEY NOT NULL,
|
|
181
|
-
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
|
182
|
-
updated_at timestamp with time zone DEFAULT now() NOT NULL,
|
|
183
|
-
resource_id varchar(32),
|
|
184
|
-
workflow_id varchar(32) NOT NULL,
|
|
185
|
-
status text DEFAULT 'pending' NOT NULL,
|
|
186
|
-
input jsonb NOT NULL,
|
|
187
|
-
output jsonb,
|
|
188
|
-
error text,
|
|
189
|
-
current_step_id varchar(256) NOT NULL,
|
|
190
|
-
timeline jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
191
|
-
paused_at timestamp with time zone,
|
|
192
|
-
resumed_at timestamp with time zone,
|
|
193
|
-
completed_at timestamp with time zone,
|
|
194
|
-
timeout_at timestamp with time zone,
|
|
195
|
-
retry_count integer DEFAULT 0 NOT NULL,
|
|
196
|
-
max_retries integer DEFAULT 0 NOT NULL,
|
|
197
|
-
job_id varchar(256),
|
|
198
|
-
idempotency_key varchar(256)
|
|
199
|
-
);
|
|
200
|
-
`, []);
|
|
201
|
-
await db.executeSql(`
|
|
202
|
-
CREATE INDEX workflow_runs_created_at_idx ON workflow_runs USING btree (created_at);
|
|
203
|
-
CREATE INDEX workflow_runs_resource_id_created_at_idx ON workflow_runs USING btree (resource_id, created_at DESC);
|
|
204
|
-
CREATE INDEX workflow_runs_status_created_at_idx ON workflow_runs USING btree (status, created_at DESC);
|
|
205
|
-
CREATE INDEX workflow_runs_workflow_id_created_at_idx ON workflow_runs USING btree (workflow_id, created_at DESC);
|
|
206
|
-
CREATE INDEX workflow_runs_resource_id_workflow_id_created_at_idx ON workflow_runs USING btree (resource_id, workflow_id, created_at DESC);
|
|
207
|
-
CREATE UNIQUE INDEX workflow_runs_idempotency_key_idx ON workflow_runs (idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
208
|
-
`, []);
|
|
209
|
-
} else {
|
|
210
|
-
await db.executeSql(`
|
|
211
|
-
DROP INDEX IF EXISTS workflow_runs_workflow_id_idx;
|
|
212
|
-
DROP INDEX IF EXISTS workflow_runs_resource_id_idx;
|
|
213
|
-
CREATE INDEX IF NOT EXISTS workflow_runs_created_at_idx ON workflow_runs USING btree (created_at);
|
|
214
|
-
CREATE INDEX IF NOT EXISTS workflow_runs_resource_id_created_at_idx ON workflow_runs USING btree (resource_id, created_at DESC);
|
|
215
|
-
CREATE INDEX IF NOT EXISTS workflow_runs_status_created_at_idx ON workflow_runs USING btree (status, created_at DESC);
|
|
216
|
-
CREATE INDEX IF NOT EXISTS workflow_runs_workflow_id_created_at_idx ON workflow_runs USING btree (workflow_id, created_at DESC);
|
|
217
|
-
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);
|
|
218
|
-
ALTER TABLE workflow_runs ADD COLUMN IF NOT EXISTS idempotency_key varchar(256);
|
|
219
|
-
CREATE UNIQUE INDEX IF NOT EXISTS workflow_runs_idempotency_key_idx ON workflow_runs (idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
220
|
-
`, []);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// src/db/queries.ts
|
|
225
|
-
import ksuid from "ksuid";
|
|
226
|
-
function generateKSUID(prefix) {
|
|
227
|
-
return `${prefix ? `${prefix}_` : ""}${ksuid.randomSync().string}`;
|
|
228
|
-
}
|
|
229
|
-
function mapRowToWorkflowRun(row) {
|
|
230
|
-
return {
|
|
231
|
-
id: row.id,
|
|
232
|
-
createdAt: new Date(row.created_at),
|
|
233
|
-
updatedAt: new Date(row.updated_at),
|
|
234
|
-
resourceId: row.resource_id,
|
|
235
|
-
workflowId: row.workflow_id,
|
|
236
|
-
status: row.status,
|
|
237
|
-
input: typeof row.input === "string" ? JSON.parse(row.input) : row.input,
|
|
238
|
-
output: typeof row.output === "string" ? row.output.trim().startsWith("{") || row.output.trim().startsWith("[") ? JSON.parse(row.output) : row.output : row.output ?? null,
|
|
239
|
-
error: row.error,
|
|
240
|
-
currentStepId: row.current_step_id,
|
|
241
|
-
timeline: typeof row.timeline === "string" ? JSON.parse(row.timeline) : row.timeline,
|
|
242
|
-
pausedAt: row.paused_at ? new Date(row.paused_at) : null,
|
|
243
|
-
resumedAt: row.resumed_at ? new Date(row.resumed_at) : null,
|
|
244
|
-
completedAt: row.completed_at ? new Date(row.completed_at) : null,
|
|
245
|
-
timeoutAt: row.timeout_at ? new Date(row.timeout_at) : null,
|
|
246
|
-
retryCount: row.retry_count,
|
|
247
|
-
maxRetries: row.max_retries,
|
|
248
|
-
jobId: row.job_id,
|
|
249
|
-
idempotencyKey: row.idempotency_key
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
async function insertWorkflowRun({
|
|
253
|
-
resourceId,
|
|
254
|
-
workflowId,
|
|
255
|
-
currentStepId,
|
|
256
|
-
status,
|
|
257
|
-
input,
|
|
258
|
-
maxRetries,
|
|
259
|
-
timeoutAt,
|
|
260
|
-
idempotencyKey
|
|
261
|
-
}, db) {
|
|
262
|
-
const runId = generateKSUID("run");
|
|
263
|
-
const now = new Date;
|
|
264
|
-
const result = await db.executeSql(`INSERT INTO workflow_runs (
|
|
265
|
-
id,
|
|
266
|
-
resource_id,
|
|
267
|
-
workflow_id,
|
|
268
|
-
current_step_id,
|
|
269
|
-
status,
|
|
270
|
-
input,
|
|
271
|
-
max_retries,
|
|
272
|
-
timeout_at,
|
|
273
|
-
created_at,
|
|
274
|
-
updated_at,
|
|
275
|
-
timeline,
|
|
276
|
-
retry_count,
|
|
277
|
-
idempotency_key
|
|
278
|
-
)
|
|
279
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
|
280
|
-
ON CONFLICT (idempotency_key) WHERE idempotency_key IS NOT NULL DO NOTHING
|
|
281
|
-
RETURNING *`, [
|
|
282
|
-
runId,
|
|
283
|
-
resourceId ?? null,
|
|
284
|
-
workflowId,
|
|
285
|
-
currentStepId,
|
|
286
|
-
status,
|
|
287
|
-
JSON.stringify(input),
|
|
288
|
-
maxRetries,
|
|
289
|
-
timeoutAt,
|
|
290
|
-
now,
|
|
291
|
-
now,
|
|
292
|
-
"{}",
|
|
293
|
-
0,
|
|
294
|
-
idempotencyKey ?? null
|
|
295
|
-
]);
|
|
296
|
-
if (result.rows[0]) {
|
|
297
|
-
return { run: mapRowToWorkflowRun(result.rows[0]), created: true };
|
|
298
|
-
}
|
|
299
|
-
const existing = await db.executeSql("SELECT * FROM workflow_runs WHERE idempotency_key = $1", [
|
|
300
|
-
idempotencyKey
|
|
301
|
-
]);
|
|
302
|
-
if (!existing.rows[0]) {
|
|
303
|
-
throw new Error(`Idempotency conflict: existing run not found for key "${idempotencyKey}"`);
|
|
304
|
-
}
|
|
305
|
-
return { run: mapRowToWorkflowRun(existing.rows[0]), created: false };
|
|
306
|
-
}
|
|
307
|
-
async function getWorkflowRun({
|
|
308
|
-
runId,
|
|
309
|
-
resourceId
|
|
310
|
-
}, { exclusiveLock = false, db }) {
|
|
311
|
-
const lockSuffix = exclusiveLock ? "FOR UPDATE" : "";
|
|
312
|
-
const result = resourceId ? await db.executeSql(`SELECT * FROM workflow_runs
|
|
313
|
-
WHERE id = $1 AND resource_id = $2
|
|
314
|
-
${lockSuffix}`, [runId, resourceId]) : await db.executeSql(`SELECT * FROM workflow_runs
|
|
315
|
-
WHERE id = $1
|
|
316
|
-
${lockSuffix}`, [runId]);
|
|
317
|
-
const run = result.rows[0];
|
|
318
|
-
if (!run) {
|
|
319
|
-
return null;
|
|
320
|
-
}
|
|
321
|
-
return mapRowToWorkflowRun(run);
|
|
322
|
-
}
|
|
323
|
-
async function updateWorkflowRun({
|
|
324
|
-
runId,
|
|
325
|
-
resourceId,
|
|
326
|
-
data,
|
|
327
|
-
expectedStatuses
|
|
328
|
-
}, db) {
|
|
329
|
-
const now = new Date;
|
|
330
|
-
const updates = ["updated_at = $1"];
|
|
331
|
-
const values = [now];
|
|
332
|
-
let paramIndex = 2;
|
|
333
|
-
if (data.status !== undefined) {
|
|
334
|
-
updates.push(`status = $${paramIndex}`);
|
|
335
|
-
values.push(data.status);
|
|
336
|
-
paramIndex++;
|
|
337
|
-
}
|
|
338
|
-
if (data.currentStepId !== undefined) {
|
|
339
|
-
updates.push(`current_step_id = $${paramIndex}`);
|
|
340
|
-
values.push(data.currentStepId);
|
|
341
|
-
paramIndex++;
|
|
342
|
-
}
|
|
343
|
-
if (data.timeline !== undefined) {
|
|
344
|
-
updates.push(`timeline = $${paramIndex}`);
|
|
345
|
-
values.push(JSON.stringify(data.timeline));
|
|
346
|
-
paramIndex++;
|
|
347
|
-
}
|
|
348
|
-
if (data.pausedAt !== undefined) {
|
|
349
|
-
updates.push(`paused_at = $${paramIndex}`);
|
|
350
|
-
values.push(data.pausedAt);
|
|
351
|
-
paramIndex++;
|
|
352
|
-
}
|
|
353
|
-
if (data.resumedAt !== undefined) {
|
|
354
|
-
updates.push(`resumed_at = $${paramIndex}`);
|
|
355
|
-
values.push(data.resumedAt);
|
|
356
|
-
paramIndex++;
|
|
357
|
-
}
|
|
358
|
-
if (data.completedAt !== undefined) {
|
|
359
|
-
updates.push(`completed_at = $${paramIndex}`);
|
|
360
|
-
values.push(data.completedAt);
|
|
361
|
-
paramIndex++;
|
|
362
|
-
}
|
|
363
|
-
if (data.output !== undefined) {
|
|
364
|
-
updates.push(`output = $${paramIndex}`);
|
|
365
|
-
values.push(JSON.stringify(data.output));
|
|
366
|
-
paramIndex++;
|
|
367
|
-
}
|
|
368
|
-
if (data.error !== undefined) {
|
|
369
|
-
updates.push(`error = $${paramIndex}`);
|
|
370
|
-
values.push(data.error);
|
|
371
|
-
paramIndex++;
|
|
372
|
-
}
|
|
373
|
-
if (data.retryCount !== undefined) {
|
|
374
|
-
updates.push(`retry_count = $${paramIndex}`);
|
|
375
|
-
values.push(data.retryCount);
|
|
376
|
-
paramIndex++;
|
|
377
|
-
}
|
|
378
|
-
if (data.jobId !== undefined) {
|
|
379
|
-
updates.push(`job_id = $${paramIndex}`);
|
|
380
|
-
values.push(data.jobId);
|
|
381
|
-
paramIndex++;
|
|
382
|
-
}
|
|
383
|
-
values.push(runId);
|
|
384
|
-
const idParam = paramIndex;
|
|
385
|
-
paramIndex++;
|
|
386
|
-
if (resourceId) {
|
|
387
|
-
values.push(resourceId);
|
|
388
|
-
paramIndex++;
|
|
389
|
-
}
|
|
390
|
-
if (expectedStatuses && expectedStatuses.length > 0) {
|
|
391
|
-
values.push(expectedStatuses);
|
|
392
|
-
paramIndex++;
|
|
393
|
-
}
|
|
394
|
-
let whereClause = resourceId ? `WHERE id = $${idParam} AND resource_id = $${idParam + 1}` : `WHERE id = $${idParam}`;
|
|
395
|
-
if (expectedStatuses && expectedStatuses.length > 0) {
|
|
396
|
-
whereClause += ` AND status = ANY($${paramIndex - 1})`;
|
|
397
|
-
}
|
|
398
|
-
const query = `
|
|
399
|
-
UPDATE workflow_runs
|
|
400
|
-
SET ${updates.join(", ")}
|
|
401
|
-
${whereClause}
|
|
402
|
-
RETURNING *
|
|
403
|
-
`;
|
|
404
|
-
const result = await db.executeSql(query, values);
|
|
405
|
-
const run = result.rows[0];
|
|
406
|
-
if (!run) {
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
return mapRowToWorkflowRun(run);
|
|
410
|
-
}
|
|
411
|
-
async function getWorkflowRuns({
|
|
412
|
-
resourceId,
|
|
413
|
-
startingAfter,
|
|
414
|
-
endingBefore,
|
|
415
|
-
limit = 20,
|
|
416
|
-
statuses,
|
|
417
|
-
workflowId
|
|
418
|
-
}, db) {
|
|
419
|
-
const conditions = [];
|
|
420
|
-
const values = [];
|
|
421
|
-
let paramIndex = 1;
|
|
422
|
-
if (resourceId) {
|
|
423
|
-
conditions.push(`resource_id = $${paramIndex}`);
|
|
424
|
-
values.push(resourceId);
|
|
425
|
-
paramIndex++;
|
|
426
|
-
}
|
|
427
|
-
if (statuses && statuses.length > 0) {
|
|
428
|
-
conditions.push(`status = ANY($${paramIndex})`);
|
|
429
|
-
values.push(statuses);
|
|
430
|
-
paramIndex++;
|
|
431
|
-
}
|
|
432
|
-
if (workflowId) {
|
|
433
|
-
conditions.push(`workflow_id = $${paramIndex}`);
|
|
434
|
-
values.push(workflowId);
|
|
435
|
-
paramIndex++;
|
|
436
|
-
}
|
|
437
|
-
const cursorIds = [startingAfter, endingBefore].filter(Boolean);
|
|
438
|
-
if (cursorIds.length > 0) {
|
|
439
|
-
const cursorResult = await db.executeSql("SELECT id, created_at FROM workflow_runs WHERE id = ANY($1)", [cursorIds]);
|
|
440
|
-
const cursorMap = new Map;
|
|
441
|
-
for (const row of cursorResult.rows) {
|
|
442
|
-
cursorMap.set(row.id, typeof row.created_at === "string" ? new Date(row.created_at) : row.created_at);
|
|
443
|
-
}
|
|
444
|
-
if (startingAfter) {
|
|
445
|
-
const cursor = cursorMap.get(startingAfter);
|
|
446
|
-
if (cursor) {
|
|
447
|
-
conditions.push(`created_at < $${paramIndex}`);
|
|
448
|
-
values.push(cursor);
|
|
449
|
-
paramIndex++;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (endingBefore) {
|
|
453
|
-
const cursor = cursorMap.get(endingBefore);
|
|
454
|
-
if (cursor) {
|
|
455
|
-
conditions.push(`created_at > $${paramIndex}`);
|
|
456
|
-
values.push(cursor);
|
|
457
|
-
paramIndex++;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
462
|
-
const actualLimit = Math.min(Math.max(limit, 1), 100) + 1;
|
|
463
|
-
const isBackward = !!endingBefore && !startingAfter;
|
|
464
|
-
const query = `
|
|
465
|
-
SELECT * FROM workflow_runs
|
|
466
|
-
${whereClause}
|
|
467
|
-
ORDER BY created_at ${isBackward ? "ASC" : "DESC"}
|
|
468
|
-
LIMIT $${paramIndex}
|
|
469
|
-
`;
|
|
470
|
-
values.push(actualLimit);
|
|
471
|
-
const result = await db.executeSql(query, values);
|
|
472
|
-
const rows = result.rows;
|
|
473
|
-
const hasExtraRow = rows.length > (limit ?? 20);
|
|
474
|
-
const rawItems = hasExtraRow ? rows.slice(0, limit) : rows;
|
|
475
|
-
if (isBackward) {
|
|
476
|
-
rawItems.reverse();
|
|
477
|
-
}
|
|
478
|
-
const items = rawItems.map((row) => mapRowToWorkflowRun(row));
|
|
479
|
-
const hasMore = isBackward ? items.length > 0 : hasExtraRow;
|
|
480
|
-
const hasPrev = isBackward ? hasExtraRow : !!startingAfter && items.length > 0;
|
|
481
|
-
const nextCursor = hasMore && items.length > 0 ? items[items.length - 1]?.id ?? null : null;
|
|
482
|
-
const prevCursor = hasPrev && items.length > 0 ? items[0]?.id ?? null : null;
|
|
483
|
-
return { items, nextCursor, prevCursor, hasMore, hasPrev };
|
|
484
|
-
}
|
|
485
|
-
async function withPostgresTransaction(db, callback, pool) {
|
|
486
|
-
let txDb;
|
|
487
|
-
let release;
|
|
488
|
-
if (pool) {
|
|
489
|
-
const client = await pool.connect();
|
|
490
|
-
txDb = {
|
|
491
|
-
executeSql: (text, values) => client.query(text, values)
|
|
492
|
-
};
|
|
493
|
-
release = () => client.release();
|
|
494
|
-
} else {
|
|
495
|
-
txDb = db;
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
await txDb.executeSql("BEGIN", []);
|
|
499
|
-
const result = await callback(txDb);
|
|
500
|
-
await txDb.executeSql("COMMIT", []);
|
|
501
|
-
return result;
|
|
502
|
-
} catch (error) {
|
|
503
|
-
await txDb.executeSql("ROLLBACK", []);
|
|
504
|
-
throw error;
|
|
505
|
-
} finally {
|
|
506
|
-
release?.();
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
119
|
// src/engine.ts
|
|
511
|
-
var PAUSE_EVENT_NAME = "__internal_pause";
|
|
512
|
-
var WORKFLOW_RUN_QUEUE_NAME = "workflow-run";
|
|
513
120
|
var LOG_PREFIX = "[WorkflowEngine]";
|
|
514
|
-
var DEFAULT_PGBOSS_SCHEMA = "pgboss_v12_pgworkflow";
|
|
515
121
|
var StepTypeToIcon = {
|
|
516
122
|
["run" /* RUN */]: "λ",
|
|
517
123
|
["waitFor" /* WAIT_FOR */]: "○",
|
|
@@ -618,13 +224,26 @@ class WorkflowEngine {
|
|
|
618
224
|
this.workflows.clear();
|
|
619
225
|
return this;
|
|
620
226
|
}
|
|
621
|
-
async startWorkflow({
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
idempotencyKey
|
|
626
|
-
options
|
|
627
|
-
|
|
227
|
+
async startWorkflow(refOrParams, inputArg, optionsArg) {
|
|
228
|
+
let workflowId;
|
|
229
|
+
let input;
|
|
230
|
+
let resourceId;
|
|
231
|
+
let idempotencyKey;
|
|
232
|
+
let options;
|
|
233
|
+
if (typeof refOrParams === "function" && "id" in refOrParams) {
|
|
234
|
+
workflowId = refOrParams.id;
|
|
235
|
+
input = inputArg;
|
|
236
|
+
options = optionsArg;
|
|
237
|
+
resourceId = optionsArg?.resourceId;
|
|
238
|
+
idempotencyKey = optionsArg?.idempotencyKey;
|
|
239
|
+
} else {
|
|
240
|
+
const params = refOrParams;
|
|
241
|
+
workflowId = params.workflowId;
|
|
242
|
+
input = params.input;
|
|
243
|
+
resourceId = params.resourceId;
|
|
244
|
+
idempotencyKey = params.idempotencyKey;
|
|
245
|
+
options = params.options;
|
|
246
|
+
}
|
|
628
247
|
if (!this._started) {
|
|
629
248
|
await this.start(false, { batchSize: options?.batchSize ?? 1 });
|
|
630
249
|
}
|
|
@@ -875,27 +494,29 @@ class WorkflowEngine {
|
|
|
875
494
|
return run.resourceId ?? undefined;
|
|
876
495
|
}
|
|
877
496
|
async handleWorkflowRun([job]) {
|
|
878
|
-
const { runId, resourceId, workflowId, input, event } = job?.data ?? {};
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
}
|
|
882
|
-
if (!workflowId) {
|
|
883
|
-
throw new WorkflowEngineError("Invalid workflow run job, missing workflowId", undefined, runId);
|
|
884
|
-
}
|
|
885
|
-
const workflow2 = this.workflows.get(workflowId);
|
|
886
|
-
if (!workflow2) {
|
|
887
|
-
throw new WorkflowEngineError(`Workflow ${workflowId} not found`, workflowId, runId);
|
|
888
|
-
}
|
|
889
|
-
this.logger.log("Processing workflow run...", {
|
|
890
|
-
runId,
|
|
891
|
-
workflowId
|
|
892
|
-
});
|
|
893
|
-
let run = await this.getRun({ runId });
|
|
894
|
-
if (run.workflowId !== workflowId) {
|
|
895
|
-
throw new WorkflowEngineError(`Workflow run ${runId} does not match job workflowId ${workflowId}`, workflowId, runId);
|
|
896
|
-
}
|
|
897
|
-
const scopedResourceId = this.resolveScopedResourceId(resourceId, run);
|
|
497
|
+
const { runId = "", resourceId, workflowId = "", input, event } = job?.data ?? {};
|
|
498
|
+
let run;
|
|
499
|
+
let scopedResourceId;
|
|
898
500
|
try {
|
|
501
|
+
if (!runId) {
|
|
502
|
+
throw new WorkflowEngineError("Invalid workflow run job, missing runId", workflowId);
|
|
503
|
+
}
|
|
504
|
+
if (!workflowId) {
|
|
505
|
+
throw new WorkflowEngineError("Invalid workflow run job, missing workflowId", undefined, runId);
|
|
506
|
+
}
|
|
507
|
+
const workflow2 = this.workflows.get(workflowId);
|
|
508
|
+
if (!workflow2) {
|
|
509
|
+
throw new WorkflowEngineError(`Workflow ${workflowId} not found`, workflowId, runId);
|
|
510
|
+
}
|
|
511
|
+
this.logger.log("Processing workflow run...", {
|
|
512
|
+
runId,
|
|
513
|
+
workflowId
|
|
514
|
+
});
|
|
515
|
+
run = await this.getRun({ runId });
|
|
516
|
+
if (run.workflowId !== workflowId) {
|
|
517
|
+
throw new WorkflowEngineError(`Workflow run ${runId} does not match job workflowId ${workflowId}`, workflowId, runId);
|
|
518
|
+
}
|
|
519
|
+
scopedResourceId = this.resolveScopedResourceId(resourceId, run);
|
|
899
520
|
if (run.status === "cancelled" /* CANCELLED */) {
|
|
900
521
|
this.logger.log(`Workflow run ${runId} is cancelled, skipping`);
|
|
901
522
|
return;
|
|
@@ -1039,7 +660,7 @@ class WorkflowEngine {
|
|
|
1039
660
|
});
|
|
1040
661
|
}
|
|
1041
662
|
} catch (error) {
|
|
1042
|
-
if (run.retryCount < run.maxRetries) {
|
|
663
|
+
if (run && run.retryCount < run.maxRetries) {
|
|
1043
664
|
await this.updateRun({
|
|
1044
665
|
runId,
|
|
1045
666
|
resourceId: scopedResourceId,
|
|
@@ -1061,15 +682,17 @@ class WorkflowEngine {
|
|
|
1061
682
|
});
|
|
1062
683
|
return;
|
|
1063
684
|
}
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
685
|
+
if (runId) {
|
|
686
|
+
await this.updateRun({
|
|
687
|
+
runId,
|
|
688
|
+
resourceId: scopedResourceId,
|
|
689
|
+
data: {
|
|
690
|
+
status: "failed" /* FAILED */,
|
|
691
|
+
error: error instanceof Error ? error.message : String(error),
|
|
692
|
+
jobId: job?.id
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
1073
696
|
throw error;
|
|
1074
697
|
}
|
|
1075
698
|
}
|
|
@@ -1369,12 +992,14 @@ ${error.stack}` : String(error)
|
|
|
1369
992
|
export {
|
|
1370
993
|
workflow,
|
|
1371
994
|
parseDuration,
|
|
995
|
+
createWorkflowRef,
|
|
1372
996
|
WorkflowStatus,
|
|
1373
997
|
WorkflowRunNotFoundError,
|
|
1374
998
|
WorkflowEngineError,
|
|
1375
999
|
WorkflowEngine,
|
|
1000
|
+
WorkflowClient,
|
|
1376
1001
|
StepType
|
|
1377
1002
|
};
|
|
1378
1003
|
|
|
1379
|
-
//# debugId=
|
|
1004
|
+
//# debugId=9A3E4733F40A491264756E2164756E21
|
|
1380
1005
|
//# sourceMappingURL=index.js.map
|