@moku-labs/game 0.9.0 → 0.11.0

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 (53) hide show
  1. package/README.md +81 -22
  2. package/bin/moku-game.mjs +4 -0
  3. package/dist/app/page.d.mts +44 -0
  4. package/dist/app/page.mjs +379 -0
  5. package/dist/app/system.d.mts +144 -0
  6. package/dist/app/system.mjs +329 -0
  7. package/dist/app.d.mts +37 -0
  8. package/dist/app.mjs +235 -0
  9. package/dist/assets-BHwTLVta.mjs +7391 -0
  10. package/dist/assets.d.mts +1 -1
  11. package/dist/assets.mjs +1 -7390
  12. package/dist/cli.d.mts +70 -0
  13. package/dist/cli.mjs +1685 -0
  14. package/dist/{commands-DuwCxK5t.d.mts → commands-C4QlqijJ.d.mts} +5 -5
  15. package/dist/config-CdARrrhU.mjs +81 -0
  16. package/dist/control.d.mts +11 -10
  17. package/dist/control.mjs +3 -3
  18. package/dist/{define-B6pHpdBi.d.mts → define-CCc0ozKe.d.mts} +2 -2
  19. package/dist/{dev-wKLgOw7c.mjs → dev-Cc50Dk7H.mjs} +1 -1
  20. package/dist/drafts-BawpSaVp.mjs +156 -0
  21. package/dist/fake-MpzwZhIZ.mjs +87 -0
  22. package/dist/{headless-SCQXJxQf.d.mts → headless-G4nuZ-xz.d.mts} +1 -1
  23. package/dist/hot.d.mts +4 -6
  24. package/dist/hot.mjs +8 -9
  25. package/dist/{index-BO9kZzY3.d.mts → index-8TiJQjQA.d.mts} +30 -17
  26. package/dist/index.d.mts +5 -5
  27. package/dist/index.mjs +1 -1
  28. package/dist/inspect-la_KkjCS.mjs +1 -0
  29. package/dist/inspect.d.mts +7 -7
  30. package/dist/inspect.mjs +3 -2
  31. package/dist/jsx-dev-runtime.d.mts +1 -1
  32. package/dist/jsx-runtime.d.mts +1 -1
  33. package/dist/lint.mjs +10 -6
  34. package/dist/memory-CWlqhS7l.mjs +136 -0
  35. package/dist/memory-DwDhfFWK.d.mts +50 -0
  36. package/dist/project.mjs +3 -3
  37. package/dist/{run-CofJEdYx.mjs → run-wYty_Lwo.mjs} +2 -2
  38. package/dist/{runtime-CdNJ0UBm.d.mts → runtime-DOBYM6g4.d.mts} +1 -1
  39. package/dist/{input-z_35Jwfn.mjs → session-DgukhR_7.mjs} +20 -20
  40. package/dist/{sources-DNnzyCYf.mjs → sources-Duzj6lhV.mjs} +2 -2
  41. package/dist/{src-536arXjV.mjs → src-BSbtHZWk.mjs} +35 -300
  42. package/dist/testing.d.mts +4 -50
  43. package/dist/testing.mjs +3 -87
  44. package/dist/{types-VZVWwqCg.d.mts → types-BM4shh8K.d.mts} +259 -259
  45. package/dist/{types-xXfyk56Q.d.mts → types-BUwDHKXm.d.mts} +1377 -1377
  46. package/dist/types-Ca_z_4tQ.d.mts +586 -0
  47. package/dist/{types-CQueODzo.d.mts → types-CefgYzFt.d.mts} +2 -2
  48. package/dist/{types-DUMoYPYe.d.mts → types-DAdwEJMp.d.mts} +2 -2
  49. package/dist/{types-B0v5VVa3.d.mts → types-ZQLYXo5X.d.mts} +304 -304
  50. package/dist/visual.d.mts +6 -6
  51. package/dist/visual.mjs +5 -5
  52. package/llms.txt +120 -43
  53. package/package.json +30 -2
@@ -1,11 +1,12 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
2
+ import { r as systemSource, t as memory } from "./memory-CWlqhS7l.mjs";
3
+ import { a as finishDrafts, i as enableDraftPatches, n as deepFreeze, o as openDrafts, r as dropDrafts } from "./drafts-BawpSaVp.mjs";
2
4
  import { c as findNode, d as slotNames, f as compact, i as servePaused, l as framePath, n as captureFrame, o as collectFlows, p as pushEntry, r as serveCaptures, s as describeGraph, t as cancelCaptures, u as graphHash } from "./capture-fpd83Mi5.mjs";
