@moku-labs/game 0.0.1 → 0.0.2

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.
@@ -0,0 +1,109 @@
1
+ import { K as Snapshot, P as Api, b as JournalEntry, o as Position, v as FlowGraph } from "./types-JNc_UQBo.mjs";
2
+ import { P as EntitySnapshot, o as RenderStats, t as Api$2, u as Api$1 } from "./types-BxkNNYul.mjs";
3
+ import { f as Usage, t as Api$3 } from "./types-DWILGrPn.mjs";
4
+ import { t as HeadlessApp } from "./headless-KaSWcd0s.mjs";
5
+ import { c as InputSchema, d as WatchApp, f as WatchInput, n as defineSource, o as InputArguments, r as CheatEntry, s as InputOf, u as Source } from "./define-sFoO3y6X.mjs";
6
+ import { c as UiNode } from "./types-yg_ywtT-.mjs";
7
+ import { r as UiApi } from "./types-DD-QrG_z.mjs";
8
+ import { a as SoundEntry, t as AudioApi } from "./types-DYLnSgMI.mjs";
9
+
10
+ //#region src/plugins/flow/doors/read.d.ts
11
+ /**
12
+ * Reads a source once.
13
+ *
14
+ * @param app - The app the source reads.
15
+ * @param source - The source.
16
+ * @param input - Its input; left out when every field of its schema is optional.
17
+ * @returns What the source reads.
18
+ * @example
19
+ * ```ts
20
+ * // An e2e script checks where the game rests after a tap on Play.
21
+ * read(app, sources.position); // { path: "board/awaitIntent", flow: "board", node: "awaitIntent", waiting: ["tap", "leave"] }
22
+ * read(app, sources.history, { last: 1 }); // [{ path: "home", outcome: "play", next: "board/awaitIntent", ... }]
23
+ * ```
24
+ */
25
+ declare function read<S extends InputSchema, O, App>(app: App, source: Source<S, O, App>, ...input: InputArguments<S>): O;
26
+ /**
27
+ * Watches a source: reads it on the first frame, then again whenever its change key moved,
28
+ * in the `signals` phase of the frame loop.
29
+ *
30
+ * @param app - The app the source reads.
31
+ * @param source - The source.
32
+ * @param input - Its input, or `undefined` when every field of its schema is optional.
33
+ * @param fn - Called with each value read.
34
+ * @returns Stops watching.
35
+ * @example
36
+ * ```ts
37
+ * // An editor panel follows the position; it redraws only after the graph moved.
38
+ * const stop = watch(app, sources.position, undefined, position => panel.show(position.path));
39
+ * // next frame: panel shows "home"; a tap on Play: panel shows "board/awaitIntent"
40
+ * stop();
41
+ * ```
42
+ */
43
+ declare function watch<S extends InputSchema, O, App extends WatchApp>(app: App, source: Source<S, O, App>, input: WatchInput<S>, fn: (value: O) => void): () => void;
44
+ //#endregion
45
+ //#region src/plugins/flow/doors/sources.d.ts
46
+ /**
47
+ * Every source of the engine, keyed by its short name: what an editor lists as panel data and
48
+ * MCP tools. Frozen.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * // An e2e script checks where the game rests, and the editor registers every source.
53
+ * read(app, sources.position).path; // "board/awaitIntent"
54
+ * for (const source of Object.values(sources)) registry.add(source);
55
+ * ```
56
+ */
57
+ declare const sources: Readonly<{
58
+ graph: Source<{}, FlowGraph, HeadlessApp>;
59
+ position: Source<{}, Position, HeadlessApp>;
60
+ history: Source<{
61
+ last: "number?";
62
+ }, readonly JournalEntry[], HeadlessApp>;
63
+ tainted: Source<{}, boolean, HeadlessApp>;
64
+ cheats: Source<{}, readonly CheatEntry[], HeadlessApp>;
65
+ model: Source<{}, Snapshot, HeadlessApp & {
66
+ readonly model: Api;
67
+ }>;
68
+ entities: Source<{
69
+ owner: "string?";
70
+ component: "string?";
71
+ }, EntitySnapshot[], HeadlessApp & {
72
+ readonly world: Api$1;
73
+ }>;
74
+ projections: Source<{}, Record<string, Record<string, number>>, HeadlessApp & {
75
+ readonly world: Api$1;
76
+ }>;
77
+ ui: Source<{}, UiNode, HeadlessApp & {
78
+ readonly ui: UiApi;
79
+ }>;
80
+ rect: Source<{
81
+ key: "string";
82
+ }, {
83
+ x: number;
84
+ y: number;
85
+ w: number;
86
+ h: number;
87
+ } | undefined, HeadlessApp & {
88
+ readonly ui: UiApi;
89
+ readonly renderer: Api$2;
90
+ }>;
91
+ render: Source<{}, RenderStats, HeadlessApp & {
92
+ readonly renderer: Api$2;
93
+ }>;
94
+ sounds: Source<{
95
+ last: "number?";
96
+ }, readonly SoundEntry[], HeadlessApp & {
97
+ readonly audio: AudioApi;
98
+ }>;
99
+ assets: Source<{}, Usage, HeadlessApp & {
100
+ readonly assets: Api$3;
101
+ }>;
102
+ log: Source<{
103
+ level: "string?";
104
+ }, readonly import("@moku-labs/common/browser").LogEntry[], HeadlessApp & {
105
+ readonly log: import("@moku-labs/common/browser").LogApi;
106
+ }>;
107
+ }>;
108
+ //#endregion
109
+ export { type InputOf, type InputSchema, type Source, defineSource, read, sources, watch };
@@ -0,0 +1,519 @@
1
+ import { i as scaleByFits } from "./visual-BDUHSRvf.mjs";
2
+ import { i as inputOrEmpty, n as isTainted, o as defineSource, t as cheatsOf } from "./session-DmAxY6Ll.mjs";
3
+ //#region src/plugins/flow/doors/read.ts
4
+ /**
5
+ * @file flow/doors — `read` and `watch`, the `/inspect` side. Neither changes the game.
6
+ */
7
+ /**
8
+ * Reads a source once.
9
+ *
10
+ * @param app - The app the source reads.
11
+ * @param source - The source.
12
+ * @param input - Its input; left out when every field of its schema is optional.
13
+ * @returns What the source reads.
14
+ * @example
15
+ * ```ts
16
+ * // An e2e script checks where the game rests after a tap on Play.
17
+ * read(app, sources.position); // { path: "board/awaitIntent", flow: "board", node: "awaitIntent", waiting: ["tap", "leave"] }
18
+ * read(app, sources.history, { last: 1 }); // [{ path: "home", outcome: "play", next: "board/awaitIntent", ... }]
19
+ * ```
20
+ */
21
+ function read(app, source, ...input) {
22
+ const [given] = input;
23
+ return source.read(app, inputOrEmpty(given));
24
+ }
25
+ /**
26
+ * The change test of a `frame` source: it is read on every frame.
27
+ *
28
+ * @returns Always true.
29
+ */
30
+ const everyFrame = () => true;
31
+ /**
32
+ * Builds the per-frame test of a change key. `commit` and `edge` compare the identity of the
33
+ * memoised `store.snapshot()` and `flow.state()`, so an unchanged frame allocates nothing.
34
+ *
35
+ * @param app - The watched app.
36
+ * @param changes - The source's change key.
37
+ * @returns A test that is true on the first frame and whenever the key moved.
38
+ */
39
+ function changeTest(app, changes) {
40
+ if (changes === "frame") return everyFrame;
41
+ const identity = changes === "commit" ? () => app.model.store.snapshot() : () => app.flow.state();
42
+ let last;
43
+ return () => {
44
+ const current = identity();
45
+ if (current === last) return false;
46
+ last = current;
47
+ return true;
48
+ };
49
+ }
50
+ /**
51
+ * Watches a source: reads it on the first frame, then again whenever its change key moved,
52
+ * in the `signals` phase of the frame loop.
53
+ *
54
+ * @param app - The app the source reads.
55
+ * @param source - The source.
56
+ * @param input - Its input, or `undefined` when every field of its schema is optional.
57
+ * @param fn - Called with each value read.
58
+ * @returns Stops watching.
59
+ * @example
60
+ * ```ts
61
+ * // An editor panel follows the position; it redraws only after the graph moved.
62
+ * const stop = watch(app, sources.position, undefined, position => panel.show(position.path));
63
+ * // next frame: panel shows "home"; a tap on Play: panel shows "board/awaitIntent"
64
+ * stop();
65
+ * ```
66
+ */
67
+ function watch(app, source, input, fn) {
68
+ const given = inputOrEmpty(input);
69
+ const changed = changeTest(app, source.changes);
70
+ return app.time.onFrame("signals", () => {
71
+ if (changed()) fn(source.read(app, given));
72
+ });
73
+ }
74
+ //#endregion
75
+ //#region src/plugins/assets/inspect.ts
76
+ /**
77
+ * @file assets plugin — the assets source of the `/inspect` door: what the loaded bundles cost.
78
+ * Production-safe: it only reads.
79
+ */
80
+ /**
81
+ * The texture memory in use, the budget and one entry per loaded bundle, sorted by name. Read
82
+ * every frame: a bundle loads asynchronously, without a model commit.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * // The editor's memory bar after the board scene loaded.
87
+ * read(app, sources.assets);
88
+ * // { textureMb: 3.5, budgetMb: 192, bundles: [{ name: "board", tier: "scene", mb: 3.5, lastUsed: 12 }] }
89
+ * ```
90
+ */
91
+ const assetsSource = defineSource({
92
+ id: "game.assets",
93
+ title: "Assets",
94
+ input: {},
95
+ changes: "frame",
96
+ read: (app) => app.assets.usage()
97
+ });
98
+ //#endregion
99
+ //#region src/plugins/audio/inspect.ts
100
+ /**
101
+ * @file audio plugin — the audio source of the `/inspect` door: the journal of started sounds.
102
+ * Production-safe: it only reads.
103
+ */
104
+ /**
105
+ * The sounds that started, oldest first: every sfx played and every music track begun, all of
106
+ * them or the last few. Empty unless `pluginConfigs.audio.journal` is above 0.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * // After a tap on Deliver, on the dev page that sets `journal: 200`.
111
+ * read(app, sources.sounds, { last: 2 });
112
+ * // [{ key: "ui.click", bus: "sfx", kind: "sfx", at: 1584 }, { key: "orders.complete", bus: "sfx", kind: "sfx", at: 1600 }]
113
+ * ```
114
+ */
115
+ const soundsSource = defineSource({
116
+ id: "game.sounds",
117
+ title: "Sounds",
118
+ input: { last: "number?" },
119
+ changes: "frame",
120
+ read: (app, { last }) => {
121
+ const entries = app.audio.journal();
122
+ return last === void 0 ? entries : entries.slice(Math.max(0, entries.length - last));
123
+ }
124
+ });
125
+ //#endregion
126
+ //#region src/plugins/model/inspect.ts
127
+ /**
128
+ * @file model plugin — the model source of the `/inspect` door: the committed player and
129
+ * session. Production-safe: it only reads.
130
+ */
131
+ /**
132
+ * The committed player, session and rng trees, the same frozen object until the next commit.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * // An editor panel shows the coins after a delivery.
137
+ * read(app, sources.model).player; // { coins: 140, orders: [] }
138
+ * ```
139
+ */
140
+ const modelSource = defineSource({
141
+ id: "game.model",
142
+ title: "Model",
143
+ input: {},
144
+ changes: "commit",
145
+ read: (app) => app.model.store.snapshot()
146
+ });
147
+ //#endregion
148
+ //#region src/plugins/renderer/inspect.ts
149
+ /**
150
+ * @file renderer plugin — the renderer source of the `/inspect` door: the frame counters.
151
+ * Production-safe: it only reads.
152
+ */
153
+ /**
154
+ * The counters of the renderer: frames per second, frame work, GPU textures and their memory,
155
+ * views and pooled display objects.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * // The editor's stats panel on the board of Timber Town.
160
+ * read(app, sources.render); // { fps: 60, frameMs: 3.4, textures: 12, textureMb: 41.25, views: 180, pooled: 24 }
161
+ * ```
162
+ */
163
+ const renderSource = defineSource({
164
+ id: "game.render",
165
+ title: "Render stats",
166
+ input: {},
167
+ changes: "frame",
168
+ read: (app) => app.renderer.stats()
169
+ });
170
+ //#endregion
171
+ //#region src/plugins/ui/inspect.ts
172
+ /**
173
+ * @file ui plugin — the ui sources of the `/inspect` door: the live screen as data and the rect
174
+ * of one element on the page. Production-safe: both only read.
175
+ */
176
+ /**
177
+ * Finds a keyed node and the nodes above it.
178
+ *
179
+ * @param node - Where the search starts.
180
+ * @param key - The key of the element.
181
+ * @returns The node and its ancestors, nearest first, or `undefined` when no node has the key.
182
+ */
183
+ function chainTo(node, key) {
184
+ if (node.key === key) return [node];
185
+ for (const child of node.children) {
186
+ const chain = chainTo(child, key);
187
+ if (chain !== void 0) {
188
+ chain.push(node);
189
+ return chain;
190
+ }
191
+ }
192
+ }
193
+ /**
194
+ * Where an element is drawn at rest, in root coordinates. Its rect in the snapshot is natural,
195
+ * so every fitted node on the way up, the element itself included, scales it about its centre,
196
+ * the way ui draws a `fit: "contain"`.
197
+ *
198
+ * @param chain - The element and its ancestors, nearest first.
199
+ * @returns The drawn rect.
200
+ */
201
+ function drawnRect(chain) {
202
+ return scaleByFits(chain[0].rect, chain.map((node) => ({
203
+ rect: node.rect,
204
+ fit: node.fitScale ?? 1
205
+ })));
206
+ }
207
+ /**
208
+ * Maps a rect in reference units to CSS px of the page through both of its corners.
209
+ *
210
+ * @param renderer - The renderer, for its viewport.
211
+ * @param rect - The rect in reference units.
212
+ * @returns The rect in CSS px; the same numbers while the renderer is inert.
213
+ */
214
+ function onPage(renderer, rect) {
215
+ const topLeft = renderer.viewport.toScreen({
216
+ x: rect.x,
217
+ y: rect.y
218
+ });
219
+ const bottomRight = renderer.viewport.toScreen({
220
+ x: rect.x + rect.w,
221
+ y: rect.y + rect.h
222
+ });
223
+ return {
224
+ x: topLeft.x,
225
+ y: topLeft.y,
226
+ w: bottomRight.x - topLeft.x,
227
+ h: bottomRight.y - topLeft.y
228
+ };
229
+ }
230
+ /**
231
+ * The live screen as plain data: every root, every element with its natural rect, its style,
232
+ * its state flags and the fit scale of a fitted one.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * // The editor's tree panel lists the HUD.
237
+ * read(app, sources.ui).children.map(child => child.key); // ["coins", "settings", "order"]
238
+ * ```
239
+ */
240
+ const uiSource = defineSource({
241
+ id: "game.ui",
242
+ title: "UI tree",
243
+ input: {},
244
+ changes: "frame",
245
+ read: (app) => app.ui.tree()
246
+ });
247
+ /**
248
+ * Where an element is on the page, in CSS px: its layout box, so a bare button with no fill has
249
+ * one, scaled by every fitted element above it, through the viewport. In reference units while
250
+ * the renderer is inert.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * // An e2e script clicks Home's Play plank on a 390 px wide phone.
255
+ * read(app, sources.rect, { key: "play" }); // { x: 101.9, y: 469.2, w: 189.2, h: 54.6 }
256
+ * read(app, sources.rect, { key: "nothing" }); // undefined: not on screen
257
+ * ```
258
+ */
259
+ const rectSource = defineSource({
260
+ id: "game.rect",
261
+ title: "Element rect",
262
+ input: { key: "string" },
263
+ changes: "frame",
264
+ read: (app, { key }) => {
265
+ const chain = app.ui.find(key) !== void 0 ? chainTo(app.ui.tree(), key) : void 0;
266
+ return chain === void 0 ? void 0 : onPage(app.renderer, drawnRect(chain));
267
+ }
268
+ });
269
+ //#endregion
270
+ //#region src/plugins/world/inspect.ts
271
+ /**
272
+ * @file world plugin — the world sources of the `/inspect` door: the entities, filtered by owner
273
+ * and component, and the projection keys. Production-safe: every source only reads.
274
+ */
275
+ /**
276
+ * Tells whether an entity carries a component, as a JSON value or as a skipped one.
277
+ *
278
+ * @param entity - The entity snapshot.
279
+ * @param component - The component name.
280
+ * @returns True when the entity carries it.
281
+ */
282
+ function carries(entity, component) {
283
+ return Object.hasOwn(entity.components, component) || entity.skipped.includes(component);
284
+ }
285
+ /**
286
+ * Every entity of the world as plain JSON, sorted by index: all of them, the ones of one owner,
287
+ * the ones that carry a component, or both.
288
+ *
289
+ * @example
290
+ * ```ts
291
+ * // Which board items are on screen right now?
292
+ * read(app, sources.entities, { owner: "board.items" });
293
+ * // [{ id: 1048576, index: 0, generation: 1, owner: { kind: "projection", name: "board.items" },
294
+ * // components: { Layer: { name: "items" } }, skipped: ["Display"] }]
295
+ * ```
296
+ */
297
+ const entitiesSource = defineSource({
298
+ id: "game.entities",
299
+ title: "Entities",
300
+ input: {
301
+ owner: "string?",
302
+ component: "string?"
303
+ },
304
+ changes: "frame",
305
+ read: (app, { owner, component }) => app.world.ecs.snapshot().entities.filter((entity) => (owner === void 0 || entity.owner.name === owner) && (component === void 0 || carries(entity, component)))
306
+ });
307
+ /**
308
+ * The projection keys of the world: projection name to key to the entity of its live view.
309
+ *
310
+ * @example
311
+ * ```ts
312
+ * // An agent finds the item it is about to drag.
313
+ * read(app, sources.projections)["board.items"]; // { i5: 1048580, i7: 1048581 }
314
+ * ```
315
+ */
316
+ const projectionsSource = defineSource({
317
+ id: "game.projections",
318
+ title: "Projections",
319
+ input: {},
320
+ changes: "commit",
321
+ read: (app) => {
322
+ const keys = {};
323
+ for (const { id } of app.world.ecs.snapshot().entities) {
324
+ const address = app.world.projection.keyOf(id);
325
+ if (address === void 0) continue;
326
+ keys[address.projection] = {
327
+ ...keys[address.projection],
328
+ [address.key]: id
329
+ };
330
+ }
331
+ return keys;
332
+ }
333
+ });
334
+ //#endregion
335
+ //#region src/plugins/flow/inspect.ts
336
+ /** The levels of `Log.LogLevel`, least severe first, as the names a caller passes. */
337
+ const LOG_LEVELS = [
338
+ "debug",
339
+ "info",
340
+ "warn",
341
+ "error"
342
+ ];
343
+ /**
344
+ * The rank of a log level: 0 for `debug` up to 3 for `error`.
345
+ *
346
+ * @param level - The level the caller named.
347
+ * @returns Its rank.
348
+ * @throws {Error} When the log has no such level.
349
+ * @example
350
+ * ```ts
351
+ * rankOf("warn"); // 2
352
+ * ```
353
+ */
354
+ function rankOf(level) {
355
+ const rank = LOG_LEVELS.indexOf(level);
356
+ if (rank !== -1) return rank;
357
+ throw new Error("[game] game.log takes the level \"debug\", \"info\", \"warn\" or \"error\".\n Leave the level out to read every entry.");
358
+ }
359
+ /**
360
+ * Turns the flow state into the position a panel shows.
361
+ *
362
+ * @param state - The flow state.
363
+ * @returns The path, the top flow and node, and what the gate waits for.
364
+ * @example
365
+ * ```ts
366
+ * positionOf(app.flow.state()).node; // "awaitIntent"
367
+ * ```
368
+ */
369
+ function positionOf(state) {
370
+ const top = state.stack.at(-1);
371
+ return {
372
+ path: state.path,
373
+ flow: top?.flow,
374
+ node: top?.node,
375
+ waiting: state.pending.gate ?? []
376
+ };
377
+ }
378
+ /**
379
+ * The flow graph: flows, nodes with their flags and outcomes, edges and slots.
380
+ *
381
+ * @example
382
+ * ```ts
383
+ * // The editor draws the graph of Timber Town.
384
+ * read(app, sources.graph).flows.main?.edges.home; // { play: "board" }
385
+ * ```
386
+ */
387
+ const graphSource = defineSource({
388
+ id: "game.graph",
389
+ title: "Graph",
390
+ input: {},
391
+ changes: "edge",
392
+ read: (app) => app.flow.describe()
393
+ });
394
+ /**
395
+ * Where the graph rests: the path, the node and the intents the gate waits for.
396
+ *
397
+ * @example
398
+ * ```ts
399
+ * // After a tap on Play on the home screen.
400
+ * read(app, sources.position); // { path: "board/awaitIntent", flow: "board", node: "awaitIntent", waiting: ["tap", "leave"] }
401
+ * ```
402
+ */
403
+ const positionSource = defineSource({
404
+ id: "game.position",
405
+ title: "Position",
406
+ input: {},
407
+ changes: "edge",
408
+ read: (app) => positionOf(app.flow.state())
409
+ });
410
+ /**
411
+ * The edges taken since the last checkpoint, all of them or the last few.
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * // Why was the last move refused?
416
+ * read(app, sources.history, { last: 1 }); // [{ path: "board/merge", outcome: "rejected", payload: { reason: "empty" }, ... }]
417
+ * ```
418
+ */
419
+ const historySource = defineSource({
420
+ id: "game.history",
421
+ title: "History",
422
+ input: { last: "number?" },
423
+ changes: "edge",
424
+ read: (app, { last }) => {
425
+ const entries = app.flow.history();
426
+ return last === void 0 ? entries : entries.slice(Math.max(0, entries.length - last));
427
+ }
428
+ });
429
+ /**
430
+ * Whether a cheat or a raw command changed this session: a tainted session is no played one.
431
+ *
432
+ * @example
433
+ * ```ts
434
+ * // After run(app, commands.restore, { repro }).
435
+ * read(app, sources.tainted); // true
436
+ * ```
437
+ */
438
+ const taintedSource = defineSource({
439
+ id: "game.tainted",
440
+ title: "Tainted",
441
+ input: {},
442
+ changes: "frame",
443
+ read: (app) => isTainted(app)
444
+ });
445
+ /**
446
+ * The cheat and raw commands run on this app, oldest first, the last 500.
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * // After run(app, commands.restore, { bookmark }) on frame 96.
451
+ * read(app, sources.cheats); // [{ id: "game.restore", input: { bookmark: { path: "home", ... } }, frame: 96 }]
452
+ * ```
453
+ */
454
+ const cheatsSource = defineSource({
455
+ id: "game.cheats",
456
+ title: "Cheats",
457
+ input: {},
458
+ changes: "frame",
459
+ read: (app) => cheatsOf(app)
460
+ });
461
+ /**
462
+ * The log trace, oldest first: every entry, or the entries at a level and above.
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * // What went wrong on the last tap?
467
+ * read(app, sources.log, { level: "warn" });
468
+ * // [{ level: "warn", event: "assets: missing texture", data: { key: "ui.gear" }, ts: 1758620000000 }]
469
+ * ```
470
+ */
471
+ const logSource = defineSource({
472
+ id: "game.log",
473
+ title: "Log",
474
+ input: { level: "string?" },
475
+ changes: "frame",
476
+ read: (app, { level }) => {
477
+ const entries = app.log.trace();
478
+ if (level === void 0) return entries;
479
+ const lowest = rankOf(level);
480
+ return entries.filter((entry) => LOG_LEVELS.indexOf(entry.level) >= lowest);
481
+ }
482
+ });
483
+ //#endregion
484
+ //#region src/plugins/flow/doors/sources.ts
485
+ /**
486
+ * @file flow/doors — the base sources: every source of the engine, keyed by a short name. Each
487
+ * descriptor lives with the plugin that owns its data; this module only gathers them. Imported
488
+ * by the door `src/inspect.ts` only, so no plugin reaches it, no import cycle forms and an
489
+ * `/inspect` bundle carries no command.
490
+ */
491
+ /**
492
+ * Every source of the engine, keyed by its short name: what an editor lists as panel data and
493
+ * MCP tools. Frozen.
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * // An e2e script checks where the game rests, and the editor registers every source.
498
+ * read(app, sources.position).path; // "board/awaitIntent"
499
+ * for (const source of Object.values(sources)) registry.add(source);
500
+ * ```
501
+ */
502
+ const sources = Object.freeze({
503
+ graph: graphSource,
504
+ position: positionSource,
505
+ history: historySource,
506
+ tainted: taintedSource,
507
+ cheats: cheatsSource,
508
+ model: modelSource,
509
+ entities: entitiesSource,
510
+ projections: projectionsSource,
511
+ ui: uiSource,
512
+ rect: rectSource,
513
+ render: renderSource,
514
+ sounds: soundsSource,
515
+ assets: assetsSource,
516
+ log: logSource
517
+ });
518
+ //#endregion
519
+ export { defineSource, read, sources, watch };
@@ -0,0 +1,2 @@
1
+ import { a as jsxs, i as jsxDEV, n as JSX, r as jsx, t as Fragment } from "./runtime-DiOTkDZz.mjs";
2
+ export { Fragment, type JSX, jsx, jsxDEV, jsxs };
@@ -0,0 +1,2 @@
1
+ import { i as jsxs, n as jsx, r as jsxDEV, t as Fragment } from "./runtime-DRlwxkIv.mjs";
2
+ export { Fragment, jsx, jsxDEV, jsxs };
@@ -0,0 +1,2 @@
1
+ import { a as jsxs, n as JSX, r as jsx, t as Fragment } from "./runtime-DiOTkDZz.mjs";
2
+ export { Fragment, type JSX, jsx, jsxs };
@@ -0,0 +1,2 @@
1
+ import { i as jsxs, n as jsx, t as Fragment } from "./runtime-DRlwxkIv.mjs";
2
+ export { Fragment, jsx, jsxs };