@dudousxd/nestjs-durable-core 0.75.2 → 0.76.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 +41 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +41 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -969,6 +969,16 @@ interface StateStore {
|
|
|
969
969
|
* being a single transaction; stores without transactions cannot give the strong guarantee.
|
|
970
970
|
*/
|
|
971
971
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
972
|
+
/**
|
|
973
|
+
* Overwrite the checkpoint at `(runId, seq)` with `checkpoint`, but only while the stored row is
|
|
974
|
+
* still `pending`; resolves whether it wrote. The engine writes a dispatched step's row from two
|
|
975
|
+
* places at once (a turn stamping its redispatch deadline, and the worker's result settling it),
|
|
976
|
+
* so a blind {@link saveCheckpoint} from a row read a moment earlier can put a `completed` step
|
|
977
|
+
* back to `pending`. This is the compare-and-set that prevents it.
|
|
978
|
+
*
|
|
979
|
+
* Optional: a store that omits it falls back to {@link saveCheckpoint}, which keeps that race.
|
|
980
|
+
*/
|
|
981
|
+
saveCheckpointIfPending?(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
972
982
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
973
983
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
974
984
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
@@ -4833,6 +4843,8 @@ declare class WorkflowEngine {
|
|
|
4833
4843
|
* capable+compatible worker has appeared, else re-parks with a fresh `wakeAt` (design §7.5). */
|
|
4834
4844
|
private dueBlockedRuns;
|
|
4835
4845
|
private callRemote;
|
|
4846
|
+
/** {@link StateStore.saveCheckpointIfPending}, or a plain save on a store without it. */
|
|
4847
|
+
private savePendingCheckpoint;
|
|
4836
4848
|
/** Enqueue a remote step task to its worker group via the transport pool. Shared by the initial
|
|
4837
4849
|
* dispatch, the opt-in self-heal re-dispatch (see `remoteRedispatchMs`), and the explicit
|
|
4838
4850
|
* {@link redispatchPending}. Carries the current trace/context so a re-dispatch is traceable. */
|
|
@@ -5508,6 +5520,9 @@ declare class CodecStateStore implements StateStore {
|
|
|
5508
5520
|
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
5509
5521
|
* engine's per-run fallback still kicks in through the wrapper. */
|
|
5510
5522
|
readonly deleteRuns?: (runIds: string[]) => Promise<void>;
|
|
5523
|
+
/** Encodes like {@link saveCheckpoint}. Bound only when the inner store has it, so the engine's
|
|
5524
|
+
* plain-save fallback still kicks in through the wrapper. */
|
|
5525
|
+
readonly saveCheckpointIfPending?: (checkpoint: StepCheckpoint) => Promise<boolean>;
|
|
5511
5526
|
/** Forwarded verbatim — retention selects by status/updatedAt/scope, never by payload. Bound only
|
|
5512
5527
|
* when the inner store prunes, so the retention poller still sees (and warns about) its absence. */
|
|
5513
5528
|
readonly pruneTerminalRuns?: (policy: RetentionPolicy, nowMs: number, limit: number) => Promise<number>;
|
|
@@ -5977,6 +5992,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
5977
5992
|
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
5978
5993
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
5979
5994
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
5995
|
+
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
5980
5996
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
5981
5997
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
5982
5998
|
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -969,6 +969,16 @@ interface StateStore {
|
|
|
969
969
|
* being a single transaction; stores without transactions cannot give the strong guarantee.
|
|
970
970
|
*/
|
|
971
971
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
972
|
+
/**
|
|
973
|
+
* Overwrite the checkpoint at `(runId, seq)` with `checkpoint`, but only while the stored row is
|
|
974
|
+
* still `pending`; resolves whether it wrote. The engine writes a dispatched step's row from two
|
|
975
|
+
* places at once (a turn stamping its redispatch deadline, and the worker's result settling it),
|
|
976
|
+
* so a blind {@link saveCheckpoint} from a row read a moment earlier can put a `completed` step
|
|
977
|
+
* back to `pending`. This is the compare-and-set that prevents it.
|
|
978
|
+
*
|
|
979
|
+
* Optional: a store that omits it falls back to {@link saveCheckpoint}, which keeps that race.
|
|
980
|
+
*/
|
|
981
|
+
saveCheckpointIfPending?(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
972
982
|
/** Used by recovery on boot to find runs to resume (crashed, left `running`). */
|
|
973
983
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
974
984
|
/** The oldest `pending` runs awaiting dispatch (FIFO, by `createdAt`), capped at `limit`. */
|
|
@@ -4833,6 +4843,8 @@ declare class WorkflowEngine {
|
|
|
4833
4843
|
* capable+compatible worker has appeared, else re-parks with a fresh `wakeAt` (design §7.5). */
|
|
4834
4844
|
private dueBlockedRuns;
|
|
4835
4845
|
private callRemote;
|
|
4846
|
+
/** {@link StateStore.saveCheckpointIfPending}, or a plain save on a store without it. */
|
|
4847
|
+
private savePendingCheckpoint;
|
|
4836
4848
|
/** Enqueue a remote step task to its worker group via the transport pool. Shared by the initial
|
|
4837
4849
|
* dispatch, the opt-in self-heal re-dispatch (see `remoteRedispatchMs`), and the explicit
|
|
4838
4850
|
* {@link redispatchPending}. Carries the current trace/context so a re-dispatch is traceable. */
|
|
@@ -5508,6 +5520,9 @@ declare class CodecStateStore implements StateStore {
|
|
|
5508
5520
|
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
5509
5521
|
* engine's per-run fallback still kicks in through the wrapper. */
|
|
5510
5522
|
readonly deleteRuns?: (runIds: string[]) => Promise<void>;
|
|
5523
|
+
/** Encodes like {@link saveCheckpoint}. Bound only when the inner store has it, so the engine's
|
|
5524
|
+
* plain-save fallback still kicks in through the wrapper. */
|
|
5525
|
+
readonly saveCheckpointIfPending?: (checkpoint: StepCheckpoint) => Promise<boolean>;
|
|
5511
5526
|
/** Forwarded verbatim — retention selects by status/updatedAt/scope, never by payload. Bound only
|
|
5512
5527
|
* when the inner store prunes, so the retention poller still sees (and warns about) its absence. */
|
|
5513
5528
|
readonly pruneTerminalRuns?: (policy: RetentionPolicy, nowMs: number, limit: number) => Promise<number>;
|
|
@@ -5977,6 +5992,7 @@ declare class InMemoryStateStore implements StateStore {
|
|
|
5977
5992
|
pruneTerminalRuns(policy: RetentionPolicy, nowMs: number, limit: number): Promise<number>;
|
|
5978
5993
|
getCheckpoint(runId: string, seq: number): Promise<StepCheckpoint | null>;
|
|
5979
5994
|
saveCheckpoint(checkpoint: StepCheckpoint): Promise<void>;
|
|
5995
|
+
saveCheckpointIfPending(checkpoint: StepCheckpoint): Promise<boolean>;
|
|
5980
5996
|
listIncompleteRuns(namespace?: string): Promise<WorkflowRun[]>;
|
|
5981
5997
|
listPendingRuns(limit: number, namespace?: string): Promise<WorkflowRun[]>;
|
|
5982
5998
|
listDueTimers(nowMs: number, namespace?: string): Promise<WorkflowRun[]>;
|
package/dist/index.js
CHANGED
|
@@ -4178,7 +4178,18 @@ var WorkflowEngine = class {
|
|
|
4178
4178
|
createdAt: now,
|
|
4179
4179
|
updatedAt: now
|
|
4180
4180
|
};
|
|
4181
|
-
|
|
4181
|
+
try {
|
|
4182
|
+
await this.store.createRun(run);
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
const winner = await this.store.getRun(runId).catch(() => null);
|
|
4185
|
+
if (!winner) throw err;
|
|
4186
|
+
return {
|
|
4187
|
+
runId,
|
|
4188
|
+
status: winner.status,
|
|
4189
|
+
output: winner.output,
|
|
4190
|
+
error: winner.error
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4182
4193
|
await this.runDispatcher.dispatch(runId);
|
|
4183
4194
|
if (this.controlPlane) {
|
|
4184
4195
|
void this.controlPlane.publishControl({
|
|
@@ -7091,10 +7102,12 @@ var WorkflowEngine = class {
|
|
|
7091
7102
|
if (this.remoteRedispatchMs == null) throw new WorkflowSuspended();
|
|
7092
7103
|
if (existing.wakeAt == null) {
|
|
7093
7104
|
const wakeAt = this.clock() + this.remoteRedispatchMs;
|
|
7094
|
-
await this.
|
|
7105
|
+
if (!await this.savePendingCheckpoint({
|
|
7095
7106
|
...existing,
|
|
7096
7107
|
wakeAt
|
|
7097
|
-
})
|
|
7108
|
+
})) {
|
|
7109
|
+
return this.callRemote(runId, seq, step, input, queue, transport, void 0, admission, routing);
|
|
7110
|
+
}
|
|
7098
7111
|
throw new WorkflowSuspended(wakeAt);
|
|
7099
7112
|
}
|
|
7100
7113
|
if (this.clock() < existing.wakeAt) throw new WorkflowSuspended(existing.wakeAt);
|
|
@@ -7107,7 +7120,7 @@ var WorkflowEngine = class {
|
|
|
7107
7120
|
const reAttempt = existing.attempts + 1;
|
|
7108
7121
|
const nextDeadline = this.clock() + this.remoteRedispatchMs;
|
|
7109
7122
|
const reEnqueuedAt = /* @__PURE__ */ new Date();
|
|
7110
|
-
await this.
|
|
7123
|
+
const claimed = await this.savePendingCheckpoint({
|
|
7111
7124
|
...existing,
|
|
7112
7125
|
attempts: reAttempt,
|
|
7113
7126
|
wakeAt: nextDeadline,
|
|
@@ -7121,6 +7134,9 @@ var WorkflowEngine = class {
|
|
|
7121
7134
|
lostStepEvent(this.clock(), reAttempt, "redispatched")
|
|
7122
7135
|
]
|
|
7123
7136
|
});
|
|
7137
|
+
if (!claimed) {
|
|
7138
|
+
return this.callRemote(runId, seq, step, input, queue, transport, void 0, admission, routing);
|
|
7139
|
+
}
|
|
7124
7140
|
await this.dispatchRemoteTask({
|
|
7125
7141
|
runId,
|
|
7126
7142
|
seq,
|
|
@@ -7211,6 +7227,12 @@ var WorkflowEngine = class {
|
|
|
7211
7227
|
this.awaitedRemoteSteps.get(runId)?.add(seq);
|
|
7212
7228
|
throw new WorkflowSuspended();
|
|
7213
7229
|
}
|
|
7230
|
+
/** {@link StateStore.saveCheckpointIfPending}, or a plain save on a store without it. */
|
|
7231
|
+
async savePendingCheckpoint(checkpoint) {
|
|
7232
|
+
if (this.store.saveCheckpointIfPending) return this.store.saveCheckpointIfPending(checkpoint);
|
|
7233
|
+
await this.store.saveCheckpoint(checkpoint);
|
|
7234
|
+
return true;
|
|
7235
|
+
}
|
|
7214
7236
|
/** Enqueue a remote step task to its worker group via the transport pool. Shared by the initial
|
|
7215
7237
|
* dispatch, the opt-in self-heal re-dispatch (see `remoteRedispatchMs`), and the explicit
|
|
7216
7238
|
* {@link redispatchPending}. Carries the current trace/context so a re-dispatch is traceable. */
|
|
@@ -7241,7 +7263,7 @@ var WorkflowEngine = class {
|
|
|
7241
7263
|
if (run && (run.status === "cancelled" || run.status === "completed")) return;
|
|
7242
7264
|
const finishedAt = /* @__PURE__ */ new Date();
|
|
7243
7265
|
const startedAt = result.startedAt ? new Date(result.startedAt) : cp.startedAt;
|
|
7244
|
-
await this.
|
|
7266
|
+
const claimed = await this.savePendingCheckpoint({
|
|
7245
7267
|
...cp,
|
|
7246
7268
|
status: result.status,
|
|
7247
7269
|
output: result.status === "completed" ? result.output : cp.output,
|
|
@@ -7250,6 +7272,7 @@ var WorkflowEngine = class {
|
|
|
7250
7272
|
startedAt,
|
|
7251
7273
|
finishedAt
|
|
7252
7274
|
});
|
|
7275
|
+
if (!claimed) return;
|
|
7253
7276
|
this.emit({
|
|
7254
7277
|
type: result.status === "completed" ? "step.completed" : "step.failed",
|
|
7255
7278
|
runId: result.runId,
|
|
@@ -7642,6 +7665,9 @@ var CodecStateStore = class {
|
|
|
7642
7665
|
/** Forwarded verbatim (deletes touch no payload). Bound only when the inner store has it, so the
|
|
7643
7666
|
* engine's per-run fallback still kicks in through the wrapper. */
|
|
7644
7667
|
deleteRuns;
|
|
7668
|
+
/** Encodes like {@link saveCheckpoint}. Bound only when the inner store has it, so the engine's
|
|
7669
|
+
* plain-save fallback still kicks in through the wrapper. */
|
|
7670
|
+
saveCheckpointIfPending;
|
|
7645
7671
|
/** Forwarded verbatim — retention selects by status/updatedAt/scope, never by payload. Bound only
|
|
7646
7672
|
* when the inner store prunes, so the retention poller still sees (and warns about) its absence. */
|
|
7647
7673
|
pruneTerminalRuns;
|
|
@@ -7669,6 +7695,8 @@ var CodecStateStore = class {
|
|
|
7669
7695
|
if (inner.tryAdmitSingleton) this.tryAdmitSingleton = inner.tryAdmitSingleton.bind(inner);
|
|
7670
7696
|
const deleteRuns = inner.deleteRuns;
|
|
7671
7697
|
if (deleteRuns) this.deleteRuns = (ids) => deleteRuns.call(inner, ids);
|
|
7698
|
+
const saveIfPending = inner.saveCheckpointIfPending;
|
|
7699
|
+
if (saveIfPending) this.saveCheckpointIfPending = (cp) => saveIfPending.call(inner, this.encCp(cp));
|
|
7672
7700
|
const prune = inner.pruneTerminalRuns;
|
|
7673
7701
|
if (prune) this.pruneTerminalRuns = (policy, now, limit) => prune.call(inner, policy, now, limit);
|
|
7674
7702
|
const count = inner.countRuns;
|
|
@@ -8232,6 +8260,14 @@ var InMemoryStateStore = class {
|
|
|
8232
8260
|
...checkpoint
|
|
8233
8261
|
});
|
|
8234
8262
|
}
|
|
8263
|
+
async saveCheckpointIfPending(checkpoint) {
|
|
8264
|
+
const key = this.key(checkpoint.runId, checkpoint.seq);
|
|
8265
|
+
if (this.checkpoints.get(key)?.status !== "pending") return false;
|
|
8266
|
+
this.checkpoints.set(key, {
|
|
8267
|
+
...checkpoint
|
|
8268
|
+
});
|
|
8269
|
+
return true;
|
|
8270
|
+
}
|
|
8235
8271
|
async listIncompleteRuns(namespace) {
|
|
8236
8272
|
return [
|
|
8237
8273
|
...this.runs.values()
|