@almadar/core 2.1.3 → 2.2.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/domain-language/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +175 -7
- package/dist/index.js.map +1 -1
- package/dist/state-machine/index.d.ts +177 -0
- package/dist/state-machine/index.js +171 -0
- package/dist/state-machine/index.js.map +1 -0
- package/package.json +10 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Machine Graph Algorithm Types
|
|
3
|
+
*
|
|
4
|
+
* Pure types used by BFS, guard payload, and replay path algorithms.
|
|
5
|
+
* These are decoupled from the verify-specific types (no Playwright, no HTTP).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
/** An edge in the state graph adjacency list */
|
|
10
|
+
interface StateEdge {
|
|
11
|
+
event: string;
|
|
12
|
+
to: string;
|
|
13
|
+
}
|
|
14
|
+
/** A node in the BFS queue */
|
|
15
|
+
interface BFSNode {
|
|
16
|
+
state: string;
|
|
17
|
+
depth: number;
|
|
18
|
+
}
|
|
19
|
+
/** A node in the BFS queue with a replay path */
|
|
20
|
+
interface BFSPathNode {
|
|
21
|
+
fromState: string;
|
|
22
|
+
replayPath: ReplayStep[];
|
|
23
|
+
}
|
|
24
|
+
/** A single step to replay to reach a given state */
|
|
25
|
+
interface ReplayStep {
|
|
26
|
+
/** Event to fire */
|
|
27
|
+
event: string;
|
|
28
|
+
/** State we're coming from */
|
|
29
|
+
fromState: string;
|
|
30
|
+
/** State we're transitioning to */
|
|
31
|
+
toState: string;
|
|
32
|
+
/** Slot the pattern renders in */
|
|
33
|
+
slot: string;
|
|
34
|
+
/** Pattern expected in the slot after this step */
|
|
35
|
+
expectedPattern?: string;
|
|
36
|
+
/** True if this step requires entity rows to be visible before clicking */
|
|
37
|
+
needsEntityData: boolean;
|
|
38
|
+
/** Payload to supply for this step (derived from guard pass case if guarded) */
|
|
39
|
+
guardPayload?: Record<string, unknown>;
|
|
40
|
+
/** Full payload schema for mock data generation */
|
|
41
|
+
payloadSchema?: PayloadFieldSchema[];
|
|
42
|
+
}
|
|
43
|
+
/** Payload field definition (name + type) */
|
|
44
|
+
interface PayloadFieldSchema {
|
|
45
|
+
name: string;
|
|
46
|
+
type: string;
|
|
47
|
+
required?: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Pass and fail payloads derived from a guard s-expression */
|
|
50
|
+
interface GuardPayload {
|
|
51
|
+
pass: Record<string, unknown>;
|
|
52
|
+
fail: Record<string, unknown>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Minimal transition interface for graph algorithms.
|
|
56
|
+
* Compatible with both @almadar/core Transition and orbital-verify UnifiedTransition.
|
|
57
|
+
*/
|
|
58
|
+
interface GraphTransition {
|
|
59
|
+
from: string;
|
|
60
|
+
event: string;
|
|
61
|
+
to: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* State Graph Construction
|
|
66
|
+
*
|
|
67
|
+
* Build an adjacency list from state machine transitions.
|
|
68
|
+
* Extracted from orbital-verify-unified/src/analyze.ts.
|
|
69
|
+
*
|
|
70
|
+
* @packageDocumentation
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Build an adjacency list: state -> [{ event, to }].
|
|
75
|
+
* Wildcard transitions (from === '*') are excluded since they
|
|
76
|
+
* don't represent fixed edges in the graph.
|
|
77
|
+
*/
|
|
78
|
+
declare function buildStateGraph(transitions: GraphTransition[]): Map<string, StateEdge[]>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* BFS Reachability Algorithms
|
|
82
|
+
*
|
|
83
|
+
* Breadth-first search over state machine graphs.
|
|
84
|
+
* Extracted from orbital-verify-unified/src/analyze.ts and phase3-server.ts.
|
|
85
|
+
*
|
|
86
|
+
* @packageDocumentation
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Collect all reachable states from an initial state via BFS.
|
|
91
|
+
*
|
|
92
|
+
* @param transitions - State machine transitions
|
|
93
|
+
* @param initialState - Starting state
|
|
94
|
+
* @param maxDepth - Maximum BFS depth (default: 5)
|
|
95
|
+
* @returns Set of reachable state names
|
|
96
|
+
*/
|
|
97
|
+
declare function collectReachableStates(transitions: GraphTransition[], initialState: string, maxDepth?: number): Set<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Walk all (state, event) pairs reachable via BFS and invoke a callback for each.
|
|
100
|
+
* Used by the server verification to POST each transition and check the response.
|
|
101
|
+
*
|
|
102
|
+
* @param transitions - State machine transitions
|
|
103
|
+
* @param initialState - Starting state
|
|
104
|
+
* @param maxDepth - Maximum BFS depth (default: 5)
|
|
105
|
+
* @param visitor - Callback invoked for each (state, edge) pair.
|
|
106
|
+
* Return true to enqueue the target state for further exploration.
|
|
107
|
+
*/
|
|
108
|
+
declare function walkStatePairs(transitions: GraphTransition[], initialState: string, maxDepth: number, visitor: (state: string, edge: StateEdge, depth: number) => Promise<boolean>): Promise<{
|
|
109
|
+
visitedPairs: Set<string>;
|
|
110
|
+
walkedEdges: number;
|
|
111
|
+
}>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Guard Payload Builder
|
|
115
|
+
*
|
|
116
|
+
* Derive pass and fail payloads from guard s-expressions.
|
|
117
|
+
* Extracted from orbital-verify-unified/src/analyze.ts.
|
|
118
|
+
*
|
|
119
|
+
* @packageDocumentation
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extract the first @payload path segment from a binding reference.
|
|
124
|
+
* "@payload.item" -> "item", "@payload.data.weight" -> "data".
|
|
125
|
+
* Returns null for non-payload refs (@entity, @state, @user, etc.).
|
|
126
|
+
*/
|
|
127
|
+
declare function extractPayloadFieldRef(ref: unknown): string | null;
|
|
128
|
+
/**
|
|
129
|
+
* Derive pass and fail payloads from a guard s-expression.
|
|
130
|
+
*
|
|
131
|
+
* Pass payload: satisfies the guard condition (transition fires).
|
|
132
|
+
* Fail payload: violates the guard condition (transition is blocked).
|
|
133
|
+
*
|
|
134
|
+
* For @entity.* and @user.* guards: returns { pass: {}, fail: {} } because
|
|
135
|
+
* these reference runtime state that cannot be directly faked in tests.
|
|
136
|
+
*
|
|
137
|
+
* Handles operators: not-nil, nil, eq, not-eq, gt, gte, lt, lte, and, or, not
|
|
138
|
+
*/
|
|
139
|
+
declare function buildGuardPayloads(guard: unknown): GuardPayload;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Replay Path Builder
|
|
143
|
+
*
|
|
144
|
+
* Compute the shortest path (replay steps) from an initial state
|
|
145
|
+
* to every reachable state in a state machine. Used by browser
|
|
146
|
+
* verification to navigate through states before running assertions.
|
|
147
|
+
*
|
|
148
|
+
* Extracted from orbital-verify-unified/src/analyze.ts (collectDataMutationTests).
|
|
149
|
+
*
|
|
150
|
+
* @packageDocumentation
|
|
151
|
+
*/
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Extended transition with render and payload info needed for replay path building.
|
|
155
|
+
* Compatible with orbital-verify's UnifiedTransition.
|
|
156
|
+
*/
|
|
157
|
+
interface ReplayTransition extends GraphTransition {
|
|
158
|
+
hasGuard: boolean;
|
|
159
|
+
guard?: unknown[];
|
|
160
|
+
payloadFields: string[];
|
|
161
|
+
payloadSchema: PayloadFieldSchema[];
|
|
162
|
+
renderEffects: Array<{
|
|
163
|
+
slot: string;
|
|
164
|
+
patternType: string | null;
|
|
165
|
+
}>;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build the shortest replay path from initialState to every reachable state.
|
|
169
|
+
*
|
|
170
|
+
* @param transitions - Transitions with render/payload info
|
|
171
|
+
* @param initialState - Starting state
|
|
172
|
+
* @param maxDepth - Maximum path length (default: 3, shorter than BFS exploration)
|
|
173
|
+
* @returns Map of state -> replay steps to reach it
|
|
174
|
+
*/
|
|
175
|
+
declare function buildReplayPaths(transitions: ReplayTransition[], initialState: string, maxDepth?: number): Map<string, ReplayStep[]>;
|
|
176
|
+
|
|
177
|
+
export { type BFSNode, type BFSPathNode, type GraphTransition, type GuardPayload, type PayloadFieldSchema, type ReplayStep, type ReplayTransition, type StateEdge, buildGuardPayloads, buildReplayPaths, buildStateGraph, collectReachableStates, extractPayloadFieldRef, walkStatePairs };
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// src/state-machine/graph.ts
|
|
2
|
+
function buildStateGraph(transitions) {
|
|
3
|
+
const graph = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const t of transitions) {
|
|
5
|
+
if (t.from === "*") continue;
|
|
6
|
+
if (!graph.has(t.from)) graph.set(t.from, []);
|
|
7
|
+
graph.get(t.from).push({ event: t.event, to: t.to });
|
|
8
|
+
}
|
|
9
|
+
return graph;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/state-machine/bfs.ts
|
|
13
|
+
function collectReachableStates(transitions, initialState, maxDepth = 5) {
|
|
14
|
+
const graph = buildStateGraph(transitions);
|
|
15
|
+
const visited = /* @__PURE__ */ new Set([initialState]);
|
|
16
|
+
const queue = [{ state: initialState, depth: 0 }];
|
|
17
|
+
while (queue.length > 0) {
|
|
18
|
+
const current = queue.shift();
|
|
19
|
+
if (current.depth >= maxDepth) continue;
|
|
20
|
+
const edges = graph.get(current.state) ?? [];
|
|
21
|
+
for (const edge of edges) {
|
|
22
|
+
if (!visited.has(edge.to)) {
|
|
23
|
+
visited.add(edge.to);
|
|
24
|
+
queue.push({ state: edge.to, depth: current.depth + 1 });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return visited;
|
|
29
|
+
}
|
|
30
|
+
async function walkStatePairs(transitions, initialState, maxDepth, visitor) {
|
|
31
|
+
const graph = buildStateGraph(transitions);
|
|
32
|
+
const visitedPairs = /* @__PURE__ */ new Set();
|
|
33
|
+
const queue = [{ state: initialState, depth: 0 }];
|
|
34
|
+
let walkedEdges = 0;
|
|
35
|
+
while (queue.length > 0) {
|
|
36
|
+
const current = queue.shift();
|
|
37
|
+
if (current.depth >= maxDepth) continue;
|
|
38
|
+
const edges = graph.get(current.state) ?? [];
|
|
39
|
+
for (const edge of edges) {
|
|
40
|
+
const pairKey = `${current.state}:${edge.event}`;
|
|
41
|
+
if (visitedPairs.has(pairKey)) continue;
|
|
42
|
+
visitedPairs.add(pairKey);
|
|
43
|
+
const shouldEnqueue = await visitor(current.state, edge, current.depth);
|
|
44
|
+
walkedEdges++;
|
|
45
|
+
if (shouldEnqueue) {
|
|
46
|
+
const stateVisited = [...visitedPairs].some((k) => k.startsWith(`${edge.to}:`));
|
|
47
|
+
if (!stateVisited) {
|
|
48
|
+
queue.push({ state: edge.to, depth: current.depth + 1 });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { visitedPairs, walkedEdges };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/state-machine/guard-payloads.ts
|
|
57
|
+
function extractPayloadFieldRef(ref) {
|
|
58
|
+
if (typeof ref !== "string") return null;
|
|
59
|
+
const match = ref.match(/^@payload\.([A-Za-z0-9_]+)/);
|
|
60
|
+
return match ? match[1] : null;
|
|
61
|
+
}
|
|
62
|
+
function buildGuardPayloads(guard) {
|
|
63
|
+
if (!Array.isArray(guard) || guard.length === 0) {
|
|
64
|
+
return { pass: {}, fail: {} };
|
|
65
|
+
}
|
|
66
|
+
const op = String(guard[0]);
|
|
67
|
+
if (op === "not-nil" || op === "not_nil") {
|
|
68
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
69
|
+
if (field) return { pass: { [field]: "mock-test-value" }, fail: { [field]: null } };
|
|
70
|
+
}
|
|
71
|
+
if (op === "nil") {
|
|
72
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
73
|
+
if (field) return { pass: {}, fail: { [field]: "mock-test-value" } };
|
|
74
|
+
}
|
|
75
|
+
if (op === "eq" || op === "==" || op === "=") {
|
|
76
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
77
|
+
const val = guard[2];
|
|
78
|
+
if (field && val !== void 0) {
|
|
79
|
+
const failVal = typeof val === "number" ? val + 1 : typeof val === "string" ? `not-${val}` : null;
|
|
80
|
+
return { pass: { [field]: val }, fail: { [field]: failVal } };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (op === "not-eq" || op === "!=" || op === "neq") {
|
|
84
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
85
|
+
const val = guard[2];
|
|
86
|
+
if (field && val !== void 0) {
|
|
87
|
+
const passVal = typeof val === "number" ? val + 1 : typeof val === "string" ? `not-${val}` : "other";
|
|
88
|
+
return { pass: { [field]: passVal }, fail: { [field]: val } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (op === "gt" || op === ">") {
|
|
92
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
93
|
+
const n = typeof guard[2] === "number" ? guard[2] : 0;
|
|
94
|
+
if (field) return { pass: { [field]: n + 1 }, fail: { [field]: n - 1 } };
|
|
95
|
+
}
|
|
96
|
+
if (op === "gte" || op === ">=") {
|
|
97
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
98
|
+
const n = typeof guard[2] === "number" ? guard[2] : 0;
|
|
99
|
+
if (field) return { pass: { [field]: n }, fail: { [field]: n - 1 } };
|
|
100
|
+
}
|
|
101
|
+
if (op === "lt" || op === "<") {
|
|
102
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
103
|
+
const n = typeof guard[2] === "number" ? guard[2] : 0;
|
|
104
|
+
if (field) return { pass: { [field]: n - 1 }, fail: { [field]: n + 1 } };
|
|
105
|
+
}
|
|
106
|
+
if (op === "lte" || op === "<=") {
|
|
107
|
+
const field = extractPayloadFieldRef(guard[1]);
|
|
108
|
+
const n = typeof guard[2] === "number" ? guard[2] : 0;
|
|
109
|
+
if (field) return { pass: { [field]: n }, fail: { [field]: n + 1 } };
|
|
110
|
+
}
|
|
111
|
+
if (op === "and") {
|
|
112
|
+
const subs = guard.slice(1).filter(Array.isArray);
|
|
113
|
+
if (subs.length >= 2) {
|
|
114
|
+
const s1 = buildGuardPayloads(subs[0]);
|
|
115
|
+
const s2 = buildGuardPayloads(subs[1]);
|
|
116
|
+
return { pass: { ...s1.pass, ...s2.pass }, fail: s1.fail };
|
|
117
|
+
}
|
|
118
|
+
if (subs.length === 1) return buildGuardPayloads(subs[0]);
|
|
119
|
+
}
|
|
120
|
+
if (op === "or") {
|
|
121
|
+
const subs = guard.slice(1).filter(Array.isArray);
|
|
122
|
+
if (subs.length >= 2) {
|
|
123
|
+
const s1 = buildGuardPayloads(subs[0]);
|
|
124
|
+
const s2 = buildGuardPayloads(subs[1]);
|
|
125
|
+
return { pass: s1.pass, fail: { ...s1.fail, ...s2.fail } };
|
|
126
|
+
}
|
|
127
|
+
if (subs.length === 1) return buildGuardPayloads(subs[0]);
|
|
128
|
+
}
|
|
129
|
+
if (op === "not") {
|
|
130
|
+
const inner = buildGuardPayloads(guard[1]);
|
|
131
|
+
return { pass: inner.fail, fail: inner.pass };
|
|
132
|
+
}
|
|
133
|
+
return { pass: {}, fail: {} };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/state-machine/replay-paths.ts
|
|
137
|
+
var ENTITY_PAYLOAD_FIELDS = /* @__PURE__ */ new Set(["data", "row", "item", "id"]);
|
|
138
|
+
function buildReplayPaths(transitions, initialState, maxDepth = 3) {
|
|
139
|
+
const queue = [{ state: initialState, path: [] }];
|
|
140
|
+
const replayPaths = /* @__PURE__ */ new Map();
|
|
141
|
+
replayPaths.set(initialState, []);
|
|
142
|
+
while (queue.length > 0) {
|
|
143
|
+
const { state, path } = queue.shift();
|
|
144
|
+
if (path.length >= maxDepth) continue;
|
|
145
|
+
const fromHere = transitions.filter(
|
|
146
|
+
(t) => t.from === state && t.event !== "INIT"
|
|
147
|
+
);
|
|
148
|
+
for (const transition of fromHere) {
|
|
149
|
+
if (replayPaths.has(transition.to)) continue;
|
|
150
|
+
const renderEffect = transition.renderEffects.find((re) => re.patternType !== null);
|
|
151
|
+
const stepNeedsEntityData = transition.hasGuard || transition.payloadFields.some((f) => ENTITY_PAYLOAD_FIELDS.has(f));
|
|
152
|
+
const step = {
|
|
153
|
+
event: transition.event,
|
|
154
|
+
fromState: state,
|
|
155
|
+
toState: transition.to,
|
|
156
|
+
slot: renderEffect?.slot ?? "main",
|
|
157
|
+
expectedPattern: renderEffect?.patternType ?? void 0,
|
|
158
|
+
needsEntityData: stepNeedsEntityData,
|
|
159
|
+
payloadSchema: transition.payloadSchema.length > 0 ? transition.payloadSchema : void 0
|
|
160
|
+
};
|
|
161
|
+
const newPath = [...path, step];
|
|
162
|
+
replayPaths.set(transition.to, newPath);
|
|
163
|
+
queue.push({ state: transition.to, path: newPath });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return replayPaths;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { buildGuardPayloads, buildReplayPaths, buildStateGraph, collectReachableStates, extractPayloadFieldRef, walkStatePairs };
|
|
170
|
+
//# sourceMappingURL=index.js.map
|
|
171
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/state-machine/graph.ts","../../src/state-machine/bfs.ts","../../src/state-machine/guard-payloads.ts","../../src/state-machine/replay-paths.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * State Graph Construction\n *\n * Build an adjacency list from state machine transitions.\n * Extracted from orbital-verify-unified/src/analyze.ts.\n *\n * @packageDocumentation\n */\n\nimport type { GraphTransition, StateEdge } from './types.js';\n\n/**\n * Build an adjacency list: state -> [{ event, to }].\n * Wildcard transitions (from === '*') are excluded since they\n * don't represent fixed edges in the graph.\n */\nexport function buildStateGraph(\n transitions: GraphTransition[]\n): Map<string, StateEdge[]> {\n const graph = new Map<string, StateEdge[]>();\n for (const t of transitions) {\n if (t.from === '*') continue;\n if (!graph.has(t.from)) graph.set(t.from, []);\n graph.get(t.from)!.push({ event: t.event, to: t.to });\n }\n return graph;\n}\n","/**\n * BFS Reachability Algorithms\n *\n * Breadth-first search over state machine graphs.\n * Extracted from orbital-verify-unified/src/analyze.ts and phase3-server.ts.\n *\n * @packageDocumentation\n */\n\nimport type { BFSNode, StateEdge } from './types.js';\nimport { buildStateGraph } from './graph.js';\nimport type { GraphTransition } from './types.js';\n\n/**\n * Collect all reachable states from an initial state via BFS.\n *\n * @param transitions - State machine transitions\n * @param initialState - Starting state\n * @param maxDepth - Maximum BFS depth (default: 5)\n * @returns Set of reachable state names\n */\nexport function collectReachableStates(\n transitions: GraphTransition[],\n initialState: string,\n maxDepth = 5\n): Set<string> {\n const graph = buildStateGraph(transitions);\n const visited = new Set<string>([initialState]);\n const queue: BFSNode[] = [{ state: initialState, depth: 0 }];\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.depth >= maxDepth) continue;\n\n const edges = graph.get(current.state) ?? [];\n for (const edge of edges) {\n if (!visited.has(edge.to)) {\n visited.add(edge.to);\n queue.push({ state: edge.to, depth: current.depth + 1 });\n }\n }\n }\n\n return visited;\n}\n\n/**\n * Walk all (state, event) pairs reachable via BFS and invoke a callback for each.\n * Used by the server verification to POST each transition and check the response.\n *\n * @param transitions - State machine transitions\n * @param initialState - Starting state\n * @param maxDepth - Maximum BFS depth (default: 5)\n * @param visitor - Callback invoked for each (state, edge) pair.\n * Return true to enqueue the target state for further exploration.\n */\nexport async function walkStatePairs(\n transitions: GraphTransition[],\n initialState: string,\n maxDepth: number,\n visitor: (state: string, edge: StateEdge, depth: number) => Promise<boolean>\n): Promise<{ visitedPairs: Set<string>; walkedEdges: number }> {\n const graph = buildStateGraph(transitions);\n const visitedPairs = new Set<string>();\n const queue: BFSNode[] = [{ state: initialState, depth: 0 }];\n let walkedEdges = 0;\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n if (current.depth >= maxDepth) continue;\n\n const edges = graph.get(current.state) ?? [];\n for (const edge of edges) {\n const pairKey = `${current.state}:${edge.event}`;\n if (visitedPairs.has(pairKey)) continue;\n visitedPairs.add(pairKey);\n\n const shouldEnqueue = await visitor(current.state, edge, current.depth);\n walkedEdges++;\n\n if (shouldEnqueue) {\n const stateVisited = [...visitedPairs].some((k) => k.startsWith(`${edge.to}:`));\n if (!stateVisited) {\n queue.push({ state: edge.to, depth: current.depth + 1 });\n }\n }\n }\n }\n\n return { visitedPairs, walkedEdges };\n}\n","/**\n * Guard Payload Builder\n *\n * Derive pass and fail payloads from guard s-expressions.\n * Extracted from orbital-verify-unified/src/analyze.ts.\n *\n * @packageDocumentation\n */\n\nimport type { GuardPayload } from './types.js';\n\n/**\n * Extract the first @payload path segment from a binding reference.\n * \"@payload.item\" -> \"item\", \"@payload.data.weight\" -> \"data\".\n * Returns null for non-payload refs (@entity, @state, @user, etc.).\n */\nexport function extractPayloadFieldRef(ref: unknown): string | null {\n if (typeof ref !== 'string') return null;\n const match = ref.match(/^@payload\\.([A-Za-z0-9_]+)/);\n return match ? match[1] : null;\n}\n\n/**\n * Derive pass and fail payloads from a guard s-expression.\n *\n * Pass payload: satisfies the guard condition (transition fires).\n * Fail payload: violates the guard condition (transition is blocked).\n *\n * For @entity.* and @user.* guards: returns { pass: {}, fail: {} } because\n * these reference runtime state that cannot be directly faked in tests.\n *\n * Handles operators: not-nil, nil, eq, not-eq, gt, gte, lt, lte, and, or, not\n */\nexport function buildGuardPayloads(guard: unknown): GuardPayload {\n if (!Array.isArray(guard) || guard.length === 0) {\n return { pass: {}, fail: {} };\n }\n\n const op = String(guard[0]);\n\n if (op === 'not-nil' || op === 'not_nil') {\n const field = extractPayloadFieldRef(guard[1]);\n if (field) return { pass: { [field]: 'mock-test-value' }, fail: { [field]: null } };\n }\n\n if (op === 'nil') {\n const field = extractPayloadFieldRef(guard[1]);\n if (field) return { pass: {}, fail: { [field]: 'mock-test-value' } };\n }\n\n if (op === 'eq' || op === '==' || op === '=') {\n const field = extractPayloadFieldRef(guard[1]);\n const val = guard[2];\n if (field && val !== undefined) {\n const failVal =\n typeof val === 'number' ? val + 1\n : typeof val === 'string' ? `not-${val}`\n : null;\n return { pass: { [field]: val }, fail: { [field]: failVal } };\n }\n }\n\n if (op === 'not-eq' || op === '!=' || op === 'neq') {\n const field = extractPayloadFieldRef(guard[1]);\n const val = guard[2];\n if (field && val !== undefined) {\n const passVal =\n typeof val === 'number' ? val + 1\n : typeof val === 'string' ? `not-${val}`\n : 'other';\n return { pass: { [field]: passVal }, fail: { [field]: val } };\n }\n }\n\n if (op === 'gt' || op === '>') {\n const field = extractPayloadFieldRef(guard[1]);\n const n = typeof guard[2] === 'number' ? guard[2] : 0;\n if (field) return { pass: { [field]: n + 1 }, fail: { [field]: n - 1 } };\n }\n\n if (op === 'gte' || op === '>=') {\n const field = extractPayloadFieldRef(guard[1]);\n const n = typeof guard[2] === 'number' ? guard[2] : 0;\n if (field) return { pass: { [field]: n }, fail: { [field]: n - 1 } };\n }\n\n if (op === 'lt' || op === '<') {\n const field = extractPayloadFieldRef(guard[1]);\n const n = typeof guard[2] === 'number' ? guard[2] : 0;\n if (field) return { pass: { [field]: n - 1 }, fail: { [field]: n + 1 } };\n }\n\n if (op === 'lte' || op === '<=') {\n const field = extractPayloadFieldRef(guard[1]);\n const n = typeof guard[2] === 'number' ? guard[2] : 0;\n if (field) return { pass: { [field]: n }, fail: { [field]: n + 1 } };\n }\n\n if (op === 'and') {\n const subs = (guard.slice(1) as unknown[]).filter(Array.isArray);\n if (subs.length >= 2) {\n const s1 = buildGuardPayloads(subs[0]);\n const s2 = buildGuardPayloads(subs[1]);\n return { pass: { ...s1.pass, ...s2.pass }, fail: s1.fail };\n }\n if (subs.length === 1) return buildGuardPayloads(subs[0]);\n }\n\n if (op === 'or') {\n const subs = (guard.slice(1) as unknown[]).filter(Array.isArray);\n if (subs.length >= 2) {\n const s1 = buildGuardPayloads(subs[0]);\n const s2 = buildGuardPayloads(subs[1]);\n return { pass: s1.pass, fail: { ...s1.fail, ...s2.fail } };\n }\n if (subs.length === 1) return buildGuardPayloads(subs[0]);\n }\n\n if (op === 'not') {\n const inner = buildGuardPayloads(guard[1]);\n return { pass: inner.fail, fail: inner.pass };\n }\n\n return { pass: {}, fail: {} };\n}\n","/**\n * Replay Path Builder\n *\n * Compute the shortest path (replay steps) from an initial state\n * to every reachable state in a state machine. Used by browser\n * verification to navigate through states before running assertions.\n *\n * Extracted from orbital-verify-unified/src/analyze.ts (collectDataMutationTests).\n *\n * @packageDocumentation\n */\n\nimport type { GraphTransition, ReplayStep, PayloadFieldSchema } from './types.js';\n\n/** Entity-data sentinel payload fields: presence means transition needs a selected row */\nconst ENTITY_PAYLOAD_FIELDS = new Set(['data', 'row', 'item', 'id']);\n\n/**\n * Extended transition with render and payload info needed for replay path building.\n * Compatible with orbital-verify's UnifiedTransition.\n */\nexport interface ReplayTransition extends GraphTransition {\n hasGuard: boolean;\n guard?: unknown[];\n payloadFields: string[];\n payloadSchema: PayloadFieldSchema[];\n renderEffects: Array<{\n slot: string;\n patternType: string | null;\n }>;\n}\n\n/**\n * Build the shortest replay path from initialState to every reachable state.\n *\n * @param transitions - Transitions with render/payload info\n * @param initialState - Starting state\n * @param maxDepth - Maximum path length (default: 3, shorter than BFS exploration)\n * @returns Map of state -> replay steps to reach it\n */\nexport function buildReplayPaths(\n transitions: ReplayTransition[],\n initialState: string,\n maxDepth = 3\n): Map<string, ReplayStep[]> {\n type QueueNode = { state: string; path: ReplayStep[] };\n\n const queue: QueueNode[] = [{ state: initialState, path: [] }];\n const replayPaths = new Map<string, ReplayStep[]>();\n replayPaths.set(initialState, []);\n\n while (queue.length > 0) {\n const { state, path } = queue.shift()!;\n if (path.length >= maxDepth) continue;\n\n const fromHere = transitions.filter(\n (t) => t.from === state && t.event !== 'INIT',\n );\n\n for (const transition of fromHere) {\n if (replayPaths.has(transition.to)) continue;\n\n const renderEffect = transition.renderEffects.find((re) => re.patternType !== null);\n const stepNeedsEntityData =\n transition.hasGuard ||\n transition.payloadFields.some((f) => ENTITY_PAYLOAD_FIELDS.has(f));\n\n const step: ReplayStep = {\n event: transition.event,\n fromState: state,\n toState: transition.to,\n slot: renderEffect?.slot ?? 'main',\n expectedPattern: renderEffect?.patternType ?? undefined,\n needsEntityData: stepNeedsEntityData,\n payloadSchema: transition.payloadSchema.length > 0 ? transition.payloadSchema : undefined,\n };\n\n const newPath = [...path, step];\n replayPaths.set(transition.to, newPath);\n queue.push({ state: transition.to, path: newPath });\n }\n }\n\n return replayPaths;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almadar/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Core schema types and definitions for Almadar",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"./domain-language": {
|
|
19
19
|
"import": "./dist/domain-language/index.js",
|
|
20
20
|
"types": "./dist/domain-language/index.d.ts"
|
|
21
|
+
},
|
|
22
|
+
"./state-machine": {
|
|
23
|
+
"import": "./dist/state-machine/index.js",
|
|
24
|
+
"types": "./dist/state-machine/index.d.ts"
|
|
21
25
|
}
|
|
22
26
|
},
|
|
23
27
|
"files": [
|
|
@@ -34,7 +38,11 @@
|
|
|
34
38
|
"devDependencies": {
|
|
35
39
|
"tsup": "^8.0.0",
|
|
36
40
|
"typescript": "^5.4.0",
|
|
37
|
-
"vitest": "^1.4.0"
|
|
41
|
+
"vitest": "^1.4.0",
|
|
42
|
+
"eslint": "10.0.0",
|
|
43
|
+
"@typescript-eslint/parser": "8.56.0",
|
|
44
|
+
"@almadar/eslint-plugin": ">=2.3.0",
|
|
45
|
+
"@types/node": "^22.0.0"
|
|
38
46
|
},
|
|
39
47
|
"repository": {
|
|
40
48
|
"type": "git",
|