3
5
  import { A as system, C as Exiting, D as component, E as Tree, T as Order, _ as Parent, a as visualOf$1, b as Transform, c as Text, g as NineSlice, h as Display, j as tag, k as resource, m as textFor, n as fitScaleOf, o as visualRectOf, p as readStyle, r as samePose, s as Countdown, t as CONTENT, u as builtInStyles, v as Shape, w as Layer, x as componentsFor, y as Sprite } from "./visual-dMW8iqAE.mjs";
4
- import { n as isDev } from "./dev-wKLgOw7c.mjs";
6
+ import { n as isDev } from "./dev-Cc50Dk7H.mjs";
5
7
  import { i as isComponentDefinition, n as flatten, r as defineComponent } from "./flatten-BkXuscAV.mjs";
6
8
  import { envPlugin, logPlugin } from "@moku-labs/common/browser";
7
9
  import { createCoreConfig } from "@moku-labs/core";
8
- import { applyPatches, createDraft, enablePatches, finishDraft } from "immer";
9
10
  /**
10
11
  * Core config of the engine: `log` and `env` on every plugin context.
11
12
  */
@@ -127,37 +128,6 @@ function createClockApi(ctx) {
127
128
  };
128
129
  }
129
130
  //#endregion
130
- //#region src/plugins/clock/system.ts
131
- /**
132
- * Tells whether a handle was produced by this source.
133
- *
134
- * @param handle - Handle given back to `clearTimer`.
135
- * @returns `true` when the handle carries a platform timer.
136
- * @example
137
- * ```ts
138
- * isSystemTimer({ timer: 7 }); // true
139
- * isSystemTimer(7); // false: a handle of another source is ignored
140
- * ```
141
- */
142
- function isSystemTimer(handle) {
143
- return typeof handle === "object" && handle !== null && "timer" in handle;
144
- }
145
- /**
146
- * Creates the system source: epoch milliseconds from the device clock and real timers.
147
- *
148
- * @returns A source reading `Date.now` and scheduling with `setTimeout`.
149
- */
150
- function systemSource() {
151
- return {
152
- now: () => Date.now(),
153
- setTimer: (callback, delayMs) => ({ timer: setTimeout(callback, delayMs) }),
154
- clearTimer: (handle) => {
155
- if (!isSystemTimer(handle)) return;
156
- clearTimeout(handle.timer);
157
- }
158
- };
159
- }
160
- //#endregion
161
131
  //#region src/plugins/clock/state.ts
