@moku-labs/game 0.15.19 → 0.15.22

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 (37) hide show
  1. package/dist/app/page.d.mts +1 -1
  2. package/dist/app/page.mjs +1 -1
  3. package/dist/app/system.d.mts +3 -3
  4. package/dist/app.d.mts +3 -3
  5. package/dist/app.mjs +1 -1
  6. package/dist/assets.d.mts +1 -1
  7. package/dist/{capture-D7XvoHdH.mjs → capture-PTJXDpxK.mjs} +117 -16
  8. package/dist/{commands-G9maNqTK.d.mts → commands-BvNp27KS.d.mts} +5 -5
  9. package/dist/control.d.mts +4 -4
  10. package/dist/control.mjs +1 -1
  11. package/dist/{define-BAuGN0UM.d.mts → define-DAVDb7dr.d.mts} +2 -2
  12. package/dist/{headless-uUUH54Ij.d.mts → headless-BCwsb1wR.d.mts} +1 -1
  13. package/dist/{headless-BA6ffzqD.mjs → headless-Bxxl_mQX.mjs} +1 -1
  14. package/dist/{index-Cku9aRz_.d.mts → index-CKXNW8_t.d.mts} +26 -4
  15. package/dist/index.d.mts +5 -5
  16. package/dist/index.mjs +3 -3
  17. package/dist/inspect.d.mts +6 -6
  18. package/dist/inspect.mjs +1 -1
  19. package/dist/jsx-dev-runtime.d.mts +1 -1
  20. package/dist/jsx-runtime.d.mts +1 -1
  21. package/dist/{run-BqYmcNo7.mjs → run-DRcc2SKF.mjs} +2 -2
  22. package/dist/{runtime-CuMDZhii.d.mts → runtime-Dt7VdhqR.d.mts} +1 -1
  23. package/dist/{sources-BYKXYPhd.mjs → sources-Dpc4NXvG.mjs} +1 -1
  24. package/dist/{src-CXfBuKpl.mjs → src-BcfQy5vB.mjs} +1293 -277
  25. package/dist/testing.d.mts +4 -4
  26. package/dist/testing.mjs +2 -2
  27. package/dist/{types-4T4QTU48.d.mts → types-B02c7d6g.d.mts} +19 -4
  28. package/dist/{types-bwefoJES.d.mts → types-BFGyVykz.d.mts} +213 -19
  29. package/dist/{types-DMsvVZZT.d.mts → types-C8JK9u1Y.d.mts} +2 -2
  30. package/dist/{types-rr56N0Ab.d.mts → types-COWBfy2L.d.mts} +8 -5
  31. package/dist/{types-DQtBMqRE.d.mts → types-YxQts-fd.d.mts} +4 -4
  32. package/dist/{visual-B-c5OIFB.d.mts → visual-Dv67ITPu.d.mts} +6 -6
  33. package/dist/{visual-DNM5oFYx.mjs → visual-kEC9nb-o.mjs} +20 -2
  34. package/dist/visual.d.mts +1 -1
  35. package/dist/visual.mjs +2 -2
  36. package/llms.txt +2 -2
  37. package/package.json +1 -1
package/dist/visual.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-B-c5OIFB.mjs";
1
+ import { a as VisualApp, c as VisualRenderer, d as VisualStart, f as VisualStep, h as VisualTolerance, i as CheckpointResult, l as VisualReport, m as VisualTestResult, n as runVisualTests, o as VisualOptions, p as VisualTest, r as defineVisualTest, s as VisualPage, t as parseVisualArgv, u as VisualSetup } from "./visual-Dv67ITPu.mjs";
2
2
  export { type CheckpointResult, type VisualApp, type VisualOptions, type VisualPage, type VisualRenderer, type VisualReport, type VisualSetup, type VisualStart, type VisualStep, type VisualTest, type VisualTestResult, type VisualTolerance, defineVisualTest, parseVisualArgv, runVisualTests };
