@moku-labs/game 0.13.0 → 0.13.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.
Files changed (39) hide show
  1. package/README.md +3 -3
  2. package/dist/app/page.d.mts +2 -1
  3. package/dist/app/page.mjs +1 -0
  4. package/dist/app/system.d.mts +3 -3
  5. package/dist/app.d.mts +2 -2
  6. package/dist/app.mjs +1 -1
  7. package/dist/assets.d.mts +1 -1
  8. package/dist/assets.mjs +1 -1
  9. package/dist/cli.d.mts +69 -3
  10. package/dist/cli.mjs +482 -57
  11. package/dist/{commands-DvFr8Uwp.d.mts → commands-BrgeMP5y.d.mts} +5 -5
  12. package/dist/control.d.mts +4 -4
  13. package/dist/{define-CCc0ozKe.d.mts → define-ZmWbWxon.d.mts} +2 -2
  14. package/dist/{headless-G4nuZ-xz.d.mts → headless-Dgxvp6Ym.d.mts} +1 -1
  15. package/dist/hot.d.mts +3 -3
  16. package/dist/hot.mjs +11 -9
  17. package/dist/{index-rnOYAriI.d.mts → index-CdbE_jGs.d.mts} +12 -4
  18. package/dist/index.d.mts +5 -5
  19. package/dist/index.mjs +1 -1
  20. package/dist/inspect.d.mts +7 -7
  21. package/dist/jsx-dev-runtime.d.mts +1 -1
  22. package/dist/jsx-runtime.d.mts +1 -1
  23. package/dist/{memory-DwDhfFWK.d.mts → memory-DJ2sPExv.d.mts} +1 -1
  24. package/dist/project.mjs +7 -2954
  25. package/dist/{runtime-B276Gg0s.d.mts → runtime-6qakboTm.d.mts} +1 -1
  26. package/dist/{src-SmT-XxVf.mjs → src-CltFFKtc.mjs} +955 -108
  27. package/dist/testing.d.mts +4 -4
  28. package/dist/testing.mjs +1 -1
  29. package/dist/{types-D_2G6Q68.d.mts → types-B3T2X6To.d.mts} +8 -4
  30. package/dist/{types-ZQLYXo5X.d.mts → types-C6y8EfEB.d.mts} +54 -5
  31. package/dist/{types-BrTpz9oL.d.mts → types-CmuwAjkV.d.mts} +19 -6
  32. package/dist/{types-DAdwEJMp.d.mts → types-D57SntUt.d.mts} +2 -2
  33. package/dist/{types-DLghaGEy.d.mts → types-DGIkfZB7.d.mts} +4 -4
  34. package/dist/{types-Bcdo5-2w.d.mts → types-zRGJdFGX.d.mts} +7 -3
  35. package/dist/visual.d.mts +6 -6
  36. package/dist/watch-CWI_on4T.mjs +2960 -0
  37. package/llms.txt +11 -7
  38. package/package.json +4 -2
  39. package/dist/{assets-DYiKxPBX.mjs → assets-BrKSg1O6.mjs} +1 -1
package/llms.txt CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 2D puzzle game engine on TypeScript, built as a Layer-2 framework on @moku-labs/core. The game is a deterministic graph of business logic. State commits only on edges. Position is data. Player input and world events are answers a node waits for. The screen is a projection of committed state. V1 is the logic plugins `time`, `lifecycle`, `model`, `clock`, `flow` and a headless testing entry; V2 adds the screen plugins `world`, `renderer`, `input`, `assets`, `scenes`, a Pixi v8 renderer loaded lazily, and the `@moku-labs/game/assets` entry that generates typed asset keys. V3 adds the interface plugins `anim`, `i18n`, `text`, `ui` (`screen` lists all nine) and the opt-in `audio`: choreographies as data, strings as data, text from MSDF fonts, screens in JSX laid out by Yoga, sound as an effect a node awaits. V4 adds two doors for the editor: `@moku-labs/game/inspect` reads a running game, `@moku-labs/game/control` drives a dev build. V5 adds the opt-in plugin `effects`: particles and WGSL/GLSL filters as components. V6 adds the opt-in plugin `platform`: the Back button, haptics and keep-awake through a provider the game passes. The game shell makes a game a folder of data: `@moku-labs/game/app` (`defineGameApp`, `config.ts` as `GameConfig`), the page `@moku-labs/game/app/page`, the system shell `@moku-labs/game/app/system` and the bin `moku-game` that serves, builds, packages and packs the folder. The package is published on npm as `@moku-labs/game`, and this file ships inside it.
4
4
 