162
132
  /**
163
133
  * @file clock plugin — state factory.
@@ -765,263 +735,6 @@ function createRngState() {
765
735
  return {};
766
736
  }
767
737
  //#endregion
768
- //#region src/plugins/model/store/drafts.ts
769
- /** Root order of a commit result, so two commits never report the same roots in another order. */
770
- const rootOrder = Object.freeze([
771
- "player",
772
- "session",
773
- "rng"
774
- ]);
775
- /**
776
- * Turns on Immer's patch recording. Idempotent, and called from the state factory rather than at
777
- * module scope: a module-scope call would run on import, even when no app is created.
778
- *
779
- */
780
- function enableDraftPatches() {
781
- enablePatches();
782
- }
783
- /**
784
- * Freezes a tree and every branch of it. Immer freezes what it produces; this covers the trees
785
- * that never went through a draft — the initial trees and the ones handed to `restore()`.
786
- *
787
- * @param tree - The tree to freeze, in place.
788
- * @returns The same reference, now frozen.
789
- * @example
790
- * ```ts
791
- * const tree = deepFreeze({ player: { bag: ["key"] } });
792
- * Object.isFrozen(tree.player.bag); // true
793
- * ```
794
- */
795
- function deepFreeze(tree) {
796
- if (!tree || typeof tree !== "object") return tree;
797
- for (const branch of Object.values(tree)) deepFreeze(branch);
798
- return Object.freeze(tree);
799
- }
800
- /**
801
- * Maps one Immer patch to our own structural patch, so no Immer type reaches a public type.
802
- * A removal carries no value, and `exactOptionalPropertyTypes` forbids an explicit `undefined`.
803
- *
804
- * @param patch - The Immer patch.
805
- * @param path - Path of the patch inside its own tree, without the container segment.
806
- * @returns The structural patch.
807
- * @example
808
- * ```ts
809
- * toPatch({ op: "remove", path: ["doc", "player", "key"] }, ["player", "key"]);
810
- * // { op: "remove", path: ["player", "key"] }
811
- * ```
812
- */
813
- function toPatch(patch, path) {
814
- if (patch.op === "remove") return {
815
- op: patch.op,
816
- path
817
- };
818
- return {
819
- op: patch.op,
820
- path,
821
- value: patch.value
822
- };
823
- }
824
- /**
825
- * Derives the touched roots from the patches of one commit, in a fixed order.
826
- *
827
- * @param docPatches - Patches of the save document.
828
- * @param sessionPatches - Patches of the session tree.
829
- * @returns The roots a listener has to reconcile.
830
- * @example
831
- * ```ts
832
- * rootsOf([{ op: "add", path: ["rng", "streams", "dice"], value: 7 }], []); // ["rng"]
833
- * rootsOf([], [{ op: "replace", path: ["rolls"], value: 1 }]); // ["session"]
834
- * ```
835
- */
836
- function rootsOf(docPatches, sessionPatches) {
837
- const touched = /* @__PURE__ */ new Set();
838
- for (const patch of docPatches) {
839
- const [head] = patch.path;
840
- if (head === "player" || head === "rng") touched.add(head);
841
- }
842
- if (sessionPatches.length > 0) touched.add("session");
843
- return rootOrder.filter((root) => touched.has(root));
844
- }
845
- /**
846
- * Opens mutable drafts of the frozen save document and session tree.
847
- * Both live in one draft container, so one `finishDraft` closes the transaction and the patches
848
- * arrive in one list, tagged by their container key.
849
- *
850
- * @param doc - Frozen save document.
851
- * @param session - Frozen session tree.
852
- * @returns The open drafts of one transaction.
853
- * @example
854
- * ```ts
855
- * const pair = openDrafts({ player: { coins: 1 }, rng: { seed: 7, streams: {} } }, { rolls: 0 });
856
- * pair.session; // a mutable draft of { rolls: 0 }: a write never reaches the frozen input
857
- * ```
858
- */
859
- function openDrafts(doc, session) {
860
- return createDraft({
861
- doc,
862
- session
863
- });
864
- }
865
- /**
866
- * Finishes the drafts: the new frozen trees, the patches per tree and the touched roots.
867
- * The drafts are revoked, so a draft that leaked into a view throws on the next touch instead of
868
- * corrupting the committed tree in silence.
869
- *
870
- * @param pair - Open drafts of one transaction.
871
- * @returns The frozen trees, the patches split by tree, and the touched roots.
872
- */
873
- function finishDrafts(pair) {
874
- const collected = [];
875
- const finished = finishDraft(pair, (patches) => {
876
- collected.push(...patches);
877
- });
878
- const docPatches = [];
879
- const sessionPatches = [];
880
- for (const patch of collected) {
881
- const [container, ...path] = patch.path;
882
- (container === "doc" ? docPatches : sessionPatches).push(toPatch(patch, path));
883
- }
884
- return {
885
- doc: finished.doc,
886
- session: finished.session,
887
- patches: {
888
- doc: docPatches,
889
- session: sessionPatches
890
- },
891
- roots: rootsOf(docPatches, sessionPatches)
892
- };
893
- }
894
- /**
895
- * Applies the patches of one commit to a document, the way a save seam stores what it was handed.
896
- * A whole-document patch carries the path `[]` and replaces the document. The input is left as it
897
- * is: the result is a new frozen document.
898
- *
899
- * @param document - The document the patches apply to.
900
- * @param patches - The patches of one commit, in order.
901
- * @returns The document after the patches.
902
- * @throws {Error} When a patch names a path the document does not have.
903
- * @example
904
- * ```ts
905
- * applyTo({ player: { coins: 0 } }, [{ op: "replace", path: ["player", "coins"], value: 4 }]);
906
- * // { player: { coins: 4 } }
907
- * ```
908
- */
909
- function applyTo(document, patches) {
910
- enablePatches();
911
- return applyPatches(document, patches);
912
- }
913
- /**
914
- * Drops the drafts without a result: the base trees stay as they are and the drafts are revoked.
915
- *
916
- * @param pair - Open drafts of one transaction.
917
- */
918
- function dropDrafts(pair) {
919
- finishDraft(pair);
920
- }
921
- //#endregion
922
- //#region src/plugins/model/store/providers/memory.ts
923
- /** Seed of a save built by `saveOf` when the caller does not pin one. */
924
- const defaultSeed = 1;
925
- /** The base a patch applies to when the provider was never handed a document. */
926
- const noDocument = {};
927
- /**
928
- * Creates an in-memory provider that keeps what it was committed and records every call.
929
- * It is the default when a game configures no `playerProvider`, and the double every test uses:
930
- * `provider.calls` is the whole persistence protocol of one run, in order. The document lives as
931
- * long as the provider does, so a second app over the same instance reads what the first one
932
- * wrote, and nothing survives the process.
933
- *
934
- * `load()` reads the fixture, or everything committed since; without either, the player is new.
935
- * `commitDurable()` resolves at once: there is no disk behind it.
936
- *
937
- * @param fixture - The save `load()` starts from. Omitted: a new player.
938
- * @param fixture.state - The saved document.
939
- * @param fixture.version - Schema version of the saved document.
940
- * @returns A provider that keeps its document and records its calls.
941
- * @example
942
- * ```ts
943
- * // A new player is saved at once. One roll reaches the provider at the next rest node.
944
- * const provider = memory();
945
- * const game = await createHeadless(createGame(provider));
946
- *
947
- * await game.walk([{ at: "home", intent: "roll" }]);
948
- * provider.calls.map(call => call.method); // ["load", "commit", "commit"]
949
- * ```
950
- */
951
- function memory(fixture) {
952
- const calls = [];
953
- const held = {
954
- document: fixture?.state,
955
- version: fixture?.version ?? 0
956
- };
957
- /**
958
- * Applies the patches of one commit to the held document.
959
- *
960
- * @param patches - Doc patches since the last commit.
961
- * @param version - Schema version this build writes.
962
- * @throws {Error} When a patch names a path the held document does not have.
963
- */
964
- const hold = (patches, version) => {
965
- held.document = applyTo(held.document ?? noDocument, patches);
966
- held.version = version;
967
- };
968
- return {
969
- calls,
970
- load: async () => {
971
- calls.push({ method: "load" });
972
- const document = held.document;
973
- if (document === void 0) return null;
974
- return {
975
- state: document,
976
- version: held.version
977
- };
978
- },
979
- commit: (patches, version) => {
980
- calls.push({
981
- method: "commit",
982
- patches,
983
- version
984
- });
985
- hold(patches, version);
986
- },
987
- commitDurable: async (patches, txId, version) => {
988
- calls.push({
989
- method: "commitDurable",
990
- patches,
991
- txId,
992
- version
993
- });
994
- hold(patches, version);
995
- },
996
- flush: async () => {
997
- calls.push({ method: "flush" });
998
- }
999
- };
1000
- }
1001
- /**
1002
- * Builds a save document from a player tree, with no rng streams drawn yet.
1003
- * Short fixtures in tests read as `saveOf({ coins: 5 })` instead of spelling out the rng branch.
1004
- *
1005
- * @param player - Player tree of the save.
1006
- * @param seed - Rng seed of the save.
1007
- * @returns The save document.
1008
- * @example
1009
- * ```ts
1010
- * // A returning player with 5 coins. The test starts from this save, not from `initialPlayer`.
1011
- * saveOf({ coins: 5 }, 42); // { player: { coins: 5 }, rng: { seed: 42, streams: {} } }
1012
- * const provider = memory({ state: saveOf({ coins: 5 }, 42), version: 1 });
1013
- * ```
1014
- */
1015
- function saveOf(player, seed = defaultSeed) {
1016
- return {
1017
- player,
1018
- rng: {
1019
- seed,
1020
- streams: {}
1021
- }
1022
- };
1023
- }
1024
- //#endregion
1025
738
  //#region src/plugins/model/store/state.ts
