pg-workflows 0.10.0 → 0.12.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 +43 -0
- package/dist/client.entry.cjs +3 -11
- package/dist/client.entry.d.cts +26 -6
- package/dist/client.entry.d.ts +26 -6
- package/dist/client.entry.js +6 -2
- package/dist/client.entry.js.map +4 -4
- package/dist/index.cjs +221 -49
- package/dist/index.d.cts +27 -22
- package/dist/index.d.ts +27 -22
- package/dist/index.js +226 -41
- package/dist/index.js.map +8 -7
- package/dist/shared/{chunk-nygamc7b.js → chunk-ahxqsytt.js} +3 -13
- package/dist/shared/chunk-ahxqsytt.js.map +16 -0
- package/package.json +10 -1
- package/dist/shared/chunk-nygamc7b.js.map +0 -16
package/dist/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_PGBOSS_SCHEMA,
|
|
3
3
|
PAUSE_EVENT_NAME,
|
|
4
|
-
StepType,
|
|
5
4
|
WORKFLOW_RUN_DLQ_QUEUE_NAME,
|
|
6
5
|
WORKFLOW_RUN_QUEUE_NAME,
|
|
7
6
|
WorkflowClient,
|
|
@@ -21,32 +20,7 @@ import {
|
|
|
21
20
|
waitForTimelineKey,
|
|
22
21
|
withPostgresTransaction,
|
|
23
22
|
workflow
|
|
24
|
-
} from "./shared/chunk-
|
|
25
|
-
// src/duration.ts
|
|
26
|
-
import parse from "parse-duration";
|
|
27
|
-
var MS_PER_SECOND = 1000;
|
|
28
|
-
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
29
|
-
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
30
|
-
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
31
|
-
var MS_PER_WEEK = 7 * MS_PER_DAY;
|
|
32
|
-
function parseDuration(duration) {
|
|
33
|
-
if (typeof duration === "string") {
|
|
34
|
-
if (duration.trim() === "") {
|
|
35
|
-
throw new WorkflowEngineError("Invalid duration: empty string");
|
|
36
|
-
}
|
|
37
|
-
const ms2 = parse(duration);
|
|
38
|
-
if (ms2 == null || ms2 <= 0) {
|
|
39
|
-
throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
|
|
40
|
-
}
|
|
41
|
-
return ms2;
|
|
42
|
-
}
|
|
43
|
-
const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
|
|
44
|
-
const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
|
|
45
|
-
if (ms <= 0) {
|
|
46
|
-
throw new WorkflowEngineError("Invalid duration: must be a positive value");
|
|
47
|
-
}
|
|
48
|
-
return ms;
|
|
49
|
-
}
|
|
23
|
+
} from "./shared/chunk-ahxqsytt.js";
|
|
50
24
|
// src/engine.ts
|
|
51
25
|
import { merge } from "es-toolkit";
|
|
52
26
|
import pg from "pg";
|
|
@@ -122,6 +96,32 @@ function parseWorkflowHandler(handler) {
|
|
|
122
96
|
return { steps: Array.from(steps.values()) };
|
|
123
97
|
}
|
|
124
98
|
|
|
99
|
+
// src/duration.ts
|
|
100
|
+
import parse from "parse-duration";
|
|
101
|
+
var MS_PER_SECOND = 1000;
|
|
102
|
+
var MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
103
|
+
var MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
104
|
+
var MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
105
|
+
var MS_PER_WEEK = 7 * MS_PER_DAY;
|
|
106
|
+
function parseDuration(duration) {
|
|
107
|
+
if (typeof duration === "string") {
|
|
108
|
+
if (duration.trim() === "") {
|
|
109
|
+
throw new WorkflowEngineError("Invalid duration: empty string");
|
|
110
|
+
}
|
|
111
|
+
const ms2 = parse(duration);
|
|
112
|
+
if (ms2 == null || ms2 <= 0) {
|
|
113
|
+
throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
|
|
114
|
+
}
|
|
115
|
+
return ms2;
|
|
116
|
+
}
|
|
117
|
+
const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
|
|
118
|
+
const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
|
|
119
|
+
if (ms <= 0) {
|
|
120
|
+
throw new WorkflowEngineError("Invalid duration: must be a positive value");
|
|
121
|
+
}
|
|
122
|
+
return ms;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
125
|
// src/engine.ts
|
|
126
126
|
var LOG_PREFIX = "[WorkflowEngine]";
|
|
127
127
|
var StepTypeToIcon = {
|
|
@@ -271,7 +271,7 @@ class WorkflowEngine {
|
|
|
271
271
|
async startWorkflow(refOrParams, inputArg, optionsArg) {
|
|
272
272
|
const { workflowId, input, resourceId, idempotencyKey, options } = this.resolveWorkflowRunParameters(refOrParams, inputArg, optionsArg);
|
|
273
273
|
if (!this._started) {
|
|
274
|
-
await this.start(false
|
|
274
|
+
await this.start(false);
|
|
275
275
|
}
|
|
276
276
|
const { run } = await this.createWorkflowRun({
|
|
277
277
|
workflowId,
|
|
@@ -732,19 +732,32 @@ class WorkflowEngine {
|
|
|
732
732
|
};
|
|
733
733
|
let step = { ...baseStep };
|
|
734
734
|
const plugins = workflow2.plugins ?? [];
|
|
735
|
-
for (const plugin of plugins) {
|
|
736
|
-
const extra = plugin.methods(step);
|
|
737
|
-
step = { ...step, ...extra };
|
|
738
|
-
}
|
|
739
735
|
const context = {
|
|
740
736
|
input: run.input,
|
|
741
737
|
workflowId: run.workflowId,
|
|
742
738
|
runId: run.id,
|
|
743
|
-
|
|
739
|
+
resourceId: run.resourceId ?? undefined,
|
|
740
|
+
attempt: run.retryCount,
|
|
741
|
+
get timeline() {
|
|
742
|
+
return run?.timeline ?? {};
|
|
743
|
+
},
|
|
744
744
|
logger: this.logger,
|
|
745
745
|
step
|
|
746
746
|
};
|
|
747
|
-
const
|
|
747
|
+
for (const plugin of plugins) {
|
|
748
|
+
const extra = plugin.methods(step, context);
|
|
749
|
+
step = { ...step, ...extra };
|
|
750
|
+
context.step = step;
|
|
751
|
+
}
|
|
752
|
+
let next = () => workflow2.handler(context);
|
|
753
|
+
for (const plugin of [...plugins].reverse()) {
|
|
754
|
+
if (plugin.wrap) {
|
|
755
|
+
const inner = next;
|
|
756
|
+
const wrap = plugin.wrap;
|
|
757
|
+
next = () => wrap(context, inner);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
const result = await next();
|
|
748
761
|
run = await this.getRun({ runId, resourceId: scopedResourceId });
|
|
749
762
|
const isLastParsedStep = run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id;
|
|
750
763
|
const hasPluginSteps = (workflow2.plugins?.length ?? 0) > 0;
|
|
@@ -1037,7 +1050,7 @@ class WorkflowEngine {
|
|
|
1037
1050
|
if (output === undefined) {
|
|
1038
1051
|
output = {};
|
|
1039
1052
|
}
|
|
1040
|
-
|
|
1053
|
+
const updated = await this.updateRun({
|
|
1041
1054
|
runId: run.id,
|
|
1042
1055
|
resourceId: run.resourceId ?? undefined,
|
|
1043
1056
|
data: {
|
|
@@ -1049,6 +1062,7 @@ class WorkflowEngine {
|
|
|
1049
1062
|
})
|
|
1050
1063
|
}
|
|
1051
1064
|
}, { db });
|
|
1065
|
+
Object.assign(run, updated);
|
|
1052
1066
|
return output;
|
|
1053
1067
|
} catch (error) {
|
|
1054
1068
|
this.logger.error(`Step ${stepId} failed:`, error, {
|
|
@@ -1276,19 +1290,190 @@ ${error.stack}` : String(error)
|
|
|
1276
1290
|
}, this.db);
|
|
1277
1291
|
}
|
|
1278
1292
|
}
|
|
1293
|
+
// src/plugins/otel.ts
|
|
1294
|
+
import {
|
|
1295
|
+
context as otelContext,
|
|
1296
|
+
SpanStatusCode,
|
|
1297
|
+
trace
|
|
1298
|
+
} from "@opentelemetry/api";
|
|
1299
|
+
var DEFAULT_PREFIX = "pg_workflows";
|
|
1300
|
+
function isCachedHit(timeline, stepId, kind) {
|
|
1301
|
+
const entry = timeline[stepId];
|
|
1302
|
+
if (entry && typeof entry === "object" && "output" in entry && entry.output !== undefined) {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
if (kind === "invokeChildWorkflow" && timeline[invokeChildWorkflowTimelineKey(stepId)]) {
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
function otelPlugin(options = {}) {
|
|
1311
|
+
const tracer = options.tracer ?? trace.getTracer("pg-workflows");
|
|
1312
|
+
const prefix = options.spanNamePrefix ?? DEFAULT_PREFIX;
|
|
1313
|
+
const extraAttrs = options.attributes;
|
|
1314
|
+
return {
|
|
1315
|
+
name: "opentelemetry",
|
|
1316
|
+
methods: (step, context) => {
|
|
1317
|
+
const wrapVoidish = (kind, base) => {
|
|
1318
|
+
return async (stepId, ...args) => {
|
|
1319
|
+
if (isCachedHit(context.timeline, stepId, kind)) {
|
|
1320
|
+
return base(stepId, ...args);
|
|
1321
|
+
}
|
|
1322
|
+
const capturedCtx = otelContext.active();
|
|
1323
|
+
const startTime = new Date;
|
|
1324
|
+
let result;
|
|
1325
|
+
let originalErr;
|
|
1326
|
+
let thrownError;
|
|
1327
|
+
try {
|
|
1328
|
+
result = await base(stepId, ...args);
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
originalErr = err;
|
|
1331
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1332
|
+
}
|
|
1333
|
+
const span = tracer.startSpan(`${prefix}.step.${kind}`, {
|
|
1334
|
+
startTime,
|
|
1335
|
+
attributes: { "step.id": stepId, "step.type": kind }
|
|
1336
|
+
}, capturedCtx);
|
|
1337
|
+
if (thrownError) {
|
|
1338
|
+
span.recordException(thrownError);
|
|
1339
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1340
|
+
span.end();
|
|
1341
|
+
throw originalErr;
|
|
1342
|
+
}
|
|
1343
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1344
|
+
span.end();
|
|
1345
|
+
return result;
|
|
1346
|
+
};
|
|
1347
|
+
};
|
|
1348
|
+
return {
|
|
1349
|
+
run: async (stepId, handler) => {
|
|
1350
|
+
if (isCachedHit(context.timeline, stepId, "run")) {
|
|
1351
|
+
return step.run(stepId, handler);
|
|
1352
|
+
}
|
|
1353
|
+
const capturedCtx = otelContext.active();
|
|
1354
|
+
const startTime = new Date;
|
|
1355
|
+
let result;
|
|
1356
|
+
let originalErr;
|
|
1357
|
+
let thrownError;
|
|
1358
|
+
try {
|
|
1359
|
+
result = await step.run(stepId, handler);
|
|
1360
|
+
} catch (err) {
|
|
1361
|
+
originalErr = err;
|
|
1362
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1363
|
+
}
|
|
1364
|
+
if (result === undefined && !thrownError) {
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
const span = tracer.startSpan(`${prefix}.step.run`, {
|
|
1368
|
+
startTime,
|
|
1369
|
+
attributes: { "step.id": stepId, "step.type": "run" }
|
|
1370
|
+
}, capturedCtx);
|
|
1371
|
+
if (thrownError) {
|
|
1372
|
+
span.recordException(thrownError);
|
|
1373
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1374
|
+
span.end();
|
|
1375
|
+
throw originalErr;
|
|
1376
|
+
}
|
|
1377
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1378
|
+
span.end();
|
|
1379
|
+
return result;
|
|
1380
|
+
},
|
|
1381
|
+
waitFor: wrapVoidish("waitFor", step.waitFor),
|
|
1382
|
+
delay: wrapVoidish("delay", step.delay),
|
|
1383
|
+
sleep: wrapVoidish("delay", step.delay),
|
|
1384
|
+
waitUntil: wrapVoidish("waitUntil", step.waitUntil),
|
|
1385
|
+
pause: wrapVoidish("pause", step.pause),
|
|
1386
|
+
poll: async (stepId, conditionFn, pollOptions) => {
|
|
1387
|
+
const capturedCtx = otelContext.active();
|
|
1388
|
+
const startTime = new Date;
|
|
1389
|
+
let result;
|
|
1390
|
+
let originalErr;
|
|
1391
|
+
let thrownError;
|
|
1392
|
+
try {
|
|
1393
|
+
result = await step.poll(stepId, conditionFn, pollOptions);
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
originalErr = err;
|
|
1396
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1397
|
+
}
|
|
1398
|
+
const span = tracer.startSpan(`${prefix}.step.poll`, {
|
|
1399
|
+
startTime,
|
|
1400
|
+
attributes: { "step.id": stepId, "step.type": "poll" }
|
|
1401
|
+
}, capturedCtx);
|
|
1402
|
+
if (thrownError) {
|
|
1403
|
+
span.recordException(thrownError);
|
|
1404
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1405
|
+
span.end();
|
|
1406
|
+
throw originalErr;
|
|
1407
|
+
}
|
|
1408
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1409
|
+
span.end();
|
|
1410
|
+
return result;
|
|
1411
|
+
},
|
|
1412
|
+
invokeChildWorkflow: async (stepId, refOrParams, inputArg, optionsArg) => {
|
|
1413
|
+
if (isCachedHit(context.timeline, stepId, "invokeChildWorkflow")) {
|
|
1414
|
+
return step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
|
|
1415
|
+
}
|
|
1416
|
+
const capturedCtx = otelContext.active();
|
|
1417
|
+
const startTime = new Date;
|
|
1418
|
+
let result;
|
|
1419
|
+
let originalErr;
|
|
1420
|
+
let thrownError;
|
|
1421
|
+
try {
|
|
1422
|
+
result = await step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
|
|
1423
|
+
} catch (err) {
|
|
1424
|
+
originalErr = err;
|
|
1425
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1426
|
+
}
|
|
1427
|
+
const span = tracer.startSpan(`${prefix}.step.invokeChildWorkflow`, {
|
|
1428
|
+
startTime,
|
|
1429
|
+
attributes: { "step.id": stepId, "step.type": "invokeChildWorkflow" }
|
|
1430
|
+
}, capturedCtx);
|
|
1431
|
+
if (thrownError) {
|
|
1432
|
+
span.recordException(thrownError);
|
|
1433
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1434
|
+
span.end();
|
|
1435
|
+
throw originalErr;
|
|
1436
|
+
}
|
|
1437
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1438
|
+
span.end();
|
|
1439
|
+
return result;
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
},
|
|
1443
|
+
wrap: (context, next) => tracer.startActiveSpan(`${prefix}.workflow.run`, {
|
|
1444
|
+
attributes: {
|
|
1445
|
+
"workflow.id": context.workflowId,
|
|
1446
|
+
"workflow.run_id": context.runId,
|
|
1447
|
+
"workflow.attempt": context.attempt,
|
|
1448
|
+
...context.resourceId ? { "workflow.resource_id": context.resourceId } : {},
|
|
1449
|
+
...extraAttrs ? extraAttrs(context) : {}
|
|
1450
|
+
}
|
|
1451
|
+
}, async (span) => {
|
|
1452
|
+
try {
|
|
1453
|
+
const result = await next();
|
|
1454
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1455
|
+
return result;
|
|
1456
|
+
} catch (err) {
|
|
1457
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1458
|
+
span.recordException(error);
|
|
1459
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
1460
|
+
throw err;
|
|
1461
|
+
} finally {
|
|
1462
|
+
span.end();
|
|
1463
|
+
}
|
|
1464
|
+
})
|
|
1465
|
+
};
|
|
1466
|
+
}
|
|
1279
1467
|
export {
|
|
1280
1468
|
workflow,
|
|
1281
|
-
|
|
1282
|
-
validateResourceId,
|
|
1283
|
-
parseDuration,
|
|
1469
|
+
otelPlugin,
|
|
1284
1470
|
createWorkflowRef,
|
|
1285
1471
|
WorkflowStatus,
|
|
1286
1472
|
WorkflowRunNotFoundError,
|
|
1287
1473
|
WorkflowEngineError,
|
|
1288
1474
|
WorkflowEngine,
|
|
1289
|
-
WorkflowClient
|
|
1290
|
-
StepType
|
|
1475
|
+
WorkflowClient
|
|
1291
1476
|
};
|
|
1292
1477
|
|
|
1293
|
-
//# debugId=
|
|
1478
|
+
//# debugId=3131CBB2B482181264756E2164756E21
|
|
1294
1479
|
//# sourceMappingURL=index.js.map
|