@moku-labs/game 0.7.1 → 0.9.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/README.md +12 -8
- package/dist/assets.d.mts +97 -33
- package/dist/assets.mjs +350 -110
- package/dist/{commands-mzE9PX0H.d.mts → commands-DuwCxK5t.d.mts} +5 -4
- package/dist/control.d.mts +4 -3
- package/dist/control.mjs +2 -2
- package/dist/{define-BbgXv4cy.d.mts → define-B6pHpdBi.d.mts} +4 -3
- package/dist/headless-BsNdJs9W.mjs +186 -0
- package/dist/headless-SCQXJxQf.d.mts +181 -0
- package/dist/hot.d.mts +4 -2
- package/dist/hot.mjs +8 -6
- package/dist/index-BO9kZzY3.d.mts +1611 -0
- package/dist/index.d.mts +6 -1596
- package/dist/index.mjs +4 -34334
- package/dist/{input-CK4nxBO1.mjs → input-z_35Jwfn.mjs} +2 -2
- package/dist/inspect.d.mts +7 -6
- package/dist/inspect.mjs +2 -2
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/lint.d.mts +55 -4
- package/dist/lint.mjs +993 -40
- package/dist/project.d.mts +57 -41
- package/dist/project.mjs +994 -505
- package/dist/{run-Bcyg32M5.mjs → run-CofJEdYx.mjs} +4 -187
- package/dist/{runtime-HTlmRb81.d.mts → runtime-CdNJ0UBm.d.mts} +1 -1
- package/dist/{sources-DVWJ8nfr.mjs → sources-DNnzyCYf.mjs} +2 -2
- package/dist/src-536arXjV.mjs +34644 -0
- package/dist/testing.d.mts +85 -283
- package/dist/testing.mjs +158 -2109
- package/dist/{types-BMEn0tSC.d.mts → types-B0v5VVa3.d.mts} +10 -9
- package/dist/{types-ClF8say0.d.mts → types-CQueODzo.d.mts} +2 -2
- package/dist/{types-CYYKjvLn.d.mts → types-DUMoYPYe.d.mts} +5 -182
- package/dist/{types-CXyipKhE.d.mts → types-VZVWwqCg.d.mts} +42 -4
- package/dist/{types-BaUUcIaN.d.mts → types-xXfyk56Q.d.mts} +1 -1
- package/dist/{visual-DUvvPPCt.mjs → visual-dMW8iqAE.mjs} +8 -4
- package/dist/visual.d.mts +330 -0
- package/dist/visual.mjs +2142 -0
- package/llms.txt +59 -21
- package/package.json +7 -8
- package/dist/memory-CvgdnsQO.mjs +0 -259
|
@@ -1,190 +1,7 @@
|
|
|
1
|
-
import { a as sheetAlone
|
|
1
|
+
import { a as sheetAlone } from "./capture-fpd83Mi5.mjs";
|
|
2
2
|
import { n as isDev, r as notInstalled, t as controlRefused } from "./dev-wKLgOw7c.mjs";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
const noPayload = null;
|
|
6
|
-
/** Seed of a repro that pins no rng state, the seed `saveOf` writes. */
|
|
7
|
-
const defaultSeed = 1;
|
|
8
|
-
/**
|
|
9
|
-
* Turns whatever `run()` rejected with into an error that can be thrown again.
|
|
10
|
-
*
|
|
11
|
-
* @param value - The rejection value.
|
|
12
|
-
* @returns The value itself when it is an error, a wrapped one otherwise.
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* asError("offline").message; // "[game] The headless run failed.\n offline."
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
function asError(value) {
|
|
19
|
-
if (value instanceof Error) return value;
|
|
20
|
-
return /* @__PURE__ */ new Error(`[game] The headless run failed.\n ${String(value)}.`);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Starts the one loop unless the app's own `onStart` already did, and keeps a fatal error until
|
|
24
|
-
* someone asks for it. `run()` rejects only on a fatal error, and nobody awaits it here.
|
|
25
|
-
*
|
|
26
|
-
* @param app - The started app.
|
|
27
|
-
* @returns The holder of the fatal error.
|
|
28
|
-
*/
|
|
29
|
-
function startLoop(app) {
|
|
30
|
-
const fatal = { error: void 0 };
|
|
31
|
-
if (app.flow.state().running) return fatal;
|
|
32
|
-
app.flow.run().catch((error) => {
|
|
33
|
-
fatal.error = error;
|
|
34
|
-
});
|
|
35
|
-
return fatal;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Waits until the loop rests for the first time, or ends. An empty route walks nowhere: it only
|
|
39
|
-
* waits for the gate the loop opens at the node it enters, so a start that is a chain of transit
|
|
40
|
-
* nodes is played out before anything reads the game.
|
|
41
|
-
*
|
|
42
|
-
* @param app - The started app whose loop is running.
|
|
43
|
-
* @param fatal - Holder of the fatal error of `run()`.
|
|
44
|
-
* @returns Resolves once the loop rests at its first rest node.
|
|
45
|
-
* @throws {Error} When the loop failed fatally instead of reaching a rest node.
|
|
46
|
-
*/
|
|
47
|
-
async function settleAtFirstRest(app, fatal) {
|
|
48
|
-
await app.flow.walk([]).catch((error) => {
|
|
49
|
-
throw asError(fatal.error ?? error);
|
|
50
|
-
});
|
|
51
|
-
if (fatal.error !== void 0) throw asError(fatal.error);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Starts an app headless: sets flow mode `"fast"`, awaits `app.start()`, starts `flow.run()`
|
|
55
|
-
* unless the app's own `onStart` already did, and waits until the loop rests for the first time.
|
|
56
|
-
* `game.state().path` therefore names the first rest node of the graph as soon as the call
|
|
57
|
-
* resolves. A fatal error of the loop rejects this call, and a later one is re-thrown by `walk`
|
|
58
|
-
* and by `stop`, so a headless test never loses it.
|
|
59
|
-
*
|
|
60
|
-
* @param app - An app that is not started yet.
|
|
61
|
-
* @returns The game: walk it, answer it, read it, stop it.
|
|
62
|
-
* @throws {Error} When the loop failed fatally before it reached its first rest node.
|
|
63
|
-
* @example
|
|
64
|
-
* ```ts
|
|
65
|
-
* // A test starts the merge game without a screen. The call resolves at the first rest node.
|
|
66
|
-
* const game = await createHeadless(app);
|
|
67
|
-
*
|
|
68
|
-
* game.state().path; // "home": the transit node "boot" was played out
|
|
69
|
-
* game.state().mode; // "fast"
|
|
70
|
-
* await game.stop();
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
async function createHeadless(app) {
|
|
74
|
-
app.flow.setMode("fast");
|
|
75
|
-
await app.start();
|
|
76
|
-
const fatal = startLoop(app);
|
|
77
|
-
await settleAtFirstRest(app, fatal);
|
|
78
|
-
return {
|
|
79
|
-
walk: async (route) => {
|
|
80
|
-
const state = await app.flow.walk(route).catch((error) => {
|
|
81
|
-
throw asError(fatal.error ?? error);
|
|
82
|
-
});
|
|
83
|
-
if (fatal.error !== void 0) throw asError(fatal.error);
|
|
84
|
-
return state;
|
|
85
|
-
},
|
|
86
|
-
answer: (answer) => app.flow.gate.answer(answer),
|
|
87
|
-
state: () => app.flow.state(),
|
|
88
|
-
history: () => app.flow.history(),
|
|
89
|
-
stop: async () => {
|
|
90
|
-
await app.stop();
|
|
91
|
-
if (fatal.error !== void 0) throw asError(fatal.error);
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Reads the rest node a repro starts at: its checkpoint, or the start of the main flow.
|
|
97
|
-
*
|
|
98
|
-
* @param app - The started app.
|
|
99
|
-
* @param repro - Starting state, optional checkpoint and the route.
|
|
100
|
-
* @returns The path of the node the repro enters.
|
|
101
|
-
* @throws {Error} When the graph has no main flow to start from.
|
|
102
|
-
*/
|
|
103
|
-
function reproPath(app, repro) {
|
|
104
|
-
if (repro.checkpoint !== void 0) return repro.checkpoint;
|
|
105
|
-
const graph = app.flow.describe();
|
|
106
|
-
const start = graph.flows[graph.main]?.start;
|
|
107
|
-
if (start === void 0) throw new Error(`[game] runRepro() found no flow "${graph.main}" to start from.\n Name the checkpoint the repro was taken at.`);
|
|
108
|
-
return start;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Builds the bookmark a repro is entered with: its state, its checkpoint and the hash of the
|
|
112
|
-
* graph that runs now, so a checkpoint is accepted and any other rest node is checked. The route
|
|
113
|
-
* is not part of it: the caller walks it after the restore.
|
|
114
|
-
*
|
|
115
|
-
* @param app - The started app.
|
|
116
|
-
* @param repro - Starting state, optional checkpoint and the route.
|
|
117
|
-
* @returns The bookmark to restore.
|
|
118
|
-
* @throws {Error} When the repro names no checkpoint and the graph has no main flow.
|
|
119
|
-
* @example
|
|
120
|
-
* ```ts
|
|
121
|
-
* // The game.restore command loads a bug report taken at the "home" checkpoint.
|
|
122
|
-
* const bookmark = reproBookmark(app, { player: { coins: 7 }, checkpoint: "home", route: [] });
|
|
123
|
-
* bookmark.path; // "home"
|
|
124
|
-
* bookmark.rng; // { seed: 1, streams: {} }: the repro pinned no rng
|
|
125
|
-
* await app.flow.restore(bookmark);
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
function reproBookmark(app, repro) {
|
|
129
|
-
return {
|
|
130
|
-
path: reproPath(app, repro),
|
|
131
|
-
input: noPayload,
|
|
132
|
-
player: repro.player,
|
|
133
|
-
session: repro.session ?? {},
|
|
134
|
-
rng: repro.rng ?? {
|
|
135
|
-
seed: defaultSeed,
|
|
136
|
-
streams: {}
|
|
137
|
-
},
|
|
138
|
-
graph: graphHash(app.flow.describe())
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
/**
|
|
142
|
-
* Plays a repro: starts the app headless, restores the bookmark built from its state and
|
|
143
|
-
* checkpoint, then walks its route. The app keeps running, so the caller can read more than the
|
|
144
|
-
* result and stops it itself.
|
|
145
|
-
*
|
|
146
|
-
* @param app - An app that is not started yet.
|
|
147
|
-
* @param repro - Starting state, optional checkpoint and the route.
|
|
148
|
-
* @returns The path the run ended at, and the committed state.
|
|
149
|
-
* @example
|
|
150
|
-
* ```ts
|
|
151
|
-
* // A bug report on the dice game: "reset keeps my coins". Replay it from the "home" checkpoint.
|
|
152
|
-
* const reset = { at: "home", intent: "reset" };
|
|
153
|
-
* const result = await runRepro(app, { player: { coins: 7 }, checkpoint: "home", route: [reset] });
|
|
154
|
-
*
|
|
155
|
-
* result.player; // { coins: 0 }; result.path is ["home"]
|
|
156
|
-
* await app.stop(); // the app keeps running after the run
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
async function runRepro(app, repro) {
|
|
160
|
-
const game = await createHeadless(app);
|
|
161
|
-
await app.flow.restore(reproBookmark(app, repro));
|
|
162
|
-
const state = await game.walk(repro.route);
|
|
163
|
-
const ended = app.flow.bookmark();
|
|
164
|
-
return {
|
|
165
|
-
path: state.stack.map((frame) => frame.node),
|
|
166
|
-
player: ended.player,
|
|
167
|
-
session: ended.session
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Steps the frame loop by hand: `count` calls of `app.time.step(deltaMs)`. A headless game has no
|
|
172
|
-
* frame source, so this is the only thing that moves the frame phases.
|
|
173
|
-
*
|
|
174
|
-
* @param app - A started app.
|
|
175
|
-
* @param count - Number of frames.
|
|
176
|
-
* @param deltaMs - Milliseconds per frame.
|
|
177
|
-
* @example
|
|
178
|
-
* ```ts
|
|
179
|
-
* // A headless game has no frame source: the test moves the six frame phases by hand.
|
|
180
|
-
* stepFrames(app, 2, 16);
|
|
181
|
-
* app.time.snapshot(); // frame: 2, delta: 16, elapsed: 32
|
|
182
|
-
* ```
|
|
183
|
-
*/
|
|
184
|
-
function stepFrames(app, count, deltaMs) {
|
|
185
|
-
for (let frame = 0; frame < count; frame += 1) app.time.step(deltaMs);
|
|
186
|
-
}
|
|
187
|
-
//#endregion
|
|
3
|
+
import { n as reproBookmark } from "./headless-BsNdJs9W.mjs";
|
|
4
|
+
import { a as isProjectionTarget, i as recordCheat, o as readTarget, r as isTainted, s as defineCommand, t as inputOrEmpty } from "./input-z_35Jwfn.mjs";
|
|
188
5
|
//#region src/plugins/anim/control.ts
|
|
189
6
|
/**
|
|
190
7
|
* @file anim plugin — the anim command of the `/control` door: switch reduced motion. Dev builds
|
|
@@ -1282,4 +1099,4 @@ async function run(app, command, ...input) {
|
|
|
1282
1099
|
};
|
|
1283
1100
|
}
|
|
1284
1101
|
//#endregion
|
|
1285
|
-
export {
|
|
1102
|
+
export { commands as n, run as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-
|
|
1
|
+
import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-xXfyk56Q.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/plugins/ui/jsx/runtime.d.ts
|
|
4
4
|
/** What the compiler passes as props: the children plus whatever the tag declared. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as scaleByFits, w as Layer } from "./visual-
|
|
1
|
+
import { i as scaleByFits, w as Layer } from "./visual-dMW8iqAE.mjs";
|
|
2
2
|
import { r as notInstalled } from "./dev-wKLgOw7c.mjs";
|
|
3
|
-
import { c as defineSource, n as cheatsOf, o as readTarget, r as isTainted, t as inputOrEmpty } from "./input-
|
|
3
|
+
import { c as defineSource, n as cheatsOf, o as readTarget, r as isTainted, t as inputOrEmpty } from "./input-z_35Jwfn.mjs";
|
|
4
4
|
//#region src/plugins/flow/doors/read.ts
|
|
5
5
|
/**
|
|
6
6
|
* @file flow/doors — `read` and `watch`, the `/inspect` side. Neither changes the game.
|