package/dist/visual.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { n as read, t as sources } from "./sources-BYKXYPhd.mjs";
2
- import { n as commands, t as run } from "./run-BqYmcNo7.mjs";
1
+ import { n as read, t as sources } from "./sources-Dpc4NXvG.mjs";
2
+ import { n as commands, t as run } from "./run-DRcc2SKF.mjs";
3
3
  import path from "node:path";
4
4
  import { createBrandConsole } from "@moku-labs/common/cli";
5
5
  import { mkdir, readFile, writeFile } from "node:fs/promises";
package/llms.txt CHANGED
@@ -44,7 +44,7 @@ Root exports:
44
44
  - A pure wait ends in two ways. A gate answer whose `intent` is an outcome name: the answer payload becomes the outcome payload. Or a world event whose `type` is listed in the node's `inbox`: the event payload becomes the outcome payload.
45
45
  - Transition: rest node to rest node. It is a transaction. On an error the runner discards, rolls back to the last rest point, retries `retries` times, then enters `safeNode`. A failure inside `safeNode` rejects `run()`.
46
46
  - Three ways to run an effect in a node. `await fx(descriptor)`: the node waits for it. `fx.start(descriptor)`: it starts now and the node goes on; write it in place of `void fx(...)`. A failure of a started effect is the log entry `flow:fx-start-failed` and never fails the node. A `cosmetic` descriptor (`sfx`, `play`, `haptic`, `music`) that fails, awaited or started, is the warn entry `flow:fx-cosmetic-failed` with `{ kind, message }` and resolves `undefined`; a descriptor with `answers` and a `guide` are refused, they have to be awaited. `fx.emit(hint(...))`: released after the commit of the edge, dropped in fast mode.
47
- - `await fx(descriptor)` is typed by the descriptor (`Flow.Descriptor<Result>`): `schedule`, `guide`, `music`, `sfx`, `haptic`, `play` resolve `void`; `load` resolves `Assets.LoadResult | undefined` (`undefined` without `assets`, as in `game.headless()`); `popup(Component, props)` resolves `Flow.Answer<outcome names>`, so comparing it with an intent the component does not declare is a compile error; a descriptor written by hand resolves `unknown`. A game declares its own: `const envRead = defineEffect<{ notice: boolean }, EnvRead>("view-env.read", { fast: "run" })`, then `await fx(envRead({ notice: true }))` is `EnvRead` and `envRead.handle(ctx.require(flowPlugin).fx, payload => ...)` registers the one handler (returns the remover). Only `{ fast: "run" }` without `cosmetic` resolves `Result`; any other declaration resolves `Result | undefined`. `NodeFx` is generic: a hand-written fake is typed `as NodeFx`, or use `fakeFx`.
47
+ - `await fx(descriptor)` is typed by the descriptor (`Flow.Descriptor<Result>`): `schedule`, `guide`, `music`, `sfx`, `haptic`, `play` resolve `void`; `load` resolves `Assets.LoadResult | undefined` (`undefined` without `assets`, as in `game.headless()`); `ready` resolves `boolean | undefined` (`true` when every bundle is loaded and on the GPU, `undefined` without `assets`, so compare `=== false`); `popup(Component, props)` resolves `Flow.Answer<outcome names>`, so comparing it with an intent the component does not declare is a compile error; a descriptor written by hand resolves `unknown`. A game declares its own: `const envRead = defineEffect<{ notice: boolean }, EnvRead>("view-env.read", { fast: "run" })`, then `await fx(envRead({ notice: true }))` is `EnvRead` and `envRead.handle(ctx.require(flowPlugin).fx, payload => ...)` registers the one handler (returns the remover). Only `{ fast: "run" }` without `cosmetic` resolves `Result`; any other declaration resolves `Result | undefined`. `NodeFx` is generic: a hand-written fake is typed `as NodeFx`, or use `fakeFx`.
48
48
  - Every failure is written into the log by the engine: an `error` entry `flow:error` with `{ path, rolledBackTo, retry, error: { message, stack } }`. Read it with `app.log.trace()`. A game needs no plugin for it.
