pg-workflows 0.11.0 → 0.13.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 +39 -9
- package/dist/client.entry.d.cts +30 -1
- package/dist/client.entry.d.ts +30 -1
- package/dist/client.entry.js +1 -1
- package/dist/client.entry.js.map +7 -7
- package/dist/index.cjs +331 -16
- package/dist/index.d.cts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +300 -9
- package/dist/index.js.map +12 -10
- package/dist/shared/{chunk-ahxqsytt.js → chunk-5xswmve7.js} +41 -11
- package/dist/shared/chunk-5xswmve7.js.map +16 -0
- package/package.json +11 -1
- package/dist/shared/chunk-ahxqsytt.js.map +0 -16
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ type WorkflowRun = {
|
|
|
23
23
|
parentRunId: string | null;
|
|
24
24
|
parentStepId: string | null;
|
|
25
25
|
parentResourceId: string | null;
|
|
26
|
+
/** Set when the run was started by a recurring schedule; the timestamp the schedule fired. */
|
|
27
|
+
scheduledAt: Date | null;
|
|
26
28
|
};
|
|
27
29
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
28
30
|
type DurationObject = {
|
|
@@ -33,6 +35,7 @@ type DurationObject = {
|
|
|
33
35
|
seconds?: number;
|
|
34
36
|
};
|
|
35
37
|
type Duration = string | DurationObject;
|
|
38
|
+
type Schedule = string | Exclude<Duration, string>;
|
|
36
39
|
declare enum WorkflowStatus {
|
|
37
40
|
PENDING = "pending",
|
|
38
41
|
RUNNING = "running",
|
|
@@ -63,6 +66,18 @@ type WorkflowOptions<I extends InputParameters> = {
|
|
|
63
66
|
timeout?: number;
|
|
64
67
|
retries?: number;
|
|
65
68
|
inputSchema?: I;
|
|
69
|
+
/**
|
|
70
|
+
* Recurring schedule. Accepts a cron expression (`'0 9 * * 1-5'`),
|
|
71
|
+
* a duration string (`'5m'`, `'1 hour'`), or a `DurationObject`.
|
|
72
|
+
*/
|
|
73
|
+
schedule?: Schedule;
|
|
74
|
+
/** IANA timezone for cron expressions. Defaults to UTC. Ignored for duration-based schedules. */
|
|
75
|
+
timezone?: string;
|
|
76
|
+
};
|
|
77
|
+
/** Metadata about a scheduled fire, exposed on `ctx.schedule` for runs triggered by a schedule. */
|
|
78
|
+
type ScheduleContext = {
|
|
79
|
+
/** Time the schedule fired this run. */
|
|
80
|
+
timestamp: Date;
|
|
66
81
|
};
|
|
67
82
|
type StepBaseContext = {
|
|
68
83
|
run: <T>(stepId: string, handler: () => Promise<T>) => Promise<T>;
|
|
@@ -126,7 +141,13 @@ interface WorkflowPlugin<
|
|
|
126
141
|
TStepExt = object
|
|
127
142
|
> {
|
|
128
143
|
name: string;
|
|
129
|
-
methods: (step: TStepBase) => TStepExt;
|
|
144
|
+
methods: (step: TStepBase, context: WorkflowContext) => TStepExt;
|
|
145
|
+
/**
|
|
146
|
+
* Optional middleware around the workflow handler call. Composes in
|
|
147
|
+
* registration order — the first plugin passed to `.use()` wraps everything
|
|
148
|
+
* inside. Implementations MUST call `next()` exactly once.
|
|
149
|
+
*/
|
|
150
|
+
wrap?: (context: WorkflowContext, next: () => Promise<unknown>) => Promise<unknown>;
|
|
130
151
|
}
|
|
131
152
|
type WorkflowContext<
|
|
132
153
|
TInput extends InputParameters = InputParameters,
|
|
@@ -136,8 +157,14 @@ type WorkflowContext<
|
|
|
136
157
|
step: TStep;
|
|
137
158
|
workflowId: string;
|
|
138
159
|
runId: string;
|
|
160
|
+
/** Tenant/scope identifier set when the run was started, if any. */
|
|
161
|
+
resourceId?: string;
|
|
162
|
+
/** Zero-based retry attempt number (= `run.retryCount`). */
|
|
163
|
+
attempt: number;
|
|
139
164
|
timeline: Record<string, unknown>;
|
|
140
165
|
logger: WorkflowLogger;
|
|
166
|
+
/** Set only for runs triggered by a recurring schedule. */
|
|
167
|
+
schedule?: ScheduleContext;
|
|
141
168
|
};
|
|
142
169
|
type WorkflowDefinition<TInput extends InputParameters = InputParameters> = {
|
|
143
170
|
id: string;
|
|
@@ -146,6 +173,8 @@ type WorkflowDefinition<TInput extends InputParameters = InputParameters> = {
|
|
|
146
173
|
inputSchema?: TInput;
|
|
147
174
|
timeout?: number;
|
|
148
175
|
retries?: number;
|
|
176
|
+
schedule?: Schedule;
|
|
177
|
+
timezone?: string;
|
|
149
178
|
plugins?: WorkflowPlugin[];
|
|
150
179
|
};
|
|
151
180
|
type StepInternalDefinition = {
|
|
@@ -321,6 +350,8 @@ declare class WorkflowEngine {
|
|
|
321
350
|
batchSize?: number;
|
|
322
351
|
heartbeatSeconds?: number;
|
|
323
352
|
}): Promise<void>;
|
|
353
|
+
private registerWorkflowSchedule;
|
|
354
|
+
private unscheduleWorkflow;
|
|
324
355
|
stop(): Promise<void>;
|
|
325
356
|
registerWorkflow(definition: WorkflowDefinition<InputParameters>): Promise<WorkflowEngine>;
|
|
326
357
|
unregisterWorkflow(workflowId: string): Promise<WorkflowEngine>;
|
|
@@ -373,6 +404,15 @@ declare class WorkflowEngine {
|
|
|
373
404
|
exclusiveLock?: boolean;
|
|
374
405
|
db?: Db;
|
|
375
406
|
}): Promise<WorkflowRun>;
|
|
407
|
+
/**
|
|
408
|
+
* Fetch the most recently created run for a workflow, optionally scoped to a
|
|
409
|
+
* `resourceId`. Useful for cron-style incremental syncs where the next run
|
|
410
|
+
* needs the previous run's completion timestamp as a cursor.
|
|
411
|
+
*/
|
|
412
|
+
getWorkflowLastRun({ workflowId, resourceId }: {
|
|
413
|
+
workflowId: string;
|
|
414
|
+
resourceId?: string;
|
|
415
|
+
}): Promise<WorkflowRun | null>;
|
|
376
416
|
updateRun({ runId, resourceId, data, expectedStatuses }: {
|
|
377
417
|
runId: string;
|
|
378
418
|
resourceId?: string;
|
|
@@ -452,4 +492,14 @@ declare class WorkflowEngineError extends Error {
|
|
|
452
492
|
declare class WorkflowRunNotFoundError extends WorkflowEngineError {
|
|
453
493
|
constructor(runId?: string, workflowId?: string);
|
|
454
494
|
}
|
|
455
|
-
|
|
495
|
+
import { AttributeValue, Tracer } from "@opentelemetry/api";
|
|
496
|
+
type OtelPluginOptions = {
|
|
497
|
+
/** Tracer to use. Defaults to `trace.getTracer('pg-workflows')`. */
|
|
498
|
+
tracer?: Tracer;
|
|
499
|
+
/** Prefix for all span names. Defaults to `pg_workflows`. */
|
|
500
|
+
spanNamePrefix?: string;
|
|
501
|
+
/** Extra attributes merged onto the workflow.run span. */
|
|
502
|
+
attributes?: (context: WorkflowContext) => Record<string, AttributeValue>;
|
|
503
|
+
};
|
|
504
|
+
declare function otelPlugin(options?: OtelPluginOptions): WorkflowPlugin<StepBaseContext, object>;
|
|
505
|
+
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 };
|
package/dist/index.js
CHANGED
|
@@ -8,19 +8,21 @@ import {
|
|
|
8
8
|
WorkflowRunNotFoundError,
|
|
9
9
|
WorkflowStatus,
|
|
10
10
|
createWorkflowRef,
|
|
11
|
+
getWorkflowLastRun,
|
|
11
12
|
getWorkflowRun,
|
|
12
13
|
getWorkflowRuns,
|
|
13
14
|
insertWorkflowRun,
|
|
14
15
|
invokeChildWorkflowTimelineKey,
|
|
15
16
|
isInvokeChildWorkflowTimelineEntry,
|
|
16
17
|
runMigrations,
|
|
18
|
+
scheduleQueueNameFor,
|
|
17
19
|
updateWorkflowRun,
|
|
18
20
|
validateResourceId,
|
|
19
21
|
validateWorkflowId,
|
|
20
22
|
waitForTimelineKey,
|
|
21
23
|
withPostgresTransaction,
|
|
22
24
|
workflow
|
|
23
|
-
} from "./shared/chunk-
|
|
25
|
+
} from "./shared/chunk-5xswmve7.js";
|
|
24
26
|
// src/engine.ts
|
|
25
27
|
import { merge } from "es-toolkit";
|
|
26
28
|
import pg from "pg";
|
|
@@ -122,6 +124,56 @@ function parseDuration(duration) {
|
|
|
122
124
|
return ms;
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
// src/schedule.ts
|
|
128
|
+
import { CronExpressionParser } from "cron-parser";
|
|
129
|
+
var CRON_TOKEN = /^[0-9*/,?\-LW#]+$/;
|
|
130
|
+
function looksLikeCronString(value) {
|
|
131
|
+
const tokens = value.trim().split(/\s+/);
|
|
132
|
+
if (tokens.length !== 5 && tokens.length !== 6)
|
|
133
|
+
return false;
|
|
134
|
+
return tokens.every((t) => CRON_TOKEN.test(t));
|
|
135
|
+
}
|
|
136
|
+
function validateCronExpression(expression, timezone) {
|
|
137
|
+
try {
|
|
138
|
+
CronExpressionParser.parse(expression, { tz: timezone });
|
|
139
|
+
} catch (e) {
|
|
140
|
+
throw new WorkflowEngineError(`Invalid cron expression "${expression}" (timezone: ${timezone}): ${e instanceof Error ? e.message : String(e)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function durationMsToCron(ms, original) {
|
|
144
|
+
if (ms < MS_PER_MINUTE) {
|
|
145
|
+
throw new WorkflowEngineError(`Schedule interval must be at least 1 minute; got ${ms}ms from ${JSON.stringify(original)}`);
|
|
146
|
+
}
|
|
147
|
+
if (ms % MS_PER_DAY === 0) {
|
|
148
|
+
const days = ms / MS_PER_DAY;
|
|
149
|
+
if (days === 1)
|
|
150
|
+
return "0 0 * * *";
|
|
151
|
+
throw cronStepError(original, `${days} days`);
|
|
152
|
+
}
|
|
153
|
+
if (ms % MS_PER_HOUR === 0) {
|
|
154
|
+
const hours = ms / MS_PER_HOUR;
|
|
155
|
+
if (24 % hours === 0)
|
|
156
|
+
return `0 */${hours} * * *`;
|
|
157
|
+
throw cronStepError(original, `${hours} hours`);
|
|
158
|
+
}
|
|
159
|
+
const minutes = ms / MS_PER_MINUTE;
|
|
160
|
+
if (Number.isInteger(minutes) && 60 % minutes === 0)
|
|
161
|
+
return `*/${minutes} * * * *`;
|
|
162
|
+
throw cronStepError(original, `${minutes} minutes`);
|
|
163
|
+
}
|
|
164
|
+
function cronStepError(original, label) {
|
|
165
|
+
return new WorkflowEngineError(`Schedule interval ${JSON.stringify(original)} (${label}) doesn't map cleanly to a recurring cron expression. Use a value that divides 60 minutes, 24 hours, or 1 day — or pass an explicit cron string.`);
|
|
166
|
+
}
|
|
167
|
+
function resolveSchedule(schedule, timezone) {
|
|
168
|
+
const tz = timezone ?? "UTC";
|
|
169
|
+
if (typeof schedule === "string" && looksLikeCronString(schedule)) {
|
|
170
|
+
validateCronExpression(schedule, tz);
|
|
171
|
+
return { cron: schedule, timezone: tz };
|
|
172
|
+
}
|
|
173
|
+
const ms = parseDuration(schedule);
|
|
174
|
+
return { cron: durationMsToCron(ms, schedule), timezone: tz };
|
|
175
|
+
}
|
|
176
|
+
|
|
125
177
|
// src/engine.ts
|
|
126
178
|
var LOG_PREFIX = "[WorkflowEngine]";
|
|
127
179
|
var StepTypeToIcon = {
|
|
@@ -208,10 +260,41 @@ class WorkflowEngine {
|
|
|
208
260
|
await this.boss.work(WORKFLOW_RUN_DLQ_QUEUE_NAME, { pollingIntervalSeconds: 0.5, batchSize: 1 }, (jobs) => this.handleWorkflowRunDlq(jobs));
|
|
209
261
|
this.logger.log(`Worker started for queue ${WORKFLOW_RUN_DLQ_QUEUE_NAME}`);
|
|
210
262
|
}
|
|
263
|
+
if (asEngine) {
|
|
264
|
+
const scheduled = Array.from(this.workflows.values()).flatMap((wf) => wf.schedule == null ? [] : [{ id: wf.id, resolved: resolveSchedule(wf.schedule, wf.timezone) }]);
|
|
265
|
+
await Promise.allSettled(scheduled.map(({ id, resolved }) => this.registerWorkflowSchedule(id, resolved).catch((error) => {
|
|
266
|
+
this.logger.error(`Failed to register schedule for "${id}", skipping`, error instanceof Error ? error : new Error(String(error)), { workflowId: id });
|
|
267
|
+
})));
|
|
268
|
+
}
|
|
211
269
|
this._started = true;
|
|
212
270
|
this.logger.log("Workflow engine started!");
|
|
213
271
|
}
|
|
272
|
+
async registerWorkflowSchedule(workflowId, resolvedSchedule) {
|
|
273
|
+
const scheduleQueueName = scheduleQueueNameFor(workflowId);
|
|
274
|
+
await this.boss.createQueue(scheduleQueueName);
|
|
275
|
+
await this.boss.schedule(scheduleQueueName, resolvedSchedule.cron, null, {
|
|
276
|
+
tz: resolvedSchedule.timezone
|
|
277
|
+
});
|
|
278
|
+
await this.boss.work(scheduleQueueName, { batchSize: 1, includeMetadata: true }, async (jobs) => {
|
|
279
|
+
const scheduledAt = jobs[0]?.startAfter ?? new Date;
|
|
280
|
+
try {
|
|
281
|
+
await this.createWorkflowRun({ workflowId, input: {}, scheduledAt });
|
|
282
|
+
} catch (error) {
|
|
283
|
+
this.logger.error(`Schedule fire failed to start a run for workflow "${workflowId}"`, error instanceof Error ? error : new Error(String(error)), { workflowId });
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
this.logger.log(`Schedule registered for workflow "${workflowId}": ${resolvedSchedule.cron} (${resolvedSchedule.timezone})`, { workflowId });
|
|
288
|
+
}
|
|
289
|
+
async unscheduleWorkflow(workflowId) {
|
|
290
|
+
try {
|
|
291
|
+
await this.boss.unschedule(scheduleQueueNameFor(workflowId));
|
|
292
|
+
} catch (error) {
|
|
293
|
+
this.logger.error(`Failed to unschedule "${workflowId}"`, error instanceof Error ? error : new Error(String(error)), { workflowId });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
214
296
|
async stop() {
|
|
297
|
+
await Promise.allSettled(Array.from(this.workflows.values()).filter((wf) => wf.schedule != null).map((wf) => this.unscheduleWorkflow(wf.id)));
|
|
215
298
|
await this.boss.stop();
|
|
216
299
|
if (this._ownsPool) {
|
|
217
300
|
await this.pool.end();
|
|
@@ -224,10 +307,14 @@ class WorkflowEngine {
|
|
|
224
307
|
throw new WorkflowEngineError(`Workflow ${definition.id} is already registered`, definition.id);
|
|
225
308
|
}
|
|
226
309
|
const { steps } = parseWorkflowHandler(definition.handler);
|
|
310
|
+
const resolvedSchedule = definition.schedule ? resolveSchedule(definition.schedule, definition.timezone) : undefined;
|
|
227
311
|
this.workflows.set(definition.id, {
|
|
228
312
|
...definition,
|
|
229
313
|
steps
|
|
230
314
|
});
|
|
315
|
+
if (this._started && resolvedSchedule) {
|
|
316
|
+
await this.registerWorkflowSchedule(definition.id, resolvedSchedule);
|
|
317
|
+
}
|
|
231
318
|
this.logger.log(`Registered workflow "${definition.id}" with steps:`);
|
|
232
319
|
for (const step of steps.values()) {
|
|
233
320
|
const tags = [];
|
|
@@ -242,10 +329,17 @@ class WorkflowEngine {
|
|
|
242
329
|
return this;
|
|
243
330
|
}
|
|
244
331
|
async unregisterWorkflow(workflowId) {
|
|
332
|
+
const existing = this.workflows.get(workflowId);
|
|
333
|
+
if (existing?.schedule != null && this._started) {
|
|
334
|
+
await this.unscheduleWorkflow(workflowId);
|
|
335
|
+
}
|
|
245
336
|
this.workflows.delete(workflowId);
|
|
246
337
|
return this;
|
|
247
338
|
}
|
|
248
339
|
async unregisterAllWorkflows() {
|
|
340
|
+
if (this._started) {
|
|
341
|
+
await Promise.allSettled(Array.from(this.workflows.values()).filter((wf) => wf.schedule != null).map((wf) => this.unscheduleWorkflow(wf.id)));
|
|
342
|
+
}
|
|
249
343
|
this.workflows.clear();
|
|
250
344
|
return this;
|
|
251
345
|
}
|
|
@@ -295,6 +389,7 @@ class WorkflowEngine {
|
|
|
295
389
|
parentRunId,
|
|
296
390
|
parentStepId,
|
|
297
391
|
parentResourceId,
|
|
392
|
+
scheduledAt,
|
|
298
393
|
enqueue = true,
|
|
299
394
|
db
|
|
300
395
|
}) {
|
|
@@ -328,7 +423,8 @@ class WorkflowEngine {
|
|
|
328
423
|
idempotencyKey,
|
|
329
424
|
parentRunId,
|
|
330
425
|
parentStepId,
|
|
331
|
-
parentResourceId
|
|
426
|
+
parentResourceId,
|
|
427
|
+
scheduledAt
|
|
332
428
|
}, targetDb);
|
|
333
429
|
const insertAndEnqueue = async (targetDb) => {
|
|
334
430
|
const result = await insertRun(targetDb);
|
|
@@ -511,6 +607,14 @@ class WorkflowEngine {
|
|
|
511
607
|
}
|
|
512
608
|
return run;
|
|
513
609
|
}
|
|
610
|
+
async getWorkflowLastRun({
|
|
611
|
+
workflowId,
|
|
612
|
+
resourceId
|
|
613
|
+
}) {
|
|
614
|
+
validateWorkflowId(workflowId);
|
|
615
|
+
validateResourceId(resourceId);
|
|
616
|
+
return getWorkflowLastRun({ workflowId, resourceId }, this.db);
|
|
617
|
+
}
|
|
514
618
|
async updateRun({
|
|
515
619
|
runId,
|
|
516
620
|
resourceId,
|
|
@@ -732,21 +836,33 @@ class WorkflowEngine {
|
|
|
732
836
|
};
|
|
733
837
|
let step = { ...baseStep };
|
|
734
838
|
const plugins = workflow2.plugins ?? [];
|
|
735
|
-
for (const plugin of plugins) {
|
|
736
|
-
const extra = plugin.methods(step);
|
|
737
|
-
step = { ...step, ...extra };
|
|
738
|
-
}
|
|
739
839
|
const context = {
|
|
740
840
|
input: run.input,
|
|
741
841
|
workflowId: run.workflowId,
|
|
742
842
|
runId: run.id,
|
|
843
|
+
resourceId: run.resourceId ?? undefined,
|
|
844
|
+
attempt: run.retryCount,
|
|
743
845
|
get timeline() {
|
|
744
846
|
return run?.timeline ?? {};
|
|
745
847
|
},
|
|
746
848
|
logger: this.logger,
|
|
747
|
-
step
|
|
849
|
+
step,
|
|
850
|
+
schedule: run.scheduledAt ? { timestamp: run.scheduledAt } : undefined
|
|
748
851
|
};
|
|
749
|
-
const
|
|
852
|
+
for (const plugin of plugins) {
|
|
853
|
+
const extra = plugin.methods(step, context);
|
|
854
|
+
step = { ...step, ...extra };
|
|
855
|
+
context.step = step;
|
|
856
|
+
}
|
|
857
|
+
let next = () => workflow2.handler(context);
|
|
858
|
+
for (const plugin of [...plugins].reverse()) {
|
|
859
|
+
if (plugin.wrap) {
|
|
860
|
+
const inner = next;
|
|
861
|
+
const wrap = plugin.wrap;
|
|
862
|
+
next = () => wrap(context, inner);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
const result = await next();
|
|
750
866
|
run = await this.getRun({ runId, resourceId: scopedResourceId });
|
|
751
867
|
const isLastParsedStep = run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id;
|
|
752
868
|
const hasPluginSteps = (workflow2.plugins?.length ?? 0) > 0;
|
|
@@ -1279,8 +1395,183 @@ ${error.stack}` : String(error)
|
|
|
1279
1395
|
}, this.db);
|
|
1280
1396
|
}
|
|
1281
1397
|
}
|
|
1398
|
+
// src/plugins/otel.ts
|
|
1399
|
+
import {
|
|
1400
|
+
context as otelContext,
|
|
1401
|
+
SpanStatusCode,
|
|
1402
|
+
trace
|
|
1403
|
+
} from "@opentelemetry/api";
|
|
1404
|
+
var DEFAULT_PREFIX = "pg_workflows";
|
|
1405
|
+
function isCachedHit(timeline, stepId, kind) {
|
|
1406
|
+
const entry = timeline[stepId];
|
|
1407
|
+
if (entry && typeof entry === "object" && "output" in entry && entry.output !== undefined) {
|
|
1408
|
+
return true;
|
|
1409
|
+
}
|
|
1410
|
+
if (kind === "invokeChildWorkflow" && timeline[invokeChildWorkflowTimelineKey(stepId)]) {
|
|
1411
|
+
return true;
|
|
1412
|
+
}
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
function otelPlugin(options = {}) {
|
|
1416
|
+
const tracer = options.tracer ?? trace.getTracer("pg-workflows");
|
|
1417
|
+
const prefix = options.spanNamePrefix ?? DEFAULT_PREFIX;
|
|
1418
|
+
const extraAttrs = options.attributes;
|
|
1419
|
+
return {
|
|
1420
|
+
name: "opentelemetry",
|
|
1421
|
+
methods: (step, context) => {
|
|
1422
|
+
const wrapVoidish = (kind, base) => {
|
|
1423
|
+
return async (stepId, ...args) => {
|
|
1424
|
+
if (isCachedHit(context.timeline, stepId, kind)) {
|
|
1425
|
+
return base(stepId, ...args);
|
|
1426
|
+
}
|
|
1427
|
+
const capturedCtx = otelContext.active();
|
|
1428
|
+
const startTime = new Date;
|
|
1429
|
+
let result;
|
|
1430
|
+
let originalErr;
|
|
1431
|
+
let thrownError;
|
|
1432
|
+
try {
|
|
1433
|
+
result = await base(stepId, ...args);
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
originalErr = err;
|
|
1436
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1437
|
+
}
|
|
1438
|
+
const span = tracer.startSpan(`${prefix}.step.${kind}`, {
|
|
1439
|
+
startTime,
|
|
1440
|
+
attributes: { "step.id": stepId, "step.type": kind }
|
|
1441
|
+
}, capturedCtx);
|
|
1442
|
+
if (thrownError) {
|
|
1443
|
+
span.recordException(thrownError);
|
|
1444
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1445
|
+
span.end();
|
|
1446
|
+
throw originalErr;
|
|
1447
|
+
}
|
|
1448
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1449
|
+
span.end();
|
|
1450
|
+
return result;
|
|
1451
|
+
};
|
|
1452
|
+
};
|
|
1453
|
+
return {
|
|
1454
|
+
run: async (stepId, handler) => {
|
|
1455
|
+
if (isCachedHit(context.timeline, stepId, "run")) {
|
|
1456
|
+
return step.run(stepId, handler);
|
|
1457
|
+
}
|
|
1458
|
+
const capturedCtx = otelContext.active();
|
|
1459
|
+
const startTime = new Date;
|
|
1460
|
+
let result;
|
|
1461
|
+
let originalErr;
|
|
1462
|
+
let thrownError;
|
|
1463
|
+
try {
|
|
1464
|
+
result = await step.run(stepId, handler);
|
|
1465
|
+
} catch (err) {
|
|
1466
|
+
originalErr = err;
|
|
1467
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1468
|
+
}
|
|
1469
|
+
if (result === undefined && !thrownError) {
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
const span = tracer.startSpan(`${prefix}.step.run`, {
|
|
1473
|
+
startTime,
|
|
1474
|
+
attributes: { "step.id": stepId, "step.type": "run" }
|
|
1475
|
+
}, capturedCtx);
|
|
1476
|
+
if (thrownError) {
|
|
1477
|
+
span.recordException(thrownError);
|
|
1478
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1479
|
+
span.end();
|
|
1480
|
+
throw originalErr;
|
|
1481
|
+
}
|
|
1482
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1483
|
+
span.end();
|
|
1484
|
+
return result;
|
|
1485
|
+
},
|
|
1486
|
+
waitFor: wrapVoidish("waitFor", step.waitFor),
|
|
1487
|
+
delay: wrapVoidish("delay", step.delay),
|
|
1488
|
+
sleep: wrapVoidish("delay", step.delay),
|
|
1489
|
+
waitUntil: wrapVoidish("waitUntil", step.waitUntil),
|
|
1490
|
+
pause: wrapVoidish("pause", step.pause),
|
|
1491
|
+
poll: async (stepId, conditionFn, pollOptions) => {
|
|
1492
|
+
const capturedCtx = otelContext.active();
|
|
1493
|
+
const startTime = new Date;
|
|
1494
|
+
let result;
|
|
1495
|
+
let originalErr;
|
|
1496
|
+
let thrownError;
|
|
1497
|
+
try {
|
|
1498
|
+
result = await step.poll(stepId, conditionFn, pollOptions);
|
|
1499
|
+
} catch (err) {
|
|
1500
|
+
originalErr = err;
|
|
1501
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1502
|
+
}
|
|
1503
|
+
const span = tracer.startSpan(`${prefix}.step.poll`, {
|
|
1504
|
+
startTime,
|
|
1505
|
+
attributes: { "step.id": stepId, "step.type": "poll" }
|
|
1506
|
+
}, capturedCtx);
|
|
1507
|
+
if (thrownError) {
|
|
1508
|
+
span.recordException(thrownError);
|
|
1509
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1510
|
+
span.end();
|
|
1511
|
+
throw originalErr;
|
|
1512
|
+
}
|
|
1513
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1514
|
+
span.end();
|
|
1515
|
+
return result;
|
|
1516
|
+
},
|
|
1517
|
+
invokeChildWorkflow: async (stepId, refOrParams, inputArg, optionsArg) => {
|
|
1518
|
+
if (isCachedHit(context.timeline, stepId, "invokeChildWorkflow")) {
|
|
1519
|
+
return step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
|
|
1520
|
+
}
|
|
1521
|
+
const capturedCtx = otelContext.active();
|
|
1522
|
+
const startTime = new Date;
|
|
1523
|
+
let result;
|
|
1524
|
+
let originalErr;
|
|
1525
|
+
let thrownError;
|
|
1526
|
+
try {
|
|
1527
|
+
result = await step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
|
|
1528
|
+
} catch (err) {
|
|
1529
|
+
originalErr = err;
|
|
1530
|
+
thrownError = err instanceof Error ? err : new Error(String(err));
|
|
1531
|
+
}
|
|
1532
|
+
const span = tracer.startSpan(`${prefix}.step.invokeChildWorkflow`, {
|
|
1533
|
+
startTime,
|
|
1534
|
+
attributes: { "step.id": stepId, "step.type": "invokeChildWorkflow" }
|
|
1535
|
+
}, capturedCtx);
|
|
1536
|
+
if (thrownError) {
|
|
1537
|
+
span.recordException(thrownError);
|
|
1538
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: thrownError.message });
|
|
1539
|
+
span.end();
|
|
1540
|
+
throw originalErr;
|
|
1541
|
+
}
|
|
1542
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1543
|
+
span.end();
|
|
1544
|
+
return result;
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
},
|
|
1548
|
+
wrap: (context, next) => tracer.startActiveSpan(`${prefix}.workflow.run`, {
|
|
1549
|
+
attributes: {
|
|
1550
|
+
"workflow.id": context.workflowId,
|
|
1551
|
+
"workflow.run_id": context.runId,
|
|
1552
|
+
"workflow.attempt": context.attempt,
|
|
1553
|
+
...context.resourceId ? { "workflow.resource_id": context.resourceId } : {},
|
|
1554
|
+
...extraAttrs ? extraAttrs(context) : {}
|
|
1555
|
+
}
|
|
1556
|
+
}, async (span) => {
|
|
1557
|
+
try {
|
|
1558
|
+
const result = await next();
|
|
1559
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1560
|
+
return result;
|
|
1561
|
+
} catch (err) {
|
|
1562
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
1563
|
+
span.recordException(error);
|
|
1564
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
1565
|
+
throw err;
|
|
1566
|
+
} finally {
|
|
1567
|
+
span.end();
|
|
1568
|
+
}
|
|
1569
|
+
})
|
|
1570
|
+
};
|
|
1571
|
+
}
|
|
1282
1572
|
export {
|
|
1283
1573
|
workflow,
|
|
1574
|
+
otelPlugin,
|
|
1284
1575
|
createWorkflowRef,
|
|
1285
1576
|
WorkflowStatus,
|
|
1286
1577
|
WorkflowRunNotFoundError,
|
|
@@ -1289,5 +1580,5 @@ export {
|
|
|
1289
1580
|
WorkflowClient
|
|
1290
1581
|
};
|
|
1291
1582
|
|
|
1292
|
-
//# debugId=
|
|
1583
|
+
//# debugId=5BC543336A07527964756E2164756E21
|
|
1293
1584
|
//# sourceMappingURL=index.js.map
|