@dudousxd/nestjs-durable-core 0.34.0 → 0.36.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 +112 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -2
- package/dist/index.d.ts +46 -2
- package/dist/index.js +111 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -209,7 +209,15 @@ declare function workflowName(ref: WorkflowRef): string;
|
|
|
209
209
|
*/
|
|
210
210
|
type RunStatus =
|
|
211
211
|
/** Created + enqueued by `start`, not yet picked up — a worker will lease and execute it. */
|
|
212
|
-
'pending' | 'running' | 'suspended'
|
|
212
|
+
'pending' | 'running' | 'suspended'
|
|
213
|
+
/**
|
|
214
|
+
* Compensating cancel in progress: `cancel(runId, { compensate: true })` has been requested and the
|
|
215
|
+
* saga undo is running in the background. NON-TERMINAL — the run is still in-flight (counted by the
|
|
216
|
+
* admission gate, re-driven by recovery if the engine crashes mid-compensation), and flips to
|
|
217
|
+
* `cancelled` once compensation completes. A non-compensating cancel skips this and goes straight to
|
|
218
|
+
* `cancelled`.
|
|
219
|
+
*/
|
|
220
|
+
| 'cancelling' | 'completed' | 'failed' | 'cancelled'
|
|
213
221
|
/** Dead-letter: recovery gave up after `maxRecoveryAttempts` (a poison pill). Terminal; inspect it. */
|
|
214
222
|
| 'dead';
|
|
215
223
|
/** One execution of a workflow. The unit of durability and the unit shown in the dashboard. */
|
|
@@ -409,6 +417,15 @@ interface StateStore {
|
|
|
409
417
|
* No-op if the run doesn't exist. (Token-keyed buffered signals are transient and left alone.)
|
|
410
418
|
*/
|
|
411
419
|
deleteRun(runId: string): Promise<void>;
|
|
420
|
+
/**
|
|
421
|
+
* Hard-delete up to `limit` terminal runs that fall OUTSIDE `policy` (oldest / over the count cap
|
|
422
|
+
* first), cascading to their child rows exactly like {@link deleteRun}. Returns how many runs were
|
|
423
|
+
* deleted — call again while it returns `limit` to drain a large backlog in bounded batches.
|
|
424
|
+
*
|
|
425
|
+
* Optional: only adapters that implement a bulk prune provide it; the retention poller no-ops with a
|
|
426
|
+
* warning when the configured store omits it. See {@link RetentionPolicy} for the keep/prune rule.
|
|
427
|
+
*/
|
|
428
|
+
pruneTerminalRuns?(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
412
429
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
413
430
|
/**
|
|
414
431
|
* Persist a checkpoint and advance the run atomically. Durable semantics depend on this
|
|
@@ -483,6 +500,26 @@ interface StateStore {
|
|
|
483
500
|
*/
|
|
484
501
|
listCheckpointsByNamePrefix?(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
485
502
|
}
|
|
503
|
+
/** The terminal run statuses — a run in one of these is finished and will never change on its own.
|
|
504
|
+
* Only these are eligible for retention pruning (a {@link RetentionPolicy} targeting a non-terminal
|
|
505
|
+
* status would race the engine and delete live work). */
|
|
506
|
+
declare const TERMINAL_RUN_STATUSES: readonly RunStatus[];
|
|
507
|
+
/**
|
|
508
|
+
* One retention rule for hard-pruning terminal run history. Within `statuses` (terminal only), a run
|
|
509
|
+
* is KEPT only if it satisfies EVERY bound you set, and pruned the moment it violates any:
|
|
510
|
+
* - `maxAgeMs` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
|
|
511
|
+
* `now - maxAgeMs`.
|
|
512
|
+
* - `maxCount` — keep only the `maxCount` most-recent (by `updatedAt`) runs in the status set; prune
|
|
513
|
+
* everything past that.
|
|
514
|
+
*
|
|
515
|
+
* Set one or both (both = the most-restrictive wins: capped at `maxCount` rows AND nothing older than
|
|
516
|
+
* `maxAgeMs`). Statuses not named by any policy are never pruned.
|
|
517
|
+
*/
|
|
518
|
+
interface RetentionPolicy {
|
|
519
|
+
statuses: RunStatus[];
|
|
520
|
+
maxAgeMs?: number;
|
|
521
|
+
maxCount?: number;
|
|
522
|
+
}
|
|
486
523
|
/** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
|
|
487
524
|
type SearchAttributes = Record<string, string | number | boolean>;
|
|
488
525
|
type AttributeOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
@@ -1751,6 +1788,13 @@ declare class WorkflowEngine {
|
|
|
1751
1788
|
/** Apply a turn's commands: persist recorded local steps, dispatch remote calls, schedule timers.
|
|
1752
1789
|
* Returns the earliest timer deadline to suspend on (or undefined — suspended on a result). */
|
|
1753
1790
|
private applyCommands;
|
|
1791
|
+
/**
|
|
1792
|
+
* Undo the registered saga compensations in reverse and settle the run `cancelled`. Shared by EVERY
|
|
1793
|
+
* exit path of a run with a compensating cancel in flight — whether the resumed replay returned,
|
|
1794
|
+
* suspended, hit continue-as-new, or threw — so a cancel is never lost just because the body didn't
|
|
1795
|
+
* stop at a suspension point. Clears the in-memory cancel flag.
|
|
1796
|
+
*/
|
|
1797
|
+
private compensateAndCancel;
|
|
1754
1798
|
/** The run body, lease held + renewed by {@link execute}. */
|
|
1755
1799
|
private runExecution;
|
|
1756
1800
|
/**
|
|
@@ -2344,4 +2388,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
2344
2388
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
2345
2389
|
}
|
|
2346
2390
|
|
|
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 };
|
|
2391
|
+
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 RetentionPolicy, 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, TERMINAL_RUN_STATUSES, 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
|
@@ -209,7 +209,15 @@ declare function workflowName(ref: WorkflowRef): string;
|
|
|
209
209
|
*/
|
|
210
210
|
type RunStatus =
|
|
211
211
|
/** Created + enqueued by `start`, not yet picked up — a worker will lease and execute it. */
|
|
212
|
-
'pending' | 'running' | 'suspended'
|
|
212
|
+
'pending' | 'running' | 'suspended'
|
|
213
|
+
/**
|
|
214
|
+
* Compensating cancel in progress: `cancel(runId, { compensate: true })` has been requested and the
|
|
215
|
+
* saga undo is running in the background. NON-TERMINAL — the run is still in-flight (counted by the
|
|
216
|
+
* admission gate, re-driven by recovery if the engine crashes mid-compensation), and flips to
|
|
217
|
+
* `cancelled` once compensation completes. A non-compensating cancel skips this and goes straight to
|
|
218
|
+
* `cancelled`.
|
|
219
|
+
*/
|
|
220
|
+
| 'cancelling' | 'completed' | 'failed' | 'cancelled'
|
|
213
221
|
/** Dead-letter: recovery gave up after `maxRecoveryAttempts` (a poison pill). Terminal; inspect it. */
|
|
214
222
|
| 'dead';
|
|
215
223
|
/** One execution of a workflow. The unit of durability and the unit shown in the dashboard. */
|
|
@@ -409,6 +417,15 @@ interface StateStore {
|
|
|
409
417
|
* No-op if the run doesn't exist. (Token-keyed buffered signals are transient and left alone.)
|
|
410
418
|
*/
|
|
411
419
|
deleteRun(runId: string): Promise<void>;
|
|
420
|
+
/**
|
|
421
|
+
* Hard-delete up to `limit` terminal runs that fall OUTSIDE `policy` (oldest / over the count cap
|
|
422
|
+
* first), cascading to their child rows exactly like {@link deleteRun}. Returns how many runs were
|
|
423
|
+
* deleted — call again while it returns `limit` to drain a large backlog in bounded batches.
|
|
424
|
+
*
|
|
425
|
+
* Optional: only adapters that implement a bulk prune provide it; the retention poller no-ops with a
|
|
426
|
+
* warning when the configured store omits it. See {@link RetentionPolicy} for the keep/prune rule.
|
|
427
|
+
*/
|
|
428
|
+
pruneTerminalRuns?(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
412
429
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
413
430
|
/**
|
|
414
431
|
* Persist a checkpoint and advance the run atomically. Durable semantics depend on this
|
|
@@ -483,6 +500,26 @@ interface StateStore {
|
|
|
483
500
|
*/
|
|
484
501
|
listCheckpointsByNamePrefix?(runId: string, prefixes: string[]): Promise<StepCheckpoint[]>;
|
|
485
502
|
}
|
|
503
|
+
/** The terminal run statuses — a run in one of these is finished and will never change on its own.
|
|
504
|
+
* Only these are eligible for retention pruning (a {@link RetentionPolicy} targeting a non-terminal
|
|
505
|
+
* status would race the engine and delete live work). */
|
|
506
|
+
declare const TERMINAL_RUN_STATUSES: readonly RunStatus[];
|
|
507
|
+
/**
|
|
508
|
+
* One retention rule for hard-pruning terminal run history. Within `statuses` (terminal only), a run
|
|
509
|
+
* is KEPT only if it satisfies EVERY bound you set, and pruned the moment it violates any:
|
|
510
|
+
* - `maxAgeMs` — prune runs whose `updatedAt` (≈ the time they reached terminal) is older than
|
|
511
|
+
* `now - maxAgeMs`.
|
|
512
|
+
* - `maxCount` — keep only the `maxCount` most-recent (by `updatedAt`) runs in the status set; prune
|
|
513
|
+
* everything past that.
|
|
514
|
+
*
|
|
515
|
+
* Set one or both (both = the most-restrictive wins: capped at `maxCount` rows AND nothing older than
|
|
516
|
+
* `maxAgeMs`). Statuses not named by any policy are never pruned.
|
|
517
|
+
*/
|
|
518
|
+
interface RetentionPolicy {
|
|
519
|
+
statuses: RunStatus[];
|
|
520
|
+
maxAgeMs?: number;
|
|
521
|
+
maxCount?: number;
|
|
522
|
+
}
|
|
486
523
|
/** Typed, queryable per-run data — exact values for `eq`/`ne`, numbers/strings for range ops. */
|
|
487
524
|
type SearchAttributes = Record<string, string | number | boolean>;
|
|
488
525
|
type AttributeOp = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
@@ -1751,6 +1788,13 @@ declare class WorkflowEngine {
|
|
|
1751
1788
|
/** Apply a turn's commands: persist recorded local steps, dispatch remote calls, schedule timers.
|
|
1752
1789
|
* Returns the earliest timer deadline to suspend on (or undefined — suspended on a result). */
|
|
1753
1790
|
private applyCommands;
|
|
1791
|
+
/**
|
|
1792
|
+
* Undo the registered saga compensations in reverse and settle the run `cancelled`. Shared by EVERY
|
|
1793
|
+
* exit path of a run with a compensating cancel in flight — whether the resumed replay returned,
|
|
1794
|
+
* suspended, hit continue-as-new, or threw — so a cancel is never lost just because the body didn't
|
|
1795
|
+
* stop at a suspension point. Clears the in-memory cancel flag.
|
|
1796
|
+
*/
|
|
1797
|
+
private compensateAndCancel;
|
|
1754
1798
|
/** The run body, lease held + renewed by {@link execute}. */
|
|
1755
1799
|
private runExecution;
|
|
1756
1800
|
/**
|
|
@@ -2344,4 +2388,4 @@ declare class InMemoryTransport implements Transport, ControlPlane {
|
|
|
2344
2388
|
onControl(handler: (msg: ControlMessage) => void): void;
|
|
2345
2389
|
}
|
|
2346
2390
|
|
|
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 };
|
|
2391
|
+
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 RetentionPolicy, 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, TERMINAL_RUN_STATUSES, 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
|
@@ -733,7 +733,8 @@ var SingletonGate = class {
|
|
|
733
733
|
statuses: [
|
|
734
734
|
"pending",
|
|
735
735
|
"running",
|
|
736
|
-
"suspended"
|
|
736
|
+
"suspended",
|
|
737
|
+
"cancelling"
|
|
737
738
|
]
|
|
738
739
|
});
|
|
739
740
|
if (queued.length >= cap) {
|
|
@@ -751,7 +752,8 @@ var SingletonGate = class {
|
|
|
751
752
|
workflow: run.workflow,
|
|
752
753
|
statuses: [
|
|
753
754
|
"running",
|
|
754
|
-
"suspended"
|
|
755
|
+
"suspended",
|
|
756
|
+
"cancelling"
|
|
755
757
|
]
|
|
756
758
|
})).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id));
|
|
757
759
|
const idx = inflight.findIndex((r) => r.id === run.id);
|
|
@@ -1900,14 +1902,16 @@ var WorkflowEngine = class {
|
|
|
1900
1902
|
continue;
|
|
1901
1903
|
}
|
|
1902
1904
|
await this.store.releaseRunLock(run.id);
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1905
|
+
if (run.status !== "cancelling") {
|
|
1906
|
+
await this.store.updateRun(run.id, {
|
|
1907
|
+
status: "pending",
|
|
1908
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1907
1911
|
await this.runDispatcher.dispatch(run.id);
|
|
1908
1912
|
results.push({
|
|
1909
1913
|
runId: run.id,
|
|
1910
|
-
status: "pending"
|
|
1914
|
+
status: run.status === "cancelling" ? "cancelling" : "pending"
|
|
1911
1915
|
});
|
|
1912
1916
|
}
|
|
1913
1917
|
return results;
|
|
@@ -2026,7 +2030,7 @@ var WorkflowEngine = class {
|
|
|
2026
2030
|
* `start` when a call site needs the outcome — `await start(...); const r = await waitForRun(id)`.
|
|
2027
2031
|
*/
|
|
2028
2032
|
waitForRun(runId, opts) {
|
|
2029
|
-
const isSettled = /* @__PURE__ */ __name((s) => s !== "pending" && s !== "running", "isSettled");
|
|
2033
|
+
const isSettled = /* @__PURE__ */ __name((s) => s !== "pending" && s !== "running" && s !== "cancelling", "isSettled");
|
|
2030
2034
|
const toResult = /* @__PURE__ */ __name((run) => ({
|
|
2031
2035
|
runId,
|
|
2032
2036
|
status: run.status,
|
|
@@ -2190,8 +2194,19 @@ var WorkflowEngine = class {
|
|
|
2190
2194
|
error: run.error
|
|
2191
2195
|
};
|
|
2192
2196
|
}
|
|
2197
|
+
if (run.status === "cancelling") {
|
|
2198
|
+
return {
|
|
2199
|
+
runId,
|
|
2200
|
+
status: "cancelling"
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2193
2203
|
if (opts?.compensate && (run.status === "suspended" || run.status === "running")) {
|
|
2194
2204
|
this.cancelRequested.add(runId);
|
|
2205
|
+
await this.store.updateRun(runId, {
|
|
2206
|
+
status: "cancelling",
|
|
2207
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2208
|
+
});
|
|
2209
|
+
run.status = "cancelling";
|
|
2195
2210
|
if (this.controlPlane) {
|
|
2196
2211
|
void this.controlPlane.publishControl({
|
|
2197
2212
|
kind: "cancel",
|
|
@@ -2205,7 +2220,7 @@ var WorkflowEngine = class {
|
|
|
2205
2220
|
await this.cancelChildren(runId, opts);
|
|
2206
2221
|
return {
|
|
2207
2222
|
runId,
|
|
2208
|
-
status:
|
|
2223
|
+
status: "cancelling"
|
|
2209
2224
|
};
|
|
2210
2225
|
}
|
|
2211
2226
|
const error = {
|
|
@@ -2596,6 +2611,36 @@ var WorkflowEngine = class {
|
|
|
2596
2611
|
};
|
|
2597
2612
|
}
|
|
2598
2613
|
async runRemoteExecution(run, registered) {
|
|
2614
|
+
if (run.status === "cancelling") {
|
|
2615
|
+
const error = {
|
|
2616
|
+
message: "cancelled"
|
|
2617
|
+
};
|
|
2618
|
+
await this.store.updateRun(run.id, {
|
|
2619
|
+
status: "cancelled",
|
|
2620
|
+
error,
|
|
2621
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2622
|
+
});
|
|
2623
|
+
this.emit({
|
|
2624
|
+
type: "run.failed",
|
|
2625
|
+
runId: run.id,
|
|
2626
|
+
workflow: run.workflow,
|
|
2627
|
+
error
|
|
2628
|
+
});
|
|
2629
|
+
this.notifyCancelled(run.id);
|
|
2630
|
+
if (this.controlPlane) {
|
|
2631
|
+
void this.controlPlane.publishControl({
|
|
2632
|
+
kind: "cancel",
|
|
2633
|
+
runId: run.id,
|
|
2634
|
+
from: this.instanceId
|
|
2635
|
+
}).catch(() => void 0);
|
|
2636
|
+
}
|
|
2637
|
+
await this.store.releaseRunLock(run.id);
|
|
2638
|
+
return {
|
|
2639
|
+
runId: run.id,
|
|
2640
|
+
status: "cancelled",
|
|
2641
|
+
error
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2599
2644
|
const remote = registered.remote;
|
|
2600
2645
|
if (run.status === "pending") {
|
|
2601
2646
|
await this.store.updateRun(run.id, {
|
|
@@ -2689,6 +2734,15 @@ var WorkflowEngine = class {
|
|
|
2689
2734
|
status: "cancelled"
|
|
2690
2735
|
};
|
|
2691
2736
|
}
|
|
2737
|
+
const fresh = await this.store.getRun(run.id);
|
|
2738
|
+
if (fresh && (fresh.status === "cancelled" || fresh.status === "completed" || fresh.status === "dead")) {
|
|
2739
|
+
return {
|
|
2740
|
+
runId: run.id,
|
|
2741
|
+
status: fresh.status,
|
|
2742
|
+
output: fresh.output,
|
|
2743
|
+
error: fresh.error
|
|
2744
|
+
};
|
|
2745
|
+
}
|
|
2692
2746
|
const wakeAt = await this.applyCommands(run, decision.commands);
|
|
2693
2747
|
return this.settleRun(run, {
|
|
2694
2748
|
kind: "suspended",
|
|
@@ -2856,8 +2910,41 @@ var WorkflowEngine = class {
|
|
|
2856
2910
|
}
|
|
2857
2911
|
return wakeAt;
|
|
2858
2912
|
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Undo the registered saga compensations in reverse and settle the run `cancelled`. Shared by EVERY
|
|
2915
|
+
* exit path of a run with a compensating cancel in flight — whether the resumed replay returned,
|
|
2916
|
+
* suspended, hit continue-as-new, or threw — so a cancel is never lost just because the body didn't
|
|
2917
|
+
* stop at a suspension point. Clears the in-memory cancel flag.
|
|
2918
|
+
*/
|
|
2919
|
+
async compensateAndCancel(run, compensations) {
|
|
2920
|
+
this.cancelRequested.delete(run.id);
|
|
2921
|
+
for (let i = compensations.length - 1; i >= 0; i -= 1) {
|
|
2922
|
+
const comp = compensations[i];
|
|
2923
|
+
if (comp) await this.runCompensation(run, comp);
|
|
2924
|
+
}
|
|
2925
|
+
const error = {
|
|
2926
|
+
message: "cancelled"
|
|
2927
|
+
};
|
|
2928
|
+
await this.store.updateRun(run.id, {
|
|
2929
|
+
status: "cancelled",
|
|
2930
|
+
error,
|
|
2931
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
2932
|
+
});
|
|
2933
|
+
this.emit({
|
|
2934
|
+
type: "run.failed",
|
|
2935
|
+
runId: run.id,
|
|
2936
|
+
workflow: run.workflow,
|
|
2937
|
+
error
|
|
2938
|
+
});
|
|
2939
|
+
return {
|
|
2940
|
+
runId: run.id,
|
|
2941
|
+
status: "cancelled",
|
|
2942
|
+
error
|
|
2943
|
+
};
|
|
2944
|
+
}
|
|
2859
2945
|
/** The run body, lease held + renewed by {@link execute}. */
|
|
2860
2946
|
async runExecution(run, fn, registered) {
|
|
2947
|
+
if (run.status === "cancelling") this.cancelRequested.add(run.id);
|
|
2861
2948
|
if (run.status === "pending") {
|
|
2862
2949
|
await this.store.updateRun(run.id, {
|
|
2863
2950
|
status: "running",
|
|
@@ -2870,7 +2957,7 @@ var WorkflowEngine = class {
|
|
|
2870
2957
|
workflow: run.workflow
|
|
2871
2958
|
});
|
|
2872
2959
|
}
|
|
2873
|
-
if (registered?.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
2960
|
+
if (run.status !== "cancelling" && registered?.singleton && !await this.singletons.admit(run, registered.singleton)) {
|
|
2874
2961
|
const wakeAt = this.singletons.retryWakeAt();
|
|
2875
2962
|
await this.store.updateRun(run.id, {
|
|
2876
2963
|
status: "suspended",
|
|
@@ -2895,12 +2982,14 @@ var WorkflowEngine = class {
|
|
|
2895
2982
|
const ctx = createWorkflowCtx(this.ctxHostFor(replay), run.id, compensations, run.workflow);
|
|
2896
2983
|
try {
|
|
2897
2984
|
const output = await fn(ctx, run.input);
|
|
2985
|
+
if (this.cancelRequested.has(run.id)) return this.compensateAndCancel(run, compensations);
|
|
2898
2986
|
return this.settleRun(run, {
|
|
2899
2987
|
kind: "completed",
|
|
2900
2988
|
output
|
|
2901
2989
|
});
|
|
2902
2990
|
} catch (err) {
|
|
2903
2991
|
if (err instanceof ContinueAsNew) {
|
|
2992
|
+
if (this.cancelRequested.has(run.id)) return this.compensateAndCancel(run, compensations);
|
|
2904
2993
|
await this.store.updateRun(run.id, {
|
|
2905
2994
|
status: "completed",
|
|
2906
2995
|
output: void 0,
|
|
@@ -2924,37 +3013,13 @@ var WorkflowEngine = class {
|
|
|
2924
3013
|
};
|
|
2925
3014
|
}
|
|
2926
3015
|
if (err instanceof WorkflowSuspended) {
|
|
2927
|
-
if (this.cancelRequested.has(run.id))
|
|
2928
|
-
this.cancelRequested.delete(run.id);
|
|
2929
|
-
for (let i = compensations.length - 1; i >= 0; i -= 1) {
|
|
2930
|
-
const comp = compensations[i];
|
|
2931
|
-
if (comp) await this.runCompensation(run, comp);
|
|
2932
|
-
}
|
|
2933
|
-
const error2 = {
|
|
2934
|
-
message: "cancelled"
|
|
2935
|
-
};
|
|
2936
|
-
await this.store.updateRun(run.id, {
|
|
2937
|
-
status: "cancelled",
|
|
2938
|
-
error: error2,
|
|
2939
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
2940
|
-
});
|
|
2941
|
-
this.emit({
|
|
2942
|
-
type: "run.failed",
|
|
2943
|
-
runId: run.id,
|
|
2944
|
-
workflow: run.workflow,
|
|
2945
|
-
error: error2
|
|
2946
|
-
});
|
|
2947
|
-
return {
|
|
2948
|
-
runId: run.id,
|
|
2949
|
-
status: "cancelled",
|
|
2950
|
-
error: error2
|
|
2951
|
-
};
|
|
2952
|
-
}
|
|
3016
|
+
if (this.cancelRequested.has(run.id)) return this.compensateAndCancel(run, compensations);
|
|
2953
3017
|
return this.settleRun(run, {
|
|
2954
3018
|
kind: "suspended",
|
|
2955
3019
|
wakeAt: err.wakeAt
|
|
2956
3020
|
});
|
|
2957
3021
|
}
|
|
3022
|
+
if (this.cancelRequested.has(run.id)) return this.compensateAndCancel(run, compensations);
|
|
2958
3023
|
const error = {
|
|
2959
3024
|
message: err instanceof Error ? err.message : String(err),
|
|
2960
3025
|
stack: err instanceof Error ? err.stack : void 0
|
|
@@ -3348,6 +3413,14 @@ var RemoteStepError = class extends Error {
|
|
|
3348
3413
|
}
|
|
3349
3414
|
};
|
|
3350
3415
|
|
|
3416
|
+
// src/interfaces.ts
|
|
3417
|
+
var TERMINAL_RUN_STATUSES = [
|
|
3418
|
+
"completed",
|
|
3419
|
+
"failed",
|
|
3420
|
+
"cancelled",
|
|
3421
|
+
"dead"
|
|
3422
|
+
];
|
|
3423
|
+
|
|
3351
3424
|
// src/remote-step-factory.ts
|
|
3352
3425
|
function remoteStep(config) {
|
|
3353
3426
|
return {
|
|
@@ -3908,7 +3981,7 @@ var InMemoryStateStore = class {
|
|
|
3908
3981
|
async listIncompleteRuns() {
|
|
3909
3982
|
return [
|
|
3910
3983
|
...this.runs.values()
|
|
3911
|
-
].filter((r) => r.status === "running").map((r) => ({
|
|
3984
|
+
].filter((r) => r.status === "running" || r.status === "cancelling").map((r) => ({
|
|
3912
3985
|
...r
|
|
3913
3986
|
}));
|
|
3914
3987
|
}
|
|
@@ -4160,6 +4233,7 @@ export {
|
|
|
4160
4233
|
STATE_STORE_CANONICAL,
|
|
4161
4234
|
SignalTimeoutError,
|
|
4162
4235
|
SingletonQueueFullError,
|
|
4236
|
+
TERMINAL_RUN_STATUSES,
|
|
4163
4237
|
TRANSPORT,
|
|
4164
4238
|
TRANSPORT_CANONICAL,
|
|
4165
4239
|
WORKFLOW_NAME_KEY,
|