@dudousxd/nestjs-durable-core 0.32.0 → 0.33.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.d.cts CHANGED
@@ -283,6 +283,13 @@ interface StepCheckpoint {
283
283
  events?: StepEvent[] | undefined;
284
284
  /** For sleep steps: epoch ms the sleep elapses at. */
285
285
  wakeAt?: number | undefined;
286
+ /**
287
+ * Set on the running placeholder checkpoints of the children dispatched by one `ctx.all` call —
288
+ * a shared tag (`all:<firstSeq>`) grouping the N siblings so the dashboard can render them as one
289
+ * parallel fan-out. Optional and additive: absent on every non-`all` checkpoint. Mirrors the
290
+ * Python SDK's `parallelGroup`.
291
+ */
292
+ parallelGroup?: string | undefined;
286
293
  /**
287
294
  * When the step entered the system: for a remote step, when the engine dispatched it to the
288
295
  * transport; for a local step, when it began. Queue-wait time = `startedAt − enqueuedAt`.
@@ -636,6 +643,9 @@ type WorkflowCommand =
636
643
  startedAt?: number;
637
644
  finishedAt?: number;
638
645
  events?: StepEvent[];
646
+ /** A worker's `ctx.gather([...])` tags every step in the parallel fan with the same group, so the
647
+ * engine carries it onto the checkpoint and the dashboard renders the fan as one group. */
648
+ parallelGroup?: string;
639
649
  }
640
650
  /** `ctx.sleep(ms)` — a durable timer of `ms` duration. The engine computes the absolute deadline
641
651
  * (now + ms) when it applies the command, so the worker never reads the clock (determinism). */
@@ -650,12 +660,15 @@ type WorkflowCommand =
650
660
  seq: number;
651
661
  signal: string;
652
662
  }
653
- /** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. */
663
+ /** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. A worker's
664
+ * `ctx.all([...])` fan-out tags every dispatched child with the same `parallelGroup` so the
665
+ * dashboard can render the fan as one group (parity with the gathered `recordStep` tag). */
654
666
  | {
655
667
  kind: 'startChild';
656
668
  seq: number;
657
669
  workflow: string;
658
670
  input: unknown;
671
+ parallelGroup?: string;
659
672
  };
660
673
  /** workflow worker → engine: the result of replaying one turn of a remote workflow. */