1026
739
  /** Seed of the trees that exist before `load()` ran. A save or a new player replaces it. */
1027
740
  const preloadSeed = 0;
@@ -23777,13 +23490,24 @@ function setAwake(ctx, provider, on) {
23777
23490
  //#endregion
23778
23491
  //#region src/plugins/platform/api.ts
23779
23492
  /**
23780
- * @file platform plugin — API factory and the Back chain. One press runs three steps and the
23781
- * first that takes it wins: the Escape button of the top popup, the intent `back` of the resting
23782
- * node, the provider leaving the app.
23493
+ * @file platform plugin — API factory, the Back chain and the exit. One press runs three steps and
23494
+ * the first that takes it wins: the Escape button of the top popup, the intent `back` of the
23495
+ * resting node, the provider leaving the app. `exit()` is that last step alone.
23783
23496
  */
23784
23497
  /** The intent a resting node lists to take the Back press itself. */
23785
23498
  const BACK_INTENT = "back";
23786
23499
  /**
23500
+ * Leaves the app through the provider, guarded like every provider call. Without a provider it
23501
+ * does nothing. The last step of the Back chain and `exit()` both go through here.
23502
+ *
23503
+ * @param ctx - Kernel context of the platform plugin.
23504
+ */
23505
+ function leave(ctx) {
23506
+ const provider = ctx.config.provider;
23507
+ if (provider === void 0) return;
23508
+ safely(ctx.log, "exit", () => provider.exit());
23509
+ }
23510
+ /**
23787
23511
  * Runs the Back chain once. `back()` and the provider's `onBack` both go through here, so a test
23788
23512
  * and a real press end the same way. Escape is what ui answers with the `escape` button of its
23789
23513
  * top root; it also ends the editing of a text field. While the graph moves between nodes the gate
@@ -23794,22 +23518,24 @@ const BACK_INTENT = "back";
23794
23518
  * @returns What took the press, `"none"` without a provider.
23795
23519
  */
23796
23520
  function runBack(ctx) {
23797
- const provider = ctx.config.provider;
23798
- if (provider === void 0) return "none";
23521
+ if (ctx.config.provider === void 0) return "none";
23799
23522
  if (ctx.require(inputPlugin).pressKey("Escape")) return "popup";
23800
23523
  const gate = ctx.require(flowPlugin).gate;
23801
23524
  if (gate.answer({ intent: BACK_INTENT }) || !gate.state().open) return "intent";
23802
- safely(ctx.log, "exit", () => provider.exit());
23525
+ leave(ctx);
23803
23526
  return "exit";
23804
23527
  }
23805
23528
  /**
23806
- * Creates the platform API: the Back chain on demand.
23529
+ * Creates the platform API: the Back chain and the exit on demand.
23807
23530
  *
23808
23531
  * @param ctx - Kernel context of the platform plugin.
23809
23532
  * @returns The plugin API.
23810
23533
  */
23811
23534
  function createPlatformApi(ctx) {
23812
- return { back: () => runBack(ctx) };
23535
+ return {
23536
+ back: () => runBack(ctx),
23537
+ exit: () => leave(ctx)
23538
+ };
23813
23539
  }
23814
23540
  //#endregion
23815
23541
  //#region src/plugins/platform/state.ts
@@ -34532,21 +34258,30 @@ function defineMotion(spec) {
34532
34258
  * | effects | maxPasses | 24, render passes per frame above which one dev warning fires per crossing |
34533
34259
  * | effects | phone | "auto", a coarse pointer and a short side of at most 820 CSS px |
34534
34260
  * | effects | blur | { quality: 2, phoneResolution: 0.5 }, what a Blur with quality 0 and resolution 0 resolves to; shallow merge: a game that sets `blur` gives both fields |
34535
- * | platform | provider | undefined, inert: no subscription, no haptic handler, `back()` answers "none" |
34261
+ * | platform | provider | undefined, inert: no subscription, no haptic handler, `back()` answers "none"; the engine page passes the system shell's provider |
34536
34262
  * | platform | keepAwake | false |
34537
34263
  *
34538
34264
  * Subpaths next to the root:
34539
34265
  *
34540
34266
  * | Subpath | What |
34541
34267
  * |---|---|
34268
+ * | `@moku-labs/game/app` | `defineGameApp`, the game as one data object with `headless()` and `screen()`; `startMoment`; the types of `index.ts` and `config.ts` (`GameConfig`) |
34269
+ * | `@moku-labs/game/app/page` | `startPage`, the page the generated `.moku/main.ts` calls: `?player=` scenarios, the system shell, the dev agents |
34270
+ * | `@moku-labs/game/app/system` | the system shell over the optional peer `@moku-labs/system`: `systemShellOf`, `fromSystem`, `storeSave`, `createSystemApp` |
34271
+ * | `@moku-labs/game/cli` | `runCli` of the bin `moku-game` and `preparePage` for the editor, node and bun only |
34542
34272
  * | `@moku-labs/game/testing` | headless play, repro runs, isolated feature tests (`isolate`, `stub`), the in-memory save, the fake clock |
34543
34273
  * | `@moku-labs/game/visual` | the visual tests, node and bun only |
34544
34274
  * | `@moku-labs/game/assets` | the build-time asset key scanner and the production packer (`packAssets`, `--pack <dir>`), node and bun only |
34545
34275
  * | `@moku-labs/game/inspect` | the editor's read door: `read`, `watch`, `defineSource`, the `sources` catalogue |
34546
34276
  * | `@moku-labs/game/control` | the editor's write door, dev builds only: `run`, `defineCommand`, the `commands` catalogue |
34277
+ * | `@moku-labs/game/hot` | the Bun plugin that hot swaps views; `moku-game dev` lists it in its bunfig |
34278
+ * | `@moku-labs/game/lint` | the oxlint JS plugin `moku-game` with the game lint rules |
34279
+ * | `@moku-labs/game/project` | the project index of a game's sources, node and bun only |
34547
34280
  * | `@moku-labs/game/jsx-runtime`, `/jsx-dev-runtime` | the JSX runtimes `jsxImportSource: "@moku-labs/game"` names |
34548
34281
  * | `@moku-labs/game/fonts/*` | the MSDF body font `font-body.fnt` + `.png` and its OFL licence; a game copies them into `features/ui/assets/` |
34282
+ * | bin `moku-game` | `dev`, `build`, `native <verb>`, `keys`, `pack`: serves, builds, packages and packs a game folder, bun only |
34549
34283
  * | bin `moku-game-assets` | the asset scanner CLI, bun only |
34284
+ * | bin `moku-game-index` | the project index CLI, bun only |
34550
34285
  *
34551
34286
  * @file The package root: the composed framework and its public exports.
34552
34287
  * @example
@@ -34641,4 +34376,4 @@ const screen = [
34641
34376
  uiPlugin
34642
34377
  ];
34643
34378
  //#endregion
34644
- export { sfx as $, types_exports$13 as A, defineFilter as At, defineStyle as B, flowPlugin as Bt, types_exports$6 as C, Alpha as Ct, types_exports$10 as D, Glow as Dt, types_exports$9 as E, Displacement as Et, resolve as F, AnimPlayer as Ft, external as G, modelPlugin as Gt, platformPlugin as H, slot as Ht, Box as I, Animation as It, mark as J, saveOf as Jt, frames as K, SaveUnreadableError as Kt, LocalWrite as L, Frames as Lt, types_exports$15 as M, scenesPlugin as Mt, types_exports$16 as N, assetsPlugin as Nt, types_exports$11 as O, Noise as Ot, uiPlugin as P, animPlugin as Pt, set as Q, popup as R, rendererPlugin as Rt, types_exports$5 as S, Emitter as St, types_exports$8 as T, ColorMatrix as Tt, HAPTIC_KINDS as U, to as Ut, textPlugin as V, exit as Vt, defineAnimation as W, type as Wt, play$1 as X, timePlugin as Xt, parallel as Y, lifecyclePlugin as Yt, sequence as Z, clockPlugin as Zt, types_exports as _, Touchable as _t, defineMotion as a, wait as at, types_exports$3 as b, i18nPlugin as bt, schedule as c, DropTarget as ct, tr as d, Pointer as dt, spawn as et, defineFeature as f, PointerOver as ft, load as g, Tappable as gt, defineBundles as h, Swipeable as ht, screen as i, use as it, types_exports$14 as j, audioPlugin as jt, types_exports$12 as k, Outline as kt, projection as l, Held as lt, music as m, Pressed as mt, createPlugin as n, stagger as nt, guide as o, inputPlugin as ot, defineEmitter as p, Pressable as pt, haptic as q, memory as qt, defineGame as r, tween as rt, hint as s, Draggable as st, createApp as t, spawned as tt, defineScene as u, Hovered as ut, types_exports$1 as v, Traceable as vt, types_exports$7 as w, Blur as wt, types_exports$4 as x, effectsPlugin as xt, types_exports$2 as y, Traced as yt, defineTokens as z, worldPlugin as zt };
34379
+ export { sfx as $, types_exports$13 as A, defineFilter as At, defineStyle as B, flowPlugin as Bt, types_exports$6 as C, Alpha as Ct, types_exports$10 as D, Glow as Dt, types_exports$9 as E, Displacement as Et, resolve as F, AnimPlayer as Ft, external as G, modelPlugin as Gt, platformPlugin as H, slot as Ht, Box as I, Animation as It, mark as J, timePlugin as Jt, frames as K, SaveUnreadableError as Kt, LocalWrite as L, Frames as Lt, types_exports$15 as M, scenesPlugin as Mt, types_exports$16 as N, assetsPlugin as Nt, types_exports$11 as O, Noise as Ot, uiPlugin as P, animPlugin as Pt, set as Q, popup as R, rendererPlugin as Rt, types_exports$5 as S, Emitter as St, types_exports$8 as T, ColorMatrix as Tt, HAPTIC_KINDS as U, to as Ut, textPlugin as V, exit as Vt, defineAnimation as W, type as Wt, play$1 as X, parallel as Y, clockPlugin as Yt, sequence as Z, types_exports as _, Touchable as _t, defineMotion as a, wait as at, types_exports$3 as b, i18nPlugin as bt, schedule as c, DropTarget as ct, tr as d, Pointer as dt, spawn as et, defineFeature as f, PointerOver as ft, load as g, Tappable as gt, defineBundles as h, Swipeable as ht, screen as i, use as it, types_exports$14 as j, audioPlugin as jt, types_exports$12 as k, Outline as kt, projection as l, Held as lt, music as m, Pressed as mt, createPlugin as n, stagger as nt, guide as o, inputPlugin as ot, defineEmitter as p, Pressable as pt, haptic as q, lifecyclePlugin as qt, defineGame as r, tween as rt, hint as s, Draggable as st, createApp as t, spawned as tt, defineScene as u, Hovered as ut, types_exports$1 as v, Traceable as vt, types_exports$7 as w, Blur as wt, types_exports$4 as x, effectsPlugin as xt, types_exports$2 as y, Traced as yt, defineTokens as z, worldPlugin as zt };
@@ -1,6 +1,7 @@
1
- import { $t as PlayerStateProvider, Et as AnyFlow, Jt as Json, Mt as NodeTable, en as ProviderCall, gn as FakeClock, mn as ClockSource, mt as FeaturePlugin, tn as SaveDoc } from "./types-B0v5VVa3.mjs";
2
- import { a as createHeadless, i as ReproResult, n as HeadlessGame, o as runRepro, r as Repro, s as stepFrames, t as HeadlessApp } from "./headless-SCQXJxQf.mjs";
3
- import { t as createApp } from "./index-BO9kZzY3.mjs";
1
+ import { Dt as AnyFlow, Ht as ClockSource, Nt as NodeTable, Wt as FakeClock, Xt as Json, ht as FeaturePlugin } from "./types-ZQLYXo5X.mjs";
2
+ import { t as createApp } from "./index-8TiJQjQA.mjs";
3
+ import { n as saveOf, t as memory } from "./memory-DwDhfFWK.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-G4nuZ-xz.mjs";
4
5
  import { AnyPluginInstance } from "@moku-labs/core";
5
6
 
6
7
  //#region src/plugins/clock/fake.d.ts
@@ -25,53 +26,6 @@ import { AnyPluginInstance } from "@moku-labs/core";
25
26
  */
26
27
  declare function fakeClock(start?: number): FakeClock;
27
28
  //#endregion
28
- //#region src/plugins/model/store/providers/memory.d.ts
29
- /**
30
- * Creates an in-memory provider that keeps what it was committed and records every call.
31
- * It is the default when a game configures no `playerProvider`, and the double every test uses:
32
- * `provider.calls` is the whole persistence protocol of one run, in order. The document lives as
33
- * long as the provider does, so a second app over the same instance reads what the first one
34
- * wrote, and nothing survives the process.
35
- *
36
- * `load()` reads the fixture, or everything committed since; without either, the player is new.
37
- * `commitDurable()` resolves at once: there is no disk behind it.
38
- *
39
- * @param fixture - The save `load()` starts from. Omitted: a new player.
40
- * @param fixture.state - The saved document.
41
- * @param fixture.version - Schema version of the saved document.
42
- * @returns A provider that keeps its document and records its calls.
43
- * @example
44
- * ```ts
45
- * // A new player is saved at once. One roll reaches the provider at the next rest node.
46
- * const provider = memory();
47
- * const game = await createHeadless(createGame(provider));
48
- *
49
- * await game.walk([{ at: "home", intent: "roll" }]);
50
- * provider.calls.map(call => call.method); // ["load", "commit", "commit"]
51
- * ```
52
- */
53
- declare function memory(fixture?: {
54
- state: SaveDoc;
55
- version: number;
56
- }): PlayerStateProvider & {
57
- calls: ProviderCall[];
58
- };
59
- /**
60
- * Builds a save document from a player tree, with no rng streams drawn yet.
61
- * Short fixtures in tests read as `saveOf({ coins: 5 })` instead of spelling out the rng branch.
62
- *
63
- * @param player - Player tree of the save.
64
- * @param seed - Rng seed of the save.
65
- * @returns The save document.
66
- * @example
67
- * ```ts
68
- * // A returning player with 5 coins. The test starts from this save, not from `initialPlayer`.
69
- * saveOf({ coins: 5 }, 42); // { player: { coins: 5 }, rng: { seed: 42, streams: {} } }
70
- * const provider = memory({ state: saveOf({ coins: 5 }, 42), version: 1 });
71
- * ```
72
- */
73
- declare function saveOf(player: Json, seed?: number): SaveDoc;
74
- //#endregion
75
29
  //#region src/testing/isolate.d.ts
76
30
  /**
77
31
  * A stub for one node or sub-flow of the isolated flow: the outcome it ends with, and its payload.
package/dist/testing.mjs CHANGED
@@ -1,91 +1,7 @@
1
- import { Jt as saveOf, Vt as exit, Wt as type, qt as memory, t as createApp } from "./src-536arXjV.mjs";
1
+ import { Vt as exit, Wt as type, t as createApp } from "./src-BSbtHZWk.mjs";
2
+ import { n as saveOf, t as memory } from "./memory-CWlqhS7l.mjs";
3
+ import { t as fakeClock } from "./fake-MpzwZhIZ.mjs";
2
4
  import { i as stepFrames, r as runRepro, t as createHeadless } from "./headless-BsNdJs9W.mjs";
3
- //#region src/plugins/clock/fake.ts
4
- /**
5
- * Removes and returns the timer that is due first, up to and including `until`.
6
- * Timers due at the same moment come out in the order they were scheduled.
7
- *
8
- * @param state - Innards of the fake clock.
9
- * @param until - Latest moment that counts as due.
10
- * @returns The timer to fire, or `undefined` when nothing is due.
11
- */
12
- function takeDue(state, until) {
13
- let earliest;
14
- for (const timer of state.timers) {
15
- if (timer.at > until) continue;
16
- if (earliest === void 0 || timer.at < earliest.at) earliest = timer;
17
- }
18
- if (earliest === void 0) return void 0;
19
- const due = earliest;
20
- state.timers = state.timers.filter((timer) => timer !== due);
21
- return due;
22
- }
23
- /**
24
- * Moves the clock to `until`, firing every timer that falls due on the way, in due order.
25
- * A timer scheduled inside a callback fires in the same run when it is due before `until`.
26
- *
27
- * @param state - Innards of the fake clock.
28
- * @param until - Moment to stop at.
29
- */
30
- function runDue(state, until) {
31
- let due = takeDue(state, until);
32
- while (due !== void 0) {
33
- state.now = Math.max(state.now, due.at);
34
- due.callback();
35
- due = takeDue(state, until);
36
- }
37
- state.now = Math.max(state.now, until);
38
- }
39
- /**
40
- * Creates a fake source for tests. Timers fire synchronously inside `advance`, in due order. A
41
- * negative delay counts as zero and an unknown handle is ignored.
42
- *
43
- * @param start - Starting moment in epoch milliseconds. Defaults to 0.
44
- * @returns A clock source with the test-only controls `advance` and `set`.
45
- * @example
46
- * ```ts
47
- * // An energy point refills 60 s after it was spent. The test moves the clock, not the minute.
48
- * const clock = fakeClock(1000);
49
- * const app = createApp({ pluginConfigs: { clock: { source: clock } } });
50
- * const seen: number[] = [];
51
- *
52
- * app.clock.onElapsed(({ now }) => seen.push(now));
53
- * app.clock.scheduleAt(61_000);
54
- * clock.advance(60_000); // the timer fires inside advance, nothing waits
55
- * seen; // [61000]
56
- * ```
57
- */
58
- function fakeClock(start = 0) {
59
- const state = {
60
- now: start,
61
- nextId: 1,
62
- timers: []
63
- };
64
- return {
65
- now: () => state.now,
66
- setTimer: (callback, delayMs) => {
67
- const id = state.nextId;
68
- state.nextId += 1;
69
- state.timers.push({
70
- id,
71
- at: state.now + Math.max(0, delayMs),
72
- callback
73
- });
74
- return id;
75
- },
76
- clearTimer: (handle) => {
77
- if (typeof handle !== "number") return;
78
- state.timers = state.timers.filter((timer) => timer.id !== handle);
79
- },
80
- advance: (ms) => {
81
- runDue(state, state.now + Math.max(0, ms));
82
- },
83
- set: (moment) => {
84
- state.now = moment;
85
- }
86
- };
87
- }
88
- //#endregion
89
5
  //#region src/testing/isolate.ts
90
6
  const noPayload = null;
91
7
  /** Id of the harness main flow. */