@junejuly-lockstep/reconciler 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/LICENSE +21 -0
- package/README.md +182 -0
- package/dist/boot.d.ts +51 -0
- package/dist/boot.d.ts.map +1 -0
- package/dist/boot.js +162 -0
- package/dist/boot.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +180 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +140 -0
- package/dist/config.js.map +1 -0
- package/dist/contract.d.ts +61 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +154 -0
- package/dist/contract.js.map +1 -0
- package/dist/determinism.d.ts +23 -0
- package/dist/determinism.d.ts.map +1 -0
- package/dist/determinism.js +102 -0
- package/dist/determinism.js.map +1 -0
- package/dist/errors.d.ts +25 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/exitCodes.d.ts +33 -0
- package/dist/exitCodes.d.ts.map +1 -0
- package/dist/exitCodes.js +49 -0
- package/dist/exitCodes.js.map +1 -0
- package/dist/extract.d.ts +30 -0
- package/dist/extract.d.ts.map +1 -0
- package/dist/extract.js +376 -0
- package/dist/extract.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +17 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +55 -0
- package/dist/init.js.map +1 -0
- package/dist/journeys.d.ts +34 -0
- package/dist/journeys.d.ts.map +1 -0
- package/dist/journeys.js +276 -0
- package/dist/journeys.js.map +1 -0
- package/dist/paths.d.ts +26 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +80 -0
- package/dist/paths.js.map +1 -0
- package/dist/reconcile.d.ts +63 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +316 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/report.d.ts +33 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +97 -0
- package/dist/report.js.map +1 -0
- package/dist/sync.d.ts +46 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +110 -0
- package/dist/sync.js.map +1 -0
- package/dist/upload.d.ts +33 -0
- package/dist/upload.d.ts.map +1 -0
- package/dist/upload.js +86 -0
- package/dist/upload.js.map +1 -0
- package/dist/util.d.ts +24 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +97 -0
- package/dist/util.js.map +1 -0
- package/dist/visual.d.ts +49 -0
- package/dist/visual.d.ts.map +1 -0
- package/dist/visual.js +145 -0
- package/dist/visual.js.map +1 -0
- package/dist/walk.d.ts +147 -0
- package/dist/walk.d.ts.map +1 -0
- package/dist/walk.js +433 -0
- package/dist/walk.js.map +1 -0
- package/package.json +78 -0
- package/templates/example.journey.ts +90 -0
- package/templates/lockstep-reconcile.yml +219 -0
- package/templates/lockstep.config.example.ts +46 -0
- package/templates/msw-handlers.example.ts +113 -0
- package/templates/scenarios.example.ts +32 -0
package/dist/init.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { copyFileSync, existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureDir, packageRoot, toPosix } from './util.js';
|
|
4
|
+
/** Template file -> destination in the client repo. */
|
|
5
|
+
export const INIT_FILES = [
|
|
6
|
+
{
|
|
7
|
+
template: 'lockstep.config.example.ts',
|
|
8
|
+
dest: 'lockstep.config.ts',
|
|
9
|
+
purpose: 'project config (contract item 1)',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
template: 'example.journey.ts',
|
|
13
|
+
dest: 'src/journeys/book-a-visit.journey.ts',
|
|
14
|
+
purpose: 'example journey: machine + meta (item 2)',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
template: 'scenarios.example.ts',
|
|
18
|
+
dest: 'src/journeys/scenarios.ts',
|
|
19
|
+
purpose: 'named personas with seeds (item 3)',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
template: 'msw-handlers.example.ts',
|
|
23
|
+
dest: 'src/mocks/handlers.ts',
|
|
24
|
+
purpose: 'seeded, faker-free MSW handlers (item 4)',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
template: 'lockstep-reconcile.yml',
|
|
28
|
+
dest: '.github/workflows/lockstep-reconcile.yml',
|
|
29
|
+
purpose: 'CI workflow (item 6)',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
export function templatesDir() {
|
|
33
|
+
return path.join(packageRoot(), 'templates');
|
|
34
|
+
}
|
|
35
|
+
export function listTemplates() {
|
|
36
|
+
return readdirSync(templatesDir()).sort();
|
|
37
|
+
}
|
|
38
|
+
/** Copy the templates into `cwd`; existing files are kept unless `force`. */
|
|
39
|
+
export function init(cwd, opts = {}) {
|
|
40
|
+
const written = [];
|
|
41
|
+
const skipped = [];
|
|
42
|
+
for (const f of INIT_FILES) {
|
|
43
|
+
const src = path.join(templatesDir(), f.template);
|
|
44
|
+
const dest = path.join(cwd, f.dest);
|
|
45
|
+
if (existsSync(dest) && !opts.force) {
|
|
46
|
+
skipped.push(toPosix(f.dest));
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
ensureDir(path.dirname(dest));
|
|
50
|
+
copyFileSync(src, dest);
|
|
51
|
+
written.push(toPosix(f.dest));
|
|
52
|
+
}
|
|
53
|
+
return { written, skipped };
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=init.js.map
|
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE5D,uDAAuD;AACvD,MAAM,CAAC,MAAM,UAAU,GAAuE;IAC5F;QACE,QAAQ,EAAE,4BAA4B;QACtC,IAAI,EAAE,oBAAoB;QAC1B,OAAO,EAAE,kCAAkC;KAC5C;IACD;QACE,QAAQ,EAAE,oBAAoB;QAC9B,IAAI,EAAE,sCAAsC;QAC5C,OAAO,EAAE,0CAA0C;KACpD;IACD;QACE,QAAQ,EAAE,sBAAsB;QAChC,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,oCAAoC;KAC9C;IACD;QACE,QAAQ,EAAE,yBAAyB;QACnC,IAAI,EAAE,uBAAuB;QAC7B,OAAO,EAAE,0CAA0C;KACpD;IACD;QACE,QAAQ,EAAE,wBAAwB;QAClC,IAAI,EAAE,0CAA0C;QAChD,OAAO,EAAE,sBAAsB;KAChC;CACF,CAAC;AAEF,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5C,CAAC;AAOD,6EAA6E;AAC7E,MAAM,UAAU,IAAI,CAAC,GAAW,EAAE,OAA4B,EAAE;IAC9D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9B,SAAS;QACX,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JourneyModel, Scenario, type MachineJson } from '@junejuly-lockstep/model';
|
|
2
|
+
/** Shape a `*.journey.ts` module must export. */
|
|
3
|
+
export interface JourneyModule {
|
|
4
|
+
/** XState v5 `createMachine(...)` result or the plain machine config. */
|
|
5
|
+
machine: unknown;
|
|
6
|
+
/** Lockstep metadata: narrative, per-state meta, trigger bindings, assets. */
|
|
7
|
+
meta: unknown;
|
|
8
|
+
/** Optional overrides; default derived from the file name. */
|
|
9
|
+
key?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Derive the journey key from `src/journeys/book-a-visit.journey.ts` -> `book-a-visit`. */
|
|
13
|
+
export declare function journeyKeyFromFile(file: string): string;
|
|
14
|
+
export declare function journeyNameFromKey(key: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Convert whatever the journey file exported into the JSON subset the model understands.
|
|
17
|
+
* Accepts an XState v5 machine (uses `.config`) or a plain config object. Functions are
|
|
18
|
+
* replaced by their names so guards/actions become string references.
|
|
19
|
+
*/
|
|
20
|
+
export declare function toMachineJson(machine: unknown): MachineJson;
|
|
21
|
+
/** Deep-copy JSON-ish data, replacing functions with their names and dropping undefined. */
|
|
22
|
+
export declare function stripFunctions(value: unknown): unknown;
|
|
23
|
+
export interface LoadedJourney {
|
|
24
|
+
model: JourneyModel;
|
|
25
|
+
/** Posix path relative to the repo root. */
|
|
26
|
+
file: string;
|
|
27
|
+
}
|
|
28
|
+
/** Load one journey module (already imported) into a validated `JourneyModel`. */
|
|
29
|
+
export declare function journeyFromModule(mod: unknown, file: string): JourneyModel;
|
|
30
|
+
/** Load every journey file matching the glob, sorted by path for determinism. */
|
|
31
|
+
export declare function loadJourneys(rootDir: string, journeysGlob: string): Promise<LoadedJourney[]>;
|
|
32
|
+
/** Load the scenarios file: an array of `Scenario` or a record keyed by name. Sorted by name. */
|
|
33
|
+
export declare function loadScenarios(rootDir: string, scenariosPath: string): Promise<Scenario[]>;
|
|
34
|
+
//# sourceMappingURL=journeys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journeys.d.ts","sourceRoot":"","sources":["../src/journeys.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,QAAQ,EAER,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAKlC,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,OAAO,EAAE,OAAO,CAAC;IACjB,8EAA8E;IAC9E,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,WAAW,CAsC3D;AAgED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAYtD;AAMD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CA8B1E;AAmBD,iFAAiF;AACjF,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,aAAa,EAAE,CAAC,CAyB1B;AAOD,iGAAiG;AACjG,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAkC/F"}
|
package/dist/journeys.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fg from 'fast-glob';
|
|
3
|
+
import { JourneyModel, Scenario, validateJourney, } from '@junejuly-lockstep/model';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { ContractViolation } from './errors.js';
|
|
6
|
+
import { importTs, toPosix } from './util.js';
|
|
7
|
+
/** Derive the journey key from `src/journeys/book-a-visit.journey.ts` -> `book-a-visit`. */
|
|
8
|
+
export function journeyKeyFromFile(file) {
|
|
9
|
+
const base = path.basename(file);
|
|
10
|
+
return base
|
|
11
|
+
.replace(/\.journey\.(t|j)sx?$/, '')
|
|
12
|
+
.replace(/\.(t|j)sx?$/, '')
|
|
13
|
+
.replace(/[^a-zA-Z0-9-]+/g, '-')
|
|
14
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
|
|
15
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1-$2')
|
|
16
|
+
.toLowerCase()
|
|
17
|
+
.replace(/^-+|-+$/g, '');
|
|
18
|
+
}
|
|
19
|
+
export function journeyNameFromKey(key) {
|
|
20
|
+
return key
|
|
21
|
+
.split('-')
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.map((w, i) => (i === 0 ? w.charAt(0).toUpperCase() + w.slice(1) : w))
|
|
24
|
+
.join(' ');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert whatever the journey file exported into the JSON subset the model understands.
|
|
28
|
+
* Accepts an XState v5 machine (uses `.config`) or a plain config object. Functions are
|
|
29
|
+
* replaced by their names so guards/actions become string references.
|
|
30
|
+
*/
|
|
31
|
+
export function toMachineJson(machine) {
|
|
32
|
+
const config = unwrapMachineConfig(machine);
|
|
33
|
+
const id = typeof config['id'] === 'string' ? config['id'] : 'machine';
|
|
34
|
+
const statesRaw = isRecord(config['states']) ? config['states'] : {};
|
|
35
|
+
const states = {};
|
|
36
|
+
for (const [key, value] of Object.entries(statesRaw)) {
|
|
37
|
+
if (!isRecord(value))
|
|
38
|
+
continue;
|
|
39
|
+
const state = {};
|
|
40
|
+
if (isRecord(value['on'])) {
|
|
41
|
+
const on = {};
|
|
42
|
+
for (const [event, t] of Object.entries(value['on'])) {
|
|
43
|
+
const normalized = normalizeTransition(t, id);
|
|
44
|
+
if (normalized)
|
|
45
|
+
on[event] = normalized;
|
|
46
|
+
}
|
|
47
|
+
state.on = on;
|
|
48
|
+
}
|
|
49
|
+
if (typeof value['type'] === 'string') {
|
|
50
|
+
const type = value['type'];
|
|
51
|
+
if (['atomic', 'final', 'compound', 'parallel', 'history'].includes(type)) {
|
|
52
|
+
state.type = type;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (typeof value['description'] === 'string')
|
|
56
|
+
state.description = value['description'];
|
|
57
|
+
if (Array.isArray(value['tags']))
|
|
58
|
+
state.tags = value['tags'].filter((t) => typeof t === 'string');
|
|
59
|
+
if (isRecord(value['meta']))
|
|
60
|
+
state.meta = stripFunctions(value['meta']);
|
|
61
|
+
states[key] = state;
|
|
62
|
+
}
|
|
63
|
+
const out = {
|
|
64
|
+
id,
|
|
65
|
+
initial: typeof config['initial'] === 'string' ? config['initial'] : (Object.keys(states)[0] ?? ''),
|
|
66
|
+
states,
|
|
67
|
+
};
|
|
68
|
+
if (isRecord(config['context']))
|
|
69
|
+
out.context = stripFunctions(config['context']);
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
function unwrapMachineConfig(machine) {
|
|
73
|
+
if (!isRecord(machine))
|
|
74
|
+
throw new Error('journey must export `machine` (createMachine result or config object)');
|
|
75
|
+
// XState v5 StateMachine exposes the original config as `.config`.
|
|
76
|
+
if (isRecord(machine['config']) && isRecord(machine['config']['states']))
|
|
77
|
+
return machine['config'];
|
|
78
|
+
return machine;
|
|
79
|
+
}
|
|
80
|
+
function normalizeTransition(t, machineId) {
|
|
81
|
+
if (typeof t === 'string')
|
|
82
|
+
return normalizeTarget(t, machineId);
|
|
83
|
+
if (Array.isArray(t)) {
|
|
84
|
+
const list = t
|
|
85
|
+
.map((x) => normalizeTransitionConfig(x, machineId))
|
|
86
|
+
.filter((x) => !!x);
|
|
87
|
+
return list.length ? list : null;
|
|
88
|
+
}
|
|
89
|
+
return normalizeTransitionConfig(t, machineId);
|
|
90
|
+
}
|
|
91
|
+
function normalizeTransitionConfig(t, machineId) {
|
|
92
|
+
if (typeof t === 'string')
|
|
93
|
+
return { target: normalizeTarget(t, machineId) };
|
|
94
|
+
if (!isRecord(t))
|
|
95
|
+
return null;
|
|
96
|
+
const rawTarget = Array.isArray(t['target']) ? t['target'][0] : t['target'];
|
|
97
|
+
if (typeof rawTarget !== 'string')
|
|
98
|
+
return null; // targetless (internal) transitions carry no navigation
|
|
99
|
+
const out = {
|
|
100
|
+
target: normalizeTarget(rawTarget, machineId),
|
|
101
|
+
};
|
|
102
|
+
const guard = nameOf(t['guard'] ?? t['cond']);
|
|
103
|
+
if (guard)
|
|
104
|
+
out.guard = guard;
|
|
105
|
+
const actions = Array.isArray(t['actions'])
|
|
106
|
+
? t['actions']
|
|
107
|
+
: t['actions'] !== undefined
|
|
108
|
+
? [t['actions']]
|
|
109
|
+
: [];
|
|
110
|
+
const names = actions.map(nameOf).filter((a) => !!a);
|
|
111
|
+
if (names.length)
|
|
112
|
+
out.actions = names;
|
|
113
|
+
if (typeof t['description'] === 'string')
|
|
114
|
+
out.description = t['description'];
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/** `#machineId.state` -> `state`, `.state` -> `state`. Other targets pass through. */
|
|
118
|
+
function normalizeTarget(target, machineId) {
|
|
119
|
+
if (target.startsWith(`#${machineId}.`))
|
|
120
|
+
return target.slice(machineId.length + 2);
|
|
121
|
+
if (target.startsWith('.'))
|
|
122
|
+
return target.slice(1);
|
|
123
|
+
return target;
|
|
124
|
+
}
|
|
125
|
+
function nameOf(v) {
|
|
126
|
+
if (v === undefined || v === null)
|
|
127
|
+
return null;
|
|
128
|
+
if (typeof v === 'string')
|
|
129
|
+
return v;
|
|
130
|
+
if (typeof v === 'function')
|
|
131
|
+
return v.name || 'inline';
|
|
132
|
+
if (isRecord(v) && typeof v['type'] === 'string')
|
|
133
|
+
return v['type'];
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
/** Deep-copy JSON-ish data, replacing functions with their names and dropping undefined. */
|
|
137
|
+
export function stripFunctions(value) {
|
|
138
|
+
if (typeof value === 'function')
|
|
139
|
+
return value.name || 'inline';
|
|
140
|
+
if (Array.isArray(value))
|
|
141
|
+
return value.map(stripFunctions);
|
|
142
|
+
if (isRecord(value)) {
|
|
143
|
+
const out = {};
|
|
144
|
+
for (const [k, v] of Object.entries(value)) {
|
|
145
|
+
if (v === undefined)
|
|
146
|
+
continue;
|
|
147
|
+
out[k] = stripFunctions(v);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
function isRecord(v) {
|
|
154
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
155
|
+
}
|
|
156
|
+
/** Load one journey module (already imported) into a validated `JourneyModel`. */
|
|
157
|
+
export function journeyFromModule(mod, file) {
|
|
158
|
+
const m = unwrapModule(mod);
|
|
159
|
+
if (!m.machine)
|
|
160
|
+
throw new ContractViolation(`journey:${file}`, 'does not export `machine`');
|
|
161
|
+
if (!m.meta)
|
|
162
|
+
throw new ContractViolation(`journey:${file}`, 'does not export `meta` (narrative, states, bindings)');
|
|
163
|
+
const key = typeof m.key === 'string' ? m.key : journeyKeyFromFile(file);
|
|
164
|
+
const name = typeof m.name === 'string' ? m.name : journeyNameFromKey(key);
|
|
165
|
+
const candidate = {
|
|
166
|
+
key,
|
|
167
|
+
name,
|
|
168
|
+
machine: toMachineJson(m.machine),
|
|
169
|
+
meta: stripFunctions(m.meta),
|
|
170
|
+
};
|
|
171
|
+
const parsed = JourneyModel.safeParse(candidate);
|
|
172
|
+
if (!parsed.success) {
|
|
173
|
+
throw ContractViolation.fromList(parsed.error.issues.map((i) => ({
|
|
174
|
+
item: `journey:${file}:${i.path.join('.')}`,
|
|
175
|
+
message: i.message,
|
|
176
|
+
})));
|
|
177
|
+
}
|
|
178
|
+
const errors = validateJourney(parsed.data);
|
|
179
|
+
if (errors.length) {
|
|
180
|
+
throw ContractViolation.fromList(errors.map((e) => ({ item: `journey:${file}`, message: e })));
|
|
181
|
+
}
|
|
182
|
+
return parsed.data;
|
|
183
|
+
}
|
|
184
|
+
function unwrapModule(mod) {
|
|
185
|
+
if (!isRecord(mod))
|
|
186
|
+
return {};
|
|
187
|
+
if (isRecord(mod['default']) && ('machine' in mod['default'] || 'meta' in mod['default'])) {
|
|
188
|
+
return {
|
|
189
|
+
...mod['default'],
|
|
190
|
+
...pick(mod, ['machine', 'meta', 'key', 'name']),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return mod;
|
|
194
|
+
}
|
|
195
|
+
function pick(obj, keys) {
|
|
196
|
+
const out = {};
|
|
197
|
+
for (const k of keys)
|
|
198
|
+
if (obj[k] !== undefined)
|
|
199
|
+
out[k] = obj[k];
|
|
200
|
+
return out;
|
|
201
|
+
}
|
|
202
|
+
/** Load every journey file matching the glob, sorted by path for determinism. */
|
|
203
|
+
export async function loadJourneys(rootDir, journeysGlob) {
|
|
204
|
+
const files = (await fg(journeysGlob, { cwd: rootDir, onlyFiles: true })).map(toPosix).sort();
|
|
205
|
+
const out = [];
|
|
206
|
+
const seen = new Map();
|
|
207
|
+
for (const file of files) {
|
|
208
|
+
let mod;
|
|
209
|
+
try {
|
|
210
|
+
mod = await importTs(path.resolve(rootDir, file));
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
throw new ContractViolation(`journey:${file}`, `failed to load: ${err instanceof Error ? err.message : String(err)}`);
|
|
214
|
+
}
|
|
215
|
+
const model = journeyFromModule(mod, file);
|
|
216
|
+
const dup = seen.get(model.key);
|
|
217
|
+
if (dup)
|
|
218
|
+
throw new ContractViolation(`journey:${file}`, `duplicate journey key "${model.key}" (also in ${dup})`);
|
|
219
|
+
seen.set(model.key, file);
|
|
220
|
+
out.push({ model, file });
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
const ScenariosExport = z.union([
|
|
225
|
+
z.array(Scenario),
|
|
226
|
+
z.record(z.string(), Scenario.omit({ name: true }).extend({ name: z.string().optional() })),
|
|
227
|
+
]);
|
|
228
|
+
/** Load the scenarios file: an array of `Scenario` or a record keyed by name. Sorted by name. */
|
|
229
|
+
export async function loadScenarios(rootDir, scenariosPath) {
|
|
230
|
+
let mod;
|
|
231
|
+
try {
|
|
232
|
+
mod = await importTs(path.resolve(rootDir, scenariosPath));
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new ContractViolation('scenarios', `failed to load ${scenariosPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
236
|
+
}
|
|
237
|
+
const raw = scenariosFromModule(mod);
|
|
238
|
+
const parsed = ScenariosExport.safeParse(stripFunctions(raw));
|
|
239
|
+
if (!parsed.success) {
|
|
240
|
+
throw ContractViolation.fromList(parsed.error.issues.map((i) => ({
|
|
241
|
+
item: `scenarios:${i.path.join('.') || '<root>'}`,
|
|
242
|
+
message: i.message,
|
|
243
|
+
})));
|
|
244
|
+
}
|
|
245
|
+
const list = Array.isArray(parsed.data)
|
|
246
|
+
? parsed.data
|
|
247
|
+
: Object.entries(parsed.data).map(([name, s]) => Scenario.parse({ ...s, name: s.name ?? name }));
|
|
248
|
+
if (list.length === 0)
|
|
249
|
+
throw new ContractViolation('scenarios', `${scenariosPath} exports no scenarios`);
|
|
250
|
+
const names = new Set();
|
|
251
|
+
for (const s of list) {
|
|
252
|
+
if (names.has(s.name))
|
|
253
|
+
throw new ContractViolation('scenarios', `duplicate scenario name "${s.name}"`);
|
|
254
|
+
names.add(s.name);
|
|
255
|
+
}
|
|
256
|
+
return [...list].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
257
|
+
}
|
|
258
|
+
function looksLikeScenarios(v) {
|
|
259
|
+
if (Array.isArray(v))
|
|
260
|
+
return true;
|
|
261
|
+
if (!isRecord(v))
|
|
262
|
+
return false;
|
|
263
|
+
const values = Object.values(v);
|
|
264
|
+
return values.length > 0 && values.every((s) => isRecord(s) && typeof s['seed'] === 'number');
|
|
265
|
+
}
|
|
266
|
+
/** Accept `export default [...]`, `export const scenarios = [...]`, or a record keyed by name. */
|
|
267
|
+
function scenariosFromModule(mod) {
|
|
268
|
+
if (!isRecord(mod))
|
|
269
|
+
return mod;
|
|
270
|
+
for (const candidate of [mod['scenarios'], mod['default'], mod]) {
|
|
271
|
+
if (looksLikeScenarios(candidate))
|
|
272
|
+
return candidate;
|
|
273
|
+
}
|
|
274
|
+
return mod['default'] ?? mod;
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=journeys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journeys.js","sourceRoot":"","sources":["../src/journeys.ts"],"names":[],"mappings":"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"}
|
package/dist/paths.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type AnyStateMachine } from 'xstate';
|
|
2
|
+
import { type MachineJson } from '@junejuly-lockstep/model';
|
|
3
|
+
export interface PathStep {
|
|
4
|
+
from: string;
|
|
5
|
+
event: string;
|
|
6
|
+
to: string;
|
|
7
|
+
}
|
|
8
|
+
export interface EnumeratedPath {
|
|
9
|
+
/** State the path ends in. */
|
|
10
|
+
target: string;
|
|
11
|
+
steps: PathStep[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Build an executable XState v5 machine from the JSON model. Every named guard is stubbed
|
|
15
|
+
* to `true` so guarded branches are reachable during enumeration; the walker exercises the
|
|
16
|
+
* seeded scenario, which is what decides the real branch.
|
|
17
|
+
*/
|
|
18
|
+
export declare function machineFromJson(json: MachineJson): AnyStateMachine;
|
|
19
|
+
/**
|
|
20
|
+
* Enumerate one shortest path per reachable state with `@xstate/graph`. Output is sorted
|
|
21
|
+
* by target state key then by event sequence so walks are ordered identically every run.
|
|
22
|
+
*/
|
|
23
|
+
export declare function enumeratePaths(json: MachineJson): EnumeratedPath[];
|
|
24
|
+
/** Declared transitions that no enumerated path exercises (e.g. unreachable states). */
|
|
25
|
+
export declare function uncoveredTransitions(json: MachineJson, paths: EnumeratedPath[]): PathStep[];
|
|
26
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE3E,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,CAYlE;AAQD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,cAAc,EAAE,CAgClE;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,QAAQ,EAAE,CAK3F"}
|
package/dist/paths.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getShortestPaths } from '@xstate/graph';
|
|
2
|
+
import { createMachine } from 'xstate';
|
|
3
|
+
import { transitionsOf } from '@junejuly-lockstep/model';
|
|
4
|
+
/**
|
|
5
|
+
* Build an executable XState v5 machine from the JSON model. Every named guard is stubbed
|
|
6
|
+
* to `true` so guarded branches are reachable during enumeration; the walker exercises the
|
|
7
|
+
* seeded scenario, which is what decides the real branch.
|
|
8
|
+
*/
|
|
9
|
+
export function machineFromJson(json) {
|
|
10
|
+
const guards = {};
|
|
11
|
+
const actions = {};
|
|
12
|
+
for (const t of transitionsOf(json))
|
|
13
|
+
if (t.guard)
|
|
14
|
+
guards[t.guard] = () => true;
|
|
15
|
+
for (const state of Object.values(json.states)) {
|
|
16
|
+
for (const value of Object.values(state.on ?? {})) {
|
|
17
|
+
const configs = typeof value === 'string' ? [] : Array.isArray(value) ? value : [value];
|
|
18
|
+
for (const c of configs)
|
|
19
|
+
for (const a of c.actions ?? [])
|
|
20
|
+
actions[a] = () => { };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// The model's JSON subset is valid XState config by construction.
|
|
24
|
+
return createMachine(json, { guards, actions });
|
|
25
|
+
}
|
|
26
|
+
function stateValueKey(value) {
|
|
27
|
+
if (typeof value === 'string')
|
|
28
|
+
return value;
|
|
29
|
+
if (value && typeof value === 'object')
|
|
30
|
+
return Object.keys(value)[0] ?? '';
|
|
31
|
+
return String(value);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Enumerate one shortest path per reachable state with `@xstate/graph`. Output is sorted
|
|
35
|
+
* by target state key then by event sequence so walks are ordered identically every run.
|
|
36
|
+
*/
|
|
37
|
+
export function enumeratePaths(json) {
|
|
38
|
+
const machine = machineFromJson(json);
|
|
39
|
+
const paths = getShortestPaths(machine, {
|
|
40
|
+
// Serialize by state value only: context never changes in the JSON subset we execute.
|
|
41
|
+
serializeState: (snapshot) => stateValueKey(snapshot.value),
|
|
42
|
+
});
|
|
43
|
+
const out = paths.map((p) => {
|
|
44
|
+
const steps = [];
|
|
45
|
+
let from = json.initial;
|
|
46
|
+
for (const step of p.steps) {
|
|
47
|
+
// @xstate/graph records the implicit `xstate.init` entry into the initial state; it is not a transition.
|
|
48
|
+
if (step.event.type.startsWith('xstate.'))
|
|
49
|
+
continue;
|
|
50
|
+
const to = stateValueKey(step.state.value);
|
|
51
|
+
steps.push({ from, event: step.event.type, to });
|
|
52
|
+
from = to;
|
|
53
|
+
}
|
|
54
|
+
return { target: stateValueKey(p.state.value), steps };
|
|
55
|
+
});
|
|
56
|
+
// Drop degenerate duplicates (same target, same steps), then order deterministically.
|
|
57
|
+
const seen = new Set();
|
|
58
|
+
const unique = out.filter((p) => {
|
|
59
|
+
const key = `${p.target}|${p.steps.map((s) => `${s.from}-${s.event}-${s.to}`).join('>')}`;
|
|
60
|
+
if (seen.has(key))
|
|
61
|
+
return false;
|
|
62
|
+
seen.add(key);
|
|
63
|
+
return true;
|
|
64
|
+
});
|
|
65
|
+
return unique.sort((a, b) => {
|
|
66
|
+
if (a.target !== b.target)
|
|
67
|
+
return a.target < b.target ? -1 : 1;
|
|
68
|
+
const ea = a.steps.map((s) => s.event).join('>');
|
|
69
|
+
const eb = b.steps.map((s) => s.event).join('>');
|
|
70
|
+
return ea < eb ? -1 : ea > eb ? 1 : 0;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/** Declared transitions that no enumerated path exercises (e.g. unreachable states). */
|
|
74
|
+
export function uncoveredTransitions(json, paths) {
|
|
75
|
+
const covered = new Set(paths.flatMap((p) => p.steps.map((s) => `${s.from}|${s.event}|${s.to}`)));
|
|
76
|
+
return transitionsOf(json)
|
|
77
|
+
.filter((t) => !covered.has(`${t.from}|${t.event}|${t.to}`))
|
|
78
|
+
.map((t) => ({ from: t.from, event: t.event, to: t.to }));
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import type { Env, IngestRunResponse, RunKind, RunPayload } from '@junejuly-lockstep/model';
|
|
3
|
+
import { type IngestClient } from '@junejuly-lockstep/sdk';
|
|
4
|
+
import { type ExitCode, type FailOnVerdicts } from './exitCodes.js';
|
|
5
|
+
import { type WalkDeps } from './walk.js';
|
|
6
|
+
export interface ReconcileOptions {
|
|
7
|
+
/** Commit SHA under test; defaults to GITHUB_SHA or `git rev-parse HEAD`. */
|
|
8
|
+
ref?: string;
|
|
9
|
+
kind: RunKind;
|
|
10
|
+
pr?: number;
|
|
11
|
+
env: Env;
|
|
12
|
+
deployLabel?: string;
|
|
13
|
+
configPath?: string;
|
|
14
|
+
outDir?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Platform API origin to post to instead of `apiBaseUrl` from the config (also the
|
|
17
|
+
* `LOCKSTEP_API_BASE_URL` env var). For rehearsals against a local API; CI leaves it unset.
|
|
18
|
+
*/
|
|
19
|
+
apiBaseUrl?: string;
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
failOnVerdicts: FailOnVerdicts;
|
|
22
|
+
determinismCheck?: boolean;
|
|
23
|
+
declaredSeams?: string[];
|
|
24
|
+
/** Git ref whose SDL the contract diff compares against (default HEAD~1). */
|
|
25
|
+
baseRef?: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
log?: (line: string) => void;
|
|
28
|
+
/** Test seams. */
|
|
29
|
+
deps?: {
|
|
30
|
+
walk?: WalkDeps;
|
|
31
|
+
fetch?: typeof fetch;
|
|
32
|
+
client?: IngestClient;
|
|
33
|
+
now?: () => Date;
|
|
34
|
+
noBoot?: boolean;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export interface ReconcileResult {
|
|
38
|
+
payload: RunPayload;
|
|
39
|
+
response: IngestRunResponse | null;
|
|
40
|
+
exitCode: ExitCode;
|
|
41
|
+
outDir: string;
|
|
42
|
+
}
|
|
43
|
+
export declare function resolveCommitSha(ref: string | undefined, cwd: string, env?: NodeJS.ProcessEnv): string;
|
|
44
|
+
export declare const API_BASE_URL_ENV = "LOCKSTEP_API_BASE_URL";
|
|
45
|
+
/** `--api-base-url`, then `LOCKSTEP_API_BASE_URL`, then the config; must be an http(s) URL. */
|
|
46
|
+
export declare function resolveApiBaseUrl(override: string | undefined, fromConfig: string, env?: NodeJS.ProcessEnv): string;
|
|
47
|
+
/** Run git and return trimmed stdout, or null when git or the ref is unavailable. */
|
|
48
|
+
export declare function gitExec(cwd: string, args: string[]): string | null;
|
|
49
|
+
/** `git show <ref>:<path>`, or null when the ref or file does not exist. */
|
|
50
|
+
export declare function gitShowFile(cwd: string, ref: string, file: string): string | null;
|
|
51
|
+
/** The whole pipeline, in SPEC order. Throws ContractViolation or InfrastructureError. */
|
|
52
|
+
export declare function reconcile(opts: ReconcileOptions): Promise<ReconcileResult>;
|
|
53
|
+
/** `lockstep validate`: contract only, no walk. Returns a printable checklist and whether it passed. */
|
|
54
|
+
export declare function validate(opts: {
|
|
55
|
+
configPath?: string;
|
|
56
|
+
cwd?: string;
|
|
57
|
+
offline?: boolean;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
ok: boolean;
|
|
60
|
+
lines: string[];
|
|
61
|
+
}>;
|
|
62
|
+
export { readFileSync as _readFileSync };
|
|
63
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../src/reconcile.ts"],"names":[],"mappings":"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"}
|