@moku-labs/game 0.15.39 → 0.15.41

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 (34) hide show
  1. package/README.md +1 -1
  2. package/dist/app/page.d.mts +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-PTJXDpxK.mjs → capture-BtS6_oWr.mjs} +61 -1
  8. package/dist/{commands-CMFdiDcg.d.mts → commands-DLk06fiL.d.mts} +5 -5
  9. package/dist/control.d.mts +4 -4
  10. package/dist/control.mjs +1 -1
  11. package/dist/{define-D27mfOhZ.d.mts → define-CgU4dz0e.d.mts} +2 -2
  12. package/dist/{headless-CRqMxzqv.d.mts → headless-1e3gEF1Y.d.mts} +1 -1
  13. package/dist/{headless-Bxxl_mQX.mjs → headless-CCiUwvlp.mjs} +1 -1
  14. package/dist/{index-DFAjLwxi.d.mts → index-D12s9KZ2.d.mts} +19 -7
  15. package/dist/index.d.mts +5 -5
  16. package/dist/index.mjs +2 -2
  17. package/dist/inspect.d.mts +6 -6
  18. package/dist/jsx-dev-runtime.d.mts +1 -1
  19. package/dist/jsx-runtime.d.mts +1 -1
  20. package/dist/{run-G4m1APMv.mjs → run-8s7kh2QD.mjs} +2 -2
  21. package/dist/{runtime-CbLZyDXj.d.mts → runtime-Bpg0WmM8.d.mts} +1 -1
  22. package/dist/{src-B2vblgmb.mjs → src-CWVwT9UM.mjs} +1858 -721
  23. package/dist/testing.d.mts +4 -4
  24. package/dist/testing.mjs +2 -2
  25. package/dist/{types-54KzmAA6.d.mts → types-B-o8Ul1v.d.mts} +2 -2
  26. package/dist/{types-0Br0SjTB.d.mts → types-CFEtGENK.d.mts} +1 -1
  27. package/dist/{types-H1FiWmKa.d.mts → types-DQ9VHXun.d.mts} +4 -4
  28. package/dist/{types-C_dk-lXz.d.mts → types-DrkR03nL.d.mts} +413 -31
  29. package/dist/{types-CGPWYbZO.d.mts → types-gQATSj41.d.mts} +2 -2
  30. package/dist/{visual-DjTJ02c3.d.mts → visual-pHKIFBSQ.d.mts} +6 -6
  31. package/dist/visual.d.mts +1 -1
  32. package/dist/visual.mjs +1 -1
  33. package/llms.txt +14 -4
  34. 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-DjTJ02c3.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-pHKIFBSQ.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,6 +1,6 @@
1
1
  import { t as VISUAL_CLOCK } from "./fake-hnJcJeYf.mjs";
2
2
  import { n as read, t as sources } from "./sources-MfRpM8sV.mjs";
3
- import { n as commands, t as run } from "./run-G4m1APMv.mjs";
3
+ import { n as commands, t as run } from "./run-8s7kh2QD.mjs";
4
4
  import path from "node:path";
5
5
  import { createBrandConsole } from "@moku-labs/common/cli";
6
6
  import { mkdir, readFile, writeFile } from "node:fs/promises";
