@moku-labs/game 0.14.11 → 0.14.13

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.
Files changed (56) hide show
  1. package/README.md +13 -12
  2. package/dist/app/page.d.mts +8 -3
  3. package/dist/app/page.mjs +57 -12
  4. package/dist/app/system.d.mts +5 -3
  5. package/dist/app/system.mjs +2 -0
  6. package/dist/app.d.mts +15 -7
  7. package/dist/app.mjs +131 -27
  8. package/dist/{assets-BrKSg1O6.mjs → assets-CEMfBogh.mjs} +471 -120
  9. package/dist/assets.d.mts +30 -13
  10. package/dist/assets.mjs +1 -1
  11. package/dist/cli.d.mts +105 -8
  12. package/dist/cli.mjs +1036 -128
  13. package/dist/{commands-d5Zlh9hf.d.mts → commands-DAETmK86.d.mts} +43 -7
  14. package/dist/{config-CdARrrhU.mjs → config-B87aFTW-.mjs} +25 -4
  15. package/dist/control.d.mts +130 -17
  16. package/dist/control.mjs +4 -4
  17. package/dist/{define-BN15lAWb.d.mts → define-BASd9ZUh.d.mts} +32 -4
  18. package/dist/dev-DbHgx-R-.mjs +89 -0
  19. package/dist/{fake-MpzwZhIZ.mjs → fake-6mazmHK3.mjs} +20 -2
  20. package/dist/features-DowEilZm.mjs +266 -0
  21. package/dist/{headless-CrhefcLu.d.mts → headless-cFveciuY.d.mts} +1 -1
  22. package/dist/hot.d.mts +4 -2
  23. package/dist/hot.mjs +7 -4
  24. package/dist/{index-0GIP3tgm.d.mts → index-DedbED3T.d.mts} +317 -69
  25. package/dist/index.d.mts +5 -6
  26. package/dist/index.mjs +3 -3
  27. package/dist/inspect.d.mts +12 -7
  28. package/dist/inspect.mjs +2 -2
  29. package/dist/jsx-dev-runtime.d.mts +1 -1
  30. package/dist/jsx-runtime.d.mts +1 -1
  31. package/dist/lint.d.mts +8 -3
  32. package/dist/lint.mjs +432 -20
  33. package/dist/project.d.mts +15 -1
  34. package/dist/project.mjs +1 -1
  35. package/dist/{run-Bf2hKyjZ.mjs → run-BbipFRc6.mjs} +250 -51
  36. package/dist/{runtime-aLkoeoGj.d.mts → runtime-DoSlRx8X.d.mts} +1 -1
  37. package/dist/{session-DgukhR_7.mjs → session-CeKgO2hy.mjs} +30 -2
  38. package/dist/{sources-Duzj6lhV.mjs → sources-Cdvb0rOS.mjs} +33 -6
  39. package/dist/{src-Do697seR.mjs → src-BEbJ_sSx.mjs} +29154 -11121
  40. package/dist/testing.d.mts +119 -14
  41. package/dist/testing.mjs +97 -7
  42. package/dist/{types-BZa187kz.d.mts → types-7GXcXxxv.d.mts} +196 -242
  43. package/dist/{types-BT9S_Lo2.d.mts → types-BGcda5Le.d.mts} +4464 -735
  44. package/dist/{types-yiHtzg1Y.d.mts → types-C73hbmxi.d.mts} +129 -17
  45. package/dist/{types-Bc9AvZVO.d.mts → types-DngFQJro.d.mts} +43 -5
  46. package/dist/{types-kNiV7iFj.d.mts → types-GHE4J5Uf.d.mts} +1027 -192
  47. package/dist/{visual-dMW8iqAE.mjs → visual-2m4wJL_z.mjs} +111 -7
  48. package/dist/visual-_XhoGeIC.d.mts +345 -0
  49. package/dist/visual.d.mts +1 -332
  50. package/dist/visual.mjs +130 -20
  51. package/dist/{watch-CWI_on4T.mjs → watch-BEdrp80i.mjs} +149 -30
  52. package/llms.txt +103 -51
  53. package/package.json +8 -6
  54. package/dist/dev-Cc50Dk7H.mjs +0 -47
  55. package/dist/memory-DR1ibF_s.d.mts +0 -50
  56. package/dist/types-DMoi447V.d.mts +0 -656
package/dist/assets.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { u as Manifest } from "./types-BT9S_Lo2.mjs";
1
+ import { d as Manifest } from "./types-BGcda5Le.mjs";
2
2
 
3
3
  //#region src/plugins/assets/scan/cli.d.ts
4
4
  /**
@@ -122,7 +122,11 @@ declare function runCli(argv: string[], strings: StringsTools, ui?: ScanUi): Pro
122
122
  * What one compile produced.
123
123
  */
124
124
  type CompileReport = {
125
- /** True when an output on disk differs from what the compile produced. */changed: boolean; /** Every locale a feature brought a file for, and `en-XA` with `pseudo`, sorted. */
125
+ /** True when an output on disk differs from what the compile produced. */changed: boolean;
126
+ /**
127
+ * Every locale a feature, a sub-feature or a layer brought a file for, and `en-XA` with `pseudo`,
128
+ * sorted.
129
+ */
126
130
  locales: readonly string[]; /** Every message key, sorted. */
127
131
  keys: readonly string[]; /** One line per key a locale lacks. Nothing here stops a build. */
128
132
  notes: readonly string[];
@@ -149,11 +153,14 @@ type CompileOptions = {
149
153
  layers?: Readonly<Record<string, string>>;
150
154
  };
151
155
  /**
152
- * Walks `features/*​/strings/<locale>.json` of a game and the `strings/` of every layer, compiles
153
- * every message and writes the generated modules: the types module `strings.ts` and one
154
- * `strings.<locale>.ts` per locale, plus `strings.en-XA.ts` with `pseudo`. A check run writes
155
- * nothing and only reports whether something would change; it checks `strings.en-XA.ts` only with
156
- * `pseudo`.
156
+ * Walks the `strings/<locale>.json` of every feature of a game, sub-features at any depth
157
+ * included, and the `strings/` of every layer, compiles every message and writes the generated
158
+ * modules: the types module `strings.ts` with every key; one `strings.<locale>.ts` per locale with
159
+ * the keys of the top-level features and the layers; and for every sub-feature that brings string
160
+ * files one `strings.<feature>.<locale>.ts` per locale with its own keys (an empty table where it
161
+ * lacks the locale). `strings.en-XA.ts` and its per-feature twins come with `pseudo`. A game
162
+ * without sub-features writes exactly the modules 0.14.12 wrote. A check run writes nothing and
163
+ * only reports whether something would change; it checks the `en-XA` modules only with `pseudo`.
157
164
  *
158
165
  * @param root - Game source root, the folder that holds the features.
159
166
  * @param out - The generated directory the modules are written into.
@@ -172,6 +179,7 @@ type CompileOptions = {
172
179
  * // shared/strings/en.json { "ui.ok": "OK" } compile together.
173
180
  * const layered = await compileStrings("src", "src/generated", { layers: { shared: "ui" } });
174
181
  * // layered.keys: ["board.full", "ui.ok"]
182
+ * // features/episodes/ep01/strings/en.json { "ep01.title": "Ward" } also writes generated/strings.ep01.en.ts
175
183
  * ```
176
184
  */
177
185
  declare function compileStrings(root: string, out: string, options?: CompileOptions): Promise<CompileReport>;
@@ -249,7 +257,7 @@ type ExportReport = {
249
257
  */
250
258
  type ImportReport = {
251
259
  /** Every locale an exchange file was read for, sorted. */locales: readonly string[]; /** How many keys were written. */
252
- keys: number; /** The string files of the features and layers rewritten, relative to the root, sorted. */
260
+ keys: number; /** The string files of the features, sub-features and layers rewritten, relative to the root, sorted. */
253
261
  files: readonly string[];
254
262
  };
255
263
  /**
@@ -390,7 +398,11 @@ declare function packAssets(options: PackOptions): Promise<PackResult>;
390
398
  * ```
391
399
  */
392
400
  type ScanOptions = {
393
- /** Path of the game source root, the folder that holds the features. */root: string; /** Name of the folder that holds the features. Default `"features"`. */
401
+ /** Path of the game source root, the folder that holds the features. */root: string;
402
+ /**
403
+ * Name of the folder that holds the features: the folder whose owners `findFeatureOwners`
404
+ * finds, at any depth. Default `"features"`.
405
+ */
394
406
  features?: string; /** Path of the `manifest.json` to write. */
395
407
  manifest: string; /** Path of the generated key module to write. */
396
408
  keys: string; /** False for a check run: nothing is written. Default true. */
@@ -413,13 +425,17 @@ type ScanResult = {
413
425
  };
414
426
  /**
415
427
  * Walks the features and the layers of a game and writes its two generated files: one bundle per
416
- * feature or layer, split by its optional `assets.ts`, every file keyed by the key rule. A layer is
417
- * read like one more feature under its mapped name, after the features. A check run
418
- * (`write: false`) writes nothing and only reports whether something would change.
428
+ * feature owner or layer, split by its optional `assets.ts`, every file keyed by the key rule. A
429
+ * sub-feature (a folder with a door and resources inside a feature, at any depth) is an owner of
430
+ * its own: its keys and its bundle take the inner folder name, and only its bundle carries
431
+ * `parent` and `dir`. A layer is read like one more feature under its mapped name, after the
432
+ * features. A check run (`write: false`) writes nothing and only reports whether something would
433
+ * change.
419
434
  *
420
435
  * @param options - The game source root, the layers, the two output paths and whether to write.
421
436
  * @returns The manifest, the text of the key module and whether an output differed.
422
- * @throws {Error} One error that lists every problem the scan found, the layer problems first.
437
+ * @throws {Error} One error that lists every problem the scan found, the owner and layer problems
438
+ * first.
423
439
  * @example
424
440
  * ```ts
425
441
  * const { manifest, changed } = await scanAssets({
@@ -429,6 +445,7 @@ type ScanResult = {
429
445
  * layers: { shared: "ui" } // shared/assets/button/primary.png keeps the key "ui.button.primary"
430
446
  * });
431
447
  * // manifest.bundles.ui.files[0].key: "ui.button.primary", changed: true on the first run
448
+ * // features/episodes/ep01/assets/ward/room.png keeps the key "ep01.ward.room" in the bundle "ep01"
432
449
  * ```
433
450
  */
434
451
  declare function scanAssets(options: ScanOptions): Promise<ScanResult>;
package/dist/assets.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as runCli, c as emitKeys, i as compileStrings, l as emitManifest, n as importStrings, o as scanAssets, r as checkStrings, s as packAssets, t as exportStrings } from "./assets-BrKSg1O6.mjs";
1
+ import { a as runCli, c as emitKeys, i as compileStrings, l as emitManifest, n as importStrings, o as scanAssets, r as checkStrings, s as packAssets, t as exportStrings } from "./assets-CEMfBogh.mjs";
2
2
  export { checkStrings, compileStrings, emitKeys, emitManifest, exportStrings, importStrings, packAssets, runCli, scanAssets };
package/dist/cli.d.mts CHANGED
@@ -1,3 +1,23 @@
1
+ //#region src/app/engine.d.ts
2
+ /**
3
+ * The engine a page is served or built with.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * const engine: EngineFingerprint = { id: "0.14.12+9c41e0", version: "0.14.12", dist: "9c41e0…", folder: "/game/node_modules/@moku-labs/game" };
8
+ * ```
9
+ */
10
+ type EngineFingerprint = {
11
+ /** What the page and the log show: "0.14.12+9c41e0", "0.14.12+9c41e0.patch.3f2a7b", "0.0.0+src", "unknown". */id: string; /** `version` of the engine's package.json, "unknown" when no package.json of `@moku-labs/game` was found. */
12
+ version: string; /** SHA-256 (hex) of every file under the engine's `dist/`; "src" when the engine runs from its sources; "unknown". */
13
+ dist: string; /** The patch the install root declares for this version: its file (absolute) and SHA-256 (hex). */
14
+ patch?: {
15
+ file: string;
16
+ hash: string;
17
+ }; /** The engine's package folder, absolute; the folder of the resolved hot plugin when "unknown". */
18
+ folder: string;
19
+ };
20
+ //#endregion
1
21
  //#region src/app/keys.d.ts
2
22
  /** The running keys watch of one game folder, as `watchKeys` answers it. */
3
23
  type KeysWatcher = {
@@ -14,7 +34,10 @@ type KeysWatcher = {
14
34
  */
15
35
  close(): void;
16
36
  };
17
- /** What `watchKeys` is started with. */
37
+ /**
38
+ * What `watchKeys` is started with. The watch scans when an asset file or a strings file changes;
39
+ * a folder that becomes or stops being a sub-feature scans too.
40
+ */
18
41
  type WatchKeysOptions = {
19
42
  /**
20
43
  * Called with the scanner's message when a scan fails: bad JSON in a strings file, a key two
@@ -33,6 +56,22 @@ type WatchKeysOptions = {
33
56
  onError?: (message: string) => void;
34
57
  };
35
58
  //#endregion
59
+ //#region src/app/engine-watch.d.ts
60
+ /**
61
+ * One change the engine watch saw.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * // `bun patch --commit` rebuilt the engine's dist: the served engine changed.
66
+ * const change: EngineChange = { before: { id: "0.14.12+1a2b3c", version: "0.14.12", dist: "1a2b3c…", folder: "/g/node_modules/@moku-labs/game" }, after: { id: "0.14.12+9c41e0", version: "0.14.12", dist: "9c41e0…", folder: "/g/node_modules/@moku-labs/game" }, restart: true };
67
+ * ```
68
+ */
69
+ type EngineChange = {
70
+ /** The fingerprint the watch had until this change. */before: EngineFingerprint; /** The fingerprint of the engine now installed. */
71
+ after: EngineFingerprint; /** True when the served engine changed (version or dist): the page server must restart. False: only the patch file. */
72
+ restart: boolean;
73
+ };
74
+ //#endregion
36
75
  //#region src/cli.d.ts
37
76
  /**
38
77
  * What the dev page of `preparePage` is written with besides the game.
@@ -64,7 +103,36 @@ type PreparePageOptions = {
64
103
  */
65
104
  type PreparedPage = {
66
105
  /** Absolute path of `<root>/.moku/index.html`: the HTML a Bun server imports. */html: string; /** Absolute path of `<root>/.moku/bunfig.toml`: pass it as `bun --config=<bunfig>`. */
67
- bunfig: string;
106
+ bunfig: string; /** The engine the page was written for: its id is the one the page shows in `globalThis.__MOKU_GAME_ENGINE__`. */
107
+ engine: EngineFingerprint;
108
+ };
109
+ /**
110
+ * What `watchEngine` reports to.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * // The editor restarts its page server on a new engine and logs a patch that is not installed yet.
115
+ * const options: WatchEngineOptions = {
116
+ * onChange: change => (change.restart ? restartPageServer() : log.warn("the engine's patch changed; run bun install")),
117
+ * onError: message => log.warn(message)
118
+ * };
119
+ * ```
120
+ */
121
+ type WatchEngineOptions = {
122
+ /** Called with every change the watch sees. Default: the warning lines of `moku-game dev`, on the console. */onChange?: (change: EngineChange) => void; /** Where an unreadable engine goes. Default: a console warning. */
123
+ onError?: (message: string) => void;
124
+ };
125
+ /**
126
+ * The running engine watch of `watchEngine`.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const engine: EngineWatcher = watchEngine("games/timber");
131
+ * engine.close(); // when the editor closes the game
132
+ * ```
133
+ */
134
+ type EngineWatcher = {
135
+ /** Stops watching; a second call does nothing. */close(): void;
68
136
  };
69
137
  /**
70
138
  * Runs one `moku-game` command line in this process: `dev`, `build`, `native <verb>`, `keys`,
@@ -78,14 +146,16 @@ type PreparedPage = {
78
146
  * // bin/moku-game.mjs in a game folder:
79
147
  * await runCli(["dev", "--port", "0"]); // 0 after Ctrl+C; stdout had "http://localhost:<port>/"
80
148
  * await runCli(["visual", "--no-pixels"]); // 0 when every checkpoint of tests/visual/index.ts is the same
149
+ * await runCli(["build", "--debug"]); // 0: dist/web carries the editor agent; one warning names build debug-20261014T153012Z
81
150
  * await runCli(["help"]); // 0
82
151
  * ```
83
152
  */
84
153
  declare function runCli(argv: readonly string[]): Promise<number>;
85
154
  /**
86
155
  * Writes a game's dev page into `<root>/.moku/` for a server the caller starts: `index.html`,
87
- * `dev.ts`, `main.ts` with the scenarios of `tests/scenarios/` (and, with agents, the agents and
88
- * the game's `.dev` modules), and `bunfig.toml` with the engine's hot plugin. It also writes the
156
+ * `script.ts`, `dev.ts` with the flags and the engine id, `main.ts` with the scenarios of
157
+ * `tests/scenarios/` (and, with agents, the agents and the game's `.dev` modules), and
158
+ * `bunfig.toml` with the engine's hot plugin. It also writes the
89
159
  * first `assets-stamp.ts`, the empty one, when the game has none: `main.ts` imports it, and
90
160
  * `watchKeys` rewrites it when an asset file changes. The page accepts the new stamp: new bytes
91
161
  * of a file swap in place, a new or removed file reloads the page. It starts no server and no
@@ -93,15 +163,16 @@ declare function runCli(argv: readonly string[]): Promise<number>;
93
163
  *
94
164
  * @param root - The game folder, relative to the cwd or absolute.
95
165
  * @param options - The agents, the preloads and the extra bundler plugins.
96
- * @returns The absolute paths of the HTML and the bunfig.
166
+ * @returns The absolute paths of the HTML and the bunfig, and the engine the page was written for.
97
167
  * @throws {Error} When the game has no `config.ts` or `index.ts`, a config value is refused, the
98
168
  * game does not resolve `@moku-labs/game/hot`, or a preload or plugin is not a file.
99
169
  * @example
100
170
  * ```ts
101
171
  * // The editor bin, started with --root games/timber:
102
172
  * const { preparePage } = await import(Bun.resolveSync("@moku-labs/game/cli", root));
103
- * await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] });
104
- * // { html: "<cwd>/games/timber/.moku/index.html", bunfig: "<cwd>/games/timber/.moku/bunfig.toml" }
173
+ * const page = await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] });
174
+ * // { html: "<cwd>/games/timber/.moku/index.html", bunfig: "<cwd>/games/timber/.moku/bunfig.toml", engine: { … } }
175
+ * // page.engine.id: "0.14.12+9c41e0"
105
176
  * ```
106
177
  */
107
178
  declare function preparePage(root: string, options?: PreparePageOptions): Promise<PreparedPage>;
@@ -133,5 +204,31 @@ declare function preparePage(root: string, options?: PreparePageOptions): Promis
133
204
  * ```
134
205
  */
135
206
  declare function watchKeys(root: string, options?: WatchKeysOptions): Promise<KeysWatcher>;
207
+ /**
208
+ * Watches the engine a game resolves, for a page server the caller runs: the editor calls it next
209
+ * to `preparePage`, and raw `moku-game dev` runs the same watch itself. It takes the fingerprint
210
+ * of the installed engine now (the id `preparePage(...).engine` has), watches the engine's
211
+ * folders, its patch and the install root's `package.json` and lock, and after 300 ms of quiet
212
+ * fingerprints again: a new version or dist calls `onChange` with `restart: true` (the page server
213
+ * must restart, Bun caches nothing across a restart), a changed patch alone with `restart: false`
214
+ * (the install did not happen yet). An engine run from its sources is not watched. Sync: it
215
+ * scans nothing.
216
+ *
217
+ * @param root - The game folder, relative to the cwd or absolute.
218
+ * @param options - Where the changes and the errors go.
219
+ * @returns The running watch; `close()` stops it.
220
+ * @throws {Error} When the game does not resolve `@moku-labs/game/hot`.
221
+ * @example
222
+ * ```ts
223
+ * // The editor bin, next to preparePage, started with --root games/timber:
224
+ * const { preparePage, watchEngine } = await import(Bun.resolveSync("@moku-labs/game/cli", root));
225
+ * const page = await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] });
226
+ * page.engine.id; // "0.14.12+9c41e0"
227
+ * const engine = watchEngine("games/timber", { onChange: change => change.restart && restartPageServer() });
228
+ * // after `bun patch --commit` in the game: onChange({ before: { id: "0.14.12+9c41e0", … }, after: { id: "0.14.12+1a2b3c.patch.3f2a7b", … }, restart: true })
229
+ * engine.close(); // when the editor closes the game
230
+ * ```
231
+ */
232
+ declare function watchEngine(root: string, options?: WatchEngineOptions): EngineWatcher;
136
233
  //#endregion
137
- export { type KeysWatcher, PreparePageOptions, PreparedPage, type WatchKeysOptions, preparePage, runCli, watchKeys };
234
+ export { type EngineChange, type EngineFingerprint, EngineWatcher, type KeysWatcher, PreparePageOptions, PreparedPage, WatchEngineOptions, type WatchKeysOptions, preparePage, runCli, watchEngine, watchKeys };