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/dist/index.cjs CHANGED
@@ -65,16 +65,13 @@ var __export = (target, all) => {
65
65
  var exports_src = {};
66
66
  __export(exports_src, {
67
67
  workflow: () => workflow,
68
- validateWorkflowId: () => validateWorkflowId,
69
- validateResourceId: () => validateResourceId,
70
- parseDuration: () => parseDuration,
68
+ otelPlugin: () => otelPlugin,
71
69
  createWorkflowRef: () => createWorkflowRef,
72
70
  WorkflowStatus: () => WorkflowStatus,
73
71
  WorkflowRunNotFoundError: () => WorkflowRunNotFoundError,
74
72
  WorkflowEngineError: () => WorkflowEngineError,
75
73
  WorkflowEngine: () => WorkflowEngine,
76
- WorkflowClient: () => WorkflowClient,
77
- StepType: () => StepType
74
+ WorkflowClient: () => WorkflowClient
78
75
  });
79
76
  module.exports = __toCommonJS(exports_src);
80
77
 
@@ -543,16 +540,6 @@ var WorkflowStatus;
543
540
  WorkflowStatus2["FAILED"] = "failed";
544
541
  WorkflowStatus2["CANCELLED"] = "cancelled";
545
542
  })(WorkflowStatus ||= {});
546
- var StepType;
547
- ((StepType2) => {
548
- StepType2["PAUSE"] = "pause";
549
- StepType2["RUN"] = "run";
550
- StepType2["WAIT_FOR"] = "waitFor";
551
- StepType2["WAIT_UNTIL"] = "waitUntil";
552
- StepType2["DELAY"] = "delay";
553
- StepType2["POLL"] = "poll";
554
- StepType2["INVOKE_CHILD_WORKFLOW"] = "invokeChildWorkflow";
555
- })(StepType ||= {});
556
543
 
557
544
  // src/client.ts
558
545
  var LOG_PREFIX = "[WorkflowClient]";
@@ -892,31 +879,6 @@ function createWorkflowFactory(plugins = []) {
892
879
  return factory;
893
880
  }
894
881
  var workflow = createWorkflowFactory();
895
- // src/duration.ts
896
- var import_parse_duration = __toESM(require("parse-duration"));
897
- var MS_PER_SECOND = 1000;
898
- var MS_PER_MINUTE = 60 * MS_PER_SECOND;
899
- var MS_PER_HOUR = 60 * MS_PER_MINUTE;
900
- var MS_PER_DAY = 24 * MS_PER_HOUR;
901
- var MS_PER_WEEK = 7 * MS_PER_DAY;
902
- function parseDuration(duration) {
903
- if (typeof duration === "string") {
904
- if (duration.trim() === "") {
905
- throw new WorkflowEngineError("Invalid duration: empty string");
906
- }
907
- const ms2 = import_parse_duration.default(duration);
908
- if (ms2 == null || ms2 <= 0) {
909
- throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
910
- }
911
- return ms2;
912
- }
913
- const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
914
- const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
915
- if (ms <= 0) {
916
- throw new WorkflowEngineError("Invalid duration: must be a positive value");
917
- }
918
- return ms;
919
- }
920
882
  // src/engine.ts
921
883
  var import_es_toolkit2 = require("es-toolkit");
922
884
  var import_pg2 = __toESM(require("pg"));
@@ -992,6 +954,32 @@ function parseWorkflowHandler(handler) {
992
954
  return { steps: Array.from(steps.values()) };
993
955
  }
994
956
 
957
+ // src/duration.ts
958
+ var import_parse_duration = __toESM(require("parse-duration"));
959
+ var MS_PER_SECOND = 1000;
960
+ var MS_PER_MINUTE = 60 * MS_PER_SECOND;
961
+ var MS_PER_HOUR = 60 * MS_PER_MINUTE;
962
+ var MS_PER_DAY = 24 * MS_PER_HOUR;
963
+ var MS_PER_WEEK = 7 * MS_PER_DAY;
964
+ function parseDuration(duration) {
965
+ if (typeof duration === "string") {
966
+ if (duration.trim() === "") {
967
+ throw new WorkflowEngineError("Invalid duration: empty string");
968
+ }
969
+ const ms2 = import_parse_duration.default(duration);
970
+ if (ms2 == null || ms2 <= 0) {
971
+ throw new WorkflowEngineError(`Invalid duration: "${duration}"`);
972
+ }
973
+ return ms2;
974
+ }
975
+ const { weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0 } = duration;
976
+ const ms = weeks * MS_PER_WEEK + days * MS_PER_DAY + hours * MS_PER_HOUR + minutes * MS_PER_MINUTE + seconds * MS_PER_SECOND;
977
+ if (ms <= 0) {
978
+ throw new WorkflowEngineError("Invalid duration: must be a positive value");
979
+ }
980
+ return ms;
981
+ }
982
+
995
983
  // src/engine.ts