49
49
  - `recover` on a rest node: `recover: async ({ player, session, rng, fx, signal, now }) => { ... }`. It runs only when the runner gave up on a transition and enters this node as `safeNode`, before the node is entered. The rollback left the state of the failed rest point; `recover` writes the drafts so the safe node can draw itself. What it wrote commits as one edge with `outcome: "recover"` and becomes the rest point. A `recover` that throws rejects `run()`. A retry does not run it. So the safe node stays a pure wait: no `run` body, no extra outcome, no self edge.
50
50
  - Entering a rest node calls `model.store.markRest()`: the save provider gets the patches. Leaving a `barrier` node calls `markBarrier(txId)`: a durable write, and rollback cannot cross it.
@@ -526,7 +526,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
526
526
  - HUD: a scene layer `{ above: "overlays" }` is the HUD layer, drawn above every overlay and never in a picture; a scene with a HUD layer and no `ui` gets `ui` right under it. Its roots never take the keyboard: Escape and the Back press reach the top sheet under it. `HudPart({ key })` tags a HUD view; an overlay's `hud` (`"all"`, `"none"` or keys) names the parts that stay shown, the others fade with its level and take no tap. Keys are typed by `defineGame<{ …, hud: "energy" | "gems" }>()`.
527
527
  - Endless loops of views under an opaque or blurred overlay write nothing (`renderer.sync.loopsHeld(entity)`, the seam anim asks before an endless loop writes); their phase follows time.
528
528
  - A scene names bundles on top of its `bundle` from the committed state: `defineScene("ward", { bundle: "episodes-kit", bundles: ({ session }) => [session.episode], layers: {}, projections: [wardRoom] })`. `bundles` is read each time a node enters the scene, and every way in (a live entry, a restore, the `safeNode` recovery) waits for all of them before the scene is mounted and the node's gate opens. The keys are `BundleKey`s of `generated/assets.ts`.