5
- Facts: Bun only. ESM only. `"sideEffects": false`. `pixi.js` `^8.0.0` is a peer dependency, no V1 code imports it. `playwright-core` (visual tests), `sharp` (the asset pack), `typescript` (the project index), `@moku-labs/system` (the system shell and the store save) and `@moku-labs/native` (`moku-game native`) are optional peers. Node >= 24, Bun >= 1.3.14. TypeScript strict with `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`. The engine ships no genre rules: no merge, no match-3, no physics. A game brings its own pure rule functions.
5
+ Facts: Bun only. ESM only. `"sideEffects"` lists the root entry `./dist/index.mjs` only, every other entry has none. `pixi.js` `^8.0.0` is a peer dependency, no V1 code imports it. `playwright-core` (visual tests), `sharp` (the asset pack), `typescript` (the project index), `@moku-labs/system` (the system shell and the store save) and `@moku-labs/native` (`moku-game native`) are optional peers. Node >= 24, Bun >= 1.3.14. TypeScript strict with `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`. The engine ships no genre rules: no merge, no match-3, no physics. A game brings its own pure rule functions.
6
6
 
7
7
  ## Entry points
8
8
 
@@ -10,7 +10,7 @@ Facts: Bun only. ESM only. `"sideEffects": false`. `pixi.js` `^8.0.0` is a peer
10
10
  - `@moku-labs/game/app`: `defineGameApp`, `startMoment` and the types of a game's `index.ts` and `config.ts`. No `node:` import, no system or native package. See "The game shell".
11
11
  - `@moku-labs/game/app/page` (the browser): `startPage`, the page the generated `.moku/main.ts` calls.
12
12
  - `@moku-labs/game/app/system` (the browser, the native shell): `systemShellOf`, `fromSystem`, `storeSave`, `createSystemApp`, the type `SystemModules`, over the optional peer `@moku-labs/system`. The one entry that names it, in types only: the page hands it the loaders.
13
- - `@moku-labs/game/cli` (node and bun only): `runCli`, the bin `moku-game`, and `preparePage`, the editor's dev page.
13
+ - `@moku-labs/game/cli` (node and bun only): `runCli`, the bin `moku-game`, `preparePage`, the editor's dev page, and `watchKeys`, the watch that keeps `generated/` fresh next to it.
14
14
  - `@moku-labs/game/testing`: headless play, repro runs, isolated feature tests (`isolate`, `stub`), fake clock, in-memory save provider. No `node:` import.
15
15
  - `@moku-labs/game/visual` (node and bun only): the visual tests `defineVisualTest`, `runVisualTests`, `parseVisualArgv`. The bin runs them: `moku-game visual`.
16
16
  - `@moku-labs/game/assets` (node and bun only): the asset key scanner, the production packer and the string compiler. The package bin `moku-game-assets` runs it.
@@ -306,6 +306,7 @@ The page. `moku-game dev` and `build` write a `main.ts` that calls `startPage(ga
306
306
  // .moku/main.ts of the engine's mini game, written by moku-game dev
307
307
  // Written by moku-game dev. Do not edit.
308
308
  import "./dev.ts";
309
+ import "./assets-stamp.ts";
309
310
  import { startPage } from "@moku-labs/game/app/page";
310
311
  import game from "../index.ts";
311
312
  import config from "../config.ts";
@@ -317,12 +318,13 @@ await startPage(game, config, {
317
318
  ```
318
319
 
319
320
  Commands of the bin `moku-game` (bun). Every command takes `--root <dir>` (default `.`), `--preload <path>` and `--serve-plugin <path>` (both repeat), before or after the command word. Exit 0 on success, else 1 with a `[game] …` line.
