@moku-labs/game 0.13.2 → 0.13.3

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/dist/app/page.d.mts +1 -1
  2. package/dist/app/system.d.mts +3 -3
  3. package/dist/app.d.mts +2 -2
  4. package/dist/app.mjs +1 -1
  5. package/dist/assets.d.mts +1 -1
  6. package/dist/{capture-fpd83Mi5.mjs → capture-D7XvoHdH.mjs} +78 -1
  7. package/dist/{commands-BrgeMP5y.d.mts → commands-DRhJ_LIH.d.mts} +5 -5
  8. package/dist/control.d.mts +4 -4
  9. package/dist/control.mjs +1 -1
  10. package/dist/{define-ZmWbWxon.d.mts → define-Ckp70_GR.d.mts} +2 -2
  11. package/dist/{headless-BsNdJs9W.mjs → headless-BA6ffzqD.mjs} +30 -9
  12. package/dist/{headless-Dgxvp6Ym.d.mts → headless-CYgYzro0.d.mts} +1 -1
  13. package/dist/{index-CdbE_jGs.d.mts → index-CjuObHhF.d.mts} +4 -4
  14. package/dist/index.d.mts +5 -5
  15. package/dist/index.mjs +1 -1
  16. package/dist/inspect.d.mts +7 -7
  17. package/dist/jsx-dev-runtime.d.mts +1 -1
  18. package/dist/jsx-runtime.d.mts +1 -1
  19. package/dist/{memory-DJ2sPExv.d.mts → memory-DKo9rRWW.d.mts} +1 -1
  20. package/dist/{run-wYty_Lwo.mjs → run-BtyrJjmW.mjs} +130 -40
  21. package/dist/{runtime-6qakboTm.d.mts → runtime-iTxTcukZ.d.mts} +1 -1
  22. package/dist/{src-CltFFKtc.mjs → src-DlyYl_9X.mjs} +412 -122
  23. package/dist/testing.d.mts +4 -4
  24. package/dist/testing.mjs +2 -2
  25. package/dist/{types-C6y8EfEB.d.mts → types-BSMmnpFm.d.mts} +84 -24
  26. package/dist/{types-zRGJdFGX.d.mts → types-B_Bp2tWK.d.mts} +2 -2
  27. package/dist/{types-D57SntUt.d.mts → types-Bonifz5M.d.mts} +2 -2
  28. package/dist/{types-DGIkfZB7.d.mts → types-DkO3s9YX.d.mts} +4 -4
  29. package/dist/{types-CmuwAjkV.d.mts → types-Dm5Oh5bH.d.mts} +1 -1
  30. package/dist/{types-B3T2X6To.d.mts → types-dJMOXXL3.d.mts} +15 -11
  31. package/dist/visual.d.mts +6 -6
  32. package/dist/visual.mjs +1 -1
  33. package/llms.txt +4 -3
  34. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { Dt as AnyFlow, Ht as ClockSource, Nt as NodeTable, Wt as FakeClock, Xt as Json, ht as FeaturePlugin } from "./types-C6y8EfEB.mjs";
