@moku-labs/game 0.0.1 → 0.0.3
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 +495 -40
- package/dist/assets.d.mts +213 -0
- package/dist/assets.mjs +6125 -0
- package/dist/commands-BOxqlv7w.d.mts +99 -0
- package/dist/component-DGg5DqKK.mjs +44 -0
- package/dist/control.d.mts +73 -0
- package/dist/control.mjs +4 -0
- package/dist/define-BMo0pHe_.d.mts +51 -0
- package/dist/dev-CcwFrwok.mjs +413 -0
- package/dist/index.d.mts +1396 -54
- package/dist/index.mjs +26433 -2013
- package/dist/inspect.d.mts +111 -0
- package/dist/inspect.mjs +3 -0
- package/dist/jsx-dev-runtime.d.mts +2 -0
- package/dist/jsx-dev-runtime.mjs +2 -0
- package/dist/jsx-runtime.d.mts +2 -0
- package/dist/jsx-runtime.mjs +2 -0
- package/dist/memory-CvgdnsQO.mjs +259 -0
- package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-8H4AJuhK.mjs} +1 -0
- package/dist/run-tlcn1w7y.mjs +944 -0
- package/dist/runtime-BstIkrhH.d.mts +77 -0
- package/dist/runtime-DRlwxkIv.mjs +182 -0
- package/dist/session-DmAxY6Ll.mjs +145 -0
- package/dist/sources-BooL2MsX.mjs +545 -0
- package/dist/testing.d.mts +302 -88
- package/dist/testing.mjs +2005 -228
- package/dist/types-BTycq8_Z.d.mts +1196 -0
- package/dist/types-C0SLQsx0.d.mts +2058 -0
- package/dist/types-Cjf3ywai.d.mts +5982 -0
- package/dist/types-D0uYTTC3.d.mts +392 -0
- package/dist/types-DgwbzDLP.d.mts +690 -0
- package/dist/types-KlE_GL1z.d.mts +1444 -0
- package/dist/visual-NPrDkvUk.mjs +740 -0
- package/package.json +42 -5
- package/dist/registry-DWV5C0Mf.mjs +0 -666
- package/dist/types-BfsmUzLC.d.mts +0 -1908
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import { At as Json, Ht as RngState, St as Api, ft as FlowState, ht as RouteStep, pt as JournalEntry, q as Api$1, xt as Answer, zt as Snapshot } from "./types-Cjf3ywai.mjs";
|
|
2
|
+
import { Log } from "@moku-labs/common/browser";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/flow/headless.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* The part of an app the headless helpers use. Structural on purpose: any app created with the
|
|
7
|
+
* default plugins fits.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const app: HeadlessApp = createApp({ pluginConfigs: { flow: { mainFlow } } });
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
type HeadlessApp = {
|
|
15
|
+
start(): Promise<void>;
|
|
16
|
+
stop(): Promise<void>;
|
|
17
|
+
time: Api;
|
|
18
|
+
flow: Api$1;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* A game played without a screen: answers go through the gate, effects resolve instantly.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* // A test of the dice game: two rolls, no screen, no waiting.
|
|
26
|
+
* const game = await createHeadless(app);
|
|
27
|
+
* const state = await game.walk([{ at: "home", intent: "roll" }, { at: "home", intent: "roll" }]);
|
|
28
|
+
*
|
|
29
|
+
* state.path; // "home"
|
|
30
|
+
* await game.stop();
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
type HeadlessGame = {
|
|
34
|
+
/**
|
|
35
|
+
* Walks a route through the running loop.
|
|
36
|
+
*
|
|
37
|
+
* @param route - The player's answers and substituted sub-flow results, in order.
|
|
38
|
+
* @returns The state the walk ended in.
|
|
39
|
+
* @throws {Error} When a step is never reached, or the loop failed fatally.
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // Open the board and tap the generator twice. Every node's logic runs for real.
|
|
43
|
+
* const tap = { at: "board/awaitIntent", intent: "tap", payload: { generatorId: "sawmill" } };
|
|
44
|
+
* const state = await game.walk([{ at: "home", intent: "play" }, tap, tap]);
|
|
45
|
+
*
|
|
46
|
+
* state.path; // "board/awaitIntent"
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
walk(route: readonly RouteStep[]): Promise<FlowState>;
|
|
50
|
+
/**
|
|
51
|
+
* Gives one answer to the gate, the way a tap does.
|
|
52
|
+
*
|
|
53
|
+
* @param answer - Intent and optional payload.
|
|
54
|
+
* @returns Whether the gate took it.
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* // One tap without a route. The test then waits for the loop by itself.
|
|
58
|
+
* game.answer({ intent: "play" }); // true: "home" rests and lists "play"
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
answer(answer: Answer): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Reads where the graph stands.
|
|
64
|
+
*
|
|
65
|
+
* @returns Whether it runs, the path, the stack, what it waits for and the mode.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* // Right after createHeadless: the graph rests at its first rest node.
|
|
69
|
+
* game.state().path; // "home"
|
|
70
|
+
* game.state().pending; // { gate: ["play"] }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
state(): FlowState;
|
|
74
|
+
/**
|
|
75
|
+
* Reads the edges taken since the last checkpoint.
|
|
76
|
+
*
|
|
77
|
+
* @returns A copy of the journal.
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* // After a merge onto an empty cell: the last edge says why the move was refused.
|
|
81
|
+
* const last = game.history().at(-1);
|
|
82
|
+
* // last?.path: "board/merge", last?.outcome: "rejected", last?.payload: { reason: "empty" }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
history(): readonly JournalEntry[];
|
|
86
|
+
/**
|
|
87
|
+
* Stops the app, then re-throws a fatal error of the loop.
|
|
88
|
+
*
|
|
89
|
+
* @throws {Error} When the loop failed fatally.
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* // The last line of a headless test. A fatal error of the loop fails the test here.
|
|
93
|
+
* await game.stop();
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
stop(): Promise<void>;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* A reproducible run: a starting state, an optional checkpoint and the route played from it.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* const repro: Repro = { player: { coins: 0 }, checkpoint: "home", route: [{ at: "home", intent: "play" }] };
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
type Repro = {
|
|
107
|
+
player: Json;
|
|
108
|
+
session?: Json;
|
|
109
|
+
rng?: RngState;
|
|
110
|
+
checkpoint?: string;
|
|
111
|
+
route: RouteStep[];
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* What a repro run ends with: the path where the loop rests and the committed state.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const result: ReproResult = { path: ["home"], player: { coins: 3 }, session: { visits: 1 } };
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
type ReproResult = {
|
|
122
|
+
path: string[];
|
|
123
|
+
player: Json;
|
|
124
|
+
session: Json;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Starts an app headless: sets flow mode `"fast"`, awaits `app.start()`, starts `flow.run()`
|
|
128
|
+
* unless the app's own `onStart` already did, and waits until the loop rests for the first time.
|
|
129
|
+
* `game.state().path` therefore names the first rest node of the graph as soon as the call
|
|
130
|
+
* resolves. A fatal error of the loop rejects this call, and a later one is re-thrown by `walk`
|
|
131
|
+
* and by `stop`, so a headless test never loses it.
|
|
132
|
+
*
|
|
133
|
+
* @param app - An app that is not started yet.
|
|
134
|
+
* @returns The game: walk it, answer it, read it, stop it.
|
|
135
|
+
* @throws {Error} When the loop failed fatally before it reached its first rest node.
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* // A test starts the merge game without a screen. The call resolves at the first rest node.
|
|
139
|
+
* const game = await createHeadless(app);
|
|
140
|
+
*
|
|
141
|
+
* game.state().path; // "home": the transit node "boot" was played out
|
|
142
|
+
* game.state().mode; // "fast"
|
|
143
|
+
* await game.stop();
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
declare function createHeadless(app: HeadlessApp): Promise<HeadlessGame>;
|
|
147
|
+
/**
|
|
148
|
+
* Plays a repro: starts the app headless, restores the bookmark built from its state and
|
|
149
|
+
* checkpoint, then walks its route. The app keeps running, so the caller can read more than the
|
|
150
|
+
* result and stops it itself.
|
|
151
|
+
*
|
|
152
|
+
* @param app - An app that is not started yet.
|
|
153
|
+
* @param repro - Starting state, optional checkpoint and the route.
|
|
154
|
+
* @returns The path the run ended at, and the committed state.
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* // A bug report on the dice game: "reset keeps my coins". Replay it from the "home" checkpoint.
|
|
158
|
+
* const reset = { at: "home", intent: "reset" };
|
|
159
|
+
* const result = await runRepro(app, { player: { coins: 7 }, checkpoint: "home", route: [reset] });
|
|
160
|
+
*
|
|
161
|
+
* result.player; // { coins: 0 }; result.path is ["home"]
|
|
162
|
+
* await app.stop(); // the app keeps running after the run
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function runRepro(app: HeadlessApp, repro: Repro): Promise<ReproResult>;
|
|
166
|
+
/**
|
|
167
|
+
* Steps the frame loop by hand: `count` calls of `app.time.step(deltaMs)`. A headless game has no
|
|
168
|
+
* frame source, so this is the only thing that moves the frame phases.
|
|
169
|
+
*
|
|
170
|
+
* @param app - A started app.
|
|
171
|
+
* @param count - Number of frames.
|
|
172
|
+
* @param deltaMs - Milliseconds per frame.
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* // A headless game has no frame source: the test moves the six frame phases by hand.
|
|
176
|
+
* stepFrames(app, 2, 16);
|
|
177
|
+
* app.time.snapshot(); // frame: 2, delta: 16, elapsed: 32
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
declare function stepFrames(app: HeadlessApp, count: number, deltaMs: number): void;
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/plugins/flow/doors/types.d.ts
|
|
183
|
+
/**
|
|
184
|
+
* The kinds an input field can take. `json` is any JSON value the command checks itself.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* // The schema of game.step: the frame count, and the frame length in milliseconds.
|
|
189
|
+
* const kinds: InputKind[] = ["number", "number"];
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
type InputKind = "string" | "number" | "boolean" | "json";
|
|
193
|
+
/**
|
|
194
|
+
* The input of a source or a command: field name to kind. A kind ending in `?` is optional, so
|
|
195
|
+
* "key or target" is two optional fields and the command checks that one is given.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* // game.restore takes exactly one of the two.
|
|
200
|
+
* const input: InputSchema = { bookmark: "json?", repro: "json?" };
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
type InputSchema = Readonly<Record<string, InputKind | `${InputKind}?`>>;
|
|
204
|
+
/** The TypeScript type of each input kind. */
|
|
205
|
+
type KindTypes = {
|
|
206
|
+
string: string;
|
|
207
|
+
number: number;
|
|
208
|
+
boolean: boolean;
|
|
209
|
+
json: Json;
|
|
210
|
+
};
|
|
211
|
+
/** The value type of one field kind, optional or not. */
|
|
212
|
+
type ValueOf<Kind> = Kind extends `${infer Base extends InputKind}?` ? KindTypes[Base] : Kind extends InputKind ? KindTypes[Kind] : never;
|
|
213
|
+
/** The fields of a schema whose kind has no `?`. */
|
|
214
|
+
type RequiredKeys<S extends InputSchema> = { [K in keyof S]: S[K] extends InputKind ? K : never }[keyof S];
|
|
215
|
+
/** Shows an intersection as one object type. */
|
|
216
|
+
type Flat<T> = { [K in keyof T]: T[K] };
|
|
217
|
+
/**
|
|
218
|
+
* The input value a schema describes. A `?` field may be left out or be `undefined`.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```ts
|
|
222
|
+
* type Step = InputOf<{ frames: "number"; deltaMs: "number?" }>; // { frames: number; deltaMs?: number | undefined }
|
|
223
|
+
* const step: Step = { frames: 3 };
|
|
224
|
+
* ```
|
|
225
|
+
*/
|
|
226
|
+
type InputOf<S extends InputSchema> = Flat<{ -readonly [K in RequiredKeys<S>]: ValueOf<S[K]> } & { -readonly [K in Exclude<keyof S, RequiredKeys<S>>]?: ValueOf<S[K]> | undefined }>;
|
|
227
|
+
/**
|
|
228
|
+
* The input argument of `read` and `run`: optional when every field of the schema is optional,
|
|
229
|
+
* required otherwise.
|
|
230
|
+
*
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* type History = InputArguments<{ last: "number?" }>; // [input?: { last?: number | undefined }]
|
|
234
|
+
* type Rect = InputArguments<{ key: "string" }>; // [input: { key: string }]
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
type InputArguments<S extends InputSchema> = Partial<InputOf<S>> extends InputOf<S> ? [input?: InputOf<S>] : [input: InputOf<S>];
|
|
238
|
+
/**
|
|
239
|
+
* The input argument of `watch`: `undefined` is allowed when every field is optional.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* type Position = WatchInput<{}>; // {} | undefined
|
|
244
|
+
* type Rect = WatchInput<{ key: "string" }>; // { key: string }
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
type WatchInput<S extends InputSchema> = Partial<InputOf<S>> extends InputOf<S> ? InputOf<S> | undefined : InputOf<S>;
|
|
248
|
+
/**
|
|
249
|
+
* When `watch` reads a source again: every frame, when the committed model changed, or when
|
|
250
|
+
* the graph moved (an edge, a gate, a mode).
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* // game.position moves only with the graph; game.render changes every frame.
|
|
255
|
+
* const changes: Changes = "edge";
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
type Changes = "frame" | "commit" | "edge";
|
|
259
|
+
/**
|
|
260
|
+
* What a command does to the game. `route` goes through the graph and keeps the session clean;
|
|
261
|
+
* `cheat` and `raw` taint it and are journaled.
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```ts
|
|
265
|
+
* // game.restore replaces the whole state: the session is not a played one any more.
|
|
266
|
+
* const effect: Effect = "raw";
|
|
267
|
+
* ```
|
|
268
|
+
*/
|
|
269
|
+
type Effect = "read" | "route" | "cosmetic" | "cheat" | "raw";
|
|
270
|
+
/**
|
|
271
|
+
* A read-only view on a running game: an id, a title, the input it takes, when it changes and
|
|
272
|
+
* how to read it. `App` is what the reader needs: the headless app by default, a screen source
|
|
273
|
+
* asks for the screen plugins.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* // A game's .dev module: the coins a panel shows, re-read on every commit.
|
|
278
|
+
* const coins = defineSource({
|
|
279
|
+
* id: "timber.coins",
|
|
280
|
+
* title: "Coins",
|
|
281
|
+
* input: {},
|
|
282
|
+
* changes: "commit",
|
|
283
|
+
* read: (app: HeadlessApp & { model: ModelApi }) => app.model.store.snapshot().player
|
|
284
|
+
* });
|
|
285
|
+
* read(app, coins); // { coins: 120, ... }
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
type Source<S extends InputSchema, O, App = HeadlessApp> = {
|
|
289
|
+
readonly id: string;
|
|
290
|
+
readonly title: string;
|
|
291
|
+
readonly input: S;
|
|
292
|
+
readonly changes: Changes;
|
|
293
|
+
readonly read: (app: App, input: InputOf<S>) => O;
|
|
294
|
+
};
|
|
295
|
+
/**
|
|
296
|
+
* A dev-only action on a running game: an id, a title, the input it takes, its effect and how to
|
|
297
|
+
* run it. The body starts with the inline dev guard, so a production build drops it.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```ts
|
|
301
|
+
* // A game's .dev module: jump to a level through the graph, the session stays clean.
|
|
302
|
+
* const jumpToLevel = defineCommand({
|
|
303
|
+
* id: "timber.jumpToLevel",
|
|
304
|
+
* title: "Go to level",
|
|
305
|
+
* input: { level: "number" },
|
|
306
|
+
* effect: "route",
|
|
307
|
+
* run: (app, { level }) => {
|
|
308
|
+
* if (typeof __MOKU_GAME_DEV__ === "undefined" || !__MOKU_GAME_DEV__) throw controlRefused();
|
|
309
|
+
* return app.flow.walk([{ at: "home", intent: "play", payload: { level } }]);
|
|
310
|
+
* }
|
|
311
|
+
* });
|
|
312
|
+
* (await run(app, jumpToLevel, { level: 3 })).state.path; // "board/awaitIntent"
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
type Command<S extends InputSchema, O, App = HeadlessApp> = {
|
|
316
|
+
readonly id: string;
|
|
317
|
+
readonly title: string;
|
|
318
|
+
readonly input: S;
|
|
319
|
+
readonly effect: Effect;
|
|
320
|
+
readonly run: (app: App, input: InputOf<S>) => O | Promise<O>;
|
|
321
|
+
};
|
|
322
|
+
/**
|
|
323
|
+
* Where the game stands after a command: the graph path, the frame and whether the session
|
|
324
|
+
* was tainted by a cheat or a raw write.
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* const envelope: Envelope = { path: "board/awaitIntent", frame: 1840, tainted: false };
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
331
|
+
type Envelope = {
|
|
332
|
+
readonly path: string;
|
|
333
|
+
readonly frame: number;
|
|
334
|
+
readonly tainted: boolean;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* What `run` resolves with: the command's value and the envelope read after it.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```ts
|
|
341
|
+
* // A tap on Play while the home screen rests.
|
|
342
|
+
* const ran: Ran<boolean> = { value: true, state: { path: "board/awaitIntent", frame: 312, tainted: false } };
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
type Ran<O> = {
|
|
346
|
+
readonly value: O;
|
|
347
|
+
readonly state: Envelope;
|
|
348
|
+
};
|
|
349
|
+
/**
|
|
350
|
+
* One journaled cheat or raw command: its id, the input it got and the frame it ran on.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* ```ts
|
|
354
|
+
* const entry: CheatEntry = { id: "game.restore", input: { bookmark: { path: "home" } }, frame: 96 };
|
|
355
|
+
* ```
|
|
356
|
+
*/
|
|
357
|
+
type CheatEntry = {
|
|
358
|
+
readonly id: string;
|
|
359
|
+
readonly input: Readonly<Record<string, Json | undefined>>;
|
|
360
|
+
readonly frame: number;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* What `watch` needs of an app: the frame loop, the graph and the committed model it compares.
|
|
364
|
+
* Every app with the default plugins fits.
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```ts
|
|
368
|
+
* const app: WatchApp = createApp({ pluginConfigs: { flow: { mainFlow } } });
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
type WatchApp = HeadlessApp & {
|
|
372
|
+
readonly model: {
|
|
373
|
+
readonly store: {
|
|
374
|
+
snapshot(): Snapshot;
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
};
|
|
378
|
+
/**
|
|
379
|
+
* What the base commands need of an app: the headless app plus the log, where every command
|
|
380
|
+
* leaves a `moku:dev` entry. Every app with the default plugins fits.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```ts
|
|
384
|
+
* const app: ControlApp = createApp({ pluginConfigs: { flow: { mainFlow } } });
|
|
385
|
+
* app.log.trace().at(-1)?.event; // "moku:dev" after a command ran
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
type ControlApp = HeadlessApp & {
|
|
389
|
+
readonly log: Log.LogApi;
|
|
390
|
+
};
|
|
391
|
+
//#endregion
|
|
392
|
+
export { stepFrames as _, InputOf as a, Source as c, HeadlessApp as d, HeadlessGame as f, runRepro as g, createHeadless as h, InputArguments as i, WatchApp as l, ReproResult as m, Command as n, InputSchema as o, Repro as p, ControlApp as r, Ran as s, CheatEntry as t, WatchInput as u };
|