996
984
  var LOG_PREFIX2 = "[WorkflowEngine]";
997
985
  var StepTypeToIcon = {
@@ -1141,7 +1129,7 @@ class WorkflowEngine {
1141
1129
  async startWorkflow(refOrParams, inputArg, optionsArg) {
1142
1130
  const { workflowId, input, resourceId, idempotencyKey, options } = this.resolveWorkflowRunParameters(refOrParams, inputArg, optionsArg);
1143
1131
  if (!this._started) {
1144
- await this.start(false, { batchSize: options?.batchSize ?? 1 });
1132
+ await this.start(false);
1145
1133
  }
1146
1134
  const { run } = await this.createWorkflowRun({
1147
1135
  workflowId,
@@ -1602,19 +1590,32 @@ class WorkflowEngine {
1602
1590
  };
1603
1591
  let step = { ...baseStep };
1604
1592
  const plugins = workflow2.plugins ?? [];
1605
- for (const plugin of plugins) {
1606
- const extra = plugin.methods(step);
1607
- step = { ...step, ...extra };
1608
- }
1609
1593
  const context = {
1610
1594
  input: run.input,
1611
1595
  workflowId: run.workflowId,
1612
1596
  runId: run.id,
1613
- timeline: run.timeline,
1597
+ resourceId: run.resourceId ?? undefined,
1598
+ attempt: run.retryCount,
1599
+ get timeline() {
1600
+ return run?.timeline ?? {};
1601
+ },
1614
1602
  logger: this.logger,
1615
1603
  step
1616
1604
  };
1617
- const result = await workflow2.handler(context);
1605
+ for (const plugin of plugins) {
1606
+ const extra = plugin.methods(step, context);
1607
+ step = { ...step, ...extra };
1608
+ context.step = step;
1609
+ }
1610
+ let next = () => workflow2.handler(context);
1611
+ for (const plugin of [...plugins].reverse()) {
1612
+ if (plugin.wrap) {
1613
+ const inner = next;
1614
+ const wrap = plugin.wrap;
1615
+ next = () => wrap(context, inner);
1616
+ }
1617
+ }
1618
+ const result = await next();
1618
1619
  run = await this.getRun({ runId, resourceId: scopedResourceId });
1619
1620
  const isLastParsedStep = run.currentStepId === workflow2.steps[workflow2.steps.length - 1]?.id;
1620
1621
  const hasPluginSteps = (workflow2.plugins?.length ?? 0) > 0;
@@ -1907,7 +1908,7 @@ class WorkflowEngine {
1907
1908
  if (output === undefined) {
1908
1909
  output = {};
1909
1910
  }
1910
- run = await this.updateRun({
1911
+ const updated = await this.updateRun({
1911
1912
  runId: run.id,
1912
1913
  resourceId: run.resourceId ?? undefined,
1913
1914
  data: {
@@ -1919,6 +1920,7 @@ class WorkflowEngine {
1919
1920
  })
1920
1921
  }
1921
1922
  }, { db });
1923
+ Object.assign(run, updated);
1922
1924
  return output;
1923
1925
  } catch (error) {
1924
1926
  this.logger.error(`Step ${stepId} failed:`, error, {
@@ -2146,6 +2148,176 @@ ${error.stack}` : String(error)
2146
2148
  }, this.db);
2147
2149
  }
2148
2150
  }
2151
+ // src/plugins/otel.ts
2152
+ var import_api = require("@opentelemetry/api");
2153
+ var DEFAULT_PREFIX = "pg_workflows";
2154
+ function isCachedHit(timeline, stepId, kind) {
2155
+ const entry = timeline[stepId];
2156
+ if (entry && typeof entry === "object" && "output" in entry && entry.output !== undefined) {
2157
+ return true;
2158
+ }
2159
+ if (kind === "invokeChildWorkflow" && timeline[invokeChildWorkflowTimelineKey(stepId)]) {
2160
+ return true;
2161
+ }
2162
+ return false;
2163
+ }
2164
+ function otelPlugin(options = {}) {
2165
+ const tracer = options.tracer ?? import_api.trace.getTracer("pg-workflows");
2166
+ const prefix = options.spanNamePrefix ?? DEFAULT_PREFIX;
2167
+ const extraAttrs = options.attributes;
2168
+ return {
2169
+ name: "opentelemetry",
2170
+ methods: (step, context) => {
2171
+ const wrapVoidish = (kind, base) => {
2172
+ return async (stepId, ...args) => {
2173
+ if (isCachedHit(context.timeline, stepId, kind)) {
2174
+ return base(stepId, ...args);
2175
+ }
2176
+ const capturedCtx = import_api.context.active();
2177
+ const startTime = new Date;
2178
+ let result;
2179
+ let originalErr;
2180
+ let thrownError;
2181
+ try {
2182
+ result = await base(stepId, ...args);
2183
+ } catch (err) {
2184
+ originalErr = err;
2185
+ thrownError = err instanceof Error ? err : new Error(String(err));
2186
+ }
2187
+ const span = tracer.startSpan(`${prefix}.step.${kind}`, {
2188
+ startTime,
2189
+ attributes: { "step.id": stepId, "step.type": kind }
2190
+ }, capturedCtx);
2191
+ if (thrownError) {
2192
+ span.recordException(thrownError);
2193
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: thrownError.message });
2194
+ span.end();
2195
+ throw originalErr;
2196
+ }
2197
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
2198
+ span.end();
2199
+ return result;
2200
+ };
2201
+ };
2202
+ return {
2203
+ run: async (stepId, handler) => {
2204
+ if (isCachedHit(context.timeline, stepId, "run")) {
2205
+ return step.run(stepId, handler);
2206
+ }
2207
+ const capturedCtx = import_api.context.active();
2208
+ const startTime = new Date;
2209
+ let result;
2210
+ let originalErr;
2211
+ let thrownError;
2212
+ try {
2213
+ result = await step.run(stepId, handler);
2214
+ } catch (err) {
2215
+ originalErr = err;
2216
+ thrownError = err instanceof Error ? err : new Error(String(err));
2217
+ }
2218
+ if (result === undefined && !thrownError) {
2219
+ return;
2220
+ }
2221
+ const span = tracer.startSpan(`${prefix}.step.run`, {
2222
+ startTime,
2223
+ attributes: { "step.id": stepId, "step.type": "run" }
2224
+ }, capturedCtx);
2225
+ if (thrownError) {
2226
+ span.recordException(thrownError);
2227
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: thrownError.message });
2228
+ span.end();
2229
+ throw originalErr;
2230
+ }
2231
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
2232
+ span.end();
2233
+ return result;
2234
+ },
2235
+ waitFor: wrapVoidish("waitFor", step.waitFor),
2236
+ delay: wrapVoidish("delay", step.delay),
2237
+ sleep: wrapVoidish("delay", step.delay),
2238
+ waitUntil: wrapVoidish("waitUntil", step.waitUntil),
2239
+ pause: wrapVoidish("pause", step.pause),
2240
+ poll: async (stepId, conditionFn, pollOptions) => {
2241
+ const capturedCtx = import_api.context.active();
2242
+ const startTime = new Date;
2243
+ let result;
2244
+ let originalErr;
2245
+ let thrownError;
2246
+ try {
2247
+ result = await step.poll(stepId, conditionFn, pollOptions);
2248
+ } catch (err) {
2249
+ originalErr = err;
2250
+ thrownError = err instanceof Error ? err : new Error(String(err));
2251
+ }
2252
+ const span = tracer.startSpan(`${prefix}.step.poll`, {
2253
+ startTime,
2254
+ attributes: { "step.id": stepId, "step.type": "poll" }
2255
+ }, capturedCtx);
2256
+ if (thrownError) {
2257
+ span.recordException(thrownError);
2258
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: thrownError.message });
2259
+ span.end();
2260
+ throw originalErr;
2261
+ }
2262
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
2263
+ span.end();
2264
+ return result;
2265
+ },
2266
+ invokeChildWorkflow: async (stepId, refOrParams, inputArg, optionsArg) => {
2267
+ if (isCachedHit(context.timeline, stepId, "invokeChildWorkflow")) {
2268
+ return step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
2269
+ }
2270
+ const capturedCtx = import_api.context.active();
2271
+ const startTime = new Date;
2272
+ let result;
2273
+ let originalErr;
2274
+ let thrownError;
2275
+ try {
2276
+ result = await step.invokeChildWorkflow(stepId, refOrParams, inputArg, optionsArg);
2277
+ } catch (err) {
2278
+ originalErr = err;
2279
+ thrownError = err instanceof Error ? err : new Error(String(err));
2280
+ }
2281
+ const span = tracer.startSpan(`${prefix}.step.invokeChildWorkflow`, {
2282
+ startTime,
2283
+ attributes: { "step.id": stepId, "step.type": "invokeChildWorkflow" }
2284
+ }, capturedCtx);
2285
+ if (thrownError) {
2286
+ span.recordException(thrownError);
2287
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: thrownError.message });
2288
+ span.end();
2289
+ throw originalErr;
2290
+ }
2291
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
2292
+ span.end();
2293
+ return result;
2294
+ }
2295
+ };
2296
+ },
2297
+ wrap: (context, next) => tracer.startActiveSpan(`${prefix}.workflow.run`, {
2298
+ attributes: {
2299
+ "workflow.id": context.workflowId,
2300
+ "workflow.run_id": context.runId,
2301
+ "workflow.attempt": context.attempt,
2302
+ ...context.resourceId ? { "workflow.resource_id": context.resourceId } : {},
2303
+ ...extraAttrs ? extraAttrs(context) : {}
2304
+ }
2305
+ }, async (span) => {
2306
+ try {
2307
+ const result = await next();
2308
+ span.setStatus({ code: import_api.SpanStatusCode.OK });
2309
+ return result;
2310
+ } catch (err) {
2311
+ const error = err instanceof Error ? err : new Error(String(err));
2312
+ span.recordException(error);
2313
+ span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
2314
+ throw err;
2315
+ } finally {
2316
+ span.end();
2317
+ }
2318
+ })
2319
+ };
2320
+ }
2149
2321
 
2150
- //# debugId=8E8C82F1948B934364756E2164756E21
2322
+ //# debugId=BCF84547491115D464756E2164756E21
2151
2323
  //# sourceMappingURL=index.js.map
package/dist/index.d.cts CHANGED
@@ -33,7 +33,6 @@ type DurationObject = {
33
33
  seconds?: number;
34
34
  };
35
35
  type Duration = string | DurationObject;
36
- declare function parseDuration(duration: Duration): number;
37
36
  declare enum WorkflowStatus {
38
37
  PENDING = "pending",
39
38
  RUNNING = "running",
@@ -53,7 +52,7 @@ declare enum StepType {
53
52
  }
54
53
  type InputParameters = StandardSchemaV1;
55
54
  type InferInputParameters<P extends InputParameters> = StandardSchemaV1.InferOutput<P>;
56
- type WorkflowRunOptions = {
55
+ type StartWorkflowOptions = {
57
56
  resourceId?: string;
58
57
  timeout?: number;
59
58
  retries?: number;
@@ -107,13 +106,13 @@ type StepBaseContext = {
107
106
  <
108
107
  TInput extends InputParameters,
109
108
  TOutput = unknown
110
- >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: WorkflowRunOptions): Promise<TOutput>;
109
+ >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions): Promise<TOutput>;
111
110
  <TOutput = unknown>(stepId: string, params: {
112
111
  workflowId: string;
113
112
  input: unknown;
114
113
  resourceId?: string;
115
114
  idempotencyKey?: string;
116
- options?: WorkflowRunOptions;
115
+ options?: StartWorkflowOptions;
117
116
  }): Promise<TOutput>;
118
117
  };
119
118
  };
@@ -127,7 +126,13 @@ interface WorkflowPlugin<
127
126
  TStepExt = object
128
127
  > {
129
128
  name: string;
130
- methods: (step: TStepBase) => TStepExt;
129
+ methods: (step: TStepBase, context: WorkflowContext) => TStepExt;
130
+ /**
131
+ * Optional middleware around the workflow handler call. Composes in
132
+ * registration order — the first plugin passed to `.use()` wraps everything
133
+ * inside. Implementations MUST call `next()` exactly once.
134
+ */
135
+ wrap?: (context: WorkflowContext, next: () => Promise<unknown>) => Promise<unknown>;
131
136
  }
132
137
  type WorkflowContext<
133
138
  TInput extends InputParameters = InputParameters,
@@ -137,6 +142,10 @@ type WorkflowContext<
137
142
  step: TStep;
138
143
  workflowId: string;
139
144
  runId: string;
145
+ /** Tenant/scope identifier set when the run was started, if any. */
146
+ resourceId?: string;
147
+ /** Zero-based retry attempt number (= `run.retryCount`). */
148
+ attempt: number;
140
149
  timeline: Record<string, unknown>;
141
150
  logger: WorkflowLogger;
142
151
  };
@@ -197,14 +206,6 @@ interface WorkflowLogger {
197
206
  log(message: string): void;
198
207
  error(message: string, ...args: unknown[]): void;
199
208
  }
200
- type WorkflowInternalLoggerContext = {
201
- runId?: string;
202
- workflowId?: string;
203
- };
204
- interface WorkflowInternalLogger {
205
- log(message: string, context?: WorkflowInternalLoggerContext): void;
206
- error(message: string, error: Error, context?: WorkflowInternalLoggerContext): void;
207
- }
208
209
  type WorkflowClientOptions = {
209
210
  logger?: WorkflowLogger;
210
211
  /**
@@ -221,7 +222,6 @@ type WorkflowClientOptions = {
221
222
  connectionString: string;
222
223
  pool?: never;
223
224
  });
224
- type StartWorkflowOptions = WorkflowRunOptions;
225
225
  declare class WorkflowClient {
226
226
  private boss;
227
227
  private db;
@@ -306,9 +306,6 @@ declare function createWorkflowRef<
306
306
  declare const workflow: WorkflowFactory;
307
307
  import pg2 from "pg";
308
308
  import { Db, PgBoss as PgBoss2 } from "pg-boss";
309
- type StartWorkflowOptions2 = WorkflowRunOptions & {
310
- batchSize?: number;
311
- };
312
309
  type WorkflowEngineOptions = {
313
310
  workflows?: WorkflowDefinition[];
314
311
  logger?: WorkflowLogger;
@@ -339,13 +336,13 @@ declare class WorkflowEngine {
339
336
  unregisterWorkflow(workflowId: string): Promise<WorkflowEngine>;
340
337
  unregisterAllWorkflows(): Promise<WorkflowEngine>;
341
338
  private resolveWorkflowRunParameters;
342
- startWorkflow<TInput extends InputParameters>(ref: WorkflowRef<TInput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions2): Promise<WorkflowRun>;
339
+ startWorkflow<TInput extends InputParameters>(ref: WorkflowRef<TInput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions): Promise<WorkflowRun>;
343
340
  startWorkflow(params: {
344
341
  resourceId?: string;
345
342
  workflowId: string;
346
343
  input: unknown;
347
344
  idempotencyKey?: string;
348
- options?: StartWorkflowOptions2;
345
+ options?: StartWorkflowOptions;
349
346
  }): Promise<WorkflowRun>;
350
347
  private createWorkflowRun;
351
348
  private enqueueWorkflowRun;
@@ -455,8 +452,6 @@ declare class WorkflowEngine {
455
452
  }>;
456
453
  }
457
454
  import { StandardSchemaV1 as StandardSchemaV12 } from "@standard-schema/spec";
458
- declare function validateWorkflowId(workflowId: string): void;
459
- declare function validateResourceId(resourceId: string | undefined | null): void;
460
455
  declare class WorkflowEngineError extends Error {
461
456
  readonly workflowId?: string | undefined;
462
457
  readonly runId?: string | undefined;
@@ -467,4 +462,14 @@ declare class WorkflowEngineError extends Error {
467
462
  declare class WorkflowRunNotFoundError extends WorkflowEngineError {
468
463
  constructor(runId?: string, workflowId?: string);
469
464
  }
470
- export { workflow, validateWorkflowId, validateResourceId, parseDuration, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunOptions, WorkflowRunNotFoundError, WorkflowRef, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowInternalLoggerContext, WorkflowInternalLogger, WorkflowInternalDefinition, WorkflowFactory, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepType, StepInternalDefinition, StepBaseContext, StartWorkflowOptions, InputParameters, InferInputParameters, DurationObject, Duration };
465
+ import { AttributeValue, Tracer } from "@opentelemetry/api";
466
+ type OtelPluginOptions = {
467
+ /** Tracer to use. Defaults to `trace.getTracer('pg-workflows')`. */
468
+ tracer?: Tracer;
469
+ /** Prefix for all span names. Defaults to `pg_workflows`. */
470
+ spanNamePrefix?: string;
471
+ /** Extra attributes merged onto the workflow.run span. */
472
+ attributes?: (context: WorkflowContext) => Record<string, AttributeValue>;
473
+ };
474
+ declare function otelPlugin(options?: OtelPluginOptions): WorkflowPlugin<StepBaseContext, object>;
475
+ export { workflow, otelPlugin, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunNotFoundError, WorkflowRun, WorkflowRef, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepBaseContext, StartWorkflowOptions, OtelPluginOptions, InputParameters, InferInputParameters, Duration };
package/dist/index.d.ts CHANGED
@@ -33,7 +33,6 @@ type DurationObject = {
33
33
  seconds?: number;
34
34
  };
35
35
  type Duration = string | DurationObject;
36
- declare function parseDuration(duration: Duration): number;
37
36
  declare enum WorkflowStatus {
38
37
  PENDING = "pending",
39
38
  RUNNING = "running",
@@ -53,7 +52,7 @@ declare enum StepType {
53
52
  }
54
53
  type InputParameters = StandardSchemaV1;
55
54
  type InferInputParameters<P extends InputParameters> = StandardSchemaV1.InferOutput<P>;
56
- type WorkflowRunOptions = {
55
+ type StartWorkflowOptions = {
57
56
  resourceId?: string;
58
57
  timeout?: number;
59
58
  retries?: number;
@@ -107,13 +106,13 @@ type StepBaseContext = {
107
106
  <
108
107
  TInput extends InputParameters,
109
108
  TOutput = unknown
110
- >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: WorkflowRunOptions): Promise<TOutput>;
109
+ >(stepId: string, ref: WorkflowRef<TInput, TOutput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions): Promise<TOutput>;
111
110
  <TOutput = unknown>(stepId: string, params: {
112
111
  workflowId: string;
113
112
  input: unknown;
114
113
  resourceId?: string;
115
114
  idempotencyKey?: string;
116
- options?: WorkflowRunOptions;
115
+ options?: StartWorkflowOptions;
117
116
  }): Promise<TOutput>;
118
117
  };
119
118
  };
@@ -127,7 +126,13 @@ interface WorkflowPlugin<
127
126
  TStepExt = object
128
127
  > {
129
128
  name: string;
130
- methods: (step: TStepBase) => TStepExt;
129
+ methods: (step: TStepBase, context: WorkflowContext) => TStepExt;
130
+ /**
131
+ * Optional middleware around the workflow handler call. Composes in
132
+ * registration order — the first plugin passed to `.use()` wraps everything
133
+ * inside. Implementations MUST call `next()` exactly once.
134
+ */
135
+ wrap?: (context: WorkflowContext, next: () => Promise<unknown>) => Promise<unknown>;
131
136
  }
132
137
  type WorkflowContext<
133
138
  TInput extends InputParameters = InputParameters,
@@ -137,6 +142,10 @@ type WorkflowContext<
137
142
  step: TStep;
138
143
  workflowId: string;
139
144
  runId: string;
145
+ /** Tenant/scope identifier set when the run was started, if any. */
146
+ resourceId?: string;
147
+ /** Zero-based retry attempt number (= `run.retryCount`). */
148
+ attempt: number;
140
149
  timeline: Record<string, unknown>;
141
150
  logger: WorkflowLogger;
142
151
  };
@@ -197,14 +206,6 @@ interface WorkflowLogger {
197
206
  log(message: string): void;
198
207
  error(message: string, ...args: unknown[]): void;
199
208
  }
200
- type WorkflowInternalLoggerContext = {
201
- runId?: string;
202
- workflowId?: string;
203
- };
204
- interface WorkflowInternalLogger {
205
- log(message: string, context?: WorkflowInternalLoggerContext): void;
206
- error(message: string, error: Error, context?: WorkflowInternalLoggerContext): void;
207
- }
208
209
  type WorkflowClientOptions = {
209
210
  logger?: WorkflowLogger;
210
211
  /**
@@ -221,7 +222,6 @@ type WorkflowClientOptions = {
221
222
  connectionString: string;
222
223
  pool?: never;
223
224
  });
224
- type StartWorkflowOptions = WorkflowRunOptions;
225
225
  declare class WorkflowClient {
226
226
  private boss;
227
227
  private db;
@@ -306,9 +306,6 @@ declare function createWorkflowRef<
306
306
  declare const workflow: WorkflowFactory;
307
307
  import pg2 from "pg";
308
308
  import { Db, PgBoss as PgBoss2 } from "pg-boss";
309
- type StartWorkflowOptions2 = WorkflowRunOptions & {
310
- batchSize?: number;
311
- };
312
309
  type WorkflowEngineOptions = {
313
310
  workflows?: WorkflowDefinition[];
314
311
  logger?: WorkflowLogger;
@@ -339,13 +336,13 @@ declare class WorkflowEngine {
339
336
  unregisterWorkflow(workflowId: string): Promise<WorkflowEngine>;
340
337
  unregisterAllWorkflows(): Promise<WorkflowEngine>;
341
338
  private resolveWorkflowRunParameters;
342
- startWorkflow<TInput extends InputParameters>(ref: WorkflowRef<TInput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions2): Promise<WorkflowRun>;
339
+ startWorkflow<TInput extends InputParameters>(ref: WorkflowRef<TInput>, input: InferInputParameters<TInput>, options?: StartWorkflowOptions): Promise<WorkflowRun>;
343
340
  startWorkflow(params: {
344
341
  resourceId?: string;
345
342
  workflowId: string;
346
343
  input: unknown;
347
344
  idempotencyKey?: string;
348
- options?: StartWorkflowOptions2;
345
+ options?: StartWorkflowOptions;
349
346
  }): Promise<WorkflowRun>;
350
347
  private createWorkflowRun;
351
348
  private enqueueWorkflowRun;
@@ -455,8 +452,6 @@ declare class WorkflowEngine {
455
452
  }>;
456
453
  }
457
454
  import { StandardSchemaV1 as StandardSchemaV12 } from "@standard-schema/spec";
458
- declare function validateWorkflowId(workflowId: string): void;
459
- declare function validateResourceId(resourceId: string | undefined | null): void;
460
455
  declare class WorkflowEngineError extends Error {
461
456
  readonly workflowId?: string | undefined;
462
457
  readonly runId?: string | undefined;
@@ -467,4 +462,14 @@ declare class WorkflowEngineError extends Error {
467
462
  declare class WorkflowRunNotFoundError extends WorkflowEngineError {
468
463
  constructor(runId?: string, workflowId?: string);
469
464
  }
470
- export { workflow, validateWorkflowId, validateResourceId, parseDuration, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunOptions, WorkflowRunNotFoundError, WorkflowRef, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowInternalLoggerContext, WorkflowInternalLogger, WorkflowInternalDefinition, WorkflowFactory, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepType, StepInternalDefinition, StepBaseContext, StartWorkflowOptions, InputParameters, InferInputParameters, DurationObject, Duration };
465
+ import { AttributeValue, Tracer } from "@opentelemetry/api";
466
+ type OtelPluginOptions = {
467
+ /** Tracer to use. Defaults to `trace.getTracer('pg-workflows')`. */
468
+ tracer?: Tracer;
469
+ /** Prefix for all span names. Defaults to `pg_workflows`. */
470
+ spanNamePrefix?: string;
471
+ /** Extra attributes merged onto the workflow.run span. */
472
+ attributes?: (context: WorkflowContext) => Record<string, AttributeValue>;
473
+ };
474
+ declare function otelPlugin(options?: OtelPluginOptions): WorkflowPlugin<StepBaseContext, object>;
475
+ export { workflow, otelPlugin, createWorkflowRef, WorkflowStatus, WorkflowRunProgress, WorkflowRunNotFoundError, WorkflowRun, WorkflowRef, WorkflowPlugin, WorkflowOptions, WorkflowLogger, WorkflowEngineOptions, WorkflowEngineError, WorkflowEngine, WorkflowDefinition, WorkflowContext, WorkflowClientOptions, WorkflowClient, StepBaseContext, StartWorkflowOptions, OtelPluginOptions, InputParameters, InferInputParameters, Duration };