2
- import { t as createApp } from "./index-CdbE_jGs.mjs";
3
- import { n as saveOf, t as memory } from "./memory-DJ2sPExv.mjs";
4
- import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-Dgxvp6Ym.mjs";
1
+ import { Dt as AnyFlow, Ht as ClockSource, Nt as NodeTable, Wt as FakeClock, Xt as Json, ht as FeaturePlugin } from "./types-BSMmnpFm.mjs";
2
+ import { t as createApp } from "./index-CjuObHhF.mjs";
3
+ import { n as saveOf, t as memory } from "./memory-DKo9rRWW.mjs";
4
+ import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-CYgYzro0.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/plugins/clock/fake.d.ts
package/dist/testing.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { Kt as type, Ut as exit, t as createApp } from "./src-CltFFKtc.mjs";
1
+ import { Kt as type, Ut as exit, t as createApp } from "./src-DlyYl_9X.mjs";
2
2
  import { n as saveOf, t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { t as fakeClock } from "./fake-MpzwZhIZ.mjs";
4
- import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BsNdJs9W.mjs";
4
+ import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BA6ffzqD.mjs";
5
5
  //#region src/testing/isolate.ts
6
6
  const noPayload = null;
7
7
  /** Id of the harness main flow. */
@@ -836,7 +836,9 @@ type StoreState = {
836
836
  /**
837
837
  * store module API, `app.model.store`. Logic changes the trees only inside a transaction, and only
838
838
  * a rest point hands patches to the provider, so a kill between two rest nodes loses a whole
839
- * transition and never half of one.
839
+ * transition and never half of one. One restore differs: `flow.restore` of a bookmark taken at a
840
+ * transit node marks a rest point there, so the provider receives a document from between two rest
841
+ * nodes, and a kill right after it loads that state at the start node.
840
842
  *
841
843
  * @example
842
844
  * ```ts
@@ -2139,20 +2141,21 @@ type RouteStep = {
2139
2141
  };
2140
2142
  };
2141
2143
  /**
2142
- * A rest node plus a state that really existed there. Serialisable. `graph` is the hash of
2143
- * `describe()`.
2144
+ * A node plus a state that really existed there. Serialisable. `path` names a rest node, or a
2145
+ * transit node that waited at the gate for the answer of an effect (a popup); for the second the
2146
+ * state is the one committed when the node was entered. `graph` is the hash of `describe()`.
2144
2147
  *
2145
2148
  * @example
2146
2149
  * ```ts
2150
+ * // Taken while the info popup of the mini game waits for its answer, opened from "home".
2147
2151
  * const bookmark: Bookmark = {
2148
- * path: "board/awaitIntent", input: null,
2149
- * player: { coins: 7 }, session: { taps: 0 },
2150
- * rng: { seed: 42, streams: {} }, graph: "fe4d257a"
2152
+ * path: "info/show", input: null, player: { count: 0 }, session: { opened: 0 },
2153
+ * rng: { seed: 42, streams: {} }, graph: "0e0d2aa8", rest: { path: "home", input: null }
2151
2154
  * };
2152
2155
  * ```
2153
2156
  */
2154
2157
  type Bookmark = {
2155
- path: string;
2158
+ /** Path of a rest node, or of a transit node that waited at the gate. */path: string;
2156
2159
  input: Json;
2157
2160
  player: Json;
2158
2161
  session: Json;
@@ -2163,6 +2166,15 @@ type Bookmark = {
2163
2166
  * `game.restore`; `flow.restore` ignores it.
2164
2167
  */
2165
2168
  scene?: string;
2169
+ /**
2170
+ * The rest point before a transit `path`: the last rest node and its input. `restore` enters
2171
+ * it instead when the graph changed since the bookmark. Absent for a bookmark of a rest node,
2172
+ * and when no rest node came before the transit node.
2173
+ */
2174
+ rest?: {
2175
+ path: string;
2176
+ input: Json;
2177
+ };
2166
2178
  };
2167
2179
  /**
2168
2180
  * What `onEnter` callbacks learn about the node being entered.
@@ -2286,8 +2298,16 @@ type EnterCallback = (node: NodeInfo, ctx: {
2286
2298
  * game just runs: `walk.ts` and `restore` create it through `loopSeam` when they need it.
2287
2299
  */
2288
2300
  type LoopSeam = {
2289
- /** Sub-flow results a walk substitutes, by path. The loop takes each one once. */substitutions: Map<string, Result>; /** Called with the path every time the loop enters a rest node. */
2290
- rest: ((path: string) => void)[]; /** Called every time the loop opens the gate of a rest node. The walk waits on it. */
2301
+ /** Sub-flow results a walk substitutes, by path. The loop takes each one once. */substitutions: Map<string, Result>;
2302
+ /**
2303
+ * Called with the path every time the loop enters a rest node, and once when it enters the
2304
+ * node of a bookmark, which may be a transit node. `restore` resolves on the second.
2305
+ */
2306
+ rest: ((path: string) => void)[];
2307
+ /**
2308
+ * Called every time the loop opens a gate: the wait of a rest node, or an effect with answers
2309
+ * inside any node. The walk waits on it, and so does `restore` after it entered a transit node.
2310
+ */
2291
2311
  gateOpen: (() => void)[]; /** The bookmark the loop enters at the next turn. */
2292
2312
  restoring: Bookmark | undefined;
2293
2313
  };
@@ -2298,7 +2318,12 @@ type RunnerState = {
2298
2318
  /** Flush started by a background pause. `onStop` awaits it. */flushing: Promise<void> | undefined; /** Every flow of the graph by id, collected at `run()`. */
2299
2319
  flows: Map<string, AnyFlow>;
2300
2320
  enterCallbacks: Record<Stage, EnterCallback[]>; /** The position: one frame per nesting level. */
2301
- stack: Frame[]; /** The stack of the last rest node: the rollback target. */
2321
+ stack: Frame[];
2322
+ /**
2323
+ * The frames a failed node is retried from, paired with the state a rollback returns to: the
2324
+ * last rest node, or the node the graph started at or a restore entered. The last two may be
2325
+ * transit nodes, so `bookmark()` writes its `rest` only when these frames name a rest node.
2326
+ */
2302
2327
  restFrame: Frame[] | undefined; /** Inside a slot: the contribution the loop just finished. The slot continues after it. */
2303
2328
  slotAfter: string | undefined; /** Edges since the last checkpoint. */
2304
2329
  journal: JournalEntry[];
@@ -2373,7 +2398,10 @@ type RunnerApi = {
2373
2398
  /**
2374
2399
  * Walks a route in fast mode through the running loop: it answers the gate at each step's `at`
2375
2400
  * and substitutes the result of every sub-flow node the route skips. A `from` bookmark is
2376
- * entered through the same check as `restore`. The mode of the caller is put back afterwards.
2401
+ * entered through the same check as `restore`, with the same fallback. A rest node is entered
2402
+ * before the switch to fast mode: its stages see the mode of the caller. A transit node is
2403
+ * entered after the switch and without the wait `restore` adds for it: its popup is not shown,
2404
+ * the route answers it. The mode of the caller is put back afterwards.
2377
2405
  *
2378
2406
  * @param route - The player's answers and substituted sub-flow results, in order.
2379
2407
  * @param options - Walk options.
@@ -2395,32 +2423,57 @@ type RunnerApi = {
2395
2423
  from?: Bookmark;
2396
2424
  }): Promise<FlowState>;
2397
2425
  /**
2398
- * Makes a bookmark of the current rest point: the rest node plus the committed state.
2426
+ * Makes a bookmark of where the graph stands: the last rest node plus the committed state.
2427
+ * While a transit node waits at the gate for the answer of an effect (a `popup`, or any
2428
+ * descriptor with `answers`), the bookmark names that node instead. Its state is the one
2429
+ * committed when the node was entered: what the node wrote before the wait is not in it, the
2430
+ * node writes it again when it is restored. `rest` then names the rest point before the node.
2431
+ * While a transit node awaits anything else (an animation, a request) the bookmark still names
2432
+ * the last rest node.
2399
2433
  *
2400
2434
  * @returns The bookmark, ready for JSON.
2401
2435
  * @throws {Error} When the graph has no position yet.
2402
2436
  * @example
2403
2437
  * ```ts
2404
- * // A devtools button keeps the position while the board rests.
2405
- * const bookmark = app.flow.bookmark();
2406
- * bookmark.path; // "board/awaitIntent"
2407
- * JSON.stringify(bookmark); // plain data: path, input, player, session, rng and the graph hash
2438
+ * // A devtools button keeps the position. The board rests: the bookmark names its rest node.
2439
+ * const atBoard = app.flow.bookmark(); // plain data, ready for JSON.stringify
2440
+ * atBoard.path; // "board/awaitIntent"
2441
+ * // The mini game with its info popup open: the bookmark names the node that waits.
2442
+ * const atPopup = app.flow.bookmark();
2443
+ * atPopup.path; // "info/show"
2444
+ * atPopup.rest; // { path: "home", input: null }: the rest point before it
2408
2445
  * ```
2409
2446
  */
2410
2447
  bookmark(): Bookmark;
2411
2448
  /**
2412
- * Replaces the state with the bookmark's and enters its node. A checkpoint is always
2413
- * accepted; any other rest node only while the graph is unchanged.
2449
+ * Replaces the state with the bookmark's and enters its node. A rest checkpoint is always
2450
+ * accepted; any other node only while the graph is unchanged. A transit node is never taken
2451
+ * for a checkpoint: when the graph changed, such a bookmark is entered at its `rest` point
2452
+ * instead, if that is a checkpoint, and the log gets `flow:restore-fell-back`. A renamed or
2453
+ * removed node is a changed graph too: a bookmark with `rest` whose path names no node takes
2454
+ * the same fallback.
2455
+ *
2456
+ * A rest node: the promise resolves when the node is entered, before its scene stage ran. A
2457
+ * transit node runs again from its first line, live, with the bookmark's input, so its effects
2458
+ * before the gate play again; the promise resolves when its gate is open, or at the rest node
2459
+ * or the end of the loop it reaches without one. A node that does none of these keeps the
2460
+ * promise pending. An open gate is not a drawn popup: the popup shows on the next frame. And
2461
+ * `setMode` still throws there, since the graph waits inside a transit node.
2414
2462
  *
2415
2463
  * @param bookmark - The bookmark to enter.
2416
- * @returns A promise that resolves once the graph rests at the bookmark's node.
2417
- * @throws {Error} When the bookmark names no rest node of this graph, when the graph changed
2418
- * since a bookmark of a plain rest node, and before `run()`.
2464
+ * @returns A promise that resolves once a rest node is entered, or a transit node waits at
2465
+ * its gate.
2466
+ * @throws {Error} When the bookmark names no node of this graph and has no `rest`, when the
2467
+ * graph changed since the bookmark and neither it nor its `rest` point is a rest checkpoint,
2468
+ * and before `run()`.
2419
2469
  * @example
2420
2470
  * ```ts
2421
2471
  * // The next session opens where the last one stopped: state and position come back together.
2422
- * await app.flow.restore(bookmark);
2472
+ * await app.flow.restore(atBoard);
2423
2473
  * app.flow.state().path; // "board/awaitIntent"
2474
+ * // A bookmark the mini game took at its info popup: the popup waits again when this resolves.
2475
+ * await app.flow.restore(atPopup);
2476
+ * app.flow.state().pending.gate; // ["ok", "close"], and state().path is "info/show"
2424
2477
  * ```
2425
2478
  */
2426
2479
  restore(bookmark: Bookmark): Promise<void>;
@@ -2468,7 +2521,9 @@ type RunnerApi = {
2468
2521
  */
2469
2522
  history(): readonly JournalEntry[];
2470
2523
  /**
2471
- * Switches between live and fast mode. Legal before `run()` and while the graph rests.
2524
+ * Switches between live and fast mode. Legal before `run()` and while the graph rests. A
2525
+ * transit node that waits at the gate for a popup does not rest: it throws there too, also
2526
+ * right after a `restore` into such a node.
2472
2527
  *
2473
2528
  * @param mode - `"live"` or `"fast"`.
2474
2529
  * @throws {Error} When a transit node is running.
@@ -2773,7 +2828,12 @@ type Events$3 = {
2773
2828
  };
2774
2829
  index: number;
2775
2830
  now: number;
2776
- }; /** The graph reached a rest node. */
2831
+ };
2832
+ /**
2833
+ * The graph reached a rest node, or entered the node of a bookmark. The second also happens
2834
+ * for a transit node that waited at the gate: `path` is then no rest node and `checkpoint` is
2835
+ * false.
2836
+ */
2777
2837
  "flow:rest": {
2778
2838
  path: string;
2779
2839
  checkpoint: boolean;
@@ -1,5 +1,5 @@
1
- import { A as Api$1, B as ComponentType, D as FilterSlot, H as Entity, O as Point, R as ComponentHandle, Sn as Require, _ as PixiFilter, b as PixiTexture, dt as Api, h as Api$2, s as Events, t as Api$3, v as PixiModule, xn as Events$1, y as PixiSprite } from "./types-C6y8EfEB.mjs";
2
- import { x as AnimApi } from "./types-B3T2X6To.mjs";
1
+ import { A as Api$1, B as ComponentType, D as FilterSlot, H as Entity, O as Point, R as ComponentHandle, Sn as Require, _ as PixiFilter, b as PixiTexture, dt as Api, h as Api$2, s as Events, t as Api$3, v as PixiModule, xn as Events$1, y as PixiSprite } from "./types-BSMmnpFm.mjs";
2
+ import { x as AnimApi } from "./types-dJMOXXL3.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
 
@@ -1,5 +1,5 @@
1
- import { Xt as Json, an as Snapshot } from "./types-C6y8EfEB.mjs";
2
- import { t as HeadlessApp } from "./headless-Dgxvp6Ym.mjs";
1
+ import { Xt as Json, an as Snapshot } from "./types-BSMmnpFm.mjs";
2
+ import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -1,7 +1,7 @@
1
- import { Dt as AnyFlow, Ht as ClockSource, Jt as Config, Wt as FakeClock, Xt as Json, a as Config$3, g as Config$2, ht as FeaturePlugin, n as AssetsIo, pt as Config$1, tn as PlayerStateProvider, u as Manifest, v as PixiModule } from "./types-C6y8EfEB.mjs";
2
- import { o as AudioContextLike, s as Config$4 } from "./types-B3T2X6To.mjs";
3
- import { _t as audioPlugin, ct as PlatformProvider, ht as effectsPlugin, i as screen, ot as platformPlugin, t as createApp } from "./index-CdbE_jGs.mjs";
4
- import { t as memory } from "./memory-DJ2sPExv.mjs";
1
+ import { Dt as AnyFlow, Ht as ClockSource, Jt as Config, Wt as FakeClock, Xt as Json, a as Config$3, g as Config$2, ht as FeaturePlugin, n as AssetsIo, pt as Config$1, tn as PlayerStateProvider, u as Manifest, v as PixiModule } from "./types-BSMmnpFm.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-dJMOXXL3.mjs";
3
+ import { _t as audioPlugin, ct as PlatformProvider, ht as effectsPlugin, i as screen, ot as platformPlugin, t as createApp } from "./index-CjuObHhF.mjs";
4
+ import { t as memory } from "./memory-DKo9rRWW.mjs";
5
5
  import { AnyPluginInstance } from "@moku-labs/core";
6
6
 
7
7
  //#region src/app/types.d.ts
@@ -1,4 +1,4 @@
1
- import { A as Api$2, B as ComponentType, F as AnyComponent, H as Entity, I as AnyComponentType, L as AnyComponentValue, Lt as Descriptor, O as Point$2, Q as ChangeHook, Sn as Require, V as ComponentValue, Vt as Api$5, Xt as Json, at as NumericFields, dt as Api$1, h as Api$3, hn as Api, it as MotionHandle, k as ViewportSize, ot as ProjectionMotion, rt as Motion, s as Events$1, t as Api$4, ut as ViewHandle, zt as Hint } from "./types-C6y8EfEB.mjs";
1
+ import { A as Api$2, B as ComponentType, F as AnyComponent, H as Entity, I as AnyComponentType, L as AnyComponentValue, Lt as Descriptor, O as Point$2, Q as ChangeHook, Sn as Require, V as ComponentValue, Vt as Api$5, Xt as Json, at as NumericFields, dt as Api$1, h as Api$3, hn as Api, it as MotionHandle, k as ViewportSize, ot as ProjectionMotion, rt as Motion, s as Events$1, t as Api$4, ut as ViewHandle, zt as Hint } from "./types-BSMmnpFm.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
 
@@ -1,5 +1,5 @@
1
- import { A as Api$2, B as ComponentType, F as AnyComponent, H as Entity, It as TypeTag, L as AnyComponentValue, Lt as Descriptor, R as ComponentHandle, Sn as Require, Xt as Json, Yt as Events$2, at as NumericFields, ct as TrackOptions, dn as Events$3, dt as Api$1, et as Ease, h as Api$5, hn as Api, it as MotionHandle, lt as TrackSegment, nt as LayerSpec, q as Owner, qt as Api$4, s as Events$4, t as Api$3, tt as LayerSort, xn as Events$5 } from "./types-C6y8EfEB.mjs";
2
- import { A as InputApi, c as UiNode, i as Finding, n as ElementChange, o as JsxState, q as I18nApi, r as ElementMotion, rt as StylesState, x as TextApi } from "./types-CmuwAjkV.mjs";
1
+ import { A as Api$2, B as ComponentType, F as AnyComponent, H as Entity, It as TypeTag, L as AnyComponentValue, Lt as Descriptor, R as ComponentHandle, Sn as Require, Xt as Json, Yt as Events$2, at as NumericFields, ct as TrackOptions, dn as Events$3, dt as Api$1, et as Ease, h as Api$5, hn as Api, it as MotionHandle, lt as TrackSegment, nt as LayerSpec, q as Owner, qt as Api$4, s as Events$4, t as Api$3, tt as LayerSort, xn as Events$5 } from "./types-BSMmnpFm.mjs";
2
+ import { A as InputApi, c as UiNode, i as Finding, n as ElementChange, o as JsxState, q as I18nApi, r as ElementMotion, rt as StylesState, x as TextApi } from "./types-Dm5Oh5bH.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
  import { PluginCtx } from "@moku-labs/core";
5
5
  import { Node, Yoga } from "yoga-layout/load";
@@ -1297,9 +1297,10 @@ type State$2 = {
1297
1297
  /** Scene id to its declaration, filled in `onStart` from `flow.features`. */scenes: Map<string, SceneDefinition>; /** Id of the scene that is mounted now. */
1298
1298
  current: string | undefined;
1299
1299
  /**
1300
- * The scene the next rest node without its own `scene` mounts: the one a transit node named in
1301
- * fast mode, or the one the restore door expected with `expect`. A rest node clears it; an over
1302
- * rest node takes it only while nothing is mounted.
1300
+ * The scene the next node without its own `scene` mounts, at a rest node or at a transit node
1301
+ * in live mode: the one a transit node named in fast mode, or the one the restore door expected
1302
+ * with `expect`. A rest node clears it; a transit node clears it only when it took it; an over
1303
+ * node takes it only while nothing is mounted.
1303
1304
  */
1304
1305
  pending: string | undefined;
1305
1306
  owner: SceneOwner; /** The remover of the `onEnter` callback: set in `onStart`, run in `onStop`. */
@@ -1348,19 +1349,22 @@ type ScenesApi = {
1348
1349
  */
1349
1350
  current(): string | undefined;
1350
1351
  /**
1351
- * Names the scene the next rest node without its own `scene` mounts. The restore door calls it
1352
- * with the scene of a bookmark, so a bookmark at a popup comes back with the scene under it.
1353
- * An over rest node takes the expected scene only while nothing is mounted; it never switches a
1352
+ * Names the scene the next node without its own `scene` mounts. A rest node reads it, and so
1353
+ * does a transit node in live mode; a transit node of a fast walk leaves it for the rest point.
1354
+ * The restore door calls it with the scene of a bookmark, so a bookmark at a popup comes back
1355
+ * with the scene under it, also when the popup is a transit node that waits for its answer:
1356
+ * the scene of such a popup is mounted before `flow.restore` resolves.
1357
+ * An over node takes the expected scene only while nothing is mounted; it never switches a
1354
1358
  * mounted one.
1355
1359
  *
1356
1360
  * @param id - Id of a scene a feature declared.
1357
1361
  * @throws {Error} For an id no feature declared, also before `onStart` filled the registry.
1358
1362
  * @example
1359
1363
  * ```ts
1360
- * // A fresh page restores a bookmark taken at the Settings popup over Home.
1364
+ * // A fresh page restores a bookmark taken at the Settings popup over Home, a transit node
1365
+ * // that waits for its answer.
1361
1366
  * app.scenes.expect("home");
1362
- * await app.flow.restore(bookmark);
1363
- * await app.flow.walk([]); // the scene stage runs after restore resolves
1367
+ * await app.flow.restore(bookmark); // resolves when the popup's gate is open
1364
1368
  * app.scenes.current(); // "home"
1365
1369
  * ```
1366
1370
  */
package/dist/visual.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import { A as Api$1, h as Api, ln as Api$3, qt as Api$2 } from "./types-C6y8EfEB.mjs";
2
- import { r as UiApi, x as AnimApi } from "./types-B3T2X6To.mjs";
3
- import { A as InputApi } from "./types-CmuwAjkV.mjs";
4
- import { r as Repro } from "./headless-Dgxvp6Ym.mjs";
5
- import { a as InputOf, r as ControlApp } from "./types-D57SntUt.mjs";
6
- import { t as commands } from "./commands-BrgeMP5y.mjs";
1
+ import { A as Api$1, h as Api, ln as Api$3, qt as Api$2 } from "./types-BSMmnpFm.mjs";
2
+ import { r as UiApi, x as AnimApi } from "./types-dJMOXXL3.mjs";
3
+ import { A as InputApi } from "./types-Dm5Oh5bH.mjs";
4
+ import { r as Repro } from "./headless-CYgYzro0.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-Bonifz5M.mjs";
6
+ import { t as commands } from "./commands-DRhJ_LIH.mjs";
7
7
 
8
8
  //#region src/visual/types.d.ts
9
9
  /** The base commands of the `/control` door, keyed by their short names. */
package/dist/visual.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as read, t as sources } from "./sources-Duzj6lhV.mjs";
2
- import { n as commands, t as run } from "./run-wYty_Lwo.mjs";
2
+ import { n as commands, t as run } from "./run-BtyrJjmW.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
@@ -474,14 +474,15 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
474
474
  - `fx.handle(kind, handler, options?: { runInFast?: boolean }): () => void` registers the single handler of one effect kind. `fx.dispatch(descriptor)` is fire-and-forget. An awaited effect without a handler resolves `undefined` at once.
475
475
  - `features.register(name, description)`, `features.all()`, `features.contributions(slotName)`.
476
476
  - `FlowState` is `{ running, path, stack, pending: { fx?, gate? }, mode }`.
477
- - `Bookmark` is `{ path, input, player, session, rng, graph, scene? }`. Serialisable. `scene` is written by the `game.bookmark` door and read by `game.restore`; `flow.restore` ignores it. `restore` needs a running graph and the path of a rest node. A checkpoint is always accepted. Any other rest node is accepted only when the graph did not change since the bookmark.
477
+ - `Bookmark` is `{ path, input, player, session, rng, graph, scene?, rest? }`. Serialisable. `scene` is written by the `game.bookmark` door and read by `game.restore`; `flow.restore` ignores it. `path` names a rest node, or the transit node that waits at the gate for an effect that takes answers (a `popup`, or a hand-written descriptor with `answers`); `rest: { path, input }` then names the rest point before it (left out when the graph started at that node or a restore entered it), and the state is the one committed when that node was entered. `restore` needs a running graph. A rest checkpoint is always accepted. Any other node is accepted only when the graph did not change since the bookmark; with another graph a transit bookmark is entered at `rest` instead when that is a checkpoint, and the log gets `flow:restore-fell-back`.
478
+ - `restore` of a rest node resolves when the node is entered. `restore` of a transit node runs it again from its start, so its effects before the gate play again, and resolves once its gate is open. The open gate is not the draw: the popup shows on the next frame.
478
479
  - Events: `flow:edge` `{ flow, node, outcome, payload, next, patches: { doc, session }, index, now }` after the commit of an edge. `flow:rest` `{ path, checkpoint }`. `flow:error` `{ path, error, rolledBackTo, retry }`.
479
480
  - Config: `mainFlow` default `undefined` and required before `run()`, `safeNode: string | undefined` default `undefined` which means the main flow's `start`, `retries` default `1`, `settleTimeoutMs` default `2000`, `journalLimit` default `500`.
480
481
 
481
482
  ### Screen plugins (V2, built)
482
483
 
483
484
  `world`, `renderer`, `input`, `assets`, `scenes`. Each has a README at `src/plugins/<name>/README.md`; the root README lists their APIs, events and config. Rendering: WebGPU preferred, with Pixi's WebGL fallback.
484
- - `app.scenes`: `current()` the mounted scene id, `expect(id)` the scene the next rest node without its own `scene` mounts (the restore door calls it; an unknown id throws).
485
+ - `app.scenes`: `current()` the mounted scene id, `expect(id)` the scene the next node without its own `scene` mounts, a rest node or a transit node in live mode (the restore door calls it; an unknown id throws).
485
486
  - `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.
486
487
  - `app.world.projection.replace(spec)` swaps the spec of a registered projection; a mounted one runs its new `view` on the next frame. An unknown name throws. The caller is the dev hot swap.
487
488
 
@@ -592,7 +593,7 @@ Two subpaths, neither on the root (V4). An editor, MCP tools and e2e scripts rea
592
593
  - `@moku-labs/game/inspect`: `read(app, source, input?)` reads once; `watch(app, source, input, fn)` reads on the `signals` phase when the source changes and returns the stop function; `defineSource`; the frozen catalogue `sources`; the types `Source`, `InputSchema`, `InputOf`. Every source only reads, so the door is safe in a production build. Keys of `sources`: `graph`, `position`, `history`, `tainted`, `cheats`, `model`, `entities`, `projections`, `ui`, `locate`, `render`, `sounds`, `audioMuted`, `effects`, `assets`, `log`, `explain`, `diff`, `schema`, `at`; their ids are `game.<key>`. `locate` takes `{ key }` (a ui element) or `{ target }` (a view by projection key) and answers its box in CSS px, the numbers Playwright clicks. `at` takes `{ x, y }` in CSS px and lists the views under that point, topmost first. `explain` takes `{ entity }` and answers that entity in full, `undefined` for a stale id. `diff` takes `{ from, to }` frames and answers what changed per entity; it throws in a production build and for a frame outside the last 120. `schema` lists every component and tag type the world met, with the JSON kind of each field. `audioMuted` answers whether the master bus is muted. `sounds` throws `[game] The source game.sounds needs audioPlugin.`, `audioMuted` throws `[game] The source game.audioMuted needs audioPlugin.` and `effects` throws `[game] The source game.effects needs effectsPlugin.` when that plugin is not installed; the editor lists such a source as not installed.
593
594
  - `@moku-labs/game/control`: `run(app, command, input?)` resolves `{ value, state }`, `state` being `{ path, frame, tainted }`; `defineCommand`; `controlRefused`; the frozen catalogue `commands`; the types `Command`, `Ran`. Keys of `commands`: `answer`, `tap`, `drag`, `key`, `fill`, `walk`, `bookmark`, `restore`, `step`, `pause`, `resume`, `capture`, `debug`, `reducedMotion`, `timeScale`, `mute`, `trace`. `capture` resolves `{ png }`, and `{ png, legend }` with `legend: true`; `undefined` while the renderer is inert. `timeScale` takes `{ scale }`; 0 freezes game time while the frames keep running. `mute` takes `{ muted }`, mutes the master bus and answers the flag. Without `audioPlugin` it throws `[game] The command game.mute needs audioPlugin.` `trace` takes `{ path }`, a list of views by projection key, and answers whether the gate took the answer.
594
595
  - Commands run in dev builds only. Only `__MOKU_GAME_DEV__ === true` turns the dev build on: `moku-game dev` defines it `true` and sets the global in `.moku/dev.ts`, `moku-game build` defines it `false`; a game without the shell uses a bundler `define`; a test uses `vi.stubGlobal("__MOKU_GAME_DEV__", true)`. Otherwise `run` throws `[game] Control commands run in dev builds only.`
595
- - A command's effect is `read`, `route`, `cosmetic`, `cheat` or `raw`. A `cheat` or `raw` command taints the session and is journaled; read it with `sources.tainted` and `sources.cheats`. Of the base commands only `restore` is `raw`. `bookmark` answers `flow.bookmark()` plus `scene`, the mounted scene; `restore` calls `scenes.expect(scene)` before `flow.restore`, so a bookmark at a popup restores the scene under it.
596
+ - A command's effect is `read`, `route`, `cosmetic`, `cheat` or `raw`. A `cheat` or `raw` command taints the session and is journaled; read it with `sources.tainted` and `sources.cheats`. Of the base commands only `restore` is `raw`. `bookmark` answers `flow.bookmark()` plus `scene`, the mounted scene; `restore` calls `scenes.expect(scene)` before `flow.restore`, so a bookmark at a popup restores the scene under it. A bookmark taken while an effect that takes answers waits names the waiting transit node (`info/show` in the mini game) and carries `rest`; `restore` of it answers once that node's gate is open, with `pending.gate` in its value, and the popup shows on the next frame.
596
597
  - A game's own: `defineSource({ id, title, input, changes, read })` with `changes` `"frame"`, `"commit"` or `"edge"`, and `defineCommand({ id, title, input, effect, run })`. The id is camelCase words joined by dots, at least two: `dice.rolls`. An input schema maps field names to `"string"`, `"number"`, `"boolean"` or `"json"`; a trailing `?` makes one optional. A command body starts with `if (typeof __MOKU_GAME_DEV__ === "undefined" || !__MOKU_GAME_DEV__) throw controlRefused();`. Keep them in `.dev` modules that only the dev page and the tests import: the editor's dev page (`preparePage` with agents) imports every `**/*.dev.ts`, `moku-game build` none.
597
598
 
598
599
  ## Project index
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [