@dreamboard-games/sdk 0.4.0-alpha.8 → 0.4.0-alpha.9
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/REFERENCE.md +1067 -164
- package/dist/authoring/index.d.ts +4 -9
- package/dist/authoring/index.js +11 -547
- package/dist/authoring/index.js.map +1 -1
- package/dist/authoring-compiler.d.ts +27 -0
- package/dist/authoring-compiler.js +12 -0
- package/dist/authoring-compiler.js.map +1 -0
- package/dist/authoring-materialize-cli.d.ts +1 -0
- package/dist/authoring-materialize-cli.js +46 -0
- package/dist/authoring-materialize-cli.js.map +1 -0
- package/dist/browser-interaction.d.ts +1 -1
- package/dist/{chunk-3BLN5POZ.js → chunk-3PFDHTR7.js} +16 -6
- package/dist/chunk-3PFDHTR7.js.map +1 -0
- package/dist/chunk-45P5CEGH.js +176 -0
- package/dist/chunk-45P5CEGH.js.map +1 -0
- package/dist/chunk-5XTAQ4EQ.js +32 -0
- package/dist/chunk-5XTAQ4EQ.js.map +1 -0
- package/dist/chunk-7ADPYKFT.js +532 -0
- package/dist/chunk-7ADPYKFT.js.map +1 -0
- package/dist/{chunk-44N3T2A5.js → chunk-BUOODBVO.js} +2 -2
- package/dist/{chunk-ZYONCTZ2.js → chunk-GMOBG7Y7.js} +4 -4
- package/dist/{chunk-VTK4TIXC.js → chunk-JRGP6W4V.js} +3 -3
- package/dist/chunk-L3W2FPRQ.js +1793 -0
- package/dist/chunk-L3W2FPRQ.js.map +1 -0
- package/dist/{chunk-XV27IQHA.js → chunk-NGYFBNBP.js} +3 -3
- package/dist/chunk-P6IJ5YVY.js +154 -0
- package/dist/chunk-P6IJ5YVY.js.map +1 -0
- package/dist/{chunk-IYX5OO2K.js → chunk-QQNL4WTX.js} +15 -2
- package/dist/chunk-QQNL4WTX.js.map +1 -0
- package/dist/{chunk-2TAKLJJD.js → chunk-SACZWUA6.js} +139 -46
- package/dist/chunk-SACZWUA6.js.map +1 -0
- package/dist/{chunk-VTNEWMWJ.js → chunk-SFOAY5H4.js} +2 -2
- package/dist/{chunk-VTNEWMWJ.js.map → chunk-SFOAY5H4.js.map} +1 -1
- package/dist/chunk-UNVCT5Z7.js +73 -0
- package/dist/chunk-UNVCT5Z7.js.map +1 -0
- package/dist/{chunk-YGQTL63W.js → chunk-WDR7SKBM.js} +2 -2
- package/dist/chunk-XDGRC6OR.js +7039 -0
- package/dist/chunk-XDGRC6OR.js.map +1 -0
- package/dist/chunk-XLHLFTLI.js +399 -0
- package/dist/chunk-XLHLFTLI.js.map +1 -0
- package/dist/{chunk-B2YBS3SG.js → chunk-YNW3KBDE.js} +41 -13
- package/dist/chunk-YNW3KBDE.js.map +1 -0
- package/dist/codegen.js +1 -1
- package/dist/{components-BTwSIjXj.d.ts → components-BPzsSRs8.d.ts} +1 -1
- package/dist/definitions-0kjPV9wh.d.ts +294 -0
- package/dist/{diagnostics-1BWjRo6-.d.ts → diagnostics-DIi3ina7.d.ts} +2 -1
- package/dist/{index-CFTjJIXd.d.ts → index-CWzWpQzM.d.ts} +1 -1
- package/dist/{index.d-C0PKOVbh.d.ts → index.d-C7Ztirg6.d.ts} +211 -3
- package/dist/index.js +2 -2
- package/dist/package-set.d.ts +2 -2
- package/dist/package-set.js +2 -2
- package/dist/{protocol-nS2ojsGr.d.ts → protocol-B2LWcqB4.d.ts} +9 -0
- package/dist/reducer/advanced.d.ts +4 -4
- package/dist/reducer/advanced.js +7 -26
- package/dist/reducer/advanced.js.map +1 -1
- package/dist/reducer-contract.d.ts +1 -1
- package/dist/reducer-contract.js +2 -2
- package/dist/reducer.d.ts +17 -11
- package/dist/reducer.js +28 -6050
- package/dist/reducer.js.map +1 -1
- package/dist/reference-game-compiler.d.ts +45 -0
- package/dist/reference-game-compiler.js +25 -0
- package/dist/reference-game-compiler.js.map +1 -0
- package/dist/reference-game-source-manifest-cli.d.ts +1 -0
- package/dist/reference-game-source-manifest-cli.js +84 -0
- package/dist/reference-game-source-manifest-cli.js.map +1 -0
- package/dist/reference-games/index.d.ts +7 -217
- package/dist/reference-games/index.js +28 -208
- package/dist/reference-games/index.js.map +1 -1
- package/dist/runtime/primitives.d.ts +3 -3
- package/dist/runtime/primitives.js +3 -3
- package/dist/runtime/runtime-api.d.ts +1 -1
- package/dist/runtime/workspace-contract.d.ts +4 -4
- package/dist/runtime/workspace-contract.js +4 -4
- package/dist/{runtime-Dkfm1x2r.d.ts → runtime-B_w9AO2n.d.ts} +60 -5
- package/dist/{runtime-api-C6aWo9c0.d.ts → runtime-api-xCWLMQRT.d.ts} +2 -0
- package/dist/runtime.d.ts +7 -7
- package/dist/runtime.js +12 -12
- package/dist/schema-9jZzlEWZ.d.ts +220 -0
- package/dist/{stale-contract-artifact-error-C5AaZPJ8.d.ts → stale-contract-artifact-error-XLaweZtF.d.ts} +1 -1
- package/dist/testing-compiler.d.ts +36 -0
- package/dist/testing-compiler.js +236 -0
- package/dist/testing-compiler.js.map +1 -0
- package/dist/testing-runtime.d.ts +97 -21
- package/dist/testing-runtime.js +154 -95
- package/dist/testing-runtime.js.map +1 -1
- package/dist/testing.d.ts +178 -60
- package/dist/testing.js +641 -26
- package/dist/testing.js.map +1 -1
- package/dist/types-Bvptkukh.d.ts +122 -0
- package/dist/{types-CTeN_QAk.d.ts → types-_zfC_7CD.d.ts} +1 -1
- package/dist/ui/components.d.ts +2 -2
- package/dist/ui/plugin-styles.css +1 -1
- package/dist/{ui-contract-Ue6SywvO.d.ts → ui-contract-CUC7Lyoc.d.ts} +1 -1
- package/dist/ui.d.ts +3 -3
- package/dist/{views-Qx-EHBP8.d.ts → views-DE2eZxPz.d.ts} +15 -3
- package/package.json +22 -2
- package/dist/chunk-2TAKLJJD.js.map +0 -1
- package/dist/chunk-3BLN5POZ.js.map +0 -1
- package/dist/chunk-3OZMHZK3.js +0 -40
- package/dist/chunk-3OZMHZK3.js.map +0 -1
- package/dist/chunk-5CQIJHGF.js +0 -663
- package/dist/chunk-5CQIJHGF.js.map +0 -1
- package/dist/chunk-B2YBS3SG.js.map +0 -1
- package/dist/chunk-IYX5OO2K.js.map +0 -1
- package/dist/types-B_3pObUV.d.ts +0 -192
- /package/dist/{chunk-44N3T2A5.js.map → chunk-BUOODBVO.js.map} +0 -0
- /package/dist/{chunk-ZYONCTZ2.js.map → chunk-GMOBG7Y7.js.map} +0 -0
- /package/dist/{chunk-VTK4TIXC.js.map → chunk-JRGP6W4V.js.map} +0 -0
- /package/dist/{chunk-XV27IQHA.js.map → chunk-NGYFBNBP.js.map} +0 -0
- /package/dist/{chunk-YGQTL63W.js.map → chunk-WDR7SKBM.js.map} +0 -0
package/dist/testing-runtime.js
CHANGED
|
@@ -1,33 +1,91 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
ScenarioReplayError,
|
|
3
|
+
assertScenario,
|
|
4
|
+
digestScenarioJson,
|
|
5
|
+
materializeScenarioRuntimeCheckpoint,
|
|
6
|
+
replayScenario,
|
|
7
|
+
resolveScenarioCommandParams
|
|
8
|
+
} from "./chunk-L3W2FPRQ.js";
|
|
9
|
+
import "./chunk-5XTAQ4EQ.js";
|
|
10
|
+
import "./chunk-QQNL4WTX.js";
|
|
11
|
+
import {
|
|
12
|
+
interactionDomainEligibleCount
|
|
13
|
+
} from "./chunk-XDGRC6OR.js";
|
|
14
|
+
import "./chunk-YNW3KBDE.js";
|
|
15
|
+
import "./chunk-PF7L4BMG.js";
|
|
16
|
+
import "./chunk-3PFDHTR7.js";
|
|
7
17
|
import "./chunk-PZ5AY32C.js";
|
|
8
18
|
|
|
19
|
+
// src/testing/scenario-projection-digest.ts
|
|
20
|
+
function scenarioProjectionInputMetadata(input) {
|
|
21
|
+
return {
|
|
22
|
+
key: input.key,
|
|
23
|
+
kind: input.kind,
|
|
24
|
+
eligibleCount: interactionDomainEligibleCount(input.domain)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function scenarioProjectionParityFromInspectNode(node) {
|
|
28
|
+
return {
|
|
29
|
+
perspective: node.perspective.kind === "spectator" ? "spectator" : { seat: node.perspective.actor.seat },
|
|
30
|
+
flow: {
|
|
31
|
+
phase: node.flow.phase,
|
|
32
|
+
step: node.flow.step,
|
|
33
|
+
activeSeats: node.flow.activeActors.map(({ seat }) => seat),
|
|
34
|
+
pendingSeats: node.flow.pendingActors.map(({ seat }) => seat),
|
|
35
|
+
continuationWaiterSeats: node.flow.continuationWaiters.map(
|
|
36
|
+
({ seat }) => seat
|
|
37
|
+
),
|
|
38
|
+
blockedBy: node.flow.blockedBy.map(({ actor, blockers }) => ({
|
|
39
|
+
actorSeat: actor.seat,
|
|
40
|
+
blockerSeats: blockers.map(({ seat }) => seat)
|
|
41
|
+
}))
|
|
42
|
+
},
|
|
43
|
+
view: node.view,
|
|
44
|
+
interactions: node.interactions.map(
|
|
45
|
+
({ actor, interactionId, availability, inputs }) => ({
|
|
46
|
+
actorSeat: actor.seat,
|
|
47
|
+
interactionId,
|
|
48
|
+
availability,
|
|
49
|
+
inputs
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function digestScenarioProjection(projection) {
|
|
55
|
+
return digestScenarioJson({
|
|
56
|
+
digestVersion: "scenario-projection@1",
|
|
57
|
+
projection
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
9
61
|
// src/testing-runtime.ts
|
|
62
|
+
var CANDIDATE_INPUT_FIELDS = /* @__PURE__ */ new Set([
|
|
63
|
+
"reducer",
|
|
64
|
+
"scenarios",
|
|
65
|
+
"maxScenarios",
|
|
66
|
+
"maxStepsPerScenario"
|
|
67
|
+
]);
|
|
68
|
+
var MAX_DIAGNOSTIC_MESSAGE_LENGTH = 2e3;
|
|
69
|
+
var MAX_DIAGNOSTIC_TRACE_ENTRIES = 100;
|
|
10
70
|
async function runCandidateVerification(input) {
|
|
11
|
-
|
|
71
|
+
assertCandidateInputFields(input);
|
|
12
72
|
const scenarios = normalizeScenarios(input.scenarios);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
throw new Error("maxScenarios must be a positive integer.");
|
|
73
|
+
if (scenarios.length === 0) {
|
|
74
|
+
throw new Error("Candidate verification requires at least one scenario.");
|
|
16
75
|
}
|
|
76
|
+
const maxScenarios = input.maxScenarios ?? scenarios.length;
|
|
77
|
+
assertPositiveInteger(maxScenarios, "maxScenarios");
|
|
17
78
|
if (scenarios.length > maxScenarios) {
|
|
18
79
|
throw new Error(
|
|
19
80
|
`Candidate verification contains ${scenarios.length} scenarios, exceeding limit ${maxScenarios}.`
|
|
20
81
|
);
|
|
21
82
|
}
|
|
22
83
|
const maxStepsPerScenario = input.maxStepsPerScenario ?? 1e3;
|
|
23
|
-
|
|
24
|
-
throw new Error("maxStepsPerScenario must be a positive integer.");
|
|
25
|
-
}
|
|
84
|
+
assertPositiveInteger(maxStepsPerScenario, "maxStepsPerScenario");
|
|
26
85
|
const results = [];
|
|
27
86
|
for (const scenario of scenarios) {
|
|
28
87
|
results.push(
|
|
29
88
|
await runScenario({
|
|
30
|
-
bases,
|
|
31
89
|
reducer: input.reducer,
|
|
32
90
|
scenario,
|
|
33
91
|
maxStepsPerScenario
|
|
@@ -46,17 +104,34 @@ async function runCandidateVerification(input) {
|
|
|
46
104
|
};
|
|
47
105
|
}
|
|
48
106
|
async function runScenario(input) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
107
|
+
const actualSteps = input.scenario.given.length + input.scenario.when.length;
|
|
108
|
+
if (actualSteps > input.maxStepsPerScenario) {
|
|
109
|
+
return {
|
|
110
|
+
id: input.scenario.id,
|
|
111
|
+
status: "failed",
|
|
112
|
+
diagnostic: {
|
|
113
|
+
kind: "limit",
|
|
114
|
+
message: `Scenario contains ${actualSteps} replay steps, exceeding maxStepsPerScenario limit ${input.maxStepsPerScenario}.`,
|
|
115
|
+
actualSteps,
|
|
116
|
+
maxStepsPerScenario: input.maxStepsPerScenario
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
let replay;
|
|
57
121
|
try {
|
|
58
|
-
await
|
|
59
|
-
|
|
122
|
+
replay = await replayScenario({
|
|
123
|
+
game: input.reducer,
|
|
124
|
+
scenario: input.scenario
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return {
|
|
128
|
+
id: input.scenario.id,
|
|
129
|
+
status: "failed",
|
|
130
|
+
diagnostic: replayDiagnostic(error)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
await assertScenario({ replay, assertion: input.scenario.then });
|
|
60
135
|
return { id: input.scenario.id, status: "passed" };
|
|
61
136
|
} catch (error) {
|
|
62
137
|
return {
|
|
@@ -69,85 +144,69 @@ async function runScenario(input) {
|
|
|
69
144
|
};
|
|
70
145
|
}
|
|
71
146
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throw new Error(
|
|
81
|
-
`Scenario exceeded maxStepsPerScenario limit ${maxStepsPerScenario}.`
|
|
82
|
-
);
|
|
83
|
-
}
|
|
84
|
-
return runtime.submit(...args);
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
players: runtime.players,
|
|
88
|
-
seat: runtime.seat
|
|
89
|
-
};
|
|
90
|
-
return {
|
|
91
|
-
...baseContext,
|
|
92
|
-
state: () => runtime.getFrame().flow.currentPhase ?? "",
|
|
93
|
-
view: (playerId) => {
|
|
94
|
-
const frame = runtime.getFrame();
|
|
95
|
-
if (frame.perspectivePlayerId === playerId) return frame.view;
|
|
96
|
-
return runtime.getFrame().view;
|
|
97
|
-
},
|
|
98
|
-
interactions: (playerId) => {
|
|
99
|
-
const frame = runtime.getFrame();
|
|
100
|
-
if (frame.perspectivePlayerId !== playerId) return [];
|
|
101
|
-
return frame.availableInteractions;
|
|
102
|
-
},
|
|
103
|
-
explain: runtime.explain,
|
|
104
|
-
diagnostics: runtime.diagnostics,
|
|
105
|
-
expect: createExpectApi({
|
|
106
|
-
lastDiagnosticRejection: () => {
|
|
107
|
-
for (let index = runtime.diagnostics.events.length - 1; index >= 0; index -= 1) {
|
|
108
|
-
const event = runtime.diagnostics.events[index];
|
|
109
|
-
if (event?.type === "submitRejected") return event;
|
|
110
|
-
}
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
};
|
|
147
|
+
function assertCandidateInputFields(input) {
|
|
148
|
+
for (const field of Object.keys(input)) {
|
|
149
|
+
if (!CANDIDATE_INPUT_FIELDS.has(field)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`Candidate verification input contains unsupported field '${field}'.`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
115
155
|
}
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (!id) throw new Error("Candidate verification base id is required.");
|
|
121
|
-
if (!("snapshot" in base) || !base.snapshot) {
|
|
122
|
-
throw new Error(`Candidate verification base '${id}' has no snapshot.`);
|
|
123
|
-
}
|
|
124
|
-
const players = base.fingerprint?.players ?? base.players ?? base.snapshot.domain?.flow?.activePlayers?.length ?? 1;
|
|
125
|
-
return [
|
|
126
|
-
id,
|
|
127
|
-
{
|
|
128
|
-
snapshot: base.snapshot,
|
|
129
|
-
fingerprint: {
|
|
130
|
-
players,
|
|
131
|
-
contractFingerprint: base.fingerprint?.contractFingerprint
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
];
|
|
135
|
-
})
|
|
136
|
-
);
|
|
156
|
+
function assertPositiveInteger(value, field) {
|
|
157
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
158
|
+
throw new Error(`${field} must be a positive safe integer.`);
|
|
159
|
+
}
|
|
137
160
|
}
|
|
138
161
|
function normalizeScenarios(input) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
162
|
+
if (typeof input !== "object" || input === null) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
"Candidate verification scenarios must be an array or record."
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const scenarios = Array.isArray(input) ? [...input] : Object.values(input);
|
|
168
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
169
|
+
for (const scenario of scenarios) {
|
|
170
|
+
if (typeof scenario?.id !== "string" || scenario.id.length === 0) {
|
|
142
171
|
throw new Error("Candidate verification scenario id is required.");
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
}
|
|
173
|
+
if (seenIds.has(scenario.id)) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Candidate verification scenario id '${scenario.id}' is duplicated.`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
seenIds.add(scenario.id);
|
|
179
|
+
}
|
|
180
|
+
return scenarios;
|
|
181
|
+
}
|
|
182
|
+
function replayDiagnostic(error) {
|
|
183
|
+
if (error instanceof ScenarioReplayError) {
|
|
184
|
+
return {
|
|
185
|
+
kind: "replay",
|
|
186
|
+
message: boundedMessage(error),
|
|
187
|
+
scenarioId: error.scenarioId,
|
|
188
|
+
segment: error.segment,
|
|
189
|
+
index: error.index,
|
|
190
|
+
interactionId: error.interactionId,
|
|
191
|
+
errorCode: error.errorCode,
|
|
192
|
+
...error.reducerMessage === void 0 ? {} : { reducerMessage: boundedText(error.reducerMessage) },
|
|
193
|
+
trace: error.trace.slice(0, MAX_DIAGNOSTIC_TRACE_ENTRIES)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { kind: "replay", message: boundedMessage(error) };
|
|
145
197
|
}
|
|
146
198
|
function boundedMessage(error) {
|
|
147
|
-
|
|
148
|
-
|
|
199
|
+
return boundedText(error instanceof Error ? error.message : String(error));
|
|
200
|
+
}
|
|
201
|
+
function boundedText(value) {
|
|
202
|
+
return value.slice(0, MAX_DIAGNOSTIC_MESSAGE_LENGTH);
|
|
149
203
|
}
|
|
150
204
|
export {
|
|
151
|
-
|
|
205
|
+
digestScenarioProjection,
|
|
206
|
+
materializeScenarioRuntimeCheckpoint,
|
|
207
|
+
resolveScenarioCommandParams,
|
|
208
|
+
runCandidateVerification,
|
|
209
|
+
scenarioProjectionInputMetadata,
|
|
210
|
+
scenarioProjectionParityFromInspectNode
|
|
152
211
|
};
|
|
153
212
|
//# sourceMappingURL=testing-runtime.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing-runtime.ts"],"sourcesContent":["import type { Wire } from \"@dreamboard-games/reducer-contract\";\nimport { createExpectApi } from \"./testing/create-expect-api.js\";\nimport { createTestRuntime } from \"./testing/create-test-runtime.js\";\nimport type {\n BaseContext,\n BaseDefinition,\n InteractionDescriptorLike,\n ScenarioDefinition,\n SharedScenarioContext,\n} from \"./testing/definitions.js\";\nimport type { ReducerScenarioBundle } from \"./testing/reducer-scenario/types.js\";\n\ntype BaseStateArtifact = {\n readonly snapshot: Wire.ReducerSessionState;\n readonly fingerprint: {\n readonly players: number;\n readonly contractFingerprint?: string;\n };\n};\n\nexport type CandidateVerificationBase =\n | BaseStateArtifact\n | (BaseDefinition & Partial<BaseStateArtifact>);\n\nexport type CandidateVerificationScenario = ScenarioDefinition;\n\nexport type CandidateVerificationInput = {\n readonly manifest?: unknown;\n readonly bases:\n | Readonly<Record<string, CandidateVerificationBase>>\n | readonly CandidateVerificationBase[];\n readonly scenarios:\n | Readonly<Record<string, CandidateVerificationScenario>>\n | readonly CandidateVerificationScenario[];\n readonly reducer: ReducerScenarioBundle;\n readonly maxScenarios?: number;\n readonly maxStepsPerScenario?: number;\n};\n\nexport type CandidateVerificationScenarioResult = {\n readonly id: string;\n readonly status: \"passed\" | \"failed\";\n readonly diagnostic?: {\n readonly kind: \"assertion\";\n readonly message: string;\n };\n};\n\nexport type CandidateVerificationResult = {\n readonly status: \"passed\" | \"failed\";\n readonly scenarioSummary: {\n readonly total: number;\n readonly passed: number;\n readonly failed: number;\n readonly scenarios: readonly CandidateVerificationScenarioResult[];\n };\n};\n\nexport async function runCandidateVerification(\n input: CandidateVerificationInput,\n): Promise<CandidateVerificationResult> {\n const bases = normalizeBases(input.bases);\n const scenarios = normalizeScenarios(input.scenarios);\n const maxScenarios = input.maxScenarios ?? scenarios.length;\n if (!Number.isInteger(maxScenarios) || maxScenarios < 1) {\n throw new Error(\"maxScenarios must be a positive integer.\");\n }\n if (scenarios.length > maxScenarios) {\n throw new Error(\n `Candidate verification contains ${scenarios.length} scenarios, exceeding limit ${maxScenarios}.`,\n );\n }\n const maxStepsPerScenario = input.maxStepsPerScenario ?? 1000;\n if (!Number.isInteger(maxStepsPerScenario) || maxStepsPerScenario < 1) {\n throw new Error(\"maxStepsPerScenario must be a positive integer.\");\n }\n\n const results: CandidateVerificationScenarioResult[] = [];\n for (const scenario of scenarios) {\n results.push(\n await runScenario({\n bases,\n reducer: input.reducer,\n scenario,\n maxStepsPerScenario,\n }),\n );\n }\n const failed = results.filter((result) => result.status === \"failed\").length;\n return {\n status: failed === 0 ? \"passed\" : \"failed\",\n scenarioSummary: {\n total: results.length,\n passed: results.length - failed,\n failed,\n scenarios: results,\n },\n };\n}\n\nasync function runScenario(input: {\n bases: Readonly<Record<string, BaseStateArtifact>>;\n reducer: ReducerScenarioBundle;\n scenario: CandidateVerificationScenario;\n maxStepsPerScenario: number;\n}): Promise<CandidateVerificationScenarioResult> {\n const runtime = createTestRuntime({\n baseId: input.scenario.from,\n baseStates: input.bases,\n bundle: input.reducer,\n phase: input.scenario.phase,\n playerIds:\n input.bases[input.scenario.from]?.snapshot.domain?.flow?.activePlayers,\n });\n const ctx = createScenarioContext(runtime, input.maxStepsPerScenario);\n try {\n await input.scenario.when(ctx);\n await input.scenario.then(ctx);\n return { id: input.scenario.id, status: \"passed\" };\n } catch (error) {\n return {\n id: input.scenario.id,\n status: \"failed\",\n diagnostic: {\n kind: \"assertion\",\n message: boundedMessage(error),\n },\n };\n }\n}\n\nfunction createScenarioContext(\n runtime: ReturnType<typeof createTestRuntime>,\n maxStepsPerScenario: number,\n): SharedScenarioContext {\n let submittedSteps = 0;\n const baseContext: BaseContext = {\n game: {\n start: async () => undefined,\n submit: async (...args) => {\n submittedSteps += 1;\n if (submittedSteps > maxStepsPerScenario) {\n throw new Error(\n `Scenario exceeded maxStepsPerScenario limit ${maxStepsPerScenario}.`,\n );\n }\n return runtime.submit(...args);\n },\n },\n players: runtime.players,\n seat: runtime.seat,\n };\n return {\n ...baseContext,\n state: () => runtime.getFrame().flow.currentPhase ?? \"\",\n view: (playerId) => {\n const frame = runtime.getFrame();\n if (frame.perspectivePlayerId === playerId) return frame.view;\n return runtime.getFrame().view;\n },\n interactions: (playerId) => {\n const frame = runtime.getFrame();\n if (frame.perspectivePlayerId !== playerId) return [];\n return frame.availableInteractions as unknown as readonly InteractionDescriptorLike[];\n },\n explain: runtime.explain,\n diagnostics: runtime.diagnostics,\n expect: createExpectApi({\n lastDiagnosticRejection: () => {\n for (\n let index = runtime.diagnostics.events.length - 1;\n index >= 0;\n index -= 1\n ) {\n const event = runtime.diagnostics.events[index];\n if (event?.type === \"submitRejected\") return event;\n }\n return null;\n },\n }),\n };\n}\n\nfunction normalizeBases(\n input:\n | Readonly<Record<string, CandidateVerificationBase>>\n | readonly CandidateVerificationBase[],\n): Record<string, BaseStateArtifact> {\n const entries = Array.isArray(input)\n ? input.map((base) => [base.id, base] as const)\n : Object.entries(input);\n return Object.fromEntries(\n entries.map(([id, base]) => {\n if (!id) throw new Error(\"Candidate verification base id is required.\");\n if (!(\"snapshot\" in base) || !base.snapshot) {\n throw new Error(`Candidate verification base '${id}' has no snapshot.`);\n }\n const players =\n base.fingerprint?.players ??\n base.players ??\n base.snapshot.domain?.flow?.activePlayers?.length ??\n 1;\n return [\n id,\n {\n snapshot: base.snapshot,\n fingerprint: {\n players,\n contractFingerprint: base.fingerprint?.contractFingerprint,\n },\n },\n ];\n }),\n );\n}\n\nfunction normalizeScenarios(\n input:\n | Readonly<Record<string, CandidateVerificationScenario>>\n | readonly CandidateVerificationScenario[],\n): CandidateVerificationScenario[] {\n const scenarios = Array.isArray(input) ? input : Object.values(input);\n return scenarios.map((scenario) => {\n if (!scenario.id)\n throw new Error(\"Candidate verification scenario id is required.\");\n return scenario;\n });\n}\n\nfunction boundedMessage(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n return message.slice(0, 2000);\n}\n"],"mappings":";;;;;;;;;AA0DA,eAAsB,yBACpB,OACsC;AACtC,QAAM,QAAQ,eAAe,MAAM,KAAK;AACxC,QAAM,YAAY,mBAAmB,MAAM,SAAS;AACpD,QAAM,eAAe,MAAM,gBAAgB,UAAU;AACrD,MAAI,CAAC,OAAO,UAAU,YAAY,KAAK,eAAe,GAAG;AACvD,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACA,MAAI,UAAU,SAAS,cAAc;AACnC,UAAM,IAAI;AAAA,MACR,mCAAmC,UAAU,MAAM,+BAA+B,YAAY;AAAA,IAChG;AAAA,EACF;AACA,QAAM,sBAAsB,MAAM,uBAAuB;AACzD,MAAI,CAAC,OAAO,UAAU,mBAAmB,KAAK,sBAAsB,GAAG;AACrE,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,QAAM,UAAiD,CAAC;AACxD,aAAW,YAAY,WAAW;AAChC,YAAQ;AAAA,MACN,MAAM,YAAY;AAAA,QAChB;AAAA,QACA,SAAS,MAAM;AAAA,QACf;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACA,QAAM,SAAS,QAAQ,OAAO,CAAC,WAAW,OAAO,WAAW,QAAQ,EAAE;AACtE,SAAO;AAAA,IACL,QAAQ,WAAW,IAAI,WAAW;AAAA,IAClC,iBAAiB;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,QAAQ,QAAQ,SAAS;AAAA,MACzB;AAAA,MACA,WAAW;AAAA,IACb;AAAA,EACF;AACF;AAEA,eAAe,YAAY,OAKsB;AAC/C,QAAM,UAAU,kBAAkB;AAAA,IAChC,QAAQ,MAAM,SAAS;AAAA,IACvB,YAAY,MAAM;AAAA,IAClB,QAAQ,MAAM;AAAA,IACd,OAAO,MAAM,SAAS;AAAA,IACtB,WACE,MAAM,MAAM,MAAM,SAAS,IAAI,GAAG,SAAS,QAAQ,MAAM;AAAA,EAC7D,CAAC;AACD,QAAM,MAAM,sBAAsB,SAAS,MAAM,mBAAmB;AACpE,MAAI;AACF,UAAM,MAAM,SAAS,KAAK,GAAG;AAC7B,UAAM,MAAM,SAAS,KAAK,GAAG;AAC7B,WAAO,EAAE,IAAI,MAAM,SAAS,IAAI,QAAQ,SAAS;AAAA,EACnD,SAAS,OAAO;AACd,WAAO;AAAA,MACL,IAAI,MAAM,SAAS;AAAA,MACnB,QAAQ;AAAA,MACR,YAAY;AAAA,QACV,MAAM;AAAA,QACN,SAAS,eAAe,KAAK;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,sBACP,SACA,qBACuB;AACvB,MAAI,iBAAiB;AACrB,QAAM,cAA2B;AAAA,IAC/B,MAAM;AAAA,MACJ,OAAO,YAAY;AAAA,MACnB,QAAQ,UAAU,SAAS;AACzB,0BAAkB;AAClB,YAAI,iBAAiB,qBAAqB;AACxC,gBAAM,IAAI;AAAA,YACR,+CAA+C,mBAAmB;AAAA,UACpE;AAAA,QACF;AACA,eAAO,QAAQ,OAAO,GAAG,IAAI;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB,MAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,MAAM,QAAQ,SAAS,EAAE,KAAK,gBAAgB;AAAA,IACrD,MAAM,CAAC,aAAa;AAClB,YAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAI,MAAM,wBAAwB,SAAU,QAAO,MAAM;AACzD,aAAO,QAAQ,SAAS,EAAE;AAAA,IAC5B;AAAA,IACA,cAAc,CAAC,aAAa;AAC1B,YAAM,QAAQ,QAAQ,SAAS;AAC/B,UAAI,MAAM,wBAAwB,SAAU,QAAO,CAAC;AACpD,aAAO,MAAM;AAAA,IACf;AAAA,IACA,SAAS,QAAQ;AAAA,IACjB,aAAa,QAAQ;AAAA,IACrB,QAAQ,gBAAgB;AAAA,MACtB,yBAAyB,MAAM;AAC7B,iBACM,QAAQ,QAAQ,YAAY,OAAO,SAAS,GAChD,SAAS,GACT,SAAS,GACT;AACA,gBAAM,QAAQ,QAAQ,YAAY,OAAO,KAAK;AAC9C,cAAI,OAAO,SAAS,iBAAkB,QAAO;AAAA,QAC/C;AACA,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,eACP,OAGmC;AACnC,QAAM,UAAU,MAAM,QAAQ,KAAK,IAC/B,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAU,IAC5C,OAAO,QAAQ,KAAK;AACxB,SAAO,OAAO;AAAA,IACZ,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM;AAC1B,UAAI,CAAC,GAAI,OAAM,IAAI,MAAM,6CAA6C;AACtE,UAAI,EAAE,cAAc,SAAS,CAAC,KAAK,UAAU;AAC3C,cAAM,IAAI,MAAM,gCAAgC,EAAE,oBAAoB;AAAA,MACxE;AACA,YAAM,UACJ,KAAK,aAAa,WAClB,KAAK,WACL,KAAK,SAAS,QAAQ,MAAM,eAAe,UAC3C;AACF,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,UAAU,KAAK;AAAA,UACf,aAAa;AAAA,YACX;AAAA,YACA,qBAAqB,KAAK,aAAa;AAAA,UACzC;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,SAAS,mBACP,OAGiC;AACjC,QAAM,YAAY,MAAM,QAAQ,KAAK,IAAI,QAAQ,OAAO,OAAO,KAAK;AACpE,SAAO,UAAU,IAAI,CAAC,aAAa;AACjC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,iDAAiD;AACnE,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,eAAe,OAAwB;AAC9C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,QAAQ,MAAM,GAAG,GAAI;AAC9B;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/testing/scenario-projection-digest.ts","../src/testing-runtime.ts"],"sourcesContent":["import { digestScenarioJson } from \"./canonical.js\";\nimport { interactionDomainEligibleCount } from \"../reducer/bundle/trusted/interaction-domain-metadata.js\";\nimport type { InspectNode, Sha256Digest } from \"./inspection/types.js\";\n\nexport type ScenarioProjectionInteractionInput = {\n readonly key: string;\n readonly kind: string;\n readonly domain: unknown;\n};\n\n/** Shared SDK normalization for inspect DTOs and backend parity projections. */\nexport function scenarioProjectionInputMetadata(\n input: ScenarioProjectionInteractionInput,\n): {\n readonly key: string;\n readonly kind: string;\n readonly eligibleCount: number | \"lazy\";\n} {\n return {\n key: input.key,\n kind: input.kind,\n eligibleCount: interactionDomainEligibleCount(input.domain),\n };\n}\n\nexport type ScenarioProjectionParity = {\n readonly perspective: \"spectator\" | { readonly seat: number };\n readonly flow: {\n readonly phase: string;\n readonly step: string | null;\n readonly activeSeats: readonly number[];\n readonly pendingSeats: readonly number[];\n readonly continuationWaiterSeats: readonly number[];\n readonly blockedBy: readonly {\n readonly actorSeat: number;\n readonly blockerSeats: readonly number[];\n }[];\n };\n readonly view: unknown;\n readonly interactions: readonly {\n readonly actorSeat: number;\n readonly interactionId: string;\n readonly availability: {\n readonly status: string;\n readonly code?: string;\n readonly reason?: string;\n };\n readonly inputs: readonly {\n readonly key: string;\n readonly kind: string;\n readonly eligibleCount: number | \"lazy\";\n }[];\n }[];\n};\n\n/**\n * Canonical local/backend parity authority. It deliberately excludes raw\n * reducer public/private state, interaction explanations, entropy, and trace:\n * backend gameplay frames expose the selected projection, not reducer state.\n */\nexport function scenarioProjectionParityFromInspectNode(\n node: InspectNode,\n): ScenarioProjectionParity {\n return {\n perspective:\n node.perspective.kind === \"spectator\"\n ? \"spectator\"\n : { seat: node.perspective.actor.seat },\n flow: {\n phase: node.flow.phase,\n step: node.flow.step,\n activeSeats: node.flow.activeActors.map(({ seat }) => seat),\n pendingSeats: node.flow.pendingActors.map(({ seat }) => seat),\n continuationWaiterSeats: node.flow.continuationWaiters.map(\n ({ seat }) => seat,\n ),\n blockedBy: node.flow.blockedBy.map(({ actor, blockers }) => ({\n actorSeat: actor.seat,\n blockerSeats: blockers.map(({ seat }) => seat),\n })),\n },\n view: node.view,\n interactions: node.interactions.map(\n ({ actor, interactionId, availability, inputs }) => ({\n actorSeat: actor.seat,\n interactionId,\n availability,\n inputs,\n }),\n ),\n };\n}\n\nexport function digestScenarioProjection(\n projection: ScenarioProjectionParity,\n): Sha256Digest {\n return digestScenarioJson({\n digestVersion: \"scenario-projection@1\",\n projection,\n });\n}\n","import type { DispatchTraceSummaryEntry } from \"./reducer/diagnostics.js\";\nimport {\n ScenarioReplayError,\n type ScenarioDefinition,\n} from \"./testing/definitions.js\";\nimport type { ScenarioDefinitionGameLike } from \"./testing/scenario-definition-validation.js\";\nimport { assertScenario, replayScenario } from \"./testing/scenario-replay.js\";\n\nexport {\n materializeScenarioRuntimeCheckpoint,\n type ScenarioRuntimeCheckpointMaterialization,\n} from \"./testing/scenario-replay.js\";\n\n// Internal published seam for CLI scenario loading and backend parity. These\n// helpers intentionally do not appear on the author-facing `/testing` facade.\nexport { resolveScenarioCommandParams } from \"./testing/scenario-player-refs.js\";\nexport {\n digestScenarioProjection,\n scenarioProjectionInputMetadata,\n scenarioProjectionParityFromInspectNode,\n type ScenarioProjectionInteractionInput,\n type ScenarioProjectionParity,\n} from \"./testing/scenario-projection-digest.js\";\n\nexport type CandidateVerificationScenario<\n Game extends ScenarioDefinitionGameLike,\n> = ScenarioDefinition<Game>;\n\nexport type CandidateVerificationInput<\n Game extends ScenarioDefinitionGameLike,\n> = {\n /** The authored reducer game definition used by production dispatch. */\n readonly reducer: Game;\n readonly scenarios:\n | Readonly<Record<string, CandidateVerificationScenario<Game>>>\n | readonly CandidateVerificationScenario<Game>[];\n readonly maxScenarios?: number;\n readonly maxStepsPerScenario?: number;\n};\n\nexport type CandidateVerificationReplayDiagnostic = {\n readonly kind: \"replay\";\n readonly message: string;\n readonly scenarioId?: string;\n readonly segment?: \"given\" | \"when\";\n readonly index?: number;\n readonly interactionId?: string;\n readonly errorCode?: string;\n readonly reducerMessage?: string;\n readonly trace?: readonly DispatchTraceSummaryEntry[];\n};\n\nexport type CandidateVerificationAssertionDiagnostic = {\n readonly kind: \"assertion\";\n readonly message: string;\n};\n\nexport type CandidateVerificationLimitDiagnostic = {\n readonly kind: \"limit\";\n readonly message: string;\n readonly actualSteps: number;\n readonly maxStepsPerScenario: number;\n};\n\nexport type CandidateVerificationScenarioResult = {\n readonly id: string;\n readonly status: \"passed\" | \"failed\";\n readonly diagnostic?:\n | CandidateVerificationReplayDiagnostic\n | CandidateVerificationAssertionDiagnostic\n | CandidateVerificationLimitDiagnostic;\n};\n\nexport type CandidateVerificationResult = {\n readonly status: \"passed\" | \"failed\";\n readonly scenarioSummary: {\n readonly total: number;\n readonly passed: number;\n readonly failed: number;\n readonly scenarios: readonly CandidateVerificationScenarioResult[];\n };\n};\n\nconst CANDIDATE_INPUT_FIELDS = new Set([\n \"reducer\",\n \"scenarios\",\n \"maxScenarios\",\n \"maxStepsPerScenario\",\n]);\nconst MAX_DIAGNOSTIC_MESSAGE_LENGTH = 2_000;\nconst MAX_DIAGNOSTIC_TRACE_ENTRIES = 100;\n\nexport async function runCandidateVerification<\n const Game extends ScenarioDefinitionGameLike,\n>(\n input: CandidateVerificationInput<Game>,\n): Promise<CandidateVerificationResult> {\n assertCandidateInputFields(input);\n const scenarios = normalizeScenarios(input.scenarios);\n if (scenarios.length === 0) {\n throw new Error(\"Candidate verification requires at least one scenario.\");\n }\n\n const maxScenarios = input.maxScenarios ?? scenarios.length;\n assertPositiveInteger(maxScenarios, \"maxScenarios\");\n if (scenarios.length > maxScenarios) {\n throw new Error(\n `Candidate verification contains ${scenarios.length} scenarios, exceeding limit ${maxScenarios}.`,\n );\n }\n\n const maxStepsPerScenario = input.maxStepsPerScenario ?? 1_000;\n assertPositiveInteger(maxStepsPerScenario, \"maxStepsPerScenario\");\n\n const results: CandidateVerificationScenarioResult[] = [];\n for (const scenario of scenarios) {\n results.push(\n await runScenario({\n reducer: input.reducer,\n scenario,\n maxStepsPerScenario,\n }),\n );\n }\n\n const failed = results.filter((result) => result.status === \"failed\").length;\n return {\n status: failed === 0 ? \"passed\" : \"failed\",\n scenarioSummary: {\n total: results.length,\n passed: results.length - failed,\n failed,\n scenarios: results,\n },\n };\n}\n\nasync function runScenario<Game extends ScenarioDefinitionGameLike>(input: {\n readonly reducer: Game;\n readonly scenario: CandidateVerificationScenario<Game>;\n readonly maxStepsPerScenario: number;\n}): Promise<CandidateVerificationScenarioResult> {\n const actualSteps = input.scenario.given.length + input.scenario.when.length;\n if (actualSteps > input.maxStepsPerScenario) {\n return {\n id: input.scenario.id,\n status: \"failed\",\n diagnostic: {\n kind: \"limit\",\n message:\n `Scenario contains ${actualSteps} replay steps, exceeding ` +\n `maxStepsPerScenario limit ${input.maxStepsPerScenario}.`,\n actualSteps,\n maxStepsPerScenario: input.maxStepsPerScenario,\n },\n };\n }\n\n let replay;\n try {\n replay = await replayScenario({\n game: input.reducer,\n scenario: input.scenario,\n });\n } catch (error) {\n return {\n id: input.scenario.id,\n status: \"failed\",\n diagnostic: replayDiagnostic(error),\n };\n }\n\n try {\n await assertScenario({ replay, assertion: input.scenario.then });\n return { id: input.scenario.id, status: \"passed\" };\n } catch (error) {\n return {\n id: input.scenario.id,\n status: \"failed\",\n diagnostic: {\n kind: \"assertion\",\n message: boundedMessage(error),\n },\n };\n }\n}\n\nfunction assertCandidateInputFields(input: object): void {\n for (const field of Object.keys(input)) {\n if (!CANDIDATE_INPUT_FIELDS.has(field)) {\n throw new Error(\n `Candidate verification input contains unsupported field '${field}'.`,\n );\n }\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isSafeInteger(value) || value < 1) {\n throw new Error(`${field} must be a positive safe integer.`);\n }\n}\n\nfunction normalizeScenarios<Game extends ScenarioDefinitionGameLike>(\n input:\n | Readonly<Record<string, CandidateVerificationScenario<Game>>>\n | readonly CandidateVerificationScenario<Game>[],\n): CandidateVerificationScenario<Game>[] {\n if (typeof input !== \"object\" || input === null) {\n throw new Error(\n \"Candidate verification scenarios must be an array or record.\",\n );\n }\n const scenarios = Array.isArray(input) ? [...input] : Object.values(input);\n const seenIds = new Set<string>();\n for (const scenario of scenarios) {\n if (typeof scenario?.id !== \"string\" || scenario.id.length === 0) {\n throw new Error(\"Candidate verification scenario id is required.\");\n }\n if (seenIds.has(scenario.id)) {\n throw new Error(\n `Candidate verification scenario id '${scenario.id}' is duplicated.`,\n );\n }\n seenIds.add(scenario.id);\n }\n return scenarios;\n}\n\nfunction replayDiagnostic(\n error: unknown,\n): CandidateVerificationReplayDiagnostic {\n if (error instanceof ScenarioReplayError) {\n return {\n kind: \"replay\",\n message: boundedMessage(error),\n scenarioId: error.scenarioId,\n segment: error.segment,\n index: error.index,\n interactionId: error.interactionId,\n errorCode: error.errorCode,\n ...(error.reducerMessage === undefined\n ? {}\n : { reducerMessage: boundedText(error.reducerMessage) }),\n trace: error.trace.slice(0, MAX_DIAGNOSTIC_TRACE_ENTRIES),\n };\n }\n return { kind: \"replay\", message: boundedMessage(error) };\n}\n\nfunction boundedMessage(error: unknown): string {\n return boundedText(error instanceof Error ? error.message : String(error));\n}\n\nfunction boundedText(value: string): string {\n return value.slice(0, MAX_DIAGNOSTIC_MESSAGE_LENGTH);\n}\n"],"mappings":"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","names":[]}
|