320
- - `moku-game dev [--port 3000] [--packed]`: writes `.moku/index.html`, `dev.ts`, `main.ts`, `bunfig.toml` (the hot plugin first, `__MOKU_GAME_DEV__` defined `true`), runs Bun again under that bunfig in the game folder and serves `/`. It prints `› <folder>: dev server, raw assets. Ctrl+C stops it.` and then the bound URL alone on its own line, `http://localhost:3000/`. `--port 0` takes a free port. `--packed` serves `dist/assets`. A new or removed file in `tests/scenarios/` rewrites `main.ts`. A taken port: `[game] dev: port 3000 is in use.` with `Pass --port 0 for a free port.`
321
+ - `moku-game dev [--port 3000] [--packed]`: writes `.moku/index.html`, `dev.ts`, `main.ts`, `assets-stamp.ts`, `bunfig.toml` (the hot plugin first, `__MOKU_GAME_DEV__` defined `true`), runs Bun again under that bunfig in the game folder and serves `/`. It prints `› <folder>: dev server, raw assets. Ctrl+C stops it.` and then the bound URL alone on its own line, `http://localhost:3000/`. `--port 0` takes a free port. `--packed` serves `dist/assets`. A new or removed file in `tests/scenarios/` rewrites `main.ts`. On the raw files it keeps `generated/` fresh: it runs the scan of `keys` once before the page is written, and a save of an asset file (`.png`, `.webp`, `.fnt`, `.mp3`, `.m4a`) or of a `strings/<locale>.json` runs it again 100 ms after the last file event and prints `› keys: <path>` or `› keys: <path> and <n> more`; a changed or new string hot swaps through `generated/strings.<locale>.ts`, an asset file saved with new bytes swaps in the running page and a new or removed one reloads it, both through `.moku/assets-stamp.ts`, which `main.ts` imports (see "Hot swap"), and files answer `Cache-Control: no-store`. A failed scan is one warning `[game] keys: <message>` and the server runs on. A changed layer in `config.ts` needs a restart; `--packed` watches nothing. A taken port: `[game] dev: port 3000 is in use.` with `Pass --port 0 for a free port.`
321
322
  - `moku-game build [--out dist/web]`: packs the assets, bundles the page minified with `__MOKU_GAME_DEV__` defined `false` (no scenario, no agent, no `.dev` module, no `/control`), every link `./`, and copies the pack beside it with `manifest.json` next to `index.html`. Runs from any http sub-path and from Tauri, not from `file://`.
322
323
  - `moku-game native build <target> [--simulator]`, `native dev <target>`, `native doctor [<target>]`, `native clean [<target>]`: one verb of `@moku-labs/native` (a dev dependency, `^0.3.2`) over `config.ts`. App name, identifier and icon from `native`, orientation and background colour from `page`; the web build is `moku-game build`, the dev server `moku-game dev --port 5173`; system rows for `back`, `haptics`, `store`; the Tauri project in `.moku/tauri`, the apps in `dist-native`. `[game] config.ts has no native section.` without `native`.
323
- - `moku-game keys [--check]`: `generated/assets.ts`, the compiled strings and the dev manifest `generated/manifest.json`; `dev` serves it on `/manifest.json` and warns without it, `visual` stops without it. `moku-game pack [--no-cache]`: `dist/assets`. Both pass the layers of `config.ts`.
324
+ - `moku-game keys [--check]`: `generated/assets.ts`, the compiled strings and the dev manifest `generated/manifest.json`; `dev` runs the same scan at start and on save and serves the manifest on `/manifest.json`, `visual` stops without it. `moku-game pack [--no-cache]`: `dist/assets`. Both pass the layers of `config.ts`.
324
325
  - `moku-game visual [--update] [--only <name>] [--no-pixels | --pixels] [--webgl] [--dir <path>] [--tests <file>] [--url <url>]`: runs `runVisualTests` with the default export of `tests/visual/index.ts`, `{ app, tests }` (`app` is a `VisualSetup` `{ app, page? }`: `export default { app: { app: fixtureApp }, tests }`), against `tests/visual/baselines/`. The flags go through `parseVisualArgv`. The pixel leg runs on a Mac only unless `--pixels`/`--no-pixels`; without `--url` the command writes its own page into `.moku/visual/` (the page of `dev` and of the editor in `.moku/` stays), runs Bun again under `.moku/visual/bunfig.toml` as `dev` does (hot plugin, `--serve-plugin`, `--preload`), and the child serves the page on a free port, then stops it. No `manifest.json`: `[game] visual: no manifest.json in "<game>".` Exit 0 when the report is ok, else 1. `[game] visual: <file> must export default { app, tests }.` for a missing module or another shape, a bare app factory too.
325
- - `preparePage(root, { agents?, preload?, servePlugins? })` of `@moku-labs/game/cli` writes the dev page of `moku-game dev` and resolves `{ html, bunfig }`, both absolute, for a server the caller starts: `bun --config=<bunfig>`. With agents the page also imports the game's `**/*.dev.ts` and hands them to the agents. The editor calls it: `await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] })`.
326
+ - `preparePage(root, { agents?, preload?, servePlugins? })` of `@moku-labs/game/cli` writes the dev page of `moku-game dev` and resolves `{ html, bunfig }`, both absolute, for a server the caller starts: `bun --config=<bunfig>`. With agents the page also imports the game's `**/*.dev.ts` and hands them to the agents. It also writes the first, empty `.moku/assets-stamp.ts` when the game has none. The editor calls it: `await preparePage("games/timber", { agents: ["@moku-labs/editor/agent/page"] })`.
327
+ - `watchKeys(root, { onError? })` of `@moku-labs/game/cli` is the keys watch of `moku-game dev` for a server the caller runs: it runs the scan of `keys` once, resolves `{ close() }` after it, then scans again on a save of an asset or a strings file. A failed scan calls `onError(message)` (default: a console warning `[game] keys: <message>`) and the watch goes on. The editor calls it next to `preparePage`: `const keys = await watchKeys("games/timber", { onError: message => log.warn(message) })`, then `keys.close()`. It rejects with the `[game]` errors of `preparePage` for a folder that is not a game.
326
328
 