529
- - `app.assets`: `load(bundle)`, `unload(bundle)`, `isLoaded(bundle)`, `has(bundle)` (the manifest carries it, loaded or not), `texture(key)`, `font(key)`, `audio(key)`, `usage()`. Per bundle in a feature's `assets.ts`: `defineBundles({ ep01: { tier: "feature", release: "leave", mipmaps: ["items/**"] } })`. `release: "leave"` also unloads the bundle, with reason `"leave"`, at the next rest once nothing holds it: not pinned by the node entered, not named by the mounted scene, no node of its feature on the stack; a bundle the outgoing scene named leaves one rest later. The default `"budget"` leaves only under the texture budget. `mipmaps` (`true` or globs relative to `assets/`) uploads the textures with their full chain; such a texture stays loose in a pack. A sub-feature with resources gets its own bundles, named like its folder, with `dir` and `parent` in the manifest; a code-only one has none. `companions.add({ load(bundle), unload(bundle) })` adds what loads and unloads with every bundle; `i18n` adds the strings of a sub-feature that way, only when `assets` is composed. Events `assets:bundle-loaded`, `assets:bundle-progress`, `assets:bundle-unloaded` (`reason: "budget" | "request" | "leave"`), `assets:replaced` (dev). Config: `manifest`, `textureBudgetMb: 192`, `preloadDepth: 2`, `baseUrl`, `io`.
529
+ - `app.assets`: `load(bundle)`, `warm(bundle)` (loads it if needed, then puts its textures and atlas pages on the GPU through `renderer.sync.textures.upload`, `renderer.uploads` a drawn frame; joins a running or finished warm; an unload forgets it; resolves also when the renderer could not upload; headless at once), `unload(bundle)`, `isLoaded(bundle)`, `has(bundle)` (the manifest carries it, loaded or not), `texture(key)`, `font(key)`, `audio(key)`, `usage()`. Per bundle in a feature's `assets.ts`: `defineBundles({ ep01: { tier: "feature", release: "leave", mipmaps: ["items/**"] } })`. `release: "leave"` also unloads the bundle, with reason `"leave"`, at the next rest once nothing holds it: not pinned by the node entered, not named by the mounted scene, no node of its feature on the stack; a bundle the outgoing scene named leaves one rest later. The default `"budget"` leaves only under the texture budget. `mipmaps` (`true` or globs relative to `assets/`) uploads the textures with their full chain; such a texture stays loose in a pack. A sub-feature with resources gets its own bundles, named like its folder, with `dir` and `parent` in the manifest; a code-only one has none. The effect `ready(bundle | bundles)` (root export, also from `defineGame`) lets a node rest on a load and stay answerable: its handler (fast mode too) answers `true` headless, throws for an unknown bundle, answers `true` when every bundle is loaded and warmed or its last warm failed (`ctx.log.warn("assets:ready-failed", { bundle, error })`, cleared by a later successful load), otherwise starts `warm` for what is missing, answers `false` and posts `{ type: "ready" }` into `flow.inbox` once all of it settled. Recipe: a rest node `inbox: ["elapsed", "ready"]` with outcomes `ready` and `back`, and a transit node `if ((await fx(ready(list))) === false) return out.wait();`. `companions.add({ load(bundle), unload(bundle) })` adds what loads and unloads with every bundle; `i18n` adds the strings of a sub-feature that way, only when `assets` is composed. Events `assets:bundle-loaded`, `assets:bundle-progress`, `assets:bundle-unloaded` (`reason: "budget" | "request" | "leave"`), `assets:replaced` (dev). Config: `manifest`, `textureBudgetMb: 192`, `preloadDepth: 2`, `baseUrl`, `io`.
530
530
  - `resource(name, defaults)` clones plain JSON defaults into every world. `resource(name, () => value)` calls the factory once per world on first read, so the value may hold a `Map` or a function: `const Looks = resource("boardLooks", () => ({ shown: new Map<World.Entity, { look: string }>() }))`. A system keeps its own state there; `snapshot()` leaves a value that is not JSON out.
531
531
  - `Now` is the engine resource of the frame's moment: `world` writes `clock.now()` into it at start and first thing in every frame, headless too, so every system of a frame reads one moment: `const { now } = res(Now)`; a fake clock makes it exact. A system that shows time writes its component only when its picture changes. `snapshot().resources` holds `Now: { now }`. Reserved resource names: `Now`, `Pointer`, `AnimPlayer`, `UiCounters`; a game resource of one of them throws `[game] world: the resource name "Now" is the engine's (the frame's moment).`
532
532
  - When a system runs: before each run the world checks it. A system with a `query` is skipped while a term of its query has no entity (the smallest store among the terms is empty): no walk, no `mut()` mark, no timing. A system without a query always runs. `system({ ..., scenes: ["board"] })` runs only while one of those scenes is mounted or leaving: `scenes` calls `app.world.ecs.setScene(id)` at every switch, and the scene before leaves until nothing carries `Exiting` (the elements of its JSX views played their exit; the next scene's entities already stand then, so a `scenes` system writes only its own scene's entities). A dev build throws at start for a name in `scenes` no feature declares: `[game] System "board.idle" runs on the scene "bord", which no feature declares.` `ctx.resumed` is true on the first run after a skip (a cache or a remembered cue starts over then). The same for every system, the engine's included; 0 B a frame. A run with no rows does not happen any more: work a system did besides its rows (a hover, a drop or a press that ends, a resource reset, something that outlives its entities) goes to a system with `query: []` and `scenes`, reading its rows with `world.query()`. From 0.15.3: delete `systemsRun` from every feature (the type refuses it), rewrite the systems that work outside their rows, add `scenes` only to a system whose entities stand on every screen; `ecs.hold` and `ecs.isActive` are gone (a test asks `app.scenes.current()` or whether the system ran). World README, "When a system runs".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.19",
3
+ "version": "0.15.22",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [