@dudousxd/nestjs-durable-core 0.1.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/duration.d.ts +6 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +42 -0
- package/dist/duration.js.map +1 -0
- package/dist/engine.d.ts +115 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +588 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors.d.ts +34 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces.d.ts +249 -0
- package/dist/interfaces.d.ts.map +1 -0
- package/dist/interfaces.js +3 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/protocol.d.ts +12 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +35 -0
- package/dist/protocol.js.map +1 -0
- package/dist/remote-step-factory.d.ts +15 -0
- package/dist/remote-step-factory.d.ts.map +1 -0
- package/dist/remote-step-factory.js +15 -0
- package/dist/remote-step-factory.js.map +1 -0
- package/dist/scheduler.d.ts +19 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +24 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/testing/in-memory-state-store.d.ts +25 -0
- package/dist/testing/in-memory-state-store.d.ts.map +1 -0
- package/dist/testing/in-memory-state-store.js +88 -0
- package/dist/testing/in-memory-state-store.js.map +1 -0
- package/dist/testing/in-memory-transport.d.ts +16 -0
- package/dist/testing/in-memory-transport.d.ts.map +1 -0
- package/dist/testing/in-memory-transport.js +29 -0
- package/dist/testing/in-memory-transport.js.map +1 -0
- package/dist/tokens.d.ts +8 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +11 -0
- package/dist/tokens.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a human duration to milliseconds. Accepts a number (already ms) or a string like
|
|
3
|
+
* `'500ms'`, `'30s'`, `'15m'`, `'2h'`, `'7d'`, `'7 days'`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseDuration(duration: string | number): number;
|
|
6
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAsBA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAS/D"}
|
package/dist/duration.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDuration = parseDuration;
|
|
4
|
+
const UNIT_MS = {
|
|
5
|
+
ms: 1,
|
|
6
|
+
s: 1_000,
|
|
7
|
+
sec: 1_000,
|
|
8
|
+
second: 1_000,
|
|
9
|
+
seconds: 1_000,
|
|
10
|
+
m: 60_000,
|
|
11
|
+
min: 60_000,
|
|
12
|
+
minute: 60_000,
|
|
13
|
+
minutes: 60_000,
|
|
14
|
+
h: 3_600_000,
|
|
15
|
+
hr: 3_600_000,
|
|
16
|
+
hour: 3_600_000,
|
|
17
|
+
hours: 3_600_000,
|
|
18
|
+
d: 86_400_000,
|
|
19
|
+
day: 86_400_000,
|
|
20
|
+
days: 86_400_000,
|
|
21
|
+
w: 604_800_000,
|
|
22
|
+
week: 604_800_000,
|
|
23
|
+
weeks: 604_800_000,
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Parse a human duration to milliseconds. Accepts a number (already ms) or a string like
|
|
27
|
+
* `'500ms'`, `'30s'`, `'15m'`, `'2h'`, `'7d'`, `'7 days'`.
|
|
28
|
+
*/
|
|
29
|
+
function parseDuration(duration) {
|
|
30
|
+
if (typeof duration === 'number')
|
|
31
|
+
return duration;
|
|
32
|
+
const match = duration.trim().match(/^(\d+(?:\.\d+)?)\s*([a-z]+)$/i);
|
|
33
|
+
if (!match)
|
|
34
|
+
throw new Error(`invalid duration: ${duration}`);
|
|
35
|
+
const value = Number(match[1]);
|
|
36
|
+
const unit = (match[2] ?? '').toLowerCase();
|
|
37
|
+
const factor = UNIT_MS[unit];
|
|
38
|
+
if (factor === undefined)
|
|
39
|
+
throw new Error(`unknown duration unit: ${unit}`);
|
|
40
|
+
return value * factor;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":";;AA0BA,sCASC;AAnCD,MAAM,OAAO,GAA2B;IACtC,EAAE,EAAE,CAAC;IACL,CAAC,EAAE,KAAK;IACR,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,KAAK;IACb,OAAO,EAAE,KAAK;IACd,CAAC,EAAE,MAAM;IACT,GAAG,EAAE,MAAM;IACX,MAAM,EAAE,MAAM;IACd,OAAO,EAAE,MAAM;IACf,CAAC,EAAE,SAAS;IACZ,EAAE,EAAE,SAAS;IACb,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,CAAC,EAAE,UAAU;IACb,GAAG,EAAE,UAAU;IACf,IAAI,EAAE,UAAU;IAChB,CAAC,EAAE,WAAW;IACd,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,WAAW;CACnB,CAAC;AAEF;;;GAGG;AACH,SAAgB,aAAa,CAAC,QAAyB;IACrD,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACrE,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,EAAE,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IAC5E,OAAO,KAAK,GAAG,MAAM,CAAC;AACxB,CAAC"}
|
package/dist/engine.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { EngineListener, RunResult, StateStore, StepError, Transport, WorkflowCtx } from './interfaces';
|
|
2
|
+
type WorkflowFn = (ctx: WorkflowCtx, input: unknown) => Promise<unknown>;
|
|
3
|
+
export interface WorkflowEngineDeps {
|
|
4
|
+
store: StateStore;
|
|
5
|
+
transport?: Transport;
|
|
6
|
+
/** Epoch-ms clock; injectable for tests. Defaults to `Date.now`. */
|
|
7
|
+
clock?: () => number;
|
|
8
|
+
/** Unique id for this engine instance, used for recovery leases. Defaults to a random id. */
|
|
9
|
+
instanceId?: string;
|
|
10
|
+
/** Recovery lease duration in ms — how long this instance owns a run it picked up. Default 30s. */
|
|
11
|
+
leaseMs?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The orchestrator. Owns workflow state and replays runs deterministically: each step's
|
|
15
|
+
* result is checkpointed, so on resume a completed step returns its saved output instead of
|
|
16
|
+
* re-executing. Remote steps are dispatched over the Transport; their results checkpoint the
|
|
17
|
+
* same way local steps do.
|
|
18
|
+
*/
|
|
19
|
+
export declare class WorkflowEngine {
|
|
20
|
+
private readonly store;
|
|
21
|
+
private readonly transport?;
|
|
22
|
+
private readonly clock;
|
|
23
|
+
private readonly instanceId;
|
|
24
|
+
private readonly leaseMs;
|
|
25
|
+
/** Every registered workflow, keyed by `name@version` — so old versions stay runnable. */
|
|
26
|
+
private readonly workflows;
|
|
27
|
+
/** The newest registered version per workflow name — used to `start` new runs. */
|
|
28
|
+
private readonly latest;
|
|
29
|
+
/** In-flight remote steps awaiting a worker result, keyed by stepId. */
|
|
30
|
+
private readonly pending;
|
|
31
|
+
/** Per-step "reset the liveness timer" callbacks, called when a heartbeat arrives. */
|
|
32
|
+
private readonly heartbeatResets;
|
|
33
|
+
private readonly listeners;
|
|
34
|
+
/** Executions currently in flight, so a graceful shutdown can wait for them to settle. */
|
|
35
|
+
private readonly inflight;
|
|
36
|
+
private draining;
|
|
37
|
+
constructor(deps: WorkflowEngineDeps);
|
|
38
|
+
/**
|
|
39
|
+
* Register a workflow version. Register multiple versions of the same name to keep in-flight
|
|
40
|
+
* runs working across a breaking change: old runs resume on the version they started on, new
|
|
41
|
+
* runs start on the newest registered version.
|
|
42
|
+
*/
|
|
43
|
+
register(name: string, version: string, fn: WorkflowFn): void;
|
|
44
|
+
/** Subscribe to lifecycle events. Returns an unsubscribe function. */
|
|
45
|
+
subscribe(listener: EngineListener): () => void;
|
|
46
|
+
private emit;
|
|
47
|
+
start(workflow: string, input: unknown, runId: string): Promise<RunResult>;
|
|
48
|
+
resume(runId: string): Promise<RunResult>;
|
|
49
|
+
/** Track an in-flight execution so {@link drain} can wait for it. */
|
|
50
|
+
private track;
|
|
51
|
+
/**
|
|
52
|
+
* Graceful shutdown: stop picking up new runs (recovery/timer become no-ops) and wait for
|
|
53
|
+
* in-flight executions to settle, up to `timeoutMs`. Call from your app's shutdown hook so a
|
|
54
|
+
* deploy hands off cleanly instead of leaving runs to the lease timeout.
|
|
55
|
+
*/
|
|
56
|
+
drain(timeoutMs?: number): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Resume every run left incomplete by a crash or deploy. Called on boot. Completed steps
|
|
59
|
+
* replay from their checkpoints, so only the work that had not finished runs again.
|
|
60
|
+
*/
|
|
61
|
+
recoverIncomplete(): Promise<RunResult[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Resume every suspended run whose durable timer is due. Call periodically (a poller) and on
|
|
64
|
+
* boot. A run still not due re-suspends cheaply without running new work.
|
|
65
|
+
*/
|
|
66
|
+
resumeDueTimers(nowMs?: number): Promise<RunResult[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Resume each run only if this instance can acquire its recovery lease — so when several
|
|
69
|
+
* replicas recover or poll at once, each run is picked up by exactly one of them.
|
|
70
|
+
*/
|
|
71
|
+
private resumeLeased;
|
|
72
|
+
/**
|
|
73
|
+
* Deliver an external signal to the run waiting on `token` and resume it with `payload`.
|
|
74
|
+
* Returns the run result, or null if no run is waiting for that token.
|
|
75
|
+
*/
|
|
76
|
+
signal(token: string, payload: unknown): Promise<RunResult | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Report the result of a `ctx.task(name, …)` back to its run (async completion). The external
|
|
79
|
+
* worker that the task dispatched to calls this when done; the run resumes with `result`. Returns
|
|
80
|
+
* null if no run is waiting on the task (e.g. a duplicate/late delivery) — a safe no-op.
|
|
81
|
+
*/
|
|
82
|
+
completeTask(runId: string, name: string, result: unknown): Promise<RunResult | null>;
|
|
83
|
+
/** Report that a `ctx.task` failed — the run resumes and throws a FatalError at the task. */
|
|
84
|
+
failTask(runId: string, name: string, error: string): Promise<RunResult | null>;
|
|
85
|
+
/**
|
|
86
|
+
* Notify a parent that's waiting on `runId` as a child of its terminal outcome (the `ctx.child`
|
|
87
|
+
* rendezvous). A no-op when no parent is waiting, so `execute()` can call it on every run without
|
|
88
|
+
* knowing about the child feature.
|
|
89
|
+
*/
|
|
90
|
+
private notifyParent;
|
|
91
|
+
/** Cancel a run (e.g. from the dashboard). Returns null if the run does not exist. */
|
|
92
|
+
cancel(runId: string): Promise<RunResult | null>;
|
|
93
|
+
/** Checkpoint a finished step and announce it — the two things that must always happen together. */
|
|
94
|
+
private completeStep;
|
|
95
|
+
/** Checkpoint a step that failed terminally, so the failure point is visible (not just the run). */
|
|
96
|
+
private failStep;
|
|
97
|
+
private execute;
|
|
98
|
+
private makeCtx;
|
|
99
|
+
/** Persist a completed timer checkpoint (a durable sleep / signal deadline) at a logical position. */
|
|
100
|
+
private recordTimer;
|
|
101
|
+
private callRemote;
|
|
102
|
+
/**
|
|
103
|
+
* Await a remote result, but reject with `RemoteStepTimeout` if neither the result nor a heartbeat
|
|
104
|
+
* arrives within `timeoutMs`. Each heartbeat (delivered via `transport.onHeartbeat`) rearms the
|
|
105
|
+
* window, so a worker that keeps beating stays alive past `timeoutMs`.
|
|
106
|
+
*/
|
|
107
|
+
private awaitWithHeartbeat;
|
|
108
|
+
}
|
|
109
|
+
/** Raised inside the workflow when a remote worker reports a step failure. */
|
|
110
|
+
export declare class RemoteStepError extends Error {
|
|
111
|
+
readonly stepError?: StepError;
|
|
112
|
+
constructor(stepError?: StepError);
|
|
113
|
+
}
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
|