package/llms.txt CHANGED
@@ -23,7 +23,7 @@ Facts: Bun only. ESM only. `"sideEffects"` lists the root entry `./dist/index.mj
23
23
  - `@moku-labs/game/jsx-runtime` and `@moku-labs/game/jsx-dev-runtime`: what `"jsxImportSource": "@moku-labs/game"` resolves to. A game never imports them by hand.
24
24
 
25
25
  Root exports:
26
- - Functions: `createApp`, `createPlugin`, `defineGame`, `defineFeature`, `flattenFeatures`, `type`, `exit`, `to`, `slot`, `schedule`, `guide`, `hint`, `defineEffect`.
26
+ - Functions: `createApp`, `createPlugin`, `defineGame`, `defineFeature`, `flattenFeatures`, `type`, `exit`, `to`, `slot`, `contribution`, `schedule`, `guide`, `hint`, `defineEffect`.
27
27
  - Class: `SaveUnreadableError`.
28
28
  - Plugin instances: `timePlugin`, `lifecyclePlugin`, `modelPlugin`, `clockPlugin`, `flowPlugin`, `worldPlugin`, `rendererPlugin`, `inputPlugin`, `assetsPlugin`, `scenesPlugin`, `animPlugin`, `i18nPlugin`, `textPlugin`, `uiPlugin`, `audioPlugin`, `effectsPlugin`, `platformPlugin`; the list `screen` = `world`, `renderer`, `input`, `assets`, `scenes`, `anim`, `i18n`, `text`, `ui`. `effects`, `audio` and `platform` are opt-in: `plugins: [...screen, effectsPlugin, audioPlugin, platformPlugin]`, `platform` last.
29
29
  - Screen helpers from the root: `component`, `tag`, `resource`, `mut`, `system`, `projection`, `Layer`, `Order`, `Exiting`, `Tree`, `Now`, `Transform`, `Sprite`, `NineSlice`, `Shape`, `Parent`, `Display`, `Overlay`, `HudPart`, `sprite`, `Tappable`, `Pressable`, `Draggable`, `DropTarget`, `Swipeable`, `Traceable`, `Touchable`, `Held`, `Hovered`, `PointerOver`, `Pressed`, `Pointer`, `Traced`, `defineBundles`, `load`, `defineScene`.
@@ -54,7 +54,7 @@ Root exports:
54
54
  - Path: node names joined by `/` through sub-flows and slots. Examples: `"home"`, `"board/awaitIntent"`, `"afterBonus/thanks"`.
55
55
  - Edge target: a node name, `exit(...)` or `to(...)`. A payload must fit the input of the target. A node without input accepts any payload.
56
56
  - Sub-flow: a flow that declares `outcomes` is used as a node of another flow. `exit("name")` leaves it with that outcome.
57
- - Slot: `slot("name")` is a node with the single outcome `done`. Features contribute sub-flows to it. They run in `order`. `when(snapshot)` can skip one; it always reads the committed state, also what an earlier contribution of the same slot wrote. A contribution may hold a slot of its own. The slot ends with its own `done` edge after the last contribution.
57
+ - Slot: `slot("name")` is a node with the outcome `done`; `slot("name", { shop: type<{ tab: string }>() })` declares more outcomes (never `done`), and the flow that holds the slot gives each an edge, as for a node. Features contribute sub-flows to it. They run in `order`. `when(snapshot)` can skip one; it always reads the committed state, also what an earlier contribution of the same slot wrote. A contribution may hold a slot of its own. A contribution that exits `done` hands over to the next one, and the slot ends with its own `done` edge after the last. A contribution that exits an outcome the slot declares leaves the slot at once with its payload: the later contributions do not run, and the slot takes its own edge of that outcome (a journal entry and a `flow:edge`, like `done`). The next entry of a slot starts at its first contribution. `contribution(slot, { flow, order, when? })` builds the `contribute` entry and checks at compile time that every outcome of the flow is `done` or a declared outcome whose payload fits. `run()` refuses a contribution of a slot with declared outcomes that declares another outcome, and one slot name declared with different outcomes; it warns about named exits into a slot that declares none, and about two contributions without `when` in a slot that declares some. A slot never counts as a rest node: more than `max(64, 4 × n)` edges without one fail the node, `n` being the nodes of the graph expanded from the main flow (a sub-flow once per entry that uses it, a slot with all its contributions).
58
58
  - Feature: `defineFeature(name, description)` makes an ordinary plugin that registers into `flow.features` in `onInit`. Pass it in `createApp({ plugins: [...] })`. `feature.logicOnly` is the same plugin with the V1 keys only, for headless tests. `features: [episodesKitFeature, ep01Feature, ep02Feature]` declares sub-features, each in a folder of its own inside the parent's (`features/episodes/ep01/`); `flattenFeatures([homeFeature, episodesFeature])` gives the flat plugin list, a parent before its children, depth first, and a parent's `onInit` throws when a declared sub-feature is not composed. `flow.features.all()` gives `{ name, description, parent? }` per feature.
59
59
  - Modes: `"live"` and `"fast"`. In fast mode effects answer instantly, hints are dropped and only `runInFast` handlers run. All node logic runs for real.
60
60
 
@@ -92,13 +92,22 @@ function to<const TargetName extends string, Payload, Output>(
92
92
  target: TargetName,
93
93
  map: (payload: Payload) => Output // annotate the parameter type yourself
94
94
  ): Mapped<TargetName, Payload, Output>;
95
- function slot(name: string): SlotNode;
95
+ function slot<const Name extends string, const Tags extends OutcomeTags = Record<never, never>>(
96
+ name: Name,
97
+ outcomes?: Tags // { shop: type<{ tab: string }>() }; declaring done throws a TypeError
98
+ ): SlotNode<Tags, Name>; // outcomes: done, then the declared ones
99
+ function contribution<S extends SlotNode<OutcomeTags, string>, F extends AnyFlow>(
100
+ slot: S,
101
+ spec: { flow: F; order: number; when?: (snapshot: Snapshot) => boolean }
102
+ ): Readonly<Record<S["name"], ContributionSpec>>; // { [slot.name]: { flow, order, when? } }
103
+ // contribute: { ...contribution(episodeStep, { flow: storyFlow, order: 10 }) }
104
+ // compile error when storyFlow declares an outcome episodeStep does not declare, or its payload does not fit
96
105
 
97
106
  function defineFeature(name: string, description: FeatureDescription): FeaturePlugin;
98
107
  // FeatureDescription = {
99
108
  // nodes?: readonly AnyNode[];
100
109
  // flows?: readonly AnyFlow[];
101
- // contribute?: Record<string, { flow: AnyFlow; order: number; when?: (snapshot: Snapshot) => boolean }>;
110
+ // contribute?: Record<string, ContributionSpec>; // ContributionSpec = { flow: AnyFlow; order: number; when?: (snapshot: Snapshot) => boolean }
102
111
  // features?: readonly FeaturePlugin[]; // sub-features, composed right after this one
103
112
  // projections?, systems?, components?, scenes?, assets?; // V2 keys, read by world, scenes and assets
104
113
  // systemsRun?: never; // the 0.15.3 key, removed: a system runs while its query has an entity
@@ -525,6 +534,7 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
525
534
  - Overlays: `Overlay({ dim, dimColor, cover, hud, fade, hold })` on the root of an overlay (in `components`) keeps everything drawn before it as one picture, retaken in the frame anything in it changes, and with `cover: "opaque"` draws none of it once landed at alpha 1; `hold: true` (default `false`) holds the endless loops under it while it stands, as under a cover or a blurred picture (their phase follows time), so a dimmed sheet over a sharp scene draws no frame at rest; the engine draws the dim (`dim × level`, right under the root); the level follows the root's own enter and exit motion, `fade` (250 ms) only when it has none; level and landing are read through `app.renderer.sync.debug.overlays()`. A `backdrop: true` filter inside the kept range runs no pass while the picture stands (its look is in the picture) and is drawn again in the frame the overlay is gone. Types: `OverlayValue` (the component's value) and `HudPartValue` (a part of the HUD an overlay keeps shown or lets fade).
526
535
  - 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
536
  - 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.
537
+ - Scene switch look: from the start of a live switch (before its bundles and textures are awaited) the renderer draws a veil over the scene being left, above every layer, and, when the switch is not done `afterMs` after its start, a loader above it until the swap; then the veil fades out over the new scene. Config `scenes.leave` (default `{ cover: { color: 0x000000, alpha: 0.45, inMs: 150, outMs: 150 }, loader: { afterMs: 400 } }`) and a scene's `leave` (`defineScene("ward", { ..., leave: false })`) are merged field by field (the scene being left decides; `false` turns a part or the whole off); the loader is the engine's white ring, `loader: { texture: "ui.loader.ring" }` a game texture turning, or `loader: { projection: "loading" }` a projection mounted only once the loader is due, in a layer of its own the scene declares (no other projection of the scene may draw there); an abort or a throw drops both at once; fast walks, a world that is not live, headless apps and the first scene show nothing; types `SceneLeave`, `SceneCover`, `SceneLoader` (`Scenes`).
528
538
  - 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
539
  - `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
540
  - `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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.15.39",
3
+ "version": "0.15.41",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [