@moku-labs/game 0.13.3 → 0.14.1

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 (33) hide show
  1. package/README.md +2 -2
  2. package/dist/app/page.d.mts +1 -1
  3. package/dist/app/system.d.mts +3 -3
  4. package/dist/app.d.mts +2 -2
  5. package/dist/app.mjs +1 -1
  6. package/dist/assets.d.mts +1 -1
  7. package/dist/{commands-DRhJ_LIH.d.mts → commands-Cpn2ga7s.d.mts} +5 -5
  8. package/dist/control.d.mts +4 -4
  9. package/dist/control.mjs +1 -1
  10. package/dist/{define-Ckp70_GR.d.mts → define-Bwp8CNkW.d.mts} +2 -2
  11. package/dist/{headless-CYgYzro0.d.mts → headless-IfD6_JMH.d.mts} +1 -1
  12. package/dist/{index-CjuObHhF.d.mts → index-yUO4Dx2C.d.mts} +4 -4
  13. package/dist/index.d.mts +5 -5
  14. package/dist/index.mjs +1 -1
  15. package/dist/inspect.d.mts +7 -7
  16. package/dist/jsx-dev-runtime.d.mts +1 -1
  17. package/dist/jsx-runtime.d.mts +1 -1
  18. package/dist/{memory-DKo9rRWW.d.mts → memory-D1IKoFr1.d.mts} +1 -1
  19. package/dist/{run-BtyrJjmW.mjs → run-Bf2hKyjZ.mjs} +21 -3
  20. package/dist/{runtime-iTxTcukZ.d.mts → runtime-CWY6co6m.d.mts} +1 -1
  21. package/dist/{src-DlyYl_9X.mjs → src-Dgmdzl8R.mjs} +45 -19
  22. package/dist/testing.d.mts +4 -4
  23. package/dist/testing.mjs +1 -1
  24. package/dist/{types-DkO3s9YX.d.mts → types-B98iRBSG.d.mts} +4 -4
  25. package/dist/{types-Dm5Oh5bH.d.mts → types-BArxen_M.d.mts} +1 -1
  26. package/dist/{types-BSMmnpFm.d.mts → types-BWDESfx2.d.mts} +11 -2
  27. package/dist/{types-B_Bp2tWK.d.mts → types-D7MGz4c-.d.mts} +2 -2
  28. package/dist/{types-Bonifz5M.d.mts → types-InZGjvkI.d.mts} +2 -2
  29. package/dist/{types-dJMOXXL3.d.mts → types-oQoQEj-y.d.mts} +8 -5
  30. package/dist/visual.d.mts +6 -6
  31. package/dist/visual.mjs +1 -1
  32. package/llms.txt +4 -4
  33. package/package.json +3 -3
package/README.md CHANGED
@@ -9,7 +9,7 @@ You write small nodes and edge tables. The engine runs them, saves on the edges
9
9
  [![npm](https://img.shields.io/npm/v/@moku-labs/game?logo=npm&color=cb3837&label=npm)](https://www.npmjs.com/package/@moku-labs/game)
10
10
  [![types](https://img.shields.io/badge/types-included-3178c6?logo=typescript&logoColor=white)](#requirements)
11
11
  [![node](https://img.shields.io/badge/node-%3E%3D24-339933?logo=node.js&logoColor=white)](#requirements)
12
- [![bun](https://img.shields.io/badge/bun-%3E%3D1.3.14-2da44e?logo=bun&logoColor=white)](#requirements)
12
+ [![bun](https://img.shields.io/badge/bun-%3E%3D1.4.2-2da44e?logo=bun&logoColor=white)](#requirements)
13
13
  [![for @moku-labs/core](https://img.shields.io/badge/for-%40moku--labs%2Fcore-0b7285)](https://github.com/moku-labs/core)
14
14
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)
15
15
 
@@ -330,7 +330,7 @@ A save of a logic file reloads the page. What swaps and what reloads: [docs/hot-
330
330
 
331
331
  ## Requirements
332
332
 
333
- - **Node `>= 24`** and **Bun `>= 1.3.14`**. Use `bun` only.
333
+ - **Node `>= 24`** and **Bun `>= 1.4.2`**. Use `bun` only.
334
334
  - **TypeScript** in strict mode, with `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`.
335
335
  - **`pixi.js` `^8.0.0`** as a peer dependency. `playwright-core` for visual tests, `sharp` for the asset pack, `typescript` for the project index, `@moku-labs/system` for the system shell and `@moku-labs/native` for the native build are optional peers. `yoga-layout` is loaded lazily by `ui`. The string compiler uses `@formatjs/icu-messageformat-parser` at build time only.
336
336
  - **[`@moku-labs/core`](https://github.com/moku-labs/core)** is the kernel. **[`@moku-labs/common`](https://github.com/moku-labs/common)** brings `log` and `env`.
@@ -1,4 +1,4 @@
1
- import { m as StartedPage, r as GameConfig, t as AnyGameApp, u as PageOptions } from "../types-DkO3s9YX.mjs";
1
+ import { m as StartedPage, r as GameConfig, t as AnyGameApp, u as PageOptions } from "../types-B98iRBSG.mjs";
2
2
 
3
3
  //#region src/app/page.d.ts
4
4
  /**
@@ -1,6 +1,6 @@
1
- import { tn as PlayerStateProvider } from "../types-BSMmnpFm.mjs";
2
- import { ct as PlatformProvider } from "../index-CjuObHhF.mjs";
3
- import { _ as SystemShellFactory, g as SystemName, h as SystemModules } from "../types-DkO3s9YX.mjs";
1
+ import { tn as PlayerStateProvider } from "../types-BWDESfx2.mjs";
2
+ import { ct as PlatformProvider } from "../index-yUO4Dx2C.mjs";
3
+ import { _ as SystemShellFactory, g as SystemName, h as SystemModules } from "../types-B98iRBSG.mjs";
4
4
  import { Back, Haptics, KeepAwake, Lifecycle, Store } from "@moku-labs/system";
5
5
 
6
6
  //#region src/app/system.d.ts
package/dist/app.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { Xt as Json } from "./types-BSMmnpFm.mjs";
2
- import { a as GameHandle, c as MemoryProvider, d as SaveKind, f as Scenario, g as SystemName, i as GameDefinition, l as PageAgent, n as GameApp, o as GamePluginConfigs, p as ScreenSeams, r as GameConfig, s as HeadlessSeams } from "./types-DkO3s9YX.mjs";
1
+ import { Xt as Json } from "./types-BWDESfx2.mjs";
2
+ import { a as GameHandle, c as MemoryProvider, d as SaveKind, f as Scenario, g as SystemName, i as GameDefinition, l as PageAgent, n as GameApp, o as GamePluginConfigs, p as ScreenSeams, r as GameConfig, s as HeadlessSeams } from "./types-B98iRBSG.mjs";
3
3
  import { AnyPluginInstance } from "@moku-labs/core";
4
4
 
5
5
  //#region src/app/define.d.ts
package/dist/app.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Ct as effectsPlugin, H as platformPlugin, Nt as audioPlugin, i as screen, t as createApp } from "./src-DlyYl_9X.mjs";
1
+ import { Ct as effectsPlugin, H as platformPlugin, Nt as audioPlugin, i as screen, t as createApp } from "./src-Dgmdzl8R.mjs";
2
2
  import { t as memory } from "./memory-CWlqhS7l.mjs";
3
3
  import { t as fakeClock } from "./fake-MpzwZhIZ.mjs";
4
4
  //#region src/app/define.ts
package/dist/assets.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { u as Manifest } from "./types-BSMmnpFm.mjs";
1
+ import { u as Manifest } from "./types-BWDESfx2.mjs";
2
2
 
3
3
  //#region src/plugins/assets/scan/cli.d.ts
4
4
  /**
@@ -1,8 +1,8 @@
1
- import { A as Api$2, E as DebugSwitches, Ot as Bookmark, h as Api$1, jt as FlowState, ln as Api, vn as Time, w as Captured } from "./types-BSMmnpFm.mjs";
2
- import { a as AudioApi, r as UiApi, v as ScenesApi, x as AnimApi } from "./types-dJMOXXL3.mjs";
3
- import { A as InputApi } from "./types-Dm5Oh5bH.mjs";
4
- import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
5
- import { n as Command, r as ControlApp } from "./types-Bonifz5M.mjs";
1
+ import { A as Api$2, E as DebugSwitches, Ot as Bookmark, h as Api$1, jt as FlowState, ln as Api, vn as Time, w as Captured } from "./types-BWDESfx2.mjs";
2
+ import { a as AudioApi, r as UiApi, v as ScenesApi, x as AnimApi } from "./types-oQoQEj-y.mjs";
3
+ import { A as InputApi } from "./types-BArxen_M.mjs";
4
+ import { t as HeadlessApp } from "./headless-IfD6_JMH.mjs";
5
+ import { n as Command, r as ControlApp } from "./types-InZGjvkI.mjs";
6
6
 
7
7
  //#region src/plugins/flow/doors/commands.d.ts
8
8
  /**
@@ -1,7 +1,7 @@
1
- import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
2
- import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-Bonifz5M.mjs";
3
- import { t as commands } from "./commands-DRhJ_LIH.mjs";
4
- import { t as defineCommand } from "./define-Ckp70_GR.mjs";
1
+ import { t as HeadlessApp } from "./headless-IfD6_JMH.mjs";
2
+ import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-InZGjvkI.mjs";
3
+ import { t as commands } from "./commands-Cpn2ga7s.mjs";
4
+ import { t as defineCommand } from "./define-Bwp8CNkW.mjs";
5
5
 
6
6
  //#region src/plugins/flow/doors/dev.d.ts
7
7
  /**
package/dist/control.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import { t as controlRefused } from "./dev-Cc50Dk7H.mjs";
2
2
  import { s as defineCommand } from "./session-DgukhR_7.mjs";
3
- import { n as commands, t as run } from "./run-BtyrJjmW.mjs";
3
+ import { n as commands, t as run } from "./run-Bf2hKyjZ.mjs";
4
4
  export { commands, controlRefused, defineCommand, run };
@@ -1,5 +1,5 @@
1
- import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
2
- import { c as Source, n as Command, o as InputSchema } from "./types-Bonifz5M.mjs";
1
+ import { t as HeadlessApp } from "./headless-IfD6_JMH.mjs";
2
+ import { c as Source, n as Command, o as InputSchema } from "./types-InZGjvkI.mjs";
3
3
 
4
4
  //#region src/plugins/flow/doors/define.d.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- import { Bt as Answer, Mt as JournalEntry, Pt as RouteStep, Xt as Json, cn as RngState, dt as Api$1, hn as Api, jt as FlowState } from "./types-BSMmnpFm.mjs";
1
+ import { Bt as Answer, Mt as JournalEntry, Pt as RouteStep, Xt as Json, cn as RngState, dt as Api$1, hn as Api, jt as FlowState } from "./types-BWDESfx2.mjs";
2
2
 
3
3
  //#region src/plugins/flow/headless.d.ts
4
4
  /**
@@ -1,7 +1,7 @@
1
- import { $ as DescriptionNode, A as Api$6, B as ComponentType, Et as FeatureDescription, Ft as SceneIdOf, G as Mut, Gt as State$2, J as QueryTerm, Jt as Config$5, K as Narrowed, Lt as Descriptor, N as State$8, Qt as State$5, R as ComponentHandle, Rt as GuideOptions, Sn as Require, Ut as Config$2, V as ComponentValue, Vt as Api$1, X as SystemDefinition, Xt as Json, Y as ResourceType, Z as TagType, Zt as Root, _n as State$7, a as Config$1, at as NumericFields, bn as Config$9, c as LoadBundles, d as State$1, dn as Events, dt as Api$2, en as Patch, et as Ease, f as Tier, fn as PauseReason, ft as BundlesOf, g as Config$6, gn as Config$7, gt as GameTypes, h as Api$4, hn as Api$5, ht as FeaturePlugin, i as BundleSpec, j as Config$8, k as ViewportSize, kt as DefineNode, l as LoadReason, ln as Api$3, mt as EmitterIdOf, o as DefineBundles, on as StoreApi, ot as ProjectionMotion, pn as State$4, pt as Config$3, r as BundleMap, sn as RngApi, st as ProjectionSpec, t as Api, un as Config$4, vt as State$3, x as State$6, xn as Events$1, xt as defineFlow, yt as TextStylesOf, zt as Hint } from "./types-BSMmnpFm.mjs";
2
- import { A as SfxDescriptor, C as Config$10, D as Target, E as State$9, N as defineAnimation, S as AnimPlayerValue, T as MotionKeyframe, _ as SceneSpec, a as AudioApi, c as MusicDescriptor, d as State$10, g as SceneDefinition, h as LayerMap, j as Step, k as HapticKind, l as MusicOptions, m as DefineScene, n as State$12, p as AnySceneProjection, r as UiApi, s as Config$11, t as Config$12, v as ScenesApi, w as FramesValue, x as AnimApi, y as State$11, z as play } from "./types-dJMOXXL3.mjs";
3
- import { _ as NoiseValue, c as EmitterValue, d as ColorMatrixValue, f as DisplacementValue, g as GlowValue, h as FilterValueOf, i as State$13, l as AlphaValue, m as FilterSpec, n as EffectsApi, o as EmitterConfig, p as FilterComponent, s as EmitterDefinition, t as Config$13, u as BlurValue, v as OutlineValue, y as UniformDeclaration } from "./types-B_Bp2tWK.mjs";
4
- import { A as InputApi, B as SpriteOptions, E as TextValue, F as NineSliceValue, G as Argument, J as IntlKit, K as Config$14, O as defineComponent, Q as TypedTr, R as ShapeValue, S as TextBind, T as TextStyles, U as TransformValue, V as SpriteValue, W as sprite, X as Part, Y as Message, Z as State$14, _ as Config$16, b as State$16, et as IsFlags, f as defineTokens, g as BindOptions, h as BindField, it as WhenFlags, j as State$15, k as Config$15, nt as Style, p as IntrinsicElementsFor, q as I18nApi, s as PopupComponent, tt as ResolvedStyle, v as CountdownValue, w as TextStyleInput, x as TextApi, y as Point } from "./types-Dm5Oh5bH.mjs";
1
+ import { $ as DescriptionNode, A as Api$6, B as ComponentType, Et as FeatureDescription, Ft as SceneIdOf, G as Mut, Gt as State$2, J as QueryTerm, Jt as Config$5, K as Narrowed, Lt as Descriptor, N as State$8, Qt as State$5, R as ComponentHandle, Rt as GuideOptions, Sn as Require, Ut as Config$2, V as ComponentValue, Vt as Api$1, X as SystemDefinition, Xt as Json, Y as ResourceType, Z as TagType, Zt as Root, _n as State$7, a as Config$1, at as NumericFields, bn as Config$9, c as LoadBundles, d as State$1, dn as Events, dt as Api$2, en as Patch, et as Ease, f as Tier, fn as PauseReason, ft as BundlesOf, g as Config$6, gn as Config$7, gt as GameTypes, h as Api$4, hn as Api$5, ht as FeaturePlugin, i as BundleSpec, j as Config$8, k as ViewportSize, kt as DefineNode, l as LoadReason, ln as Api$3, mt as EmitterIdOf, o as DefineBundles, on as StoreApi, ot as ProjectionMotion, pn as State$4, pt as Config$3, r as BundleMap, sn as RngApi, st as ProjectionSpec, t as Api, un as Config$4, vt as State$3, x as State$6, xn as Events$1, xt as defineFlow, yt as TextStylesOf, zt as Hint } from "./types-BWDESfx2.mjs";
2
+ import { A as SfxDescriptor, C as Config$10, D as Target, E as State$9, N as defineAnimation, S as AnimPlayerValue, T as MotionKeyframe, _ as SceneSpec, a as AudioApi, c as MusicDescriptor, d as State$10, g as SceneDefinition, h as LayerMap, j as Step, k as HapticKind, l as MusicOptions, m as DefineScene, n as State$12, p as AnySceneProjection, r as UiApi, s as Config$11, t as Config$12, v as ScenesApi, w as FramesValue, x as AnimApi, y as State$11, z as play } from "./types-oQoQEj-y.mjs";
3
+ import { _ as NoiseValue, c as EmitterValue, d as ColorMatrixValue, f as DisplacementValue, g as GlowValue, h as FilterValueOf, i as State$13, l as AlphaValue, m as FilterSpec, n as EffectsApi, o as EmitterConfig, p as FilterComponent, s as EmitterDefinition, t as Config$13, u as BlurValue, v as OutlineValue, y as UniformDeclaration } from "./types-D7MGz4c-.mjs";
4
+ import { A as InputApi, B as SpriteOptions, E as TextValue, F as NineSliceValue, G as Argument, J as IntlKit, K as Config$14, O as defineComponent, Q as TypedTr, R as ShapeValue, S as TextBind, T as TextStyles, U as TransformValue, V as SpriteValue, W as sprite, X as Part, Y as Message, Z as State$14, _ as Config$16, b as State$16, et as IsFlags, f as defineTokens, g as BindOptions, h as BindField, it as WhenFlags, j as State$15, k as Config$15, nt as Style, p as IntrinsicElementsFor, q as I18nApi, s as PopupComponent, tt as ResolvedStyle, v as CountdownValue, w as TextStyleInput, x as TextApi, y as Point } from "./types-BArxen_M.mjs";
5
5
  import { Log } from "@moku-labs/common/browser";
6
6
  import { PluginCtx } from "@moku-labs/core";
7
7
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as DescriptionNode, $t as types_d_exports$9, C as types_d_exports$11, Ct as slot, Kt as types_d_exports$3, P as types_d_exports$16, St as exit, Tt as type, bt as types_d_exports$5, in as SaveUnreadableError, m as types_d_exports$1, mn as types_d_exports$8, wt as to, yn as types_d_exports$14 } from "./types-BSMmnpFm.mjs";
2
- import { B as sequence, F as frames, G as stagger, H as sfx, I as haptic, J as wait, K as tween, L as mark, M as HAPTIC_KINDS, N as defineAnimation, O as types_d_exports, P as external, R as parallel, U as spawn, V as set, W as spawned, b as types_d_exports$12, f as types_d_exports$2, i as types_d_exports$15, k as HapticKind, q as use, z as play } from "./types-dJMOXXL3.mjs";
3
- import { a as types_d_exports$4 } from "./types-B_Bp2tWK.mjs";
4
- import { $ as types_d_exports$6, C as TextFormat, D as types_d_exports$13, H as Transform, I as Parent, L as Shape, M as types_d_exports$7, N as Display, O as defineComponent, P as NineSlice, W as sprite, d as popup, f as defineTokens, g as BindOptions, l as Box, u as LocalWrite, v as CountdownValue, z as Sprite } from "./types-Dm5Oh5bH.mjs";
5
- import { $ as defineFeature, A as Held, B as Traced, C as Animation, Ct as bind, D as load, E as defineBundles, F as Pressed, G as component, H as Layer, I as Swipeable, J as system, K as mut, L as Tappable, M as Pointer, N as PointerOver, O as Draggable, P as Pressable, Q as schedule, R as Touchable, S as AnimPlayer, St as Text, T as defineScene, Tt as label, U as Order, V as Exiting, W as Tree, X as guide, Y as tag, Z as hint, _ as defineStyle, _t as audioPlugin, a as defineEmitter, at as rendererPlugin, b as messageDuration, bt as projection, c as Alpha, ct as PlatformProvider, d as Displacement, dt as lifecyclePlugin, et as worldPlugin, f as Glow, ft as inputPlugin, g as resolve, gt as clockPlugin, h as music, ht as effectsPlugin, i as screen, it as scenesPlugin, j as Hovered, k as DropTarget, l as Blur, lt as types_d_exports$10, m as Outline, mt as flowPlugin, n as createPlugin, nt as timePlugin, o as Emitter, ot as platformPlugin, p as Noise, pt as i18nPlugin, q as resource, r as defineGame, rt as textPlugin, s as defineFilter, st as BackResult, t as createApp, tt as uiPlugin, u as ColorMatrix, ut as modelPlugin, v as tr, vt as assetsPlugin, w as Frames, wt as defineTextStyles, x as defineMotion, xt as Countdown, y as messageArgument, yt as animPlugin, z as Traceable } from "./index-CjuObHhF.mjs";
1
+ import { $ as DescriptionNode, $t as types_d_exports$9, C as types_d_exports$11, Ct as slot, Kt as types_d_exports$3, P as types_d_exports$16, St as exit, Tt as type, bt as types_d_exports$5, in as SaveUnreadableError, m as types_d_exports$1, mn as types_d_exports$8, wt as to, yn as types_d_exports$14 } from "./types-BWDESfx2.mjs";
2
+ import { B as sequence, F as frames, G as stagger, H as sfx, I as haptic, J as wait, K as tween, L as mark, M as HAPTIC_KINDS, N as defineAnimation, O as types_d_exports, P as external, R as parallel, U as spawn, V as set, W as spawned, b as types_d_exports$12, f as types_d_exports$2, i as types_d_exports$15, k as HapticKind, q as use, z as play } from "./types-oQoQEj-y.mjs";
3
+ import { a as types_d_exports$4 } from "./types-D7MGz4c-.mjs";
4
+ import { $ as types_d_exports$6, C as TextFormat, D as types_d_exports$13, H as Transform, I as Parent, L as Shape, M as types_d_exports$7, N as Display, O as defineComponent, P as NineSlice, W as sprite, d as popup, f as defineTokens, g as BindOptions, l as Box, u as LocalWrite, v as CountdownValue, z as Sprite } from "./types-BArxen_M.mjs";
5
+ import { $ as defineFeature, A as Held, B as Traced, C as Animation, Ct as bind, D as load, E as defineBundles, F as Pressed, G as component, H as Layer, I as Swipeable, J as system, K as mut, L as Tappable, M as Pointer, N as PointerOver, O as Draggable, P as Pressable, Q as schedule, R as Touchable, S as AnimPlayer, St as Text, T as defineScene, Tt as label, U as Order, V as Exiting, W as Tree, X as guide, Y as tag, Z as hint, _ as defineStyle, _t as audioPlugin, a as defineEmitter, at as rendererPlugin, b as messageDuration, bt as projection, c as Alpha, ct as PlatformProvider, d as Displacement, dt as lifecyclePlugin, et as worldPlugin, f as Glow, ft as inputPlugin, g as resolve, gt as clockPlugin, h as music, ht as effectsPlugin, i as screen, it as scenesPlugin, j as Hovered, k as DropTarget, l as Blur, lt as types_d_exports$10, m as Outline, mt as flowPlugin, n as createPlugin, nt as timePlugin, o as Emitter, ot as platformPlugin, p as Noise, pt as i18nPlugin, q as resource, r as defineGame, rt as textPlugin, s as defineFilter, st as BackResult, t as createApp, tt as uiPlugin, u as ColorMatrix, ut as modelPlugin, v as tr, vt as assetsPlugin, w as Frames, wt as defineTextStyles, x as defineMotion, xt as Countdown, y as messageArgument, yt as animPlugin, z as Traceable } from "./index-yUO4Dx2C.mjs";
6
6
  export { Alpha, types_d_exports as Anim, AnimPlayer, Animation, types_d_exports$1 as Assets, types_d_exports$2 as Audio, type BackResult, type BindOptions, Blur, Box, types_d_exports$3 as Clock, ColorMatrix, Countdown, type CountdownValue, type DescriptionNode, Displacement, Display, Draggable, DropTarget, types_d_exports$4 as Effects, Emitter, Exiting, types_d_exports$5 as Flow, Frames, Glow, HAPTIC_KINDS, type HapticKind, Held, Hovered, types_d_exports$6 as I18n, types_d_exports$7 as Input, Layer, types_d_exports$8 as Lifecycle, LocalWrite, types_d_exports$9 as Model, NineSlice, Noise, Order, Outline, Parent, types_d_exports$10 as Platform, type PlatformProvider, Pointer, PointerOver, Pressable, Pressed, types_d_exports$11 as Renderer, SaveUnreadableError, types_d_exports$12 as Scenes, Shape, Sprite, Swipeable, Tappable, Text, type TextFormat, types_d_exports$13 as TextTypes, types_d_exports$14 as Time, Touchable, Traceable, Traced, Transform, Tree, types_d_exports$15 as Ui, types_d_exports$16 as World, animPlugin, assetsPlugin, audioPlugin, bind, clockPlugin, component, createApp, createPlugin, defineAnimation, defineBundles, defineComponent, defineEmitter, defineFeature, defineFilter, defineGame, defineMotion, defineScene, defineStyle, defineTextStyles, defineTokens, effectsPlugin, exit, external, flowPlugin, frames, guide, haptic, hint, i18nPlugin, inputPlugin, label, lifecyclePlugin, load, mark, messageArgument, messageDuration, modelPlugin, music, mut, parallel, platformPlugin, play, popup, projection, rendererPlugin, resolve, resource, scenesPlugin, schedule, screen, sequence, set, sfx, slot, spawn, spawned, sprite, stagger, system, tag, textPlugin, timePlugin, to, tr, tween, type, uiPlugin, use, wait, worldPlugin };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as sfx, A as types_exports$3, At as Noise, B as defineStyle, Bt as rendererPlugin, C as types_exports$10, Ct as effectsPlugin, D as types_exports$6, Dt as ColorMatrix, E as types_exports$7, Et as Blur, F as resolve, Ft as assetsPlugin, G as external, Gt as to, H as platformPlugin, Ht as flowPlugin, I as Box, It as animPlugin, J as mark, Jt as SaveUnreadableError, K as frames, Kt as type, L as LocalWrite, Lt as AnimPlayer, M as types_exports$1, Mt as defineFilter, N as types_exports, Nt as audioPlugin, O as types_exports$5, Ot as Displacement, P as uiPlugin, Pt as scenesPlugin, Q as set, R as popup, Rt as Animation, S as types_exports$11, St as messageDuration, T as types_exports$8, Tt as Alpha, U as HAPTIC_KINDS, Ut as exit, V as textPlugin, Vt as worldPlugin, W as defineAnimation, Wt as slot, X as play, Xt as timePlugin, Y as parallel, Yt as lifecyclePlugin, Z as sequence, Zt as clockPlugin, _ as types_exports$16, _t as Touchable, a as defineMotion, at as wait, b as types_exports$13, bt as i18nPlugin, c as schedule, ct as DropTarget, d as tr, dt as Pointer, et as spawn, f as defineFeature, ft as PointerOver, g as load, gt as Tappable, h as defineBundles, ht as Swipeable, i as screen, it as use, j as types_exports$2, jt as Outline, k as types_exports$4, kt as Glow, l as projection, lt as Held, m as music, mt as Pressed, n as createPlugin, nt as stagger, o as guide, ot as inputPlugin, p as defineEmitter, pt as Pressable, q as haptic, qt as modelPlugin, r as defineGame, rt as tween, s as hint, st as Draggable, t as createApp, tt as spawned, u as defineScene, ut as Hovered, v as types_exports$15, vt as Traceable, w as types_exports$9, wt as Emitter, x as types_exports$12, xt as messageArgument, y as types_exports$14, yt as Traced, z as defineTokens, zt as Frames } from "./src-DlyYl_9X.mjs";
1
+ import { $ as sfx, A as types_exports$3, At as Noise, B as defineStyle, Bt as rendererPlugin, C as types_exports$10, Ct as effectsPlugin, D as types_exports$6, Dt as ColorMatrix, E as types_exports$7, Et as Blur, F as resolve, Ft as assetsPlugin, G as external, Gt as to, H as platformPlugin, Ht as flowPlugin, I as Box, It as animPlugin, J as mark, Jt as SaveUnreadableError, K as frames, Kt as type, L as LocalWrite, Lt as AnimPlayer, M as types_exports$1, Mt as defineFilter, N as types_exports, Nt as audioPlugin, O as types_exports$5, Ot as Displacement, P as uiPlugin, Pt as scenesPlugin, Q as set, R as popup, Rt as Animation, S as types_exports$11, St as messageDuration, T as types_exports$8, Tt as Alpha, U as HAPTIC_KINDS, Ut as exit, V as textPlugin, Vt as worldPlugin, W as defineAnimation, Wt as slot, X as play, Xt as timePlugin, Y as parallel, Yt as lifecyclePlugin, Z as sequence, Zt as clockPlugin, _ as types_exports$16, _t as Touchable, a as defineMotion, at as wait, b as types_exports$13, bt as i18nPlugin, c as schedule, ct as DropTarget, d as tr, dt as Pointer, et as spawn, f as defineFeature, ft as PointerOver, g as load, gt as Tappable, h as defineBundles, ht as Swipeable, i as screen, it as use, j as types_exports$2, jt as Outline, k as types_exports$4, kt as Glow, l as projection, lt as Held, m as music, mt as Pressed, n as createPlugin, nt as stagger, o as guide, ot as inputPlugin, p as defineEmitter, pt as Pressable, q as haptic, qt as modelPlugin, r as defineGame, rt as tween, s as hint, st as Draggable, t as createApp, tt as spawned, u as defineScene, ut as Hovered, v as types_exports$15, vt as Traceable, w as types_exports$9, wt as Emitter, x as types_exports$12, xt as messageArgument, y as types_exports$14, yt as Traced, z as defineTokens, zt as Frames } from "./src-Dgmdzl8R.mjs";
2
2
  import { A as system, C as Exiting, D as component, E as Tree, O as mut, S as sprite, T as Order, _ as Parent, b as Transform, c as Text, d as defineTextStyles, f as label, g as NineSlice, h as Display, j as tag, k as resource, l as bind, s as Countdown, v as Shape, w as Layer, y as Sprite } from "./visual-dMW8iqAE.mjs";
3
3
  import { r as defineComponent } from "./flatten-BkXuscAV.mjs";
4
4
  export { Alpha, types_exports as Anim, AnimPlayer, Animation, types_exports$1 as Assets, types_exports$2 as Audio, Blur, Box, types_exports$3 as Clock, ColorMatrix, Countdown, Displacement, Display, Draggable, DropTarget, types_exports$4 as Effects, Emitter, Exiting, types_exports$5 as Flow, Frames, Glow, HAPTIC_KINDS, Held, Hovered, types_exports$6 as I18n, types_exports$7 as Input, Layer, types_exports$8 as Lifecycle, LocalWrite, types_exports$9 as Model, NineSlice, Noise, Order, Outline, Parent, types_exports$10 as Platform, Pointer, PointerOver, Pressable, Pressed, types_exports$11 as Renderer, SaveUnreadableError, types_exports$12 as Scenes, Shape, Sprite, Swipeable, Tappable, Text, types_exports$13 as TextTypes, types_exports$14 as Time, Touchable, Traceable, Traced, Transform, Tree, types_exports$15 as Ui, types_exports$16 as World, animPlugin, assetsPlugin, audioPlugin, bind, clockPlugin, component, createApp, createPlugin, defineAnimation, defineBundles, defineComponent, defineEmitter, defineFeature, defineFilter, defineGame, defineMotion, defineScene, defineStyle, defineTextStyles, defineTokens, effectsPlugin, exit, external, flowPlugin, frames, guide, haptic, hint, i18nPlugin, inputPlugin, label, lifecyclePlugin, load, mark, messageArgument, messageDuration, modelPlugin, music, mut, parallel, platformPlugin, play, popup, projection, rendererPlugin, resolve, resource, scenesPlugin, schedule, screen, sequence, set, sfx, slot, spawn, spawned, sprite, stagger, system, tag, textPlugin, timePlugin, to, tr, tween, type, uiPlugin, use, wait, worldPlugin };
@@ -1,10 +1,10 @@
1
- import { A as Api$1, At as FlowGraph, M as Explained, Mt as JournalEntry, S as Under, T as RenderStats, U as EntitySnapshot, W as FrameDiff, _t as Position, an as Snapshot, h as Api$2, p as Usage, qt as Api, t as Api$3, z as ComponentSchema } from "./types-BSMmnpFm.mjs";
2
- import { a as AudioApi, r as UiApi, u as SoundEntry } from "./types-dJMOXXL3.mjs";
3
- import { n as EffectsApi, r as EffectsStats } from "./types-B_Bp2tWK.mjs";
4
- import { c as UiNode } from "./types-Dm5Oh5bH.mjs";
5
- import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
6
- import { a as InputOf, c as Source, i as InputArguments, l as WatchApp, o as InputSchema, t as CheatEntry, u as WatchInput } from "./types-Bonifz5M.mjs";
7
- import { n as defineSource } from "./define-Ckp70_GR.mjs";
1
+ import { A as Api$1, At as FlowGraph, M as Explained, Mt as JournalEntry, S as Under, T as RenderStats, U as EntitySnapshot, W as FrameDiff, _t as Position, an as Snapshot, h as Api$2, p as Usage, qt as Api, t as Api$3, z as ComponentSchema } from "./types-BWDESfx2.mjs";
2
+ import { a as AudioApi, r as UiApi, u as SoundEntry } from "./types-oQoQEj-y.mjs";
3
+ import { n as EffectsApi, r as EffectsStats } from "./types-D7MGz4c-.mjs";
4
+ import { c as UiNode } from "./types-BArxen_M.mjs";
5
+ import { t as HeadlessApp } from "./headless-IfD6_JMH.mjs";
6
+ import { a as InputOf, c as Source, i as InputArguments, l as WatchApp, o as InputSchema, t as CheatEntry, u as WatchInput } from "./types-InZGjvkI.mjs";
7
+ import { n as defineSource } from "./define-Bwp8CNkW.mjs";
8
8
 
9
9
  //#region src/plugins/flow/doors/read.d.ts
10
10
  /**
@@ -1,2 +1,2 @@
1
- import { a as jsxs, i as jsxDEV, n as JSX, r as jsx, t as Fragment } from "./runtime-iTxTcukZ.mjs";
1
+ import { a as jsxs, i as jsxDEV, n as JSX, r as jsx, t as Fragment } from "./runtime-CWY6co6m.mjs";
2
2
  export { Fragment, type JSX, jsx, jsxDEV, jsxs };
@@ -1,2 +1,2 @@
1
- import { a as jsxs, n as JSX, r as jsx, t as Fragment } from "./runtime-iTxTcukZ.mjs";
1
+ import { a as jsxs, n as JSX, r as jsx, t as Fragment } from "./runtime-CWY6co6m.mjs";
2
2
  export { Fragment, type JSX, jsx, jsxs };
@@ -1,4 +1,4 @@
1
- import { Xt as Json, nn as ProviderCall, rn as SaveDoc, tn as PlayerStateProvider } from "./types-BSMmnpFm.mjs";
1
+ import { Xt as Json, nn as ProviderCall, rn as SaveDoc, tn as PlayerStateProvider } from "./types-BWDESfx2.mjs";
2
2
 
3
3
  //#region src/plugins/model/store/providers/memory.d.ts
4
4
  /**
@@ -959,6 +959,26 @@ const fillCommand = defineCommand({
959
959
  //#endregion
960
960
  //#region src/plugins/flow/control.ts
961
961
  /**
962
+ * Restores a bookmark with its `scene` expected first, so the node the restore enters mounts it.
963
+ * A refused restore puts the record back to the mounted scene, when one is mounted: no later
964
+ * node switches to the scene of a bookmark that was never entered. With nothing mounted the
965
+ * record stays, as the scenes API has no way to clear it.
966
+ *
967
+ * @param app - The app; with `scenes`, the bookmark's scene is expected before the restore.
968
+ * @param bookmark - The bookmark to enter.
969
+ * @throws {Error} What `flow.restore` refused the bookmark with, unchanged.
970
+ */
971
+ async function restoreWithScene(app, bookmark) {
972
+ const mounted = app.scenes?.current();
973
+ if (bookmark.scene !== void 0) app.scenes?.expect(bookmark.scene);
974
+ try {
975
+ await app.flow.restore(bookmark);
976
+ } catch (error) {
977
+ if (mounted !== void 0) app.scenes?.expect(mounted);
978
+ throw error;
979
+ }
980
+ }
981
+ /**
962
982
  * Enters a bookmark, or a repro's bookmark followed by its route. A bookmark's `scene` is handed
963
983
  * to `scenes.expect` first, so a node without a scene of its own mounts it: a rest node, or the
964
984
  * transit node of a bookmark taken while an effect that takes answers waited.
@@ -975,9 +995,7 @@ async function restoreFrom(app, input) {
975
995
  const bookmarkOnly = bookmark !== void 0 && repro === void 0;
976
996
  const reproOnly = repro !== void 0 && bookmark === void 0;
977
997
  if (bookmarkOnly) {
978
- const read = readBookmark(bookmark);
979
- if (read.scene !== void 0) app.scenes?.expect(read.scene);
980
- await app.flow.restore(read);
998
+ await restoreWithScene(app, readBookmark(bookmark));
981
999
  return app.flow.state();
982
1000
  }
983
1001
  if (reproOnly) {
@@ -1,4 +1,4 @@
1
- import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-Dm5Oh5bH.mjs";
1
+ import { a as JsxChild, m as UiIntrinsicElements, t as DescriptionNode } from "./types-BArxen_M.mjs";
2
2
 
3
3
  //#region src/plugins/ui/jsx/runtime.d.ts
4
4
  /** What the compiler passes as props: the children plus whatever the tag declared. */
@@ -3870,30 +3870,53 @@ async function runLoop(ctx, modules) {
3870
3870
  * Restores a bookmark through the running loop: the active node is aborted with reason
3871
3871
  * `"restore"`, its transaction is discarded, the state is replaced and the node is entered. An
3872
3872
  * edge that was already committed still arrives first, and the rest point it reaches is not the
3873
- * bookmark's: the promise waits for the turn in which the loop takes the bookmark.
3873
+ * bookmark's: the promise waits for the turn in which the loop takes the bookmark. A loop that
3874
+ * ends before that turn ends the wait too, after a stop or a fatal error. Nothing is entered
3875
+ * then, and the bookmark is dropped: the seam outlives the loop, and the next `run()` would enter
3876
+ * a bookmark nobody waits for.
3874
3877
  *
3875
3878
  * @param ctx - Domain context of the flow plugin.
3876
3879
  * @param _modules - Injected sibling APIs.
3877
3880
  * @param bookmark - The bookmark to enter.
3878
3881
  * @returns A promise that resolves once the loop entered the bookmark's node, before that node
3879
- * ran: the rest seam fired.
3882
+ * ran: the rest seam fired. It also resolves when the loop ended first, with nothing entered.
3880
3883
  * @throws {Error} When the loop is not running.
3881
3884
  */
3882
3885
  function restorePosition(ctx, _modules, bookmark) {
3883
3886
  const state = ctx.state.runner;
3884
- if (state.running === void 0) throw new Error("[game] flow.restore() needs a running graph.\n Call flow.run() from the createApp onStart callback first.");
3887
+ const running = state.running;
3888
+ if (running === void 0) throw new Error("[game] flow.restore() needs a running graph.\n Call flow.run() from the createApp onStart callback first.");
3885
3889
  const seam = loopSeam(state);
3886
3890
  const entered = new Promise((resolve) => {
3891
+ /**
3892
+ * Takes the listener off the rest seam.
3893
+ *
3894
+ * @returns True when it was still there, false when it had already left.
3895
+ */
3896
+ const dropListener = () => {
3897
+ const index = seam.rest.indexOf(listener);
3898
+ if (index === -1) return false;
3899
+ seam.rest.splice(index, 1);
3900
+ return true;
3901
+ };
3887
3902
  /**
3888
3903
  * Resolves once, when the loop rests at the restored node.
3889
3904
  */
3890
3905
  const listener = () => {
3891
3906
  if (seam.restoring !== void 0) return;
3892
- const index = seam.rest.indexOf(listener);
3893
- if (index !== -1) seam.rest.splice(index, 1);
3907
+ dropListener();
3908
+ resolve();
3909
+ };
3910
+ /**
3911
+ * Ends the wait when the loop ended, resolved or rejected, before it took the bookmark.
3912
+ */
3913
+ const finish = () => {
3914
+ if (!dropListener()) return;
3915
+ if (seam.restoring === bookmark) seam.restoring = void 0;
3894
3916
  resolve();
3895
3917
  };
3896
3918
  seam.rest.push(listener);
3919
+ running.then(finish, finish);
3897
3920
  });
3898
3921
  seam.restoring = bookmark;
3899
3922
  state.abort?.abort("restore");
@@ -4115,7 +4138,8 @@ async function reachGate(ctx, seam, ended) {
4115
4138
  * it entered a transit bookmark, so its caller reads a state with the open gate in it. There is no
4116
4139
  * timeout: a node that opens no gate, never rests and never ends keeps the wait pending, as its
4117
4140
  * body would. Call it only after the loop entered the node: a gate opened before that belongs to
4118
- * the node the restore aborted.
4141
+ * the node the restore aborted. After a loop that ended before the enter it returns at once: the
4142
+ * end of the loop is already there.
4119
4143
  *
4120
4144
  * @param ctx - Domain context of the flow plugin.
4121
4145
  * @returns A promise that resolves at the open gate, at a rest point or at the end of the loop.
@@ -4371,12 +4395,13 @@ function makeBookmark(ctx, modules) {
4371
4395
  * Sends the running loop to a bookmark that passed the check, the short way a walk needs: the
4372
4396
  * promise resolves as soon as the loop stands at the node, before the node ran. It stays an
4373
4397
  * `async` function of its own: the microtask turn it adds lets the loop start the stages of a
4374
- * rest node before the walk goes on and switches to fast mode.
4398
+ * rest node before the walk goes on and switches to fast mode. A loop that ends first resolves it
4399
+ * too, with nothing entered: the walk then finds its route not reached.
4375
4400
  *
4376
4401
  * @param ctx - Domain context of the flow plugin.
4377
4402
  * @param modules - Injected sibling APIs.
4378
4403
  * @param bookmark - The accepted bookmark.
4379
- * @returns A promise that resolves once the loop entered the node.
4404
+ * @returns A promise that resolves once the loop entered the node, or ended first.
4380
4405
  */
4381
4406
  async function enterAccepted(ctx, modules, bookmark) {
4382
4407
  await restorePosition(ctx, modules, bookmark);
@@ -4413,12 +4438,14 @@ async function walkFrom(ctx, modules, route, from) {
4413
4438
  * enters it. A transit node shows nothing until it ran up to its gate, so for one the promise
4414
4439
  * waits on: for the gate the node opens, or the rest point or the end of the loop it reaches
4415
4440
  * without one. The wait starts only after the loop entered the node, so a gate the aborted old
4416
- * node still opened is not taken for the new one.
4441
+ * node still opened is not taken for the new one. A loop that ends before it entered the node
4442
+ * ends both waits: nothing is entered, and the wait for the gate returns at once.
4417
4443
  *
4418
4444
  * @param ctx - Domain context of the flow plugin.
4419
4445
  * @param modules - Injected sibling APIs.
4420
4446
  * @param bookmark - The bookmark to enter.
4421
- * @returns A promise that resolves once a rest node is entered, or a transit node waits.
4447
+ * @returns A promise that resolves once a rest node is entered, or a transit node waits, or the
4448
+ * loop ended first.
4422
4449
  * @throws {Error} When the bookmark may not be entered, and before `run()`.
4423
4450
  */
4424
4451
  async function restoreBookmark(ctx, modules, bookmark) {
@@ -17546,11 +17573,11 @@ function targetOf(ctx, node, run) {
17546
17573
  return target;
17547
17574
  }
17548
17575
  /**
17549
- * Reads the scene an `over` node asks for. An over node never switches a mounted scene; it takes
17550
- * the expected scene while nothing is mounted, so a restore at a popup builds the scene under it,
17551
- * whether the popup rests or waits for its answer. In fast mode an over transit node takes
17552
- * nothing and leaves the record alone: a walk builds no scene on the way. A scene the over node
17553
- * names itself is only warned about.
17576
+ * Reads the scene an `over` node asks for. An over node takes the expected scene and clears the
17577
+ * record, whatever is mounted, so a restore at a popup builds the scene under it, whether the
17578
+ * popup rests or waits for its answer. With nothing expected it switches nothing. In fast mode an
17579
+ * over transit node takes nothing and leaves the record alone: a walk builds no scene on the way.
17580
+ * A scene the over node names itself is only warned about.
17554
17581
  *
17555
17582
  * @param ctx - Domain context of the plugin.
17556
17583
  * @param node - The over node being entered.
@@ -17560,7 +17587,6 @@ function targetOf(ctx, node, run) {
17560
17587
  function overTargetOf(ctx, node, run) {
17561
17588
  if (node.scene !== void 0) ctx.log.warn("scenes: an over node cannot name a scene", { path: node.path });
17562
17589
  if (isFastTransit(node, run)) return void 0;
17563
- if (ctx.state.current !== void 0) return void 0;
17564
17590
  return takePending(ctx);
17565
17591
  }
17566
17592
  /**
@@ -17588,9 +17614,9 @@ function apply(ctx, scene) {
17588
17614
  });
17589
17615
  }
17590
17616
  /**
17591
- * The `scene` stage of entering a node: the whole switch. An `over` node never switches a mounted
17592
- * scene, a node without a scene keeps the current one, and an aborted node returns without
17593
- * touching anything.
17617
+ * The `scene` stage of entering a node: the whole switch. An `over` node switches only to an
17618
+ * expected scene, a node without a scene and with nothing expected keeps the current one, and an
17619
+ * aborted node returns without touching anything.
17594
17620
  *
17595
17621
  * @param ctx - Domain context of the plugin.
17596
17622
  * @param node - The node being entered.
@@ -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-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";
1
+ import { Dt as AnyFlow, Ht as ClockSource, Nt as NodeTable, Wt as FakeClock, Xt as Json, ht as FeaturePlugin } from "./types-BWDESfx2.mjs";
2
+ import { t as createApp } from "./index-yUO4Dx2C.mjs";
3
+ import { n as saveOf, t as memory } from "./memory-D1IKoFr1.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-IfD6_JMH.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,4 +1,4 @@
1
- import { Kt as type, Ut as exit, t as createApp } from "./src-DlyYl_9X.mjs";
1
+ import { Kt as type, Ut as exit, t as createApp } from "./src-Dgmdzl8R.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
4
  import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BA6ffzqD.mjs";
@@ -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-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";
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-BWDESfx2.mjs";
2
+ import { o as AudioContextLike, s as Config$4 } from "./types-oQoQEj-y.mjs";
3
+ import { _t as audioPlugin, ct as PlatformProvider, ht as effectsPlugin, i as screen, ot as platformPlugin, t as createApp } from "./index-yUO4Dx2C.mjs";
4
+ import { t as memory } from "./memory-D1IKoFr1.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-BSMmnpFm.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-BWDESfx2.mjs";
2
2
  import { Log } from "@moku-labs/common/browser";
3
3
  import { PluginCtx } from "@moku-labs/core";
4
4
 
@@ -2308,7 +2308,11 @@ type LoopSeam = {
2308
2308
  * Called every time the loop opens a gate: the wait of a rest node, or an effect with answers
2309
2309
  * inside any node. The walk waits on it, and so does `restore` after it entered a transit node.
2310
2310
  */
2311
- gateOpen: (() => void)[]; /** The bookmark the loop enters at the next turn. */
2311
+ gateOpen: (() => void)[];
2312
+ /**
2313
+ * The bookmark the loop enters at the next turn. The loop clears it when it takes it. A loop
2314
+ * that ends first never takes it, so the restore that waits drops its own.
2315
+ */
2312
2316
  restoring: Bookmark | undefined;
2313
2317
  };
2314
2318
  /**
@@ -2460,9 +2464,14 @@ type RunnerApi = {
2460
2464
  * promise pending. An open gate is not a drawn popup: the popup shows on the next frame. And
2461
2465
  * `setMode` still throws there, since the graph waits inside a transit node.
2462
2466
  *
2467
+ * The promise also resolves when the graph stops before the bookmark was entered: `onStop`
2468
+ * ended the loop, or `run()` rejected. Nothing is entered then and the state is not replaced.
2469
+ * `state()` shows whether it was: its `path` is the node the graph stands at.
2470
+ *
2463
2471
  * @param bookmark - The bookmark to enter.
2464
2472
  * @returns A promise that resolves once a rest node is entered, or a transit node waits at
2465
- * its gate.
2473
+ * its gate. It also resolves when the graph stops first, with nothing entered: read
2474
+ * `state()` to tell.
2466
2475
  * @throws {Error} When the bookmark names no node of this graph and has no `rest`, when the
2467
2476
  * graph changed since the bookmark and neither it nor its `rest` point is a rest checkpoint,
2468
2477
  * and before `run()`.
@@ -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-BSMmnpFm.mjs";
2
- import { x as AnimApi } from "./types-dJMOXXL3.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-BWDESfx2.mjs";
2
+ import { x as AnimApi } from "./types-oQoQEj-y.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-BSMmnpFm.mjs";
2
- import { t as HeadlessApp } from "./headless-CYgYzro0.mjs";
1
+ import { Xt as Json, an as Snapshot } from "./types-BWDESfx2.mjs";
2
+ import { t as HeadlessApp } from "./headless-IfD6_JMH.mjs";
3
3
  import { Log } from "@moku-labs/common/browser";
4
4
 
5
5
  //#region src/plugins/flow/doors/types.d.ts
@@ -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-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";
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-BWDESfx2.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-BArxen_M.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";
@@ -1300,7 +1300,8 @@ type State$2 = {
1300
1300
  * The scene the next node without its own `scene` mounts, at a rest node or at a transit node
1301
1301
  * in live mode: the one a transit node named in fast mode, or the one the restore door expected
1302
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
+ * node takes it and clears it, whatever is mounted, except an over transit node of a fast walk,
1304
+ * which leaves it alone.
1304
1305
  */
1305
1306
  pending: string | undefined;
1306
1307
  owner: SceneOwner; /** The remover of the `onEnter` callback: set in `onStart`, run in `onStop`. */
@@ -1354,8 +1355,10 @@ type ScenesApi = {
1354
1355
  * The restore door calls it with the scene of a bookmark, so a bookmark at a popup comes back
1355
1356
  * with the scene under it, also when the popup is a transit node that waits for its answer:
1356
1357
  * 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
1358
- * mounted one.
1358
+ * An over node takes the expected scene too, whatever is mounted, so a popup bookmark restored
1359
+ * on a page that shows another scene switches to the scene of the bookmark. With nothing
1360
+ * expected an over node never switches. An over transit node of a fast walk mounts nothing and
1361
+ * keeps the record.
1359
1362
  *
1360
1363
  * @param id - Id of a scene a feature declared.
1361
1364
  * @throws {Error} For an id no feature declared, also before `onStart` filled the registry.
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-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";
1
+ import { A as Api$1, h as Api, ln as Api$3, qt as Api$2 } from "./types-BWDESfx2.mjs";
2
+ import { r as UiApi, x as AnimApi } from "./types-oQoQEj-y.mjs";
3
+ import { A as InputApi } from "./types-BArxen_M.mjs";
4
+ import { r as Repro } from "./headless-IfD6_JMH.mjs";
5
+ import { a as InputOf, r as ControlApp } from "./types-InZGjvkI.mjs";
6
+ import { t as commands } from "./commands-Cpn2ga7s.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-BtyrJjmW.mjs";
2
+ import { n as commands, t as run } from "./run-Bf2hKyjZ.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
@@ -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"` 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.
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.4.2. 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
 
@@ -475,14 +475,14 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
475
475
  - `features.register(name, description)`, `features.all()`, `features.contributions(slotName)`.
476
476
  - `FlowState` is `{ running, path, stack, pending: { fx?, gate? }, mode }`.
477
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
+ - `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. `restore` also resolves when the loop ends before the bookmark was entered, after a stop or a fatal error, and nothing is entered then.
479
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 }`.
480
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`.
481
481
 
482
482
  ### Screen plugins (V2, built)
483
483
 
484
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.
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
+ - `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). An `over` node takes it the same way, also over another mounted scene.
486
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.
487
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.
488
488
 
@@ -593,7 +593,7 @@ Two subpaths, neither on the root (V4). An editor, MCP tools and e2e scripts rea
593
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.
594
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.
595
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.`
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
+ - 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, also on a page that shows another scene. 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.
597
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.
598
598
 
599
599
  ## Project index
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/game",
3
- "version": "0.13.3",
3
+ "version": "0.14.1",
4
4
  "description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -95,7 +95,7 @@
95
95
  },
96
96
  "engines": {
97
97
  "node": ">=24.0.0",
98
- "bun": ">=1.3.14"
98
+ "bun": ">=1.4.2"
99
99
  },
100
100
  "scripts": {
101
101
  "lint": "biome check . && eslint .",
@@ -160,7 +160,7 @@
160
160
  "@moku-labs/core": "1.7.1",
161
161
  "@moku-labs/native": "0.3.2",
162
162
  "@moku-labs/system": "0.3.1",
163
- "@types/bun": "1.3.14",
163
+ "@types/bun": "1.4.2",
164
164
  "@vitest/coverage-istanbul": "4.0.18",
165
165
  "eslint": "9.39.3",
166
166
  "eslint-config-biome": "2.1.3",