327
329
  ## More patterns
328
330
 
@@ -623,9 +625,11 @@ Dev only. A save of a view file swaps the module in the running page: same state
623
625
  plugins = ["@moku-labs/game/hot"]
624
626
  ```
625
627
 
626
- - View files swap: `.tsx`, `styles.ts`, `view.ts`, `animations.ts`, `effects.ts`, `generated/strings.<locale>.ts`. On the layered layout also any `.ts` directly in a kind folder: `styles/`, `motion/`, `effects/`, `views/`, `world/projections/`, `world/layout/`, in a feature or in `shared/`. Styles, components, function components, projections of a registered name, animations, text styles, emitters and strings. After a strings JSON edit run `bun run assets:keys`; its output is what swaps.
628
+ - View files swap: `.tsx`, `styles.ts`, `view.ts`, `animations.ts`, `effects.ts`, `generated/strings.<locale>.ts`. On the layered layout also any `.ts` directly in a kind folder: `styles/`, `motion/`, `effects/`, `views/`, `world/projections/`, `world/layout/`, in a feature or in `shared/`. Styles, components, function components, projections of a registered name, animations, text styles, emitters and strings. Under `moku-game dev` or the editor's `watchKeys` a save of a strings JSON compiles by itself and its output swaps, a new key too. A game with a server of its own runs `bun run assets:keys` after a strings JSON edit; its output is what swaps.
627
629
  - Logic files reload the page: `rules/`, `nodes/`, `flows/`, `state.ts`, `tables.ts`, `kit.ts`, a feature `index.ts`, `world/components/`, `world/systems/`. A view file that exports a scene, system, ECS component, filter, flow, node, feature, plugin, a new projection or a new animation is refused: `ui` logs `ui:hot-refused` and throws, and Bun reloads the page.
628
- - A swap logs `ui:hot-swap` and emits the global event `ui:hot-swap` `{ file, module }`.
630
+ - Asset files, under `moku-game dev` on the raw files or the editor's `watchKeys`: a texture, a bitmap font (`.fnt` or its page) or a sound saved with new bytes swaps in the loaded bundles, same key, no reload. The keys watch writes `.moku/assets-stamp.ts` `{ files, changed }`, the hot plugin lets the page accept it, and `assets` reads the changed files again. A new, removed or renamed asset file, a changed nine-slice tag too, reloads the page. So does a replace that fails. Not with `--packed`, not in a built game. A bundle that is not loaded reads the new bytes on its next load. A sound that is playing plays on; a running particle effect of a replaced texture starts again.
631
+ - A swap logs `ui:hot-swap` and emits the global event `ui:hot-swap` `{ file, module }`. `effects` hooks it for emitters, `assets` for the stamp module.
632
+ - Event `assets:replaced` `{ bundle, keys }`, dev only: `assets` emits it once per bundle after an asset swap. `keys` are the asset keys with new bytes; `app.assets.texture(key)`, `font(key)` and `audio(key)` answer the new object and the old textures are destroyed. `text`, `effects` and `audio` hook it; a game plugin that built something from a key hooks it with `depends: [assetsPlugin]` and builds it again.
629
633
  - Another layout, on a server of the game's own: `export default hot({ include, exclude })` from the game's `hot.ts`, listed as `plugins = ["./hot.ts"]`.
630
634
  - Keep a view helper in the module of the projection that uses it. A function another module stored into an object at load time keeps the old version.
631
635
 
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
- "sideEffects": false,
6
+ "sideEffects": [
7
+ "./dist/index.mjs"
8
+ ],
7
9
  "module": "./dist/index.mjs",
8
10
  "types": "./dist/index.d.mts",
9
11
  "exports": {
@@ -2,8 +2,8 @@ import { createRequire } from "node:module";
2
2
  import path from "node:path";
3
3
  import { createBrandConsole } from "@moku-labs/common/cli";
4
4
  import { pathToFileURL } from "node:url";
5
- import { createHash } from "node:crypto";
6
5
  import { mkdir, open, readFile, readdir, rm, rmdir, stat, writeFile } from "node:fs/promises";
6
+ import { createHash } from "node:crypto";
7
7
  import { MaxRectsBin, MaxRectsPacker } from "maxrects-packer";
8
8
  /** Bytes per pixel of the decoded images: RGBA, 8 bits each. */
9
9
  const CHANNELS = 4;