@dudousxd/nestjs-durable-testing 0.2.0 → 0.4.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/LICENSE +21 -0
- package/dist/index.d.ts +130 -6
- package/dist/index.js +5356 -21
- package/dist/index.js.map +1 -1
- package/package.json +24 -7
- package/dist/assertions.d.ts +0 -10
- package/dist/assertions.d.ts.map +0 -1
- package/dist/assertions.js +0 -48
- package/dist/assertions.js.map +0 -1
- package/dist/harness.d.ts +0 -23
- package/dist/harness.d.ts.map +0 -1
- package/dist/harness.js +0 -41
- package/dist/harness.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/replay.d.ts +0 -22
- package/dist/replay.d.ts.map +0 -1
- package/dist/replay.js +0 -41
- package/dist/replay.js.map +0 -1
- package/dist/steps.d.ts +0 -5
- package/dist/steps.d.ts.map +0 -1
- package/dist/steps.js +0 -19
- package/dist/steps.js.map +0 -1
- package/dist/transport-conformance.d.ts +0 -16
- package/dist/transport-conformance.d.ts.map +0 -1
- package/dist/transport-conformance.js +0 -67
- package/dist/transport-conformance.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dudousxd/nestjs-durable-testing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Testing utilities for nestjs-durable — a test engine harness, crash/flaky-step injection, and replay assertions",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Davide Carvalho",
|
|
@@ -9,21 +9,38 @@
|
|
|
9
9
|
"url": "https://github.com/DavideCarvalho/nestjs-durable.git",
|
|
10
10
|
"directory": "packages/testing"
|
|
11
11
|
},
|
|
12
|
-
"type": "
|
|
13
|
-
"main": "dist/index.js",
|
|
14
|
-
"types": "dist/index.d.ts",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@dudousxd/nestjs-durable-core": ">=0.1.0 <1.0.0"
|
|
19
|
+
"@dudousxd/nestjs-durable-core": ">=0.1.0 <1.0.0",
|
|
20
|
+
"vitest": ">=1.0.0 <4.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"vitest": {
|
|
24
|
+
"optional": true
|
|
25
|
+
}
|
|
20
26
|
},
|
|
21
27
|
"devDependencies": {
|
|
22
28
|
"typescript": "5.9.3",
|
|
23
|
-
"
|
|
29
|
+
"vitest": "2.1.9",
|
|
30
|
+
"tsup": "8.3.5",
|
|
31
|
+
"@dudousxd/nestjs-durable-core": "^0.28.0"
|
|
32
|
+
},
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"default": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
24
41
|
},
|
|
25
42
|
"scripts": {
|
|
26
|
-
"build": "
|
|
43
|
+
"build": "tsup",
|
|
27
44
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
28
45
|
}
|
|
29
46
|
}
|
package/dist/assertions.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { RunStatus, StateStore } from '@dudousxd/nestjs-durable-core';
|
|
2
|
-
export declare function assertRunStatus(store: StateStore, runId: string, status: RunStatus): Promise<void>;
|
|
3
|
-
export declare function assertOutput(store: StateStore, runId: string, expected: unknown): Promise<void>;
|
|
4
|
-
/** The names of the steps that have run, in order. */
|
|
5
|
-
export declare function recordedSteps(store: StateStore, runId: string): Promise<string[]>;
|
|
6
|
-
/** Assert every named step ran (in any order). */
|
|
7
|
-
export declare function assertStepsRan(store: StateStore, runId: string, names: string[]): Promise<void>;
|
|
8
|
-
/** Assert a step ran exactly the given number of attempts (e.g. to verify retries). */
|
|
9
|
-
export declare function assertStepAttempts(store: StateStore, runId: string, name: string, attempts: number): Promise<void>;
|
|
10
|
-
//# sourceMappingURL=assertions.d.ts.map
|
package/dist/assertions.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAQ3E,wBAAsB,eAAe,CACnC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,SAAS,GAChB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,sDAAsD;AACtD,wBAAsB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAEvF;AAED,kDAAkD;AAClD,wBAAsB,cAAc,CAClC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,uFAAuF;AACvF,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
package/dist/assertions.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.assertRunStatus = assertRunStatus;
|
|
4
|
-
exports.assertOutput = assertOutput;
|
|
5
|
-
exports.recordedSteps = recordedSteps;
|
|
6
|
-
exports.assertStepsRan = assertStepsRan;
|
|
7
|
-
exports.assertStepAttempts = assertStepAttempts;
|
|
8
|
-
async function requireRun(store, runId) {
|
|
9
|
-
const run = await store.getRun(runId);
|
|
10
|
-
if (!run)
|
|
11
|
-
throw new Error(`assertion failed: run ${runId} not found`);
|
|
12
|
-
return run;
|
|
13
|
-
}
|
|
14
|
-
async function assertRunStatus(store, runId, status) {
|
|
15
|
-
const run = await requireRun(store, runId);
|
|
16
|
-
if (run.status !== status) {
|
|
17
|
-
throw new Error(`expected run ${runId} to be "${status}", but it was "${run.status}"`);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async function assertOutput(store, runId, expected) {
|
|
21
|
-
const run = await requireRun(store, runId);
|
|
22
|
-
if (JSON.stringify(run.output) !== JSON.stringify(expected)) {
|
|
23
|
-
throw new Error(`expected run ${runId} output ${JSON.stringify(expected)}, got ${JSON.stringify(run.output)}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/** The names of the steps that have run, in order. */
|
|
27
|
-
async function recordedSteps(store, runId) {
|
|
28
|
-
return (await store.listCheckpoints(runId)).map((cp) => cp.name);
|
|
29
|
-
}
|
|
30
|
-
/** Assert every named step ran (in any order). */
|
|
31
|
-
async function assertStepsRan(store, runId, names) {
|
|
32
|
-
const ran = await recordedSteps(store, runId);
|
|
33
|
-
for (const name of names) {
|
|
34
|
-
if (!ran.includes(name)) {
|
|
35
|
-
throw new Error(`expected step "${name}" to have run; ran: [${ran.join(', ')}]`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** Assert a step ran exactly the given number of attempts (e.g. to verify retries). */
|
|
40
|
-
async function assertStepAttempts(store, runId, name, attempts) {
|
|
41
|
-
const cp = (await store.listCheckpoints(runId)).find((c) => c.name === name);
|
|
42
|
-
if (!cp)
|
|
43
|
-
throw new Error(`expected step "${name}" to have run, but it did not`);
|
|
44
|
-
if (cp.attempts !== attempts) {
|
|
45
|
-
throw new Error(`expected step "${name}" to take ${attempts} attempt(s), took ${cp.attempts}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=assertions.js.map
|
package/dist/assertions.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.js","sourceRoot":"","sources":["../src/assertions.ts"],"names":[],"mappings":";;AAQA,0CASC;AAED,oCAWC;AAGD,sCAEC;AAGD,wCAWC;AAGD,gDAWC;AA7DD,KAAK,UAAU,UAAU,CAAC,KAAiB,EAAE,KAAa;IACxD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,YAAY,CAAC,CAAC;IACtE,OAAO,GAAG,CAAC;AACb,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,KAAiB,EACjB,KAAa,EACb,MAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,gBAAgB,KAAK,WAAW,MAAM,kBAAkB,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;IACzF,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,KAAiB,EACjB,KAAa,EACb,QAAiB;IAEjB,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC3C,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CACb,gBAAgB,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAC9F,CAAC;IACJ,CAAC;AACH,CAAC;AAED,sDAAsD;AAC/C,KAAK,UAAU,aAAa,CAAC,KAAiB,EAAE,KAAa;IAClE,OAAO,CAAC,MAAM,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,kDAAkD;AAC3C,KAAK,UAAU,cAAc,CAClC,KAAiB,EACjB,KAAa,EACb,KAAe;IAEf,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,wBAAwB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;AACH,CAAC;AAED,uFAAuF;AAChF,KAAK,UAAU,kBAAkB,CACtC,KAAiB,EACjB,KAAa,EACb,IAAY,EACZ,QAAgB;IAEhB,MAAM,EAAE,GAAG,CAAC,MAAM,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC7E,IAAI,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,+BAA+B,CAAC,CAAC;IAChF,IAAI,EAAE,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,kBAAkB,IAAI,aAAa,QAAQ,qBAAqB,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjG,CAAC;AACH,CAAC"}
|
package/dist/harness.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { InMemoryStateStore, InMemoryTransport, type RunResult, WorkflowEngine } from '@dudousxd/nestjs-durable-core';
|
|
2
|
-
/** A clock you control, for testing durable sleeps without real time. */
|
|
3
|
-
export declare class MutableClock {
|
|
4
|
-
private current;
|
|
5
|
-
constructor(current?: number);
|
|
6
|
-
readonly now: () => number;
|
|
7
|
-
set(ms: number): void;
|
|
8
|
-
advance(ms: number): void;
|
|
9
|
-
}
|
|
10
|
-
export interface TestEngine {
|
|
11
|
-
engine: WorkflowEngine;
|
|
12
|
-
store: InMemoryStateStore;
|
|
13
|
-
transport: InMemoryTransport;
|
|
14
|
-
clock: MutableClock;
|
|
15
|
-
/** Advance the clock by `ms` and resume any durable sleeps now due. */
|
|
16
|
-
tick(ms: number): Promise<RunResult[]>;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* A self-contained engine wired to in-memory store + transport and a controllable clock — run a
|
|
20
|
-
* whole workflow in a unit test, with no Postgres, no Redis, and no real time.
|
|
21
|
-
*/
|
|
22
|
-
export declare function createTestEngine(): TestEngine;
|
|
23
|
-
//# sourceMappingURL=harness.d.ts.map
|
package/dist/harness.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,KAAK,SAAS,EACd,cAAc,EACf,MAAM,+BAA+B,CAAC;AAEvC,yEAAyE;AACzE,qBAAa,YAAY;IACX,OAAO,CAAC,OAAO;gBAAP,OAAO,SAAQ;IACnC,QAAQ,CAAC,GAAG,QAAO,MAAM,CAAiB;IAC1C,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAGrB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAG1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,EAAE,YAAY,CAAC;IACpB,uEAAuE;IACvE,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;CACxC;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAe7C"}
|
package/dist/harness.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MutableClock = void 0;
|
|
4
|
-
exports.createTestEngine = createTestEngine;
|
|
5
|
-
const nestjs_durable_core_1 = require("@dudousxd/nestjs-durable-core");
|
|
6
|
-
/** A clock you control, for testing durable sleeps without real time. */
|
|
7
|
-
class MutableClock {
|
|
8
|
-
current;
|
|
9
|
-
constructor(current = 1_000) {
|
|
10
|
-
this.current = current;
|
|
11
|
-
}
|
|
12
|
-
now = () => this.current;
|
|
13
|
-
set(ms) {
|
|
14
|
-
this.current = ms;
|
|
15
|
-
}
|
|
16
|
-
advance(ms) {
|
|
17
|
-
this.current += ms;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
exports.MutableClock = MutableClock;
|
|
21
|
-
/**
|
|
22
|
-
* A self-contained engine wired to in-memory store + transport and a controllable clock — run a
|
|
23
|
-
* whole workflow in a unit test, with no Postgres, no Redis, and no real time.
|
|
24
|
-
*/
|
|
25
|
-
function createTestEngine() {
|
|
26
|
-
const store = new nestjs_durable_core_1.InMemoryStateStore();
|
|
27
|
-
const transport = new nestjs_durable_core_1.InMemoryTransport();
|
|
28
|
-
const clock = new MutableClock();
|
|
29
|
-
const engine = new nestjs_durable_core_1.WorkflowEngine({ store, transport, clock: clock.now });
|
|
30
|
-
return {
|
|
31
|
-
engine,
|
|
32
|
-
store,
|
|
33
|
-
transport,
|
|
34
|
-
clock,
|
|
35
|
-
async tick(ms) {
|
|
36
|
-
clock.advance(ms);
|
|
37
|
-
return engine.resumeDueTimers(clock.now());
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=harness.js.map
|
package/dist/harness.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"harness.js","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":";;;AAgCA,4CAeC;AA/CD,uEAKuC;AAEvC,yEAAyE;AACzE,MAAa,YAAY;IACH;IAApB,YAAoB,UAAU,KAAK;QAAf,YAAO,GAAP,OAAO,CAAQ;IAAG,CAAC;IAC9B,GAAG,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC;IAC1C,GAAG,CAAC,EAAU;QACZ,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;IACpB,CAAC;IACD,OAAO,CAAC,EAAU;QAChB,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;IACrB,CAAC;CACF;AATD,oCASC;AAWD;;;GAGG;AACH,SAAgB,gBAAgB;IAC9B,MAAM,KAAK,GAAG,IAAI,wCAAkB,EAAE,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,uCAAiB,EAAE,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,oCAAc,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1E,OAAO;QACL,MAAM;QACN,KAAK;QACL,SAAS;QACT,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,EAAU;YACnB,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC"}
|
package/dist/replay.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type StepCheckpoint, WorkflowEngine, type WorkflowRun } from '@dudousxd/nestjs-durable-core';
|
|
2
|
-
export interface RunHistory {
|
|
3
|
-
run: WorkflowRun;
|
|
4
|
-
checkpoints: StepCheckpoint[];
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Replay a recorded run's history against the CURRENT workflow code and throw if they diverged.
|
|
8
|
-
*
|
|
9
|
-
* Capture a real (ideally in-flight or representative) run from production —
|
|
10
|
-
* `{ run: await store.getRun(id), checkpoints: await store.listCheckpoints(id) }` — commit it as a
|
|
11
|
-
* fixture, and assert here in CI. If a code change renamed/reordered/removed a step at a position the
|
|
12
|
-
* history already recorded, the engine raises a `NonDeterminismError` on replay and this rethrows it,
|
|
13
|
-
* catching the break *before* it reaches an in-flight run on deploy (the moment you'd otherwise
|
|
14
|
-
* silently replay the wrong checkpoint into the wrong step). Register the workflow exactly as the app
|
|
15
|
-
* does:
|
|
16
|
-
*
|
|
17
|
-
* ```ts
|
|
18
|
-
* await assertReplayable((engine) => engine.register('pipeline', '1', pipeline.run), fixture);
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export declare function assertReplayable(register: (engine: WorkflowEngine) => void, history: RunHistory): Promise<void>;
|
|
22
|
-
//# sourceMappingURL=replay.d.ts.map
|
package/dist/replay.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,cAAc,EACd,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,WAAW,CAAC;IACjB,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,EAC1C,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAoBf"}
|
package/dist/replay.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.assertReplayable = assertReplayable;
|
|
4
|
-
const nestjs_durable_core_1 = require("@dudousxd/nestjs-durable-core");
|
|
5
|
-
/**
|
|
6
|
-
* Replay a recorded run's history against the CURRENT workflow code and throw if they diverged.
|
|
7
|
-
*
|
|
8
|
-
* Capture a real (ideally in-flight or representative) run from production —
|
|
9
|
-
* `{ run: await store.getRun(id), checkpoints: await store.listCheckpoints(id) }` — commit it as a
|
|
10
|
-
* fixture, and assert here in CI. If a code change renamed/reordered/removed a step at a position the
|
|
11
|
-
* history already recorded, the engine raises a `NonDeterminismError` on replay and this rethrows it,
|
|
12
|
-
* catching the break *before* it reaches an in-flight run on deploy (the moment you'd otherwise
|
|
13
|
-
* silently replay the wrong checkpoint into the wrong step). Register the workflow exactly as the app
|
|
14
|
-
* does:
|
|
15
|
-
*
|
|
16
|
-
* ```ts
|
|
17
|
-
* await assertReplayable((engine) => engine.register('pipeline', '1', pipeline.run), fixture);
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
async function assertReplayable(register, history) {
|
|
21
|
-
const store = new nestjs_durable_core_1.InMemoryStateStore();
|
|
22
|
-
// Seed as a suspended run with no lock so resume() replays the body against the recorded
|
|
23
|
-
// checkpoints — no transport is wired, so nothing new is dispatched.
|
|
24
|
-
await store.createRun({
|
|
25
|
-
...history.run,
|
|
26
|
-
status: 'suspended',
|
|
27
|
-
lockedBy: undefined,
|
|
28
|
-
lockedUntil: undefined,
|
|
29
|
-
});
|
|
30
|
-
for (const cp of history.checkpoints)
|
|
31
|
-
await store.saveCheckpoint(cp);
|
|
32
|
-
const engine = new nestjs_durable_core_1.WorkflowEngine({ store });
|
|
33
|
-
register(engine);
|
|
34
|
-
const result = await engine.resume(history.run.id);
|
|
35
|
-
if (result.status === 'failed' && result.error?.message?.startsWith('non-determinism')) {
|
|
36
|
-
const err = new Error(result.error.message);
|
|
37
|
-
err.name = 'NonDeterminismError';
|
|
38
|
-
throw err;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
//# sourceMappingURL=replay.js.map
|
package/dist/replay.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":";;AA2BA,4CAuBC;AAlDD,uEAKuC;AAOvC;;;;;;;;;;;;;;GAcG;AACI,KAAK,UAAU,gBAAgB,CACpC,QAA0C,EAC1C,OAAmB;IAEnB,MAAM,KAAK,GAAG,IAAI,wCAAkB,EAAE,CAAC;IACvC,yFAAyF;IACzF,qEAAqE;IACrE,MAAM,KAAK,CAAC,SAAS,CAAC;QACpB,GAAG,OAAO,CAAC,GAAG;QACd,MAAM,EAAE,WAAW;QACnB,QAAQ,EAAE,SAAS;QACnB,WAAW,EAAE,SAAS;KACvB,CAAC,CAAC;IACH,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,WAAW;QAAE,MAAM,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;IAErE,MAAM,MAAM,GAAG,IAAI,oCAAc,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7C,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvF,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,GAAG,CAAC,IAAI,GAAG,qBAAqB,CAAC;QACjC,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/dist/steps.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/** A step body that throws once, then returns `value` — drives a retry, or a crash-then-resume. */
|
|
2
|
-
export declare function failOnce<T>(value: T, error?: Error): () => Promise<T>;
|
|
3
|
-
/** A step body that throws the first `n` times, then returns `value`. */
|
|
4
|
-
export declare function failTimes<T>(n: number, value: T, error?: Error): () => Promise<T>;
|
|
5
|
-
//# sourceMappingURL=steps.d.ts.map
|
package/dist/steps.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,CAAC,EACR,KAAK,GAAE,KAAqC,GAC3C,MAAM,OAAO,CAAC,CAAC,CAAC,CAElB;AAED,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,CAAC,EACzB,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,CAAC,EACR,KAAK,GAAE,KAAqC,GAC3C,MAAM,OAAO,CAAC,CAAC,CAAC,CAOlB"}
|
package/dist/steps.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.failOnce = failOnce;
|
|
4
|
-
exports.failTimes = failTimes;
|
|
5
|
-
/** A step body that throws once, then returns `value` — drives a retry, or a crash-then-resume. */
|
|
6
|
-
function failOnce(value, error = new Error('injected failure')) {
|
|
7
|
-
return failTimes(1, value, error);
|
|
8
|
-
}
|
|
9
|
-
/** A step body that throws the first `n` times, then returns `value`. */
|
|
10
|
-
function failTimes(n, value, error = new Error('injected failure')) {
|
|
11
|
-
let calls = 0;
|
|
12
|
-
return async () => {
|
|
13
|
-
calls += 1;
|
|
14
|
-
if (calls <= n)
|
|
15
|
-
throw error;
|
|
16
|
-
return value;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
//# sourceMappingURL=steps.js.map
|
package/dist/steps.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":";;AACA,4BAKC;AAGD,8BAWC;AApBD,mGAAmG;AACnG,SAAgB,QAAQ,CACtB,KAAQ,EACR,QAAe,IAAI,KAAK,CAAC,kBAAkB,CAAC;IAE5C,OAAO,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,yEAAyE;AACzE,SAAgB,SAAS,CACvB,CAAS,EACT,KAAQ,EACR,QAAe,IAAI,KAAK,CAAC,kBAAkB,CAAC;IAE5C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,IAAI,EAAE;QAChB,KAAK,IAAI,CAAC,CAAC;QACX,IAAI,KAAK,IAAI,CAAC;YAAE,MAAM,KAAK,CAAC;QAC5B,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { type Transport } from '@dudousxd/nestjs-durable-core';
|
|
2
|
-
/** A transport that can register worker handlers (every shipped transport does). */
|
|
3
|
-
export type HandleableTransport = Transport & {
|
|
4
|
-
handle(name: string, fn: (input: unknown) => Promise<unknown>): void;
|
|
5
|
-
close?(): Promise<void>;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* The contract every `Transport` must satisfy: a remote step dispatched through it round-trips to a
|
|
9
|
-
* worker and back (success), and a throwing handler surfaces as a failed run (failure). Point it at
|
|
10
|
-
* a freshly constructed transport (engine-side + worker `group` in one instance) to prove parity —
|
|
11
|
-
* useful when adding a new transport. Throws on any mismatch; closes the transport when done.
|
|
12
|
-
*
|
|
13
|
-
* `idPrefix` keeps run ids unique across invocations against real, shared infra (queues/tables).
|
|
14
|
-
*/
|
|
15
|
-
export declare function assertTransportConformance(transport: HandleableTransport, idPrefix?: string): Promise<void>;
|
|
16
|
-
//# sourceMappingURL=transport-conformance.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport-conformance.d.ts","sourceRoot":"","sources":["../src/transport-conformance.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,+BAA+B,CAAC;AAGvC,oFAAoF;AACpF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACrE,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAyBF;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAC9C,SAAS,EAAE,mBAAmB,EAC9B,QAAQ,SAAS,GAChB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.assertTransportConformance = assertTransportConformance;
|
|
4
|
-
const nestjs_durable_core_1 = require("@dudousxd/nestjs-durable-core");
|
|
5
|
-
const zod_1 = require("zod");
|
|
6
|
-
const echo = {
|
|
7
|
-
name: 'conformance.echo',
|
|
8
|
-
group: 'conformance',
|
|
9
|
-
input: zod_1.z.object({ n: zod_1.z.number() }),
|
|
10
|
-
output: zod_1.z.object({ doubled: zod_1.z.number() }),
|
|
11
|
-
__remote: true,
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* A durable `ctx.call` suspends the run; the worker result resumes it asynchronously (on whatever
|
|
15
|
-
* tick/poll the transport delivers on). Poll the store until the run reaches a terminal state —
|
|
16
|
-
* with a generous budget so a poll-based transport (DB/SQS) has time to round-trip.
|
|
17
|
-
*/
|
|
18
|
-
async function settle(store, runId, timeoutMs = 15_000) {
|
|
19
|
-
const deadline = Date.now() + timeoutMs;
|
|
20
|
-
while (Date.now() < deadline) {
|
|
21
|
-
const run = await store.getRun(runId);
|
|
22
|
-
if (run && run.status !== 'running' && run.status !== 'suspended')
|
|
23
|
-
return run;
|
|
24
|
-
await new Promise((r) => setTimeout(r, 25));
|
|
25
|
-
}
|
|
26
|
-
throw new Error(`run ${runId} did not settle within ${timeoutMs}ms`);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The contract every `Transport` must satisfy: a remote step dispatched through it round-trips to a
|
|
30
|
-
* worker and back (success), and a throwing handler surfaces as a failed run (failure). Point it at
|
|
31
|
-
* a freshly constructed transport (engine-side + worker `group` in one instance) to prove parity —
|
|
32
|
-
* useful when adding a new transport. Throws on any mismatch; closes the transport when done.
|
|
33
|
-
*
|
|
34
|
-
* `idPrefix` keeps run ids unique across invocations against real, shared infra (queues/tables).
|
|
35
|
-
*/
|
|
36
|
-
async function assertTransportConformance(transport, idPrefix = 'conf') {
|
|
37
|
-
try {
|
|
38
|
-
// One engine/store/transport — a durable remote step routes its result back to this engine,
|
|
39
|
-
// which resumes the run. (Two engines on one transport would fight over the result stream.)
|
|
40
|
-
const store = new nestjs_durable_core_1.InMemoryStateStore();
|
|
41
|
-
const engine = new nestjs_durable_core_1.WorkflowEngine({ store, transport });
|
|
42
|
-
engine.register('conf-ok', '1', async (ctx) => (await ctx.call(echo, { n: 21 })).doubled);
|
|
43
|
-
engine.register('conf-fail', '1', async (ctx) => ctx.call(echo, { n: 1 }));
|
|
44
|
-
// success: the worker doubles the input
|
|
45
|
-
transport.handle('conformance.echo', async (input) => ({
|
|
46
|
-
doubled: input.n * 2,
|
|
47
|
-
}));
|
|
48
|
-
await engine.start('conf-ok', {}, `${idPrefix}-ok`);
|
|
49
|
-
const ok = await settle(store, `${idPrefix}-ok`);
|
|
50
|
-
if (ok.status !== 'completed' || ok.output !== 42) {
|
|
51
|
-
throw new Error(`expected completed/42, got ${JSON.stringify(ok)}`);
|
|
52
|
-
}
|
|
53
|
-
// failure: a throwing handler surfaces as a failed run (re-register the same step to throw)
|
|
54
|
-
transport.handle('conformance.echo', async () => {
|
|
55
|
-
throw new Error('handler boom');
|
|
56
|
-
});
|
|
57
|
-
await engine.start('conf-fail', {}, `${idPrefix}-fail`);
|
|
58
|
-
const failed = await settle(store, `${idPrefix}-fail`);
|
|
59
|
-
if (failed.status !== 'failed') {
|
|
60
|
-
throw new Error(`expected failed, got ${JSON.stringify(failed)}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
await transport.close?.();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
//# sourceMappingURL=transport-conformance.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport-conformance.js","sourceRoot":"","sources":["../src/transport-conformance.ts"],"names":[],"mappings":"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"}
|