661
674
  interface WorkflowDecision {
@@ -693,6 +706,9 @@ interface WorkflowStepEvent {
693
706
  error?: StepError;
694
707
  /** Sub-process + log trail the step emitted so far (the handler's p-processes). */
695
708
  events?: StepEvent[];
709
+ /** A worker's `ctx.gather([...])` fan tags every step's lifecycle with the same group so the
710
+ * dashboard renders the live fan-out as one group (parity with the `recordStep` tag). */
711
+ parallelGroup?: string | undefined;
696
712
  }
697
713
  /**
698
714
  * Advances a workflow run one turn. The engine has one per workflow: the default {@link InProcess}
@@ -988,6 +1004,24 @@ interface WorkflowCtx {
988
1004
  */
989
1005
  startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>;
990
1006
  startChild(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<string>;
1007
+ /**
1008
+ * Run N children of the SAME workflow **in parallel** and wait for ALL of them: dispatches one
1009
+ * child per entry in `inputs` (concurrently, each with its own durable lifecycle), suspends — zero
1010
+ * compute — until every child reaches a terminal state, then resumes with their outputs in **input
1011
+ * order**. Child ids are group-scoped and stable (`<runId>.all.<firstSeq>.<i>`), and the running
1012
+ * placeholders share a `parallelGroup` tag so the dashboard renders the fan-out as one group.
1013
+ *
1014
+ * `mode` (default `waitAll`): `waitAll` waits for all then throws an aggregate {@link GatherError}
1015
+ * if any failed; `failFast` throws as soon as a failed child is seen (siblings are not cancelled in
1016
+ * v1 — their eventual results are ignored). Empty `inputs` returns `[]` with no side effects. The
1017
+ * wait-all / fan-out counterpart to {@link child}; parity with the Python SDK's `gather_children`.
1018
+ */
1019
+ all<C extends WorkflowClass>(workflow: C, inputs: WorkflowInputOf<C>[], opts?: {
1020
+ mode?: 'waitAll' | 'failFast';
1021
+ }): Promise<WorkflowOutputOf<C>[]>;
1022
+ all<TOutput = unknown>(workflow: string, inputs: unknown[], opts?: {
1023
+ mode?: 'waitAll' | 'failFast';
1024
+ }): Promise<TOutput[]>;
991
1025
  /**
992
1026
  * Pause the run at this point until a human resumes it from the dashboard (or
993
1027
  * `engine.continue(runId)`). Records a visible `pending` checkpoint so the breakpoint shows up
@@ -1803,6 +1837,29 @@ declare class RemoteStepTimeout extends Error {
1803
1837
  readonly timeoutMs: number;
1804
1838
  constructor(stepId: string, timeoutMs: number);
1805
1839
  }
1840
+ /**
1841
+ * Thrown by a remote {@link WorkflowExecutor} (e.g. {@link RemoteWorkflowExecutor}) when an `advance`
1842
+ * does not produce its decision within the configured `timeoutMs` — i.e. the worker is presumed gone
1843
+ * (the decision was dropped by a stall/redelivery or an instance restart spanning the in-memory waiter
1844
+ * map). Crucially this is NOT a run failure: the work may have actually completed and only the decision
1845
+ * was lost. The engine treats it as RECOVERABLE — it releases the run lease and leaves the run
1846
+ * `running` so `recoverIncomplete` re-drives it deterministically (replaying completed steps from
1847
+ * history). Distinct from a real executor error, which still fails the run.
1848
+ *
1849
+ * OPT-IN: only thrown when the executor was constructed with a `timeoutMs`. Absent a timeout, the
1850
+ * engine awaits the decision with its prior (unbounded) behavior — so existing users see no change.
1851
+ *
1852
+ * Known hazard: a timeout that fires while a worker is LEGITIMATELY still executing a not-yet-
1853
+ * checkpointed step will re-drive and re-run that in-flight step → DUPLICATE side effects. Therefore
1854
+ * the timeout is only safe when set GENEROUSLY (longer than the longest legitimate single turn). The
1855
+ * robust fix — a liveness/heartbeat-rearmed deadline so only a genuinely-dead worker re-drives — is the
1856
+ * documented follow-up (see the Track A diagnosis doc, "Part B").
1857
+ */
1858
+ declare class RemoteWorkflowTimeout extends Error {
1859
+ readonly taskId: string;
1860
+ readonly timeoutMs: number;
1861
+ constructor(taskId: string, timeoutMs: number);
1862
+ }
1806
1863
  /**
1807
1864
  * Thrown on resume when the workflow code no longer matches the recorded history: the step at a
1808
1865
  * logical position has a different name/kind than the checkpoint saved there. This means the
@@ -1816,6 +1873,24 @@ declare class NonDeterminismError extends Error {
1816
1873
  readonly seq: number;
1817
1874
  constructor(runId: string, seq: number, expected: string, recorded: string);
1818
1875
  }
1876
+ /**
1877
+ * Thrown by `ctx.all` when one or more parallel child workflows fail. Carries the per-item failures
1878
+ * (input index, child run id, error message) and presents an aggregate message summarizing the count
1879
+ * and the failing ids — the wait-all/fail-fast counterpart to a single child's FatalError. Mirrors
1880
+ * the Python SDK's `GatherFailed`.
1881
+ */
1882
+ declare class GatherError extends Error {
1883
+ readonly failures: {
1884
+ index: number;
1885
+ id: string;
1886
+ error: string;
1887
+ }[];
1888
+ constructor(failures: {
1889
+ index: number;
1890
+ id: string;
1891
+ error: string;
1892
+ }[], total?: number);
1893
+ }
1819
1894
  declare class WorkflowSuspended extends Error {
1820
1895
  /** Epoch ms to auto-resume (durable sleep), or undefined when waiting on an external signal. */
1821
1896
  readonly wakeAt?: number | undefined;
@@ -2059,6 +2134,14 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
2059
2134
  */
2060
2135
  declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
2061
2136
 
2137
+ /**
2138
+ * A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
2139
+ * local-step path (`ctx.step`) and the remote-worker path (`runStepHandler`) so a step records
2140
+ * the same {@link StepEvent} shape wherever it runs — the TypeScript twin of the Python SDK's
2141
+ * `StepContext`.
2142
+ */
2143
+ declare function createStepLogger(events: StepEvent[], now: () => number): StepLogger;
2144
+
2062
2145
  /**
2063
2146
  * Does a run's search attributes satisfy EVERY filter (AND)? A missing key never matches (so `ne`
2064
2147
  * on an absent key is false too — the attribute simply isn't there to compare). Empty/undefined
@@ -2119,8 +2202,20 @@ declare function attributeOperand(filter: AttributeFilter): string | number;
2119
2202
  * (`STATE_STORE is not available in the DurableApiModule context`). A registered symbol collapses
2120
2203
  * them to one. Mirrors the `CONTEXT_ACCESSOR` token in `@dudousxd/nestjs-durable`'s `tokens.ts`.
2121
2204
  */
2205
+ /**
2206
+ * @deprecated Use {@link STATE_STORE_CANONICAL} (`@dudousxd/nestjs-durable:state-store`). The legacy
2207
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2208
+ */
2122
2209
  declare const STATE_STORE: unique symbol;
2210
+ /**
2211
+ * @deprecated Use {@link TRANSPORT_CANONICAL} (`@dudousxd/nestjs-durable:transport`). The legacy
2212
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2213
+ */
2123
2214
  declare const TRANSPORT: unique symbol;
2215
+ /**
2216
+ * @deprecated Use {@link DURABLE_OPTIONS_CANONICAL} (`@dudousxd/nestjs-durable:options`). The legacy
2217
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2218
+ */
2124
2219
  declare const DURABLE_OPTIONS: unique symbol;
2125
2220
  /**
2126
2221
  * Canonical, cross-lib-discoverable aliases of the durable DI tokens, following the ecosystem
@@ -2220,4 +2315,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
2220
2315
  onControl(handler: (msg: ControlMessage) => void): void;
2221
2316
  }
2222
2317
 
2223
- export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, TRANSPORT_CANONICAL, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
2318
+ export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, TRANSPORT_CANONICAL, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, createStepLogger, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
package/dist/index.d.ts CHANGED
@@ -283,6 +283,13 @@ interface StepCheckpoint {
283
283
  events?: StepEvent[] | undefined;
284
284
  /** For sleep steps: epoch ms the sleep elapses at. */
285
285
  wakeAt?: number | undefined;
286
+ /**
287
+ * Set on the running placeholder checkpoints of the children dispatched by one `ctx.all` call —
288
+ * a shared tag (`all:<firstSeq>`) grouping the N siblings so the dashboard can render them as one
289
+ * parallel fan-out. Optional and additive: absent on every non-`all` checkpoint. Mirrors the
290
+ * Python SDK's `parallelGroup`.
291
+ */
292
+ parallelGroup?: string | undefined;
286
293
  /**
287
294
  * When the step entered the system: for a remote step, when the engine dispatched it to the
288
295
  * transport; for a local step, when it began. Queue-wait time = `startedAt − enqueuedAt`.
@@ -636,6 +643,9 @@ type WorkflowCommand =
636
643
  startedAt?: number;
637
644
  finishedAt?: number;
638
645
  events?: StepEvent[];
646
+ /** A worker's `ctx.gather([...])` tags every step in the parallel fan with the same group, so the
647
+ * engine carries it onto the checkpoint and the dashboard renders the fan as one group. */
648
+ parallelGroup?: string;
639
649
  }
640
650
  /** `ctx.sleep(ms)` — a durable timer of `ms` duration. The engine computes the absolute deadline
641
651
  * (now + ms) when it applies the command, so the worker never reads the clock (determinism). */
@@ -650,12 +660,15 @@ type WorkflowCommand =
650
660
  seq: number;
651
661
  signal: string;
652
662
  }
653
- /** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. */
663
+ /** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. A worker's
664
+ * `ctx.all([...])` fan-out tags every dispatched child with the same `parallelGroup` so the
665
+ * dashboard can render the fan as one group (parity with the gathered `recordStep` tag). */
654
666
  | {
655
667
  kind: 'startChild';
656
668
  seq: number;
657
669
  workflow: string;
658
670
  input: unknown;
671
+ parallelGroup?: string;
659
672
  };
660
673
  /** workflow worker → engine: the result of replaying one turn of a remote workflow. */
661
674
  interface WorkflowDecision {
@@ -693,6 +706,9 @@ interface WorkflowStepEvent {
693
706
  error?: StepError;
694
707
  /** Sub-process + log trail the step emitted so far (the handler's p-processes). */
695
708
  events?: StepEvent[];
709
+ /** A worker's `ctx.gather([...])` fan tags every step's lifecycle with the same group so the
710
+ * dashboard renders the live fan-out as one group (parity with the `recordStep` tag). */
711
+ parallelGroup?: string | undefined;
696
712
  }
697
713
  /**
698
714
  * Advances a workflow run one turn. The engine has one per workflow: the default {@link InProcess}
@@ -988,6 +1004,24 @@ interface WorkflowCtx {
988
1004
  */
989
1005
  startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>;
990
1006
  startChild(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<string>;
1007
+ /**
1008
+ * Run N children of the SAME workflow **in parallel** and wait for ALL of them: dispatches one
1009
+ * child per entry in `inputs` (concurrently, each with its own durable lifecycle), suspends — zero
1010
+ * compute — until every child reaches a terminal state, then resumes with their outputs in **input
1011
+ * order**. Child ids are group-scoped and stable (`<runId>.all.<firstSeq>.<i>`), and the running
1012
+ * placeholders share a `parallelGroup` tag so the dashboard renders the fan-out as one group.
1013
+ *
1014
+ * `mode` (default `waitAll`): `waitAll` waits for all then throws an aggregate {@link GatherError}
1015
+ * if any failed; `failFast` throws as soon as a failed child is seen (siblings are not cancelled in
1016
+ * v1 — their eventual results are ignored). Empty `inputs` returns `[]` with no side effects. The
1017
+ * wait-all / fan-out counterpart to {@link child}; parity with the Python SDK's `gather_children`.
1018
+ */
1019
+ all<C extends WorkflowClass>(workflow: C, inputs: WorkflowInputOf<C>[], opts?: {
1020
+ mode?: 'waitAll' | 'failFast';
1021
+ }): Promise<WorkflowOutputOf<C>[]>;
1022
+ all<TOutput = unknown>(workflow: string, inputs: unknown[], opts?: {
1023
+ mode?: 'waitAll' | 'failFast';
1024
+ }): Promise<TOutput[]>;
991
1025
  /**
992
1026
  * Pause the run at this point until a human resumes it from the dashboard (or
993
1027
  * `engine.continue(runId)`). Records a visible `pending` checkpoint so the breakpoint shows up
@@ -1803,6 +1837,29 @@ declare class RemoteStepTimeout extends Error {
1803
1837
  readonly timeoutMs: number;
1804
1838
  constructor(stepId: string, timeoutMs: number);
1805
1839
  }
1840
+ /**
1841
+ * Thrown by a remote {@link WorkflowExecutor} (e.g. {@link RemoteWorkflowExecutor}) when an `advance`
1842
+ * does not produce its decision within the configured `timeoutMs` — i.e. the worker is presumed gone
1843
+ * (the decision was dropped by a stall/redelivery or an instance restart spanning the in-memory waiter
1844
+ * map). Crucially this is NOT a run failure: the work may have actually completed and only the decision
1845
+ * was lost. The engine treats it as RECOVERABLE — it releases the run lease and leaves the run
1846
+ * `running` so `recoverIncomplete` re-drives it deterministically (replaying completed steps from
1847
+ * history). Distinct from a real executor error, which still fails the run.
1848
+ *
1849
+ * OPT-IN: only thrown when the executor was constructed with a `timeoutMs`. Absent a timeout, the
1850
+ * engine awaits the decision with its prior (unbounded) behavior — so existing users see no change.
1851
+ *
1852
+ * Known hazard: a timeout that fires while a worker is LEGITIMATELY still executing a not-yet-
1853
+ * checkpointed step will re-drive and re-run that in-flight step → DUPLICATE side effects. Therefore
1854
+ * the timeout is only safe when set GENEROUSLY (longer than the longest legitimate single turn). The
1855
+ * robust fix — a liveness/heartbeat-rearmed deadline so only a genuinely-dead worker re-drives — is the
1856
+ * documented follow-up (see the Track A diagnosis doc, "Part B").
1857
+ */
1858
+ declare class RemoteWorkflowTimeout extends Error {
1859
+ readonly taskId: string;
1860
+ readonly timeoutMs: number;
1861
+ constructor(taskId: string, timeoutMs: number);
1862
+ }
1806
1863
  /**
1807
1864
  * Thrown on resume when the workflow code no longer matches the recorded history: the step at a
1808
1865
  * logical position has a different name/kind than the checkpoint saved there. This means the
@@ -1816,6 +1873,24 @@ declare class NonDeterminismError extends Error {
1816
1873
  readonly seq: number;
1817
1874
  constructor(runId: string, seq: number, expected: string, recorded: string);
1818
1875
  }
1876
+ /**
1877
+ * Thrown by `ctx.all` when one or more parallel child workflows fail. Carries the per-item failures
1878
+ * (input index, child run id, error message) and presents an aggregate message summarizing the count
1879
+ * and the failing ids — the wait-all/fail-fast counterpart to a single child's FatalError. Mirrors
1880
+ * the Python SDK's `GatherFailed`.
1881
+ */
1882
+ declare class GatherError extends Error {
1883
+ readonly failures: {
1884
+ index: number;
1885
+ id: string;
1886
+ error: string;
1887
+ }[];
1888
+ constructor(failures: {
1889
+ index: number;
1890
+ id: string;
1891
+ error: string;
1892
+ }[], total?: number);
1893
+ }
1819
1894
  declare class WorkflowSuspended extends Error {
1820
1895
  /** Epoch ms to auto-resume (durable sleep), or undefined when waiting on an external signal. */
1821
1896
  readonly wakeAt?: number | undefined;
@@ -2059,6 +2134,14 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
2059
2134
  */
2060
2135
  declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
2061
2136
 
2137
+ /**
2138
+ * A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
2139
+ * local-step path (`ctx.step`) and the remote-worker path (`runStepHandler`) so a step records
2140
+ * the same {@link StepEvent} shape wherever it runs — the TypeScript twin of the Python SDK's
2141
+ * `StepContext`.
2142
+ */
2143
+ declare function createStepLogger(events: StepEvent[], now: () => number): StepLogger;
2144
+
2062
2145
  /**
2063
2146
  * Does a run's search attributes satisfy EVERY filter (AND)? A missing key never matches (so `ne`
2064
2147
  * on an absent key is false too — the attribute simply isn't there to compare). Empty/undefined
@@ -2119,8 +2202,20 @@ declare function attributeOperand(filter: AttributeFilter): string | number;
2119
2202
  * (`STATE_STORE is not available in the DurableApiModule context`). A registered symbol collapses
2120
2203
  * them to one. Mirrors the `CONTEXT_ACCESSOR` token in `@dudousxd/nestjs-durable`'s `tokens.ts`.
2121
2204
  */
2205
+ /**
2206
+ * @deprecated Use {@link STATE_STORE_CANONICAL} (`@dudousxd/nestjs-durable:state-store`). The legacy
2207
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2208
+ */
2122
2209
  declare const STATE_STORE: unique symbol;
2210
+ /**
2211
+ * @deprecated Use {@link TRANSPORT_CANONICAL} (`@dudousxd/nestjs-durable:transport`). The legacy
2212
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2213
+ */
2123
2214
  declare const TRANSPORT: unique symbol;
2215
+ /**
2216
+ * @deprecated Use {@link DURABLE_OPTIONS_CANONICAL} (`@dudousxd/nestjs-durable:options`). The legacy
2217
+ * token stays bound as a back-compat alias and still resolves; it will be removed in a future major.
2218
+ */
2124
2219
  declare const DURABLE_OPTIONS: unique symbol;
2125
2220
  /**
2126
2221
  * Canonical, cross-lib-discoverable aliases of the durable DI tokens, following the ecosystem
@@ -2220,4 +2315,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
2220
2315
  onControl(handler: (msg: ControlMessage) => void): void;
2221
2316
  }
2222
2317
 
2223
- export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, TRANSPORT_CANONICAL, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
2318
+ export { type AccumulatorHost, type Admission, type AdmissionBackend, type AdmissionItem, type AttributeFilter, type AttributeOp, type BackoffStrategy, type ChildCallOptions, CodecStateStore, ContinueAsNew, type ControlMessage, type ControlPlane, DURABLE_OPTIONS, DURABLE_OPTIONS_CANONICAL, type DurableWebhook, type EngineEvent, type EngineEventType, type EngineListener, Entities, type EntityConfig, type EntityHandler, type EntityHost, EventAccumulators, type EventBatchConfig, FatalError, GatherError, type GroupHealth, type Heartbeat, type HistoryEvent, InMemoryAdmissionBackend, InMemoryStateStore, InMemoryTransport, type MetricsCollector, type MetricsSnapshot, type NamedTransport, NonDeterminismError, type PayloadCodec, type QueueConfig, QueueController, type RemoteStepConfig, type RemoteStepDef, RemoteStepError, RemoteStepTimeout, type RemoteTask, RemoteWorkflowExecutor, RemoteWorkflowTimeout, type RunAttributeRow, type RunDispatcher, type RunQuery, type RunResult, type RunSchedulesOptions, type RunStatus, STATE_STORE, STATE_STORE_CANONICAL, type ScheduledWorkflow, type SearchAttributes, SignalTimeoutError, type SignalWaiter, type SingletonConfig, SingletonQueueFullError, type StartOptions, type StateStore, type StepCheckpoint, type StepError, type StepEvent, type StepHandler, type StepInterceptor, type StepInvocation, type StepKind, type StepLogger, type StepOptions, type StepResult, type StoreTransaction, type SubProcessHandle, TRANSPORT, TRANSPORT_CANONICAL, type Transport, type UpdateResult, type UpdateValidator, WORKFLOW_NAME_KEY, type WorkerHeartbeat, type WorkflowClass, type WorkflowCommand, type WorkflowCtx, type WorkflowDecision, WorkflowEngine, type WorkflowEngineDeps, type WorkflowExecutor, type WorkflowInputOf, type WorkflowOutputOf, type WorkflowRef, type WorkflowRun, type WorkflowStepEvent, WorkflowSuspended, type WorkflowTask, applyAttributeQuery, attributeColumnFor, attributeOperand, breakpointToken, collectMetrics, createStepLogger, eventMatchOf, eventMatches, eventPrefix, eventToken, matchesAttributes, normalizeAttributeRows, parseDuration, prevCronFireMs, remoteStep, runSchedules, runStepHandler, scheduledRunId, sqlComparator, stepId, workflowName };
package/dist/index.js CHANGED
@@ -385,6 +385,7 @@ function stepCheckpoint(p) {
385
385
  attempts: p.attempts,
386
386
  workerGroup: p.workerGroup,
387
387
  wakeAt: p.wakeAt,
388
+ parallelGroup: p.parallelGroup,
388
389
  enqueuedAt: p.enqueuedAt,
389
390
  startedAt: p.startedAt,
390
391
  finishedAt: p.finishedAt
@@ -402,6 +403,7 @@ function instantCheckpoint(p) {
402
403
  status: p.status ?? "completed",
403
404
  output: p.output,
404
405
  wakeAt: p.wakeAt,
406
+ parallelGroup: p.parallelGroup,
405
407
  attempts: 1,
406
408
  enqueuedAt: at,
407
409
  startedAt: at,
@@ -463,6 +465,19 @@ var RemoteStepTimeout = class extends Error {
463
465
  this.timeoutMs = timeoutMs;
464
466
  }
465
467
  };
468
+ var RemoteWorkflowTimeout = class extends Error {
469
+ static {
470
+ __name(this, "RemoteWorkflowTimeout");
471
+ }
472
+ taskId;
473
+ timeoutMs;
474
+ constructor(taskId, timeoutMs) {
475
+ super(`remote workflow task ${taskId} produced no decision within ${timeoutMs}ms \u2014 presumed dropped; re-driving via recovery`);
476
+ this.name = "RemoteWorkflowTimeout";
477
+ this.taskId = taskId;
478
+ this.timeoutMs = timeoutMs;
479
+ }
480
+ };
466
481
  var NonDeterminismError = class extends Error {
467
482
  static {
468
483
  __name(this, "NonDeterminismError");
@@ -476,6 +491,19 @@ var NonDeterminismError = class extends Error {
476
491
  this.seq = seq;
477
492
  }
478
493
  };
494
+ var GatherError = class extends Error {
495
+ static {
496
+ __name(this, "GatherError");
497
+ }
498
+ failures;
499
+ constructor(failures, total) {
500
+ const ids = failures.map((f) => f.id).join(", ");
501
+ const denom = total ?? failures.length;
502
+ super(`ctx.all: ${failures.length} of ${denom} child(ren) failed: ${ids}`);
503
+ this.name = "GatherError";
504
+ this.failures = failures;
505
+ }
506
+ };
479
507
  var WorkflowSuspended = class extends Error {
480
508
  static {
481
509
  __name(this, "WorkflowSuspended");
@@ -1145,6 +1173,72 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1145
1173
  }
1146
1174
  throw new WorkflowSuspended();
1147
1175
  }, "child");
1176
+ const all = /* @__PURE__ */ __name(async (workflow2, inputs, opts) => {
1177
+ if (inputs.length === 0) return [];
1178
+ const mode = opts?.mode ?? "waitAll";
1179
+ const name = workflowName(workflow2);
1180
+ const positions = inputs.map(() => pos.next());
1181
+ const group = `all:${positions[0]}`;
1182
+ const id = /* @__PURE__ */ __name((i) => `${runId}.all.${positions[0]}.${i}`, "id");
1183
+ const existing = await Promise.all(positions.map((seq) => readCheckpoint(seq)));
1184
+ if (mode === "failFast") {
1185
+ for (let i = 0; i < inputs.length; i += 1) {
1186
+ const cp = existing[i];
1187
+ const c = cp?.status === "completed" ? cp.output : null;
1188
+ if (c && c.ok === false) {
1189
+ throw new GatherError([
1190
+ {
1191
+ index: i,
1192
+ id: id(i),
1193
+ error: c.error ?? "unknown"
1194
+ }
1195
+ ], inputs.length);
1196
+ }
1197
+ }
1198
+ }
1199
+ let pending = false;
1200
+ for (let i = 0; i < inputs.length; i += 1) {
1201
+ const cp = existing[i];
1202
+ if (cp?.status === "completed") continue;
1203
+ pending = true;
1204
+ const seq = positions[i];
1205
+ const childId = id(i);
1206
+ await store.putSignalWaiter({
1207
+ token: `child:${childId}`,
1208
+ runId,
1209
+ seq
1210
+ });
1211
+ if (!await store.getRun(childId)) host.startChild(name, inputs[i], childId);
1212
+ if (!cp) {
1213
+ await writeCheckpoint(instantCheckpoint({
1214
+ runId,
1215
+ seq,
1216
+ name: `signal:child:${childId}`,
1217
+ kind: "signal",
1218
+ status: "running",
1219
+ parallelGroup: group
1220
+ }));
1221
+ }
1222
+ }
1223
+ if (pending) throw new WorkflowSuspended();
1224
+ const outputs = [];
1225
+ const failures = [];
1226
+ for (let i = 0; i < inputs.length; i += 1) {
1227
+ const c = existing[i]?.output;
1228
+ if (c && typeof c === "object" && "ok" in c && c.ok === false) {
1229
+ failures.push({
1230
+ index: i,
1231
+ id: id(i),
1232
+ error: c.error ?? "unknown"
1233
+ });
1234
+ outputs.push(void 0);
1235
+ } else {
1236
+ outputs.push(c?.value);
1237
+ }
1238
+ }
1239
+ if (failures.length > 0) throw new GatherError(failures, inputs.length);
1240
+ return outputs;
1241
+ }, "all");
1148
1242
  const startChild = /* @__PURE__ */ __name(async (workflow2, input, options) => {
1149
1243
  const { childId, priority } = normalizeChildOptions(options);
1150
1244
  const current = pos.next();
@@ -1279,6 +1373,7 @@ function createWorkflowCtx(host, runId, compensations, workflow = "") {
1279
1373
  waitForEvent,
1280
1374
  task,
1281
1375
  child,
1376
+ all,
1282
1377
  startChild,
1283
1378
  breakpoint,
1284
1379
  webhook,
@@ -2268,7 +2363,8 @@ var WorkflowEngine = class {
2268
2363
  attempts: 1,
2269
2364
  enqueuedAt: startedAt,
2270
2365
  startedAt,
2271
- finishedAt: startedAt
2366
+ finishedAt: startedAt,
2367
+ parallelGroup: event.parallelGroup
2272
2368
  }));
2273
2369
  this.emit({
2274
2370
  type: "step.started",
@@ -2292,7 +2388,8 @@ var WorkflowEngine = class {
2292
2388
  attempts: 1,
2293
2389
  enqueuedAt: startedAt,
2294
2390
  startedAt,
2295
- finishedAt: event.finishedAt != null ? new Date(event.finishedAt) : /* @__PURE__ */ new Date()
2391
+ finishedAt: event.finishedAt != null ? new Date(event.finishedAt) : /* @__PURE__ */ new Date(),
2392
+ parallelGroup: event.parallelGroup
2296
2393
  }));
2297
2394
  this.emit({
2298
2395
  type: failed ? "step.failed" : "step.completed",
@@ -2529,6 +2626,13 @@ var WorkflowEngine = class {
2529
2626
  try {
2530
2627
  decision = await remote.executor.advance(run, history);
2531
2628
  } catch (err) {
2629
+ if (err instanceof RemoteWorkflowTimeout) {
2630
+ await this.store.releaseRunLock(run.id);
2631
+ return {
2632
+ runId: run.id,
2633
+ status: "running"
2634
+ };
2635
+ }
2532
2636
  const error = {
2533
2637
  message: err instanceof Error ? err.message : String(err),
2534
2638
  stack: err instanceof Error ? err.stack : void 0
@@ -2648,7 +2752,11 @@ var WorkflowEngine = class {
2648
2752
  attempts: 1,
2649
2753
  enqueuedAt: startedAt,
2650
2754
  startedAt,
2651
- finishedAt
2755
+ finishedAt,
2756
+ // Carry the worker's `parallelGroup` (a Python `ctx.gather` tags every step in the fan with
2757
+ // the same group) onto the checkpoint, so the dashboard can render the steps as one parallel
2758
+ // group instead of a sequential list. Additive: undefined for ordinary sequential steps.
2759
+ parallelGroup: cmd.parallelGroup
2652
2760
  }));
2653
2761
  this.emit({
2654
2762
  type: cmd.error ? "step.failed" : "step.completed",
@@ -3283,11 +3391,12 @@ var RemoteWorkflowExecutor = class {
3283
3391
  };
3284
3392
  const decision = new Promise((resolve, reject) => {
3285
3393
  this.pending.set(taskId, resolve);
3286
- if (this.opts.timeoutMs) {
3394
+ const { timeoutMs } = this.opts;
3395
+ if (timeoutMs) {
3287
3396
  const timer = setTimeout(() => {
3288
3397
  this.pending.delete(taskId);
3289
- reject(new Error(`workflow task ${taskId} timed out after ${this.opts.timeoutMs}ms`));
3290
- }, this.opts.timeoutMs);
3398
+ reject(new RemoteWorkflowTimeout(taskId, timeoutMs));
3399
+ }, timeoutMs);
3291
3400
  timer.unref?.();
3292
3401
  }
3293
3402
  });
@@ -4014,6 +4123,7 @@ export {
4014
4123
  Entities,
4015
4124
  EventAccumulators,
4016
4125
  FatalError,
4126
+ GatherError,
4017
4127
  InMemoryAdmissionBackend,
4018
4128
  InMemoryStateStore,
4019
4129
  InMemoryTransport,
@@ -4022,6 +4132,7 @@ export {
4022
4132
  RemoteStepError,
4023
4133
  RemoteStepTimeout,
4024
4134
  RemoteWorkflowExecutor,
4135
+ RemoteWorkflowTimeout,
4025
4136
  STATE_STORE,
4026
4137
  STATE_STORE_CANONICAL,
4027
4138
  SignalTimeoutError,
@@ -4036,6 +4147,7 @@ export {
4036
4147
  attributeOperand,
4037
4148
  breakpointToken,
4038
4149
  collectMetrics,
4150
+ createStepLogger,
4039
4151
  eventMatchOf,
4040
4152
  eventMatches,
4041
4153
  eventPrefix,