@dudousxd/nestjs-durable-core 0.32.1 → 0.34.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 +155 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -8
- package/dist/index.d.ts +120 -8
- package/dist/index.js +152 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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`.
|
|
@@ -570,7 +577,13 @@ interface StepResult {
|
|
|
570
577
|
interface Heartbeat {
|
|
571
578
|
runId: string;
|
|
572
579
|
seq: number;
|
|
573
|
-
|
|
580
|
+
/**
|
|
581
|
+
* The in-flight step this beat keeps alive. ABSENT for a RUN-scoped (workflow-turn) heartbeat: a
|
|
582
|
+
* worker replaying a long workflow turn beats for its `runId` so the engine rearms the run's
|
|
583
|
+
* `advance` deadline (see `WorkflowEngineDeps.remoteAdvanceSilenceMs`). The engine then keys the
|
|
584
|
+
* liveness reset by `runId` instead of `stepId`.
|
|
585
|
+
*/
|
|
586
|
+
stepId?: string | undefined;
|
|
574
587
|
group: string;
|
|
575
588
|
}
|
|
576
589
|
/** engine → workflow worker: advance this run one turn by replaying the function against `history`. */
|
|
@@ -636,6 +649,9 @@ type WorkflowCommand =
|
|
|
636
649
|
startedAt?: number;
|
|
637
650
|
finishedAt?: number;
|
|
638
651
|
events?: StepEvent[];
|
|
652
|
+
/** A worker's `ctx.gather([...])` tags every step in the parallel fan with the same group, so the
|
|
653
|
+
* engine carries it onto the checkpoint and the dashboard renders the fan as one group. */
|
|
654
|
+
parallelGroup?: string;
|
|
639
655
|
}
|
|
640
656
|
/** `ctx.sleep(ms)` — a durable timer of `ms` duration. The engine computes the absolute deadline
|
|
641
657
|
* (now + ms) when it applies the command, so the worker never reads the clock (determinism). */
|
|
@@ -650,12 +666,15 @@ type WorkflowCommand =
|
|
|
650
666
|
seq: number;
|
|
651
667
|
signal: string;
|
|
652
668
|
}
|
|
653
|
-
/** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle.
|
|
669
|
+
/** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. A worker's
|
|
670
|
+
* `ctx.all([...])` fan-out tags every dispatched child with the same `parallelGroup` so the
|
|
671
|
+
* dashboard can render the fan as one group (parity with the gathered `recordStep` tag). */
|
|
654
672
|
| {
|
|
655
673
|
kind: 'startChild';
|
|
656
674
|
seq: number;
|
|
657
675
|
workflow: string;
|
|
658
676
|
input: unknown;
|
|
677
|
+
parallelGroup?: string;
|
|
659
678
|
};
|
|
660
679
|
/** workflow worker → engine: the result of replaying one turn of a remote workflow. */
|
|
661
680
|
interface WorkflowDecision {
|
|
@@ -693,6 +712,9 @@ interface WorkflowStepEvent {
|
|
|
693
712
|
error?: StepError;
|
|
694
713
|
/** Sub-process + log trail the step emitted so far (the handler's p-processes). */
|
|
695
714
|
events?: StepEvent[];
|
|
715
|
+
/** A worker's `ctx.gather([...])` fan tags every step's lifecycle with the same group so the
|
|
716
|
+
* dashboard renders the live fan-out as one group (parity with the `recordStep` tag). */
|
|
717
|
+
parallelGroup?: string | undefined;
|
|
696
718
|
}
|
|
697
719
|
/**
|
|
698
720
|
* Advances a workflow run one turn. The engine has one per workflow: the default {@link InProcess}
|
|
@@ -988,6 +1010,24 @@ interface WorkflowCtx {
|
|
|
988
1010
|
*/
|
|
989
1011
|
startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>;
|
|
990
1012
|
startChild(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<string>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Run N children of the SAME workflow **in parallel** and wait for ALL of them: dispatches one
|
|
1015
|
+
* child per entry in `inputs` (concurrently, each with its own durable lifecycle), suspends — zero
|
|
1016
|
+
* compute — until every child reaches a terminal state, then resumes with their outputs in **input
|
|
1017
|
+
* order**. Child ids are group-scoped and stable (`<runId>.all.<firstSeq>.<i>`), and the running
|
|
1018
|
+
* placeholders share a `parallelGroup` tag so the dashboard renders the fan-out as one group.
|
|
1019
|
+
*
|
|
1020
|
+
* `mode` (default `waitAll`): `waitAll` waits for all then throws an aggregate {@link GatherError}
|
|
1021
|
+
* if any failed; `failFast` throws as soon as a failed child is seen (siblings are not cancelled in
|
|
1022
|
+
* v1 — their eventual results are ignored). Empty `inputs` returns `[]` with no side effects. The
|
|
1023
|
+
* wait-all / fan-out counterpart to {@link child}; parity with the Python SDK's `gather_children`.
|
|
1024
|
+
*/
|
|
1025
|
+
all<C extends WorkflowClass>(workflow: C, inputs: WorkflowInputOf<C>[], opts?: {
|
|
1026
|
+
mode?: 'waitAll' | 'failFast';
|
|
1027
|
+
}): Promise<WorkflowOutputOf<C>[]>;
|
|
1028
|
+
all<TOutput = unknown>(workflow: string, inputs: unknown[], opts?: {
|
|
1029
|
+
mode?: 'waitAll' | 'failFast';
|
|
1030
|
+
}): Promise<TOutput[]>;
|
|
991
1031
|
/**
|
|
992
1032
|
* Pause the run at this point until a human resumes it from the dashboard (or
|
|
993
1033
|
* `engine.continue(runId)`). Records a visible `pending` checkpoint so the breakpoint shows up
|
|
@@ -1257,6 +1297,18 @@ interface WorkflowEngineDeps {
|
|
|
1257
1297
|
* Omit for unlimited (the default — recovery always retries).
|
|
1258
1298
|
*/
|
|
1259
1299
|
maxRecoveryAttempts?: number | undefined;
|
|
1300
|
+
/**
|
|
1301
|
+
* Opt-in liveness deadline (ms) for a remote workflow `advance`. If the worker neither returns a
|
|
1302
|
+
* decision NOR sends a run-scoped {@link Heartbeat} within this window, the engine presumes it dead:
|
|
1303
|
+
* it rejects the advance with {@link RemoteWorkflowTimeout}, releases the lease, and lets recovery
|
|
1304
|
+
* re-drive (replaying completed steps from history). Each beat REARMS the window, so a worker still
|
|
1305
|
+
* legitimately replaying a long turn (it keeps beating) is never re-driven — closing the duplicate-
|
|
1306
|
+
* side-effect hazard a fixed {@link RemoteWorkflowExecutor} `timeoutMs` cannot avoid. Omit (default)
|
|
1307
|
+
* for the prior unbounded await — byte-identical behavior when unset. If a `RemoteWorkflowExecutor` is
|
|
1308
|
+
* ALSO constructed with its own `timeoutMs`, the shorter of the two deadlines wins; prefer setting only
|
|
1309
|
+
* one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
|
|
1310
|
+
*/
|
|
1311
|
+
remoteAdvanceSilenceMs?: number | undefined;
|
|
1260
1312
|
/**
|
|
1261
1313
|
* Build the public callback URL for a `ctx.webhook()` token (e.g.
|
|
1262
1314
|
* ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
|
|
@@ -1330,6 +1382,7 @@ declare class WorkflowEngine {
|
|
|
1330
1382
|
private readonly instanceId;
|
|
1331
1383
|
private readonly leaseMs;
|
|
1332
1384
|
private readonly maxRecoveryAttempts?;
|
|
1385
|
+
private readonly remoteAdvanceSilenceMs?;
|
|
1333
1386
|
private readonly webhookUrl?;
|
|
1334
1387
|
private readonly traceparent?;
|
|
1335
1388
|
private readonly context?;
|
|
@@ -1354,7 +1407,9 @@ declare class WorkflowEngine {
|
|
|
1354
1407
|
private readonly accumulators;
|
|
1355
1408
|
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
1356
1409
|
private readonly pending;
|
|
1357
|
-
/**
|
|
1410
|
+
/** "Reset the liveness timer" callbacks keyed by the beat's target — a stepId (in-flight remote step)
|
|
1411
|
+
* or a runId (in-flight remote workflow turn) — invoked when a matching heartbeat arrives. The two id
|
|
1412
|
+
* shapes don't collide in practice: a stepId is `${runId}:${seq}`, a run key is the bare runId. */
|
|
1358
1413
|
private readonly heartbeatResets;
|
|
1359
1414
|
private readonly listeners;
|
|
1360
1415
|
/** Step interceptors (onion middleware around real local-step execution), first = outermost. */
|
|
@@ -1751,10 +1806,15 @@ declare class WorkflowEngine {
|
|
|
1751
1806
|
/** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
|
|
1752
1807
|
private callRemoteInMemory;
|
|
1753
1808
|
/**
|
|
1754
|
-
* Await
|
|
1755
|
-
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat
|
|
1756
|
-
* window, so a worker that keeps beating stays alive past
|
|
1809
|
+
* Await `resultPromise`, but reject with `onTimeout()` if neither it settles nor a heartbeat for `id`
|
|
1810
|
+
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat`, looked up in
|
|
1811
|
+
* `heartbeatResets` by `id`) REARMS the window, so a worker that keeps beating stays alive past
|
|
1812
|
+
* `timeoutMs`. Shared by the remote-STEP liveness (keyed by stepId) and the remote-WORKFLOW advance
|
|
1813
|
+
* liveness (keyed by runId); the caller supplies the timeout error and any extra cleanup via `onTimeout`.
|
|
1757
1814
|
*/
|
|
1815
|
+
private awaitWithLivenessDeadline;
|
|
1816
|
+
/** Remote-STEP liveness: await a worker result, timing out (with the step's pending-map cleanup) to
|
|
1817
|
+
* `RemoteStepTimeout` if it neither lands nor beats within `timeoutMs`. See {@link awaitWithLivenessDeadline}. */
|
|
1758
1818
|
private awaitWithHeartbeat;
|
|
1759
1819
|
}
|
|
1760
1820
|
/** Raised inside the workflow when a remote worker reports a step failure. */
|
|
@@ -1803,6 +1863,29 @@ declare class RemoteStepTimeout extends Error {
|
|
|
1803
1863
|
readonly timeoutMs: number;
|
|
1804
1864
|
constructor(stepId: string, timeoutMs: number);
|
|
1805
1865
|
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Thrown by a remote {@link WorkflowExecutor} (e.g. {@link RemoteWorkflowExecutor}) when an `advance`
|
|
1868
|
+
* does not produce its decision within the configured `timeoutMs` — i.e. the worker is presumed gone
|
|
1869
|
+
* (the decision was dropped by a stall/redelivery or an instance restart spanning the in-memory waiter
|
|
1870
|
+
* map). Crucially this is NOT a run failure: the work may have actually completed and only the decision
|
|
1871
|
+
* was lost. The engine treats it as RECOVERABLE — it releases the run lease and leaves the run
|
|
1872
|
+
* `running` so `recoverIncomplete` re-drives it deterministically (replaying completed steps from
|
|
1873
|
+
* history). Distinct from a real executor error, which still fails the run.
|
|
1874
|
+
*
|
|
1875
|
+
* OPT-IN: only thrown when the executor was constructed with a `timeoutMs`. Absent a timeout, the
|
|
1876
|
+
* engine awaits the decision with its prior (unbounded) behavior — so existing users see no change.
|
|
1877
|
+
*
|
|
1878
|
+
* Known hazard: a timeout that fires while a worker is LEGITIMATELY still executing a not-yet-
|
|
1879
|
+
* checkpointed step will re-drive and re-run that in-flight step → DUPLICATE side effects. Therefore
|
|
1880
|
+
* the timeout is only safe when set GENEROUSLY (longer than the longest legitimate single turn). The
|
|
1881
|
+
* robust fix — a liveness/heartbeat-rearmed deadline so only a genuinely-dead worker re-drives — is the
|
|
1882
|
+
* documented follow-up (see the Track A diagnosis doc, "Part B").
|
|
1883
|
+
*/
|
|
1884
|
+
declare class RemoteWorkflowTimeout extends Error {
|
|
1885
|
+
readonly taskId: string;
|
|
1886
|
+
readonly timeoutMs: number;
|
|
1887
|
+
constructor(taskId: string, timeoutMs: number);
|
|
1888
|
+
}
|
|
1806
1889
|
/**
|
|
1807
1890
|
* Thrown on resume when the workflow code no longer matches the recorded history: the step at a
|
|
1808
1891
|
* logical position has a different name/kind than the checkpoint saved there. This means the
|
|
@@ -1816,6 +1899,24 @@ declare class NonDeterminismError extends Error {
|
|
|
1816
1899
|
readonly seq: number;
|
|
1817
1900
|
constructor(runId: string, seq: number, expected: string, recorded: string);
|
|
1818
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Thrown by `ctx.all` when one or more parallel child workflows fail. Carries the per-item failures
|
|
1904
|
+
* (input index, child run id, error message) and presents an aggregate message summarizing the count
|
|
1905
|
+
* and the failing ids — the wait-all/fail-fast counterpart to a single child's FatalError. Mirrors
|
|
1906
|
+
* the Python SDK's `GatherFailed`.
|
|
1907
|
+
*/
|
|
1908
|
+
declare class GatherError extends Error {
|
|
1909
|
+
readonly failures: {
|
|
1910
|
+
index: number;
|
|
1911
|
+
id: string;
|
|
1912
|
+
error: string;
|
|
1913
|
+
}[];
|
|
1914
|
+
constructor(failures: {
|
|
1915
|
+
index: number;
|
|
1916
|
+
id: string;
|
|
1917
|
+
error: string;
|
|
1918
|
+
}[], total?: number);
|
|
1919
|
+
}
|
|
1819
1920
|
declare class WorkflowSuspended extends Error {
|
|
1820
1921
|
/** Epoch ms to auto-resume (durable sleep), or undefined when waiting on an external signal. */
|
|
1821
1922
|
readonly wakeAt?: number | undefined;
|
|
@@ -2059,6 +2160,14 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
|
|
|
2059
2160
|
*/
|
|
2060
2161
|
declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
|
|
2061
2162
|
|
|
2163
|
+
/**
|
|
2164
|
+
* A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
|
|
2165
|
+
* local-step path (`ctx.step`) and the remote-worker path (`runStepHandler`) so a step records
|
|
2166
|
+
* the same {@link StepEvent} shape wherever it runs — the TypeScript twin of the Python SDK's
|
|
2167
|
+
* `StepContext`.
|
|
2168
|
+
*/
|
|
2169
|
+
declare function createStepLogger(events: StepEvent[], now: () => number): StepLogger;
|
|
2170
|
+
|
|
2062
2171
|
/**
|
|
2063
2172
|
* Does a run's search attributes satisfy EVERY filter (AND)? A missing key never matches (so `ne`
|
|
2064
2173
|
* on an absent key is false too — the attribute simply isn't there to compare). Empty/undefined
|
|
@@ -2218,13 +2327,16 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2218
2327
|
declare class InMemoryTransport implements Transport, ControlPlane {
|
|
2219
2328
|
private readonly handlers;
|
|
2220
2329
|
private resultHandler?;
|
|
2330
|
+
private heartbeatHandler?;
|
|
2221
2331
|
private stepEventHandler?;
|
|
2222
2332
|
private readonly controlHandlers;
|
|
2223
2333
|
/** Register a fake worker handler for a step name. */
|
|
2224
2334
|
handle(name: string, fn: StepHandler): void;
|
|
2225
2335
|
dispatch(task: RemoteTask): Promise<void>;
|
|
2226
2336
|
onResult(handler: (result: StepResult) => Promise<void>): void;
|
|
2227
|
-
onHeartbeat(
|
|
2337
|
+
onHeartbeat(handler: (beat: Heartbeat) => Promise<void>): void;
|
|
2338
|
+
/** Test hook: deliver a liveness heartbeat to the engine, as a real broker would on its channel. */
|
|
2339
|
+
emitHeartbeat(beat: Heartbeat): Promise<void>;
|
|
2228
2340
|
/** Workflow step lifecycle: deliver to the engine's handler (async, mirroring a real broker). */
|
|
2229
2341
|
dispatchStepEvent(event: WorkflowStepEvent): Promise<void>;
|
|
2230
2342
|
onStepEvent(handler: (event: WorkflowStepEvent) => Promise<void>): void;
|
|
@@ -2232,4 +2344,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
2232
2344
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
2233
2345
|
}
|
|
2234
2346
|
|
|
2235
|
-
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 };
|
|
2347
|
+
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`.
|
|
@@ -570,7 +577,13 @@ interface StepResult {
|
|
|
570
577
|
interface Heartbeat {
|
|
571
578
|
runId: string;
|
|
572
579
|
seq: number;
|
|
573
|
-
|
|
580
|
+
/**
|
|
581
|
+
* The in-flight step this beat keeps alive. ABSENT for a RUN-scoped (workflow-turn) heartbeat: a
|
|
582
|
+
* worker replaying a long workflow turn beats for its `runId` so the engine rearms the run's
|
|
583
|
+
* `advance` deadline (see `WorkflowEngineDeps.remoteAdvanceSilenceMs`). The engine then keys the
|
|
584
|
+
* liveness reset by `runId` instead of `stepId`.
|
|
585
|
+
*/
|
|
586
|
+
stepId?: string | undefined;
|
|
574
587
|
group: string;
|
|
575
588
|
}
|
|
576
589
|
/** engine → workflow worker: advance this run one turn by replaying the function against `history`. */
|
|
@@ -636,6 +649,9 @@ type WorkflowCommand =
|
|
|
636
649
|
startedAt?: number;
|
|
637
650
|
finishedAt?: number;
|
|
638
651
|
events?: StepEvent[];
|
|
652
|
+
/** A worker's `ctx.gather([...])` tags every step in the parallel fan with the same group, so the
|
|
653
|
+
* engine carries it onto the checkpoint and the dashboard renders the fan as one group. */
|
|
654
|
+
parallelGroup?: string;
|
|
639
655
|
}
|
|
640
656
|
/** `ctx.sleep(ms)` — a durable timer of `ms` duration. The engine computes the absolute deadline
|
|
641
657
|
* (now + ms) when it applies the command, so the worker never reads the clock (determinism). */
|
|
@@ -650,12 +666,15 @@ type WorkflowCommand =
|
|
|
650
666
|
seq: number;
|
|
651
667
|
signal: string;
|
|
652
668
|
}
|
|
653
|
-
/** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle.
|
|
669
|
+
/** `ctx.start_child(workflow, input)` — start a child run with its own lifecycle. A worker's
|
|
670
|
+
* `ctx.all([...])` fan-out tags every dispatched child with the same `parallelGroup` so the
|
|
671
|
+
* dashboard can render the fan as one group (parity with the gathered `recordStep` tag). */
|
|
654
672
|
| {
|
|
655
673
|
kind: 'startChild';
|
|
656
674
|
seq: number;
|
|
657
675
|
workflow: string;
|
|
658
676
|
input: unknown;
|
|
677
|
+
parallelGroup?: string;
|
|
659
678
|
};
|
|
660
679
|
/** workflow worker → engine: the result of replaying one turn of a remote workflow. */
|
|
661
680
|
interface WorkflowDecision {
|
|
@@ -693,6 +712,9 @@ interface WorkflowStepEvent {
|
|
|
693
712
|
error?: StepError;
|
|
694
713
|
/** Sub-process + log trail the step emitted so far (the handler's p-processes). */
|
|
695
714
|
events?: StepEvent[];
|
|
715
|
+
/** A worker's `ctx.gather([...])` fan tags every step's lifecycle with the same group so the
|
|
716
|
+
* dashboard renders the live fan-out as one group (parity with the `recordStep` tag). */
|
|
717
|
+
parallelGroup?: string | undefined;
|
|
696
718
|
}
|
|
697
719
|
/**
|
|
698
720
|
* Advances a workflow run one turn. The engine has one per workflow: the default {@link InProcess}
|
|
@@ -988,6 +1010,24 @@ interface WorkflowCtx {
|
|
|
988
1010
|
*/
|
|
989
1011
|
startChild<C extends WorkflowClass>(workflow: C, input: WorkflowInputOf<C>, options?: string | ChildCallOptions): Promise<string>;
|
|
990
1012
|
startChild(workflow: string, input: unknown, options?: string | ChildCallOptions): Promise<string>;
|
|
1013
|
+
/**
|
|
1014
|
+
* Run N children of the SAME workflow **in parallel** and wait for ALL of them: dispatches one
|
|
1015
|
+
* child per entry in `inputs` (concurrently, each with its own durable lifecycle), suspends — zero
|
|
1016
|
+
* compute — until every child reaches a terminal state, then resumes with their outputs in **input
|
|
1017
|
+
* order**. Child ids are group-scoped and stable (`<runId>.all.<firstSeq>.<i>`), and the running
|
|
1018
|
+
* placeholders share a `parallelGroup` tag so the dashboard renders the fan-out as one group.
|
|
1019
|
+
*
|
|
1020
|
+
* `mode` (default `waitAll`): `waitAll` waits for all then throws an aggregate {@link GatherError}
|
|
1021
|
+
* if any failed; `failFast` throws as soon as a failed child is seen (siblings are not cancelled in
|
|
1022
|
+
* v1 — their eventual results are ignored). Empty `inputs` returns `[]` with no side effects. The
|
|
1023
|
+
* wait-all / fan-out counterpart to {@link child}; parity with the Python SDK's `gather_children`.
|
|
1024
|
+
*/
|
|
1025
|
+
all<C extends WorkflowClass>(workflow: C, inputs: WorkflowInputOf<C>[], opts?: {
|
|
1026
|
+
mode?: 'waitAll' | 'failFast';
|
|
1027
|
+
}): Promise<WorkflowOutputOf<C>[]>;
|
|
1028
|
+
all<TOutput = unknown>(workflow: string, inputs: unknown[], opts?: {
|
|
1029
|
+
mode?: 'waitAll' | 'failFast';
|
|
1030
|
+
}): Promise<TOutput[]>;
|
|
991
1031
|
/**
|
|
992
1032
|
* Pause the run at this point until a human resumes it from the dashboard (or
|
|
993
1033
|
* `engine.continue(runId)`). Records a visible `pending` checkpoint so the breakpoint shows up
|
|
@@ -1257,6 +1297,18 @@ interface WorkflowEngineDeps {
|
|
|
1257
1297
|
* Omit for unlimited (the default — recovery always retries).
|
|
1258
1298
|
*/
|
|
1259
1299
|
maxRecoveryAttempts?: number | undefined;
|
|
1300
|
+
/**
|
|
1301
|
+
* Opt-in liveness deadline (ms) for a remote workflow `advance`. If the worker neither returns a
|
|
1302
|
+
* decision NOR sends a run-scoped {@link Heartbeat} within this window, the engine presumes it dead:
|
|
1303
|
+
* it rejects the advance with {@link RemoteWorkflowTimeout}, releases the lease, and lets recovery
|
|
1304
|
+
* re-drive (replaying completed steps from history). Each beat REARMS the window, so a worker still
|
|
1305
|
+
* legitimately replaying a long turn (it keeps beating) is never re-driven — closing the duplicate-
|
|
1306
|
+
* side-effect hazard a fixed {@link RemoteWorkflowExecutor} `timeoutMs` cannot avoid. Omit (default)
|
|
1307
|
+
* for the prior unbounded await — byte-identical behavior when unset. If a `RemoteWorkflowExecutor` is
|
|
1308
|
+
* ALSO constructed with its own `timeoutMs`, the shorter of the two deadlines wins; prefer setting only
|
|
1309
|
+
* one (this rearmed window for heartbeat-aware workers, or the fixed `timeoutMs` otherwise).
|
|
1310
|
+
*/
|
|
1311
|
+
remoteAdvanceSilenceMs?: number | undefined;
|
|
1260
1312
|
/**
|
|
1261
1313
|
* Build the public callback URL for a `ctx.webhook()` token (e.g.
|
|
1262
1314
|
* ``(t) => `https://api.example.com/durable/webhooks/${t}` ``). Populates
|
|
@@ -1330,6 +1382,7 @@ declare class WorkflowEngine {
|
|
|
1330
1382
|
private readonly instanceId;
|
|
1331
1383
|
private readonly leaseMs;
|
|
1332
1384
|
private readonly maxRecoveryAttempts?;
|
|
1385
|
+
private readonly remoteAdvanceSilenceMs?;
|
|
1333
1386
|
private readonly webhookUrl?;
|
|
1334
1387
|
private readonly traceparent?;
|
|
1335
1388
|
private readonly context?;
|
|
@@ -1354,7 +1407,9 @@ declare class WorkflowEngine {
|
|
|
1354
1407
|
private readonly accumulators;
|
|
1355
1408
|
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
1356
1409
|
private readonly pending;
|
|
1357
|
-
/**
|
|
1410
|
+
/** "Reset the liveness timer" callbacks keyed by the beat's target — a stepId (in-flight remote step)
|
|
1411
|
+
* or a runId (in-flight remote workflow turn) — invoked when a matching heartbeat arrives. The two id
|
|
1412
|
+
* shapes don't collide in practice: a stepId is `${runId}:${seq}`, a run key is the bare runId. */
|
|
1358
1413
|
private readonly heartbeatResets;
|
|
1359
1414
|
private readonly listeners;
|
|
1360
1415
|
/** Step interceptors (onion middleware around real local-step execution), first = outermost. */
|
|
@@ -1751,10 +1806,15 @@ declare class WorkflowEngine {
|
|
|
1751
1806
|
/** In-memory await path for a remote step with a liveness `timeoutMs` (re-dispatch on timeout). */
|
|
1752
1807
|
private callRemoteInMemory;
|
|
1753
1808
|
/**
|
|
1754
|
-
* Await
|
|
1755
|
-
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat
|
|
1756
|
-
* window, so a worker that keeps beating stays alive past
|
|
1809
|
+
* Await `resultPromise`, but reject with `onTimeout()` if neither it settles nor a heartbeat for `id`
|
|
1810
|
+
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat`, looked up in
|
|
1811
|
+
* `heartbeatResets` by `id`) REARMS the window, so a worker that keeps beating stays alive past
|
|
1812
|
+
* `timeoutMs`. Shared by the remote-STEP liveness (keyed by stepId) and the remote-WORKFLOW advance
|
|
1813
|
+
* liveness (keyed by runId); the caller supplies the timeout error and any extra cleanup via `onTimeout`.
|
|
1757
1814
|
*/
|
|
1815
|
+
private awaitWithLivenessDeadline;
|
|
1816
|
+
/** Remote-STEP liveness: await a worker result, timing out (with the step's pending-map cleanup) to
|
|
1817
|
+
* `RemoteStepTimeout` if it neither lands nor beats within `timeoutMs`. See {@link awaitWithLivenessDeadline}. */
|
|
1758
1818
|
private awaitWithHeartbeat;
|
|
1759
1819
|
}
|
|
1760
1820
|
/** Raised inside the workflow when a remote worker reports a step failure. */
|
|
@@ -1803,6 +1863,29 @@ declare class RemoteStepTimeout extends Error {
|
|
|
1803
1863
|
readonly timeoutMs: number;
|
|
1804
1864
|
constructor(stepId: string, timeoutMs: number);
|
|
1805
1865
|
}
|
|
1866
|
+
/**
|
|
1867
|
+
* Thrown by a remote {@link WorkflowExecutor} (e.g. {@link RemoteWorkflowExecutor}) when an `advance`
|
|
1868
|
+
* does not produce its decision within the configured `timeoutMs` — i.e. the worker is presumed gone
|
|
1869
|
+
* (the decision was dropped by a stall/redelivery or an instance restart spanning the in-memory waiter
|
|
1870
|
+
* map). Crucially this is NOT a run failure: the work may have actually completed and only the decision
|
|
1871
|
+
* was lost. The engine treats it as RECOVERABLE — it releases the run lease and leaves the run
|
|
1872
|
+
* `running` so `recoverIncomplete` re-drives it deterministically (replaying completed steps from
|
|
1873
|
+
* history). Distinct from a real executor error, which still fails the run.
|
|
1874
|
+
*
|
|
1875
|
+
* OPT-IN: only thrown when the executor was constructed with a `timeoutMs`. Absent a timeout, the
|
|
1876
|
+
* engine awaits the decision with its prior (unbounded) behavior — so existing users see no change.
|
|
1877
|
+
*
|
|
1878
|
+
* Known hazard: a timeout that fires while a worker is LEGITIMATELY still executing a not-yet-
|
|
1879
|
+
* checkpointed step will re-drive and re-run that in-flight step → DUPLICATE side effects. Therefore
|
|
1880
|
+
* the timeout is only safe when set GENEROUSLY (longer than the longest legitimate single turn). The
|
|
1881
|
+
* robust fix — a liveness/heartbeat-rearmed deadline so only a genuinely-dead worker re-drives — is the
|
|
1882
|
+
* documented follow-up (see the Track A diagnosis doc, "Part B").
|
|
1883
|
+
*/
|
|
1884
|
+
declare class RemoteWorkflowTimeout extends Error {
|
|
1885
|
+
readonly taskId: string;
|
|
1886
|
+
readonly timeoutMs: number;
|
|
1887
|
+
constructor(taskId: string, timeoutMs: number);
|
|
1888
|
+
}
|
|
1806
1889
|
/**
|
|
1807
1890
|
* Thrown on resume when the workflow code no longer matches the recorded history: the step at a
|
|
1808
1891
|
* logical position has a different name/kind than the checkpoint saved there. This means the
|
|
@@ -1816,6 +1899,24 @@ declare class NonDeterminismError extends Error {
|
|
|
1816
1899
|
readonly seq: number;
|
|
1817
1900
|
constructor(runId: string, seq: number, expected: string, recorded: string);
|
|
1818
1901
|
}
|
|
1902
|
+
/**
|
|
1903
|
+
* Thrown by `ctx.all` when one or more parallel child workflows fail. Carries the per-item failures
|
|
1904
|
+
* (input index, child run id, error message) and presents an aggregate message summarizing the count
|
|
1905
|
+
* and the failing ids — the wait-all/fail-fast counterpart to a single child's FatalError. Mirrors
|
|
1906
|
+
* the Python SDK's `GatherFailed`.
|
|
1907
|
+
*/
|
|
1908
|
+
declare class GatherError extends Error {
|
|
1909
|
+
readonly failures: {
|
|
1910
|
+
index: number;
|
|
1911
|
+
id: string;
|
|
1912
|
+
error: string;
|
|
1913
|
+
}[];
|
|
1914
|
+
constructor(failures: {
|
|
1915
|
+
index: number;
|
|
1916
|
+
id: string;
|
|
1917
|
+
error: string;
|
|
1918
|
+
}[], total?: number);
|
|
1919
|
+
}
|
|
1819
1920
|
declare class WorkflowSuspended extends Error {
|
|
1820
1921
|
/** Epoch ms to auto-resume (durable sleep), or undefined when waiting on an external signal. */
|
|
1821
1922
|
readonly wakeAt?: number | undefined;
|
|
@@ -2059,6 +2160,14 @@ declare function prevCronFireMs(expr: string, nowMs: number, timezone?: string):
|
|
|
2059
2160
|
*/
|
|
2060
2161
|
declare function runSchedules(engine: Pick<WorkflowEngine, 'start' | 'getRun'>, schedules: readonly ScheduledWorkflow[], nowMs: number, opts?: RunSchedulesOptions): Promise<string[]>;
|
|
2061
2162
|
|
|
2163
|
+
/**
|
|
2164
|
+
* A {@link StepLogger} that appends to `events`, stamping each line with `now()`. Shared by the
|
|
2165
|
+
* local-step path (`ctx.step`) and the remote-worker path (`runStepHandler`) so a step records
|
|
2166
|
+
* the same {@link StepEvent} shape wherever it runs — the TypeScript twin of the Python SDK's
|
|
2167
|
+
* `StepContext`.
|
|
2168
|
+
*/
|
|
2169
|
+
declare function createStepLogger(events: StepEvent[], now: () => number): StepLogger;
|
|
2170
|
+
|
|
2062
2171
|
/**
|
|
2063
2172
|
* Does a run's search attributes satisfy EVERY filter (AND)? A missing key never matches (so `ne`
|
|
2064
2173
|
* on an absent key is false too — the attribute simply isn't there to compare). Empty/undefined
|
|
@@ -2218,13 +2327,16 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
2218
2327
|
declare class InMemoryTransport implements Transport, ControlPlane {
|
|
2219
2328
|
private readonly handlers;
|
|
2220
2329
|
private resultHandler?;
|
|
2330
|
+
private heartbeatHandler?;
|
|
2221
2331
|
private stepEventHandler?;
|
|
2222
2332
|
private readonly controlHandlers;
|
|
2223
2333
|
/** Register a fake worker handler for a step name. */
|
|
2224
2334
|
handle(name: string, fn: StepHandler): void;
|
|
2225
2335
|
dispatch(task: RemoteTask): Promise<void>;
|
|
2226
2336
|
onResult(handler: (result: StepResult) => Promise<void>): void;
|
|
2227
|
-
onHeartbeat(
|
|
2337
|
+
onHeartbeat(handler: (beat: Heartbeat) => Promise<void>): void;
|
|
2338
|
+
/** Test hook: deliver a liveness heartbeat to the engine, as a real broker would on its channel. */
|
|
2339
|
+
emitHeartbeat(beat: Heartbeat): Promise<void>;
|
|
2228
2340
|
/** Workflow step lifecycle: deliver to the engine's handler (async, mirroring a real broker). */
|
|
2229
2341
|
dispatchStepEvent(event: WorkflowStepEvent): Promise<void>;
|
|
2230
2342
|
onStepEvent(handler: (event: WorkflowStepEvent) => Promise<void>): void;
|
|
@@ -2232,4 +2344,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
2232
2344
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
2233
2345
|
}
|
|
2234
2346
|
|
|
2235
|
-
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 };
|
|
2347
|
+
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 };
|