@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,8 +1,8 @@
1
- import { At as FlowState, C as Captured, Dt as Bookmark, T as DebugSwitches, Ut as Time, k as Api$2, m as Api$1, sn as Api } from "./types-B0v5VVa3.mjs";
2
- import { t as HeadlessApp } from "./headless-SCQXJxQf.mjs";
3
- import { n as Command, r as ControlApp } from "./types-DUMoYPYe.mjs";
4
- import { H as InputApi } from "./types-xXfyk56Q.mjs";
5
- import { b as AnimApi, p as ScenesApi, t as AudioApi, v as UiApi } from "./types-VZVWwqCg.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-ZQLYXo5X.mjs";
2
+ import { a as AudioApi, r as UiApi, v as ScenesApi, x as AnimApi } from "./types-BM4shh8K.mjs";
3
+ import { A as InputApi } from "./types-BUwDHKXm.mjs";
4
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
5
+ import { n as Command, r as ControlApp } from "./types-DAdwEJMp.mjs";
6
6
 
7
7
  //#region src/plugins/flow/doors/commands.d.ts
8
8
  /**
@@ -0,0 +1,81 @@
1
+ //#region src/app/config.ts
2
+ /**
3
+ * The save kinds a config may name.
4
+ *
5
+ * @returns The set of save kinds.
6
+ * @example
7
+ * ```ts
8
+ * saveKinds().has("disk"); // false
9
+ * ```
10
+ */
11
+ function saveKinds() {
12
+ return /* @__PURE__ */ new Set([
13
+ "memory",
14
+ "local",
15
+ "store"
16
+ ]);
17
+ }
18
+ /**
19
+ * The system plugins the game shell wires, by their `@moku-labs/system` names.
20
+ *
21
+ * @returns The set of system names.
22
+ * @example
23
+ * ```ts
24
+ * systemNames().has("keepAwake"); // true
25
+ * systemNames().has("tray"); // false
26
+ * ```
27
+ */
28
+ function systemNames() {
29
+ return /* @__PURE__ */ new Set([
30
+ "lifecycle",
31
+ "back",
32
+ "haptics",
33
+ "keepAwake",
34
+ "store"
35
+ ]);
36
+ }
37
+ /**
38
+ * Throws for the first value of a config that TypeScript would have refused.
39
+ *
40
+ * @param config - The default export of `config.ts`.
41
+ * @throws {Error} When `save` is not a save kind, a `system` name is not wired, or the title is empty.
42
+ */
43
+ function checkConfig(config) {
44
+ if (config.save !== void 0 && !saveKinds().has(config.save)) throw new Error(`[game] config.save is "${config.save}".\n Use "memory", "local" or "store".`);
45
+ const wired = systemNames();
46
+ const unwired = config.system?.find((name) => !wired.has(name));
47
+ if (unwired !== void 0) throw new Error(`[game] config.system names "${unwired}", which the game shell does not wire.\n Use lifecycle, back, haptics, keepAwake or store.`);
48
+ if (config.page.title.trim() === "") throw new Error("[game] config.page.title is empty.\n Give the page a title in config.ts.");
49
+ }
50
+ /**
51
+ * Fills every default of a game's `config.ts`: `lang` `"en"`, `background` `"#000000"`,
52
+ * `orientation` `"portrait"`, no icons, no head tags, no system plugin, the memory save, no asset
53
+ * layers.
54
+ *
55
+ * @param config - The default export of `config.ts`.
56
+ * @returns The config with every field set.
57
+ * @throws {Error} When `save` is not a save kind, a `system` name is not wired, or the title is empty.
58
+ * @example
59
+ * ```ts
60
+ * resolveConfig({ page: { title: "T" } }).save; // "memory"
61
+ * ```
62
+ */
63
+ function resolveConfig(config) {
64
+ checkConfig(config);
65
+ return {
66
+ page: {
67
+ title: config.page.title,
68
+ lang: config.page.lang ?? "en",
69
+ background: config.page.background ?? "#000000",
70
+ orientation: config.page.orientation ?? "portrait",
71
+ icons: config.page.icons ?? {},
72
+ head: config.page.head ?? []
73
+ },
74
+ native: config.native,
75
+ system: config.system ?? [],
76
+ save: config.save ?? "memory",
77
+ assets: { layers: config.assets?.layers ?? {} }
78
+ };
79
+ }
80
+ //#endregion
81
+ export { resolveConfig as t };
@@ -1,13 +1,14 @@
1
- import { t as HeadlessApp } from "./headless-SCQXJxQf.mjs";
2
- import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-DUMoYPYe.mjs";
3
- import { t as commands } from "./commands-DuwCxK5t.mjs";
4
- import { t as defineCommand } from "./define-B6pHpdBi.mjs";
1
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
2
+ import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-DAdwEJMp.mjs";
3
+ import { t as commands } from "./commands-C4QlqijJ.mjs";
4
+ import { t as defineCommand } from "./define-CCc0ozKe.mjs";
5
5
 
6
6
  //#region src/plugins/flow/doors/dev.d.ts
7
7
  /**
8
8
  * @file flow/doors — the dev flag and the errors a door throws. `__MOKU_GAME_DEV__` is a global
9
- * the engine never replaces: a game's dev build defines it `true` (bundler `define`) or sets
10
- * `globalThis.__MOKU_GAME_DEV__` before the engine runs; undefined means a production build.
9
+ * the engine never replaces: `moku-game dev` defines it `true` (bunfig `define`) and sets
10
+ * `globalThis.__MOKU_GAME_DEV__` in `.moku/dev.ts` before the engine runs; `moku-game build`
11
+ * defines it `false`; undefined means a production build.
11
12
  *
12
13
  * Bun does not inline `isDev()` across modules (checked on Bun 1.3.14), so a branch that must
13
14
  * vanish from a production bundle writes the guard inline:
@@ -16,12 +17,12 @@ import { t as defineCommand } from "./define-B6pHpdBi.mjs";
16
17
  */
17
18
  declare global {
18
19
  /**
19
- * The dev flag of a game. A game never re-declares it: it defines it in the bundler, or sets
20
- * `globalThis.__MOKU_GAME_DEV__ = true` in its dev entry.
20
+ * The dev flag of a game. A game never re-declares it: `moku-game dev` sets it, `moku-game
21
+ * build` defines it `false`, and a test stubs it.
21
22
  *
22
23
  * @example
23
24
  * ```ts
24
- * // web/dev.ts, imported first by the dev page: /control commands run from here on.
25
+ * // .moku/dev.ts, written by moku-game dev, the first import of the dev page: /control commands run from here on.
25
26
  * globalThis.__MOKU_GAME_DEV__ = true;
26
27
  * ```
27
28
  */
@@ -33,7 +34,7 @@ declare global {
33
34
  * @returns True only when `__MOKU_GAME_DEV__` is exactly `true`.
34
35
  * @example
35
36
  * ```ts
36
- * // The dev page set the flag in web/dev.ts before the engine started.
37
+ * // The dev page set the flag in .moku/dev.ts before the engine started.
37
38
  * globalThis.__MOKU_GAME_DEV__ = true;
38
39
  * isDev(); // true
39
40
  * ```
package/dist/control.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as controlRefused } from "./dev-wKLgOw7c.mjs";
2
- import { s as defineCommand } from "./input-z_35Jwfn.mjs";
3
- import { n as commands, t as run } from "./run-CofJEdYx.mjs";
1
+ import { t as controlRefused } from "./dev-Cc50Dk7H.mjs";
2
+ import { s as defineCommand } from "./session-DgukhR_7.mjs";
3
+ import { n as commands, t as run } from "./run-wYty_Lwo.mjs";
4
4
  export { commands, controlRefused, defineCommand, run };
@@ -1,5 +1,5 @@
1
- import { t as HeadlessApp } from "./headless-SCQXJxQf.mjs";
2
- import { c as Source, n as Command, o as InputSchema } from "./types-DUMoYPYe.mjs";
1
+ import { t as HeadlessApp } from "./headless-G4nuZ-xz.mjs";
2
+ import { c as Source, n as Command, o as InputSchema } from "./types-DAdwEJMp.mjs";
3
3
 
4
4
  //#region src/plugins/flow/doors/define.d.ts
5
5
  /**
@@ -5,7 +5,7 @@
5
5
  * @returns True only when `__MOKU_GAME_DEV__` is exactly `true`.
6
6
  * @example
7
7
  * ```ts
8
- * // The dev page set the flag in web/dev.ts before the engine started.
8
+ * // The dev page set the flag in .moku/dev.ts before the engine started.
9
9
  * globalThis.__MOKU_GAME_DEV__ = true;
10
10
  * isDev(); // true
11
11
  * ```
@@ -0,0 +1,156 @@
1
+ import { applyPatches, createDraft, enablePatches, finishDraft } from "immer";
2
+ //#region src/plugins/model/store/drafts.ts
3
+ /** Root order of a commit result, so two commits never report the same roots in another order. */
4
+ const rootOrder = Object.freeze([
5
+ "player",
6
+ "session",
7
+ "rng"
8
+ ]);
9
+ /**
10
+ * Turns on Immer's patch recording. Idempotent, and called from the state factory rather than at
11
+ * module scope: a module-scope call would run on import, even when no app is created.
12
+ *
13
+ */
14
+ function enableDraftPatches() {
15
+ enablePatches();
16
+ }
17
+ /**
18
+ * Freezes a tree and every branch of it. Immer freezes what it produces; this covers the trees
19
+ * that never went through a draft — the initial trees and the ones handed to `restore()`.
20
+ *
21
+ * @param tree - The tree to freeze, in place.
22
+ * @returns The same reference, now frozen.
23
+ * @example
24
+ * ```ts
25
+ * const tree = deepFreeze({ player: { bag: ["key"] } });
26
+ * Object.isFrozen(tree.player.bag); // true
27
+ * ```
28
+ */
29
+ function deepFreeze(tree) {
30
+ if (!tree || typeof tree !== "object") return tree;
31
+ for (const branch of Object.values(tree)) deepFreeze(branch);
32
+ return Object.freeze(tree);
33
+ }
34
+ /**
35
+ * Maps one Immer patch to our own structural patch, so no Immer type reaches a public type.
36
+ * A removal carries no value, and `exactOptionalPropertyTypes` forbids an explicit `undefined`.
37
+ *
38
+ * @param patch - The Immer patch.
39
+ * @param path - Path of the patch inside its own tree, without the container segment.
40
+ * @returns The structural patch.
41
+ * @example
42
+ * ```ts
43
+ * toPatch({ op: "remove", path: ["doc", "player", "key"] }, ["player", "key"]);
44
+ * // { op: "remove", path: ["player", "key"] }
45
+ * ```
46
+ */
47
+ function toPatch(patch, path) {
48
+ if (patch.op === "remove") return {
49
+ op: patch.op,
50
+ path
51
+ };
52
+ return {
53
+ op: patch.op,
54
+ path,
55
+ value: patch.value
56
+ };
57
+ }
58
+ /**
59
+ * Derives the touched roots from the patches of one commit, in a fixed order.
60
+ *
61
+ * @param docPatches - Patches of the save document.
62
+ * @param sessionPatches - Patches of the session tree.
63
+ * @returns The roots a listener has to reconcile.
64
+ * @example
65
+ * ```ts
66
+ * rootsOf([{ op: "add", path: ["rng", "streams", "dice"], value: 7 }], []); // ["rng"]
67
+ * rootsOf([], [{ op: "replace", path: ["rolls"], value: 1 }]); // ["session"]
68
+ * ```
69
+ */
70
+ function rootsOf(docPatches, sessionPatches) {
71
+ const touched = /* @__PURE__ */ new Set();
72
+ for (const patch of docPatches) {
73
+ const [head] = patch.path;
74
+ if (head === "player" || head === "rng") touched.add(head);
75
+ }
76
+ if (sessionPatches.length > 0) touched.add("session");
77
+ return rootOrder.filter((root) => touched.has(root));
78
+ }
79
+ /**
80
+ * Opens mutable drafts of the frozen save document and session tree.
81
+ * Both live in one draft container, so one `finishDraft` closes the transaction and the patches
82
+ * arrive in one list, tagged by their container key.
83
+ *
84
+ * @param doc - Frozen save document.
85
+ * @param session - Frozen session tree.
86
+ * @returns The open drafts of one transaction.
87
+ * @example
88
+ * ```ts
89
+ * const pair = openDrafts({ player: { coins: 1 }, rng: { seed: 7, streams: {} } }, { rolls: 0 });
90
+ * pair.session; // a mutable draft of { rolls: 0 }: a write never reaches the frozen input
91
+ * ```
92
+ */
93
+ function openDrafts(doc, session) {
94
+ return createDraft({
95
+ doc,
96
+ session
97
+ });
98
+ }
99
+ /**
100
+ * Finishes the drafts: the new frozen trees, the patches per tree and the touched roots.
101
+ * The drafts are revoked, so a draft that leaked into a view throws on the next touch instead of
102
+ * corrupting the committed tree in silence.
103
+ *
104
+ * @param pair - Open drafts of one transaction.
105
+ * @returns The frozen trees, the patches split by tree, and the touched roots.
106
+ */
107
+ function finishDrafts(pair) {
108
+ const collected = [];
109
+ const finished = finishDraft(pair, (patches) => {
110
+ collected.push(...patches);
111
+ });
112
+ const docPatches = [];
113
+ const sessionPatches = [];
114
+ for (const patch of collected) {
115
+ const [container, ...path] = patch.path;
116
+ (container === "doc" ? docPatches : sessionPatches).push(toPatch(patch, path));
117
+ }
118
+ return {
119
+ doc: finished.doc,
120
+ session: finished.session,
121
+ patches: {
122
+ doc: docPatches,
123
+ session: sessionPatches
124
+ },
125
+ roots: rootsOf(docPatches, sessionPatches)
126
+ };
127
+ }
128
+ /**
129
+ * Applies the patches of one commit to a document, the way a save seam stores what it was handed.
130
+ * A whole-document patch carries the path `[]` and replaces the document. The input is left as it
131
+ * is: the result is a new frozen document.
132
+ *
133
+ * @param document - The document the patches apply to.
134
+ * @param patches - The patches of one commit, in order.
135
+ * @returns The document after the patches.
136
+ * @throws {Error} When a patch names a path the document does not have.
137
+ * @example
138
+ * ```ts
139
+ * applyTo({ player: { coins: 0 } }, [{ op: "replace", path: ["player", "coins"], value: 4 }]);
140
+ * // { player: { coins: 4 } }
141
+ * ```
142
+ */
143
+ function applyTo(document, patches) {
144
+ enablePatches();
145
+ return applyPatches(document, patches);
146
+ }
147
+ /**
148
+ * Drops the drafts without a result: the base trees stay as they are and the drafts are revoked.
149
+ *
150
+ * @param pair - Open drafts of one transaction.
151
+ */
152
+ function dropDrafts(pair) {
153
+ finishDraft(pair);
154
+ }
155
+ //#endregion
156
+ export { finishDrafts as a, enableDraftPatches as i, deepFreeze as n, openDrafts as o, dropDrafts as r, applyTo as t };
@@ -0,0 +1,87 @@
1
+ //#region src/plugins/clock/fake.ts
2
+ /**
3
+ * Removes and returns the timer that is due first, up to and including `until`.
4
+ * Timers due at the same moment come out in the order they were scheduled.
5
+ *
6
+ * @param state - Innards of the fake clock.
7
+ * @param until - Latest moment that counts as due.
8
+ * @returns The timer to fire, or `undefined` when nothing is due.
9
+ */
10
+ function takeDue(state, until) {
11
+ let earliest;
12
+ for (const timer of state.timers) {
13
+ if (timer.at > until) continue;
14
+ if (earliest === void 0 || timer.at < earliest.at) earliest = timer;
15
+ }
16
+ if (earliest === void 0) return void 0;
17
+ const due = earliest;
18
+ state.timers = state.timers.filter((timer) => timer !== due);
19
+ return due;
20
+ }
21
+ /**
22
+ * Moves the clock to `until`, firing every timer that falls due on the way, in due order.
23
+ * A timer scheduled inside a callback fires in the same run when it is due before `until`.
24
+ *
25
+ * @param state - Innards of the fake clock.
26
+ * @param until - Moment to stop at.
27
+ */
28
+ function runDue(state, until) {
29
+ let due = takeDue(state, until);
30
+ while (due !== void 0) {
31
+ state.now = Math.max(state.now, due.at);
32
+ due.callback();
33
+ due = takeDue(state, until);
34
+ }
35
+ state.now = Math.max(state.now, until);
36
+ }
37
+ /**
38
+ * Creates a fake source for tests. Timers fire synchronously inside `advance`, in due order. A
39
+ * negative delay counts as zero and an unknown handle is ignored.
40
+ *
41
+ * @param start - Starting moment in epoch milliseconds. Defaults to 0.
42
+ * @returns A clock source with the test-only controls `advance` and `set`.
43
+ * @example
44
+ * ```ts
45
+ * // An energy point refills 60 s after it was spent. The test moves the clock, not the minute.
46
+ * const clock = fakeClock(1000);
47
+ * const app = createApp({ pluginConfigs: { clock: { source: clock } } });
48
+ * const seen: number[] = [];
49
+ *
50
+ * app.clock.onElapsed(({ now }) => seen.push(now));
51
+ * app.clock.scheduleAt(61_000);
52
+ * clock.advance(60_000); // the timer fires inside advance, nothing waits
53
+ * seen; // [61000]
54
+ * ```
55
+ */
56
+ function fakeClock(start = 0) {
57
+ const state = {
58
+ now: start,
59
+ nextId: 1,
60
+ timers: []
61
+ };
62
+ return {
63
+ now: () => state.now,
64
+ setTimer: (callback, delayMs) => {
65
+ const id = state.nextId;
66
+ state.nextId += 1;
67
+ state.timers.push({
68
+ id,
69
+ at: state.now + Math.max(0, delayMs),
70
+ callback
71
+ });
72
+ return id;
73
+ },
74
+ clearTimer: (handle) => {
75
+ if (typeof handle !== "number") return;
76
+ state.timers = state.timers.filter((timer) => timer.id !== handle);
77
+ },
78
+ advance: (ms) => {
79
+ runDue(state, state.now + Math.max(0, ms));
80
+ },
81
+ set: (moment) => {
82
+ state.now = moment;
83
+ }
84
+ };
85
+ }
86
+ //#endregion
87
+ export { fakeClock as t };
@@ -1,4 +1,4 @@
1
- import { At as FlowState, Bt as Api, Jt as Json, Nt as RouteStep, jt as JournalEntry, on as RngState, ut as Api$1, zt as Answer } from "./types-B0v5VVa3.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-ZQLYXo5X.mjs";
2
2
 
3
3
  //#region src/plugins/flow/headless.d.ts
4
4
  /**
package/dist/hot.d.mts CHANGED
@@ -72,16 +72,14 @@ type HotPlugin = {
72
72
  * @returns The Bun plugin object.
73
73
  * @example
74
74
  * ```ts
75
- * // bunfig.toml of a game with the default layout:
76
- * // [serve.static]
77
- * // plugins = ["@moku-labs/game/hot"]
78
- * // A game that keeps its views in `look/` folders exports its own plugin from `hot.ts`
79
- * // and lists `plugins = ["./hot.ts"]` instead:
75
+ * // `moku-game dev` lists the default plugin in `.moku/bunfig.toml`: a game sets nothing.
76
+ * // A game with a server of its own and its views in `look/` folders exports its own plugin
77
+ * // from `hot.ts` and lists `plugins = ["./hot.ts"]` in its own bunfig.toml:
80
78
  * export default hot({ include: /\/look\/.*\.ts$/ });
81
79
  * ```
82
80
  */
83
81
  declare function hot(options?: HotOptions): HotPlugin;
84
- /** The Bun plugin with the default filters, for `plugins = ["@moku-labs/game/hot"]` in `bunfig.toml`. */
82
+ /** The Bun plugin with the default filters: the one `moku-game dev` lists, or `plugins = ["@moku-labs/game/hot"]` in a game's own `bunfig.toml`. */
85
83
  declare const _default: HotPlugin;
86
84
  //#endregion
87
85
  export { HotBuild, HotOptions, HotPlugin, _default as default, hot };
package/dist/hot.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { readFile } from "node:fs/promises";
2
1
  import path from "node:path";
2
+ import { readFile } from "node:fs/promises";
3
3
  //#region src/hot.ts
4
4
  /**
5
5
  * @file Hot entry (subpath `./hot`): the Bun plugin of the dev server that hot swaps the views of
@@ -11,8 +11,9 @@ import path from "node:path";
11
11
  * `styles/`, `motion/`, `effects/` or `views/` folder, any `.ts` directly in `world/projections/`
12
12
  * or `world/layout/`, or a generated `generated/strings.<locale>.ts`; a logic module never gets
13
13
  * the footer (`world/components/` and `world/systems/` included: they are registered at start), so
14
- * its save reaches the root and the page reloads and restores its state. The plugin lives in `[serve.static]` of
15
- * `bunfig.toml`, the dev server only: `Bun.build` of a production build never loads it.
14
+ * its save reaches the root and the page reloads and restores its state. `moku-game dev` lists the
15
+ * plugin first in `[serve.static]` of the bunfig it writes, `.moku/bunfig.toml`; a game without the
16
+ * shell lists it in its own `bunfig.toml`. The dev server only: `moku-game build` never loads it.
16
17
  */
17
18
  /**
18
19
  * The default view modules: the view files by name or by kind folder. Two literals joined, so
@@ -73,11 +74,9 @@ function footerOf(file) {
73
74
  * @returns The Bun plugin object.
74
75
  * @example
75
76
  * ```ts
76
- * // bunfig.toml of a game with the default layout:
77
- * // [serve.static]
78
- * // plugins = ["@moku-labs/game/hot"]
79
- * // A game that keeps its views in `look/` folders exports its own plugin from `hot.ts`
80
- * // and lists `plugins = ["./hot.ts"]` instead:
77
+ * // `moku-game dev` lists the default plugin in `.moku/bunfig.toml`: a game sets nothing.
78
+ * // A game with a server of its own and its views in `look/` folders exports its own plugin
79
+ * // from `hot.ts` and lists `plugins = ["./hot.ts"]` in its own bunfig.toml:
81
80
  * export default hot({ include: /\/look\/.*\.ts$/ });
82
81
  * ```
83
82
  */
@@ -104,7 +103,7 @@ function hot(options = {}) {
104
103
  }
105
104
  };
106
105
  }
107
- /** The Bun plugin with the default filters, for `plugins = ["@moku-labs/game/hot"]` in `bunfig.toml`. */
106
+ /** The Bun plugin with the default filters: the one `moku-game dev` lists, or `plugins = ["@moku-labs/game/hot"]` in a game's own `bunfig.toml`. */
108
107
  var hot_default = hot();
109
108
  //#endregion
110
109
  export { hot_default as default, hot };
@@ -1,7 +1,7 @@
1
- import { $ as Ease, A as Config$8, B as ComponentValue, Bt as Api$5, G as Narrowed, Ht as State$7, It as Descriptor, J as ResourceType, Jt as Json, Kt as Config$5, L as ComponentHandle, Lt as GuideOptions, M as State$8, O as ViewportSize, Ot as DefineNode, Pt as SceneIdOf, Q as DescriptionNode, Qt as Patch, Rt as Hint, Tt as FeatureDescription, Vt as Config$7, W as Mut, X as TagType, Xt as State$5, Y as SystemDefinition, Yt as Root, _n as State$2, _t as State$3, a as DefineBundles, an as RngApi, at as ProjectionMotion, b as State$6, bn as Events$1, bt as defineFlow, c as LoadReason, cn as Config$4, d as Tier, dn as State$4, dt as BundlesOf, ft as Config$3, h as Config$6, hn as Config$2, ht as GameTypes, i as Config$1, in as StoreApi, it as NumericFields, k as Api$6, ln as Events, m as Api$4, mt as FeaturePlugin, n as BundleMap, ot as ProjectionSpec, pn as Api$1, pt as EmitterIdOf, q as QueryTerm, r as BundleSpec, s as LoadBundles, sn as Api$3, t as Api, u as State$1, un as PauseReason, ut as Api$2, vt as TextStylesOf, xn as Require, yn as Config$9, z as ComponentType } from "./types-B0v5VVa3.mjs";
2
- import { $ as SpriteValue, A as TextStyleInput, C as Config$12, D as TextApi, E as State$11, F as Config$10, H as InputApi, I as I18nApi, L as Message, M as TextValue, O as TextBind, P as Argument, Q as SpriteOptions, R as State$9, S as BindOptions, T as Point, U as State$10, V as Config$11, X as ShapeValue, _ as Style, b as defineComponent, f as defineTokens, g as ResolvedStyle, h as IsFlags, j as TextStyles, nt as sprite, p as IntrinsicElementsFor, q as NineSliceValue, s as PopupComponent, tt as TransformValue, w as CountdownValue, x as BindField, y as WhenFlags, z as TypedTr } from "./types-xXfyk56Q.mjs";
3
- import { A as Step, C as FramesValue, E as Target, M as defineAnimation, O as HapticKind, R as play, S as Config$13, T as State$12, _ as State$15, b as AnimApi, c as AnySceneProjection, d as SceneDefinition, f as SceneSpec, g as Config$15, i as MusicOptions, k as SfxDescriptor, l as DefineScene, m as State$14, n as Config$14, o as State$13, p as ScenesApi, r as MusicDescriptor, t as AudioApi, u as LayerMap, v as UiApi, w as MotionKeyframe, x as AnimPlayerValue } from "./types-VZVWwqCg.mjs";
4
- import { _ as NoiseValue, c as EmitterValue, d as ColorMatrixValue, f as DisplacementValue, g as GlowValue, h as FilterValueOf, i as State$16, l as AlphaValue, m as FilterSpec, n as EffectsApi, o as EmitterConfig, p as FilterComponent, s as EmitterDefinition, t as Config$16, u as BlurValue, v as OutlineValue, y as UniformDeclaration } from "./types-CQueODzo.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-ZQLYXo5X.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-BM4shh8K.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-CefgYzFt.mjs";
4
+ import { $ as ResolvedStyle, A as InputApi, B as SpriteOptions, E as TextValue, F as NineSliceValue, G as Argument, J as Message, K as Config$14, O as defineComponent, Q as IsFlags, R as ShapeValue, S as TextBind, T as TextStyles, U as TransformValue, V as SpriteValue, W as sprite, X as TypedTr, Y as State$14, _ as Config$16, b as State$16, et as Style, f as defineTokens, g as BindOptions, h as BindField, j as State$15, k as Config$15, nt as WhenFlags, p as IntrinsicElementsFor, q as I18nApi, s as PopupComponent, v as CountdownValue, w as TextStyleInput, x as TextApi, y as Point } from "./types-BUwDHKXm.mjs";
5
5
  import { Log } from "@moku-labs/common/browser";
6
6
  import { PluginCtx } from "@moku-labs/core";
7
7
 
@@ -151,7 +151,7 @@ type ProjectionFor<Player, Session> = <const LayerName extends string, const Lif
151
151
  * app.anim.play(coinsFly, { from: purse, to: counter }).marks(); // []: the first frame is next
152
152
  * ```
153
153
  */
154
- declare const animPlugin: import("@moku-labs/core").PluginInstance<"anim", Config$13, State$12, AnimApi, {
154
+ declare const animPlugin: import("@moku-labs/core").PluginInstance<"anim", Config$10, State$9, AnimApi, {
155
155
  "anim:mark": {
156
156
  animation: string;
157
157
  mark: string;
@@ -212,7 +212,7 @@ declare const assetsPlugin: import("@moku-labs/core").PluginInstance<"assets", C
212
212
  * });
213
213
  * ```
214
214
  */
215
- declare const audioPlugin: import("@moku-labs/core").PluginInstance<"audio", Config$14, State$13, AudioApi, {}> & Record<never, never>;
215
+ declare const audioPlugin: import("@moku-labs/core").PluginInstance<"audio", Config$11, State$10, AudioApi, {}> & Record<never, never>;
216
216
  //#endregion
217
217
  //#region src/plugins/clock/index.d.ts
218
218
  /**
@@ -244,7 +244,7 @@ declare const clockPlugin: import("@moku-labs/core").PluginInstance<"clock", Con
244
244
  * app.effects.stats(); // { particles: 0, emitters: 0, filters: 0, renderPasses: 0 }: nothing runs before start
245
245
  * ```
246
246
  */
247
- declare const effectsPlugin: import("@moku-labs/core").PluginInstance<"effects", Config$16, State$16, EffectsApi, {}> & Record<never, never>;
247
+ declare const effectsPlugin: import("@moku-labs/core").PluginInstance<"effects", Config$13, State$13, EffectsApi, {}> & Record<never, never>;
248
248
  //#endregion
249
249
  //#region src/plugins/flow/index.d.ts
250
250
  /**
@@ -302,7 +302,7 @@ declare const flowPlugin: import("@moku-labs/core").PluginInstance<"flow", Confi
302
302
  * await app.i18n.setLocale("en"); // text re-resolves every message on i18n:locale-changed
303
303
  * ```
304
304
  */
305
- declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$10, State$9, I18nApi, {
305
+ declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Config$14, State$14, I18nApi, {
306
306
  "i18n:locale-changed": {
307
307
  locale: string;
308
308
  };
@@ -327,7 +327,7 @@ declare const i18nPlugin: import("@moku-labs/core").PluginInstance<"i18n", Confi
327
327
  * });
328
328
  * ```
329
329
  */
330
- declare const inputPlugin: import("@moku-labs/core").PluginInstance<"input", Config$11, State$10, InputApi, {}> & Record<never, never>;
330
+ declare const inputPlugin: import("@moku-labs/core").PluginInstance<"input", Config$15, State$15, InputApi, {}> & Record<never, never>;
331
331
  //#endregion
332
332
  //#region src/plugins/lifecycle/index.d.ts
333
333
  /**
@@ -476,6 +476,19 @@ type PlatformApi = {
476
476
  * ```
477
477
  */
478
478
  back(): BackResult;
479
+ /**
480
+ * Leaves the app through the provider, as the last step of `back()` does: no Escape, no intent
481
+ * `back`. A provider `exit` that throws is logged, never thrown. Without a provider it does
482
+ * nothing.
483
+ *
484
+ * @example
485
+ * ```ts
486
+ * // merge-game's exit plugin answers the fx "exit" of the Leave button.
487
+ * ctx.require(flowPlugin).fx.handle("exit", () => ctx.require(platformPlugin).exit());
488
+ * // a press of Leave: the provider leaves the app; on the web page without a provider, nothing
489
+ * ```
490
+ */
491
+ exit(): void;
479
492
  };
480
493
  /**
481
494
  * What the kernel context offers the files of the plugin.
@@ -552,7 +565,7 @@ declare const rendererPlugin: import("@moku-labs/core").PluginInstance<"renderer
552
565
  * export const awaitIntent = defineNode({ scene: "board", rest: true, outcomes: { merge: type() } });
553
566
  * ```
554
567
  */
555
- declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$14, ScenesApi, {
568
+ declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$11, ScenesApi, {
556
569
  "scenes:changed": {
557
570
  from: string | undefined;
558
571
  to: string;
@@ -577,7 +590,7 @@ declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", R
577
590
  * app.text.styles(); // ["body", "digits", "hud.digits"]
578
591
  * ```
579
592
  */
580
- declare const textPlugin: import("@moku-labs/core").PluginInstance<"text", Config$12, State$11, TextApi, {}> & Record<never, never>;
593
+ declare const textPlugin: import("@moku-labs/core").PluginInstance<"text", Config$16, State$16, TextApi, {}> & Record<never, never>;
581
594
  //#endregion
582
595
  //#region src/plugins/time/index.d.ts
583
596
  /**
@@ -609,7 +622,7 @@ declare const timePlugin: import("@moku-labs/core").PluginInstance<"time", Confi
609
622
  * app.input.tap(app.ui.find("claim") ?? 0);
610
623
  * ```
611
624
  */
612
- declare const uiPlugin: import("@moku-labs/core").PluginInstance<"ui", Config$15, State$15, UiApi, {}> & Record<never, never>;
625
+ declare const uiPlugin: import("@moku-labs/core").PluginInstance<"ui", Config$12, State$12, UiApi, {}> & Record<never, never>;
613
626
  //#endregion
614
627
  //#region src/plugins/world/index.d.ts
615
628
  /**
@@ -1569,7 +1582,7 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
1569
1582
  kind: "webgpu" | "webgl";
1570
1583
  reason: string;
1571
1584
  };
1572
- }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"input", Config$11, State$10, InputApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"assets", Config$1, State$1, Api, {
1585
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"input", Config$15, State$15, InputApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"assets", Config$1, State$1, Api, {
1573
1586
  "assets:bundle-loaded": {
1574
1587
  bundle: string;
1575
1588
  tier: Tier;
@@ -1588,13 +1601,13 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
1588
1601
  reason: "budget" | "request";
1589
1602
  keys: readonly string[];
1590
1603
  };
1591
- }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$14, ScenesApi, {
1604
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$11, ScenesApi, {
1592
1605
  "scenes:changed": {
1593
1606
  from: string | undefined;
1594
1607
  to: string;
1595
1608
  music: string | undefined;
1596
1609
  };
1597
- }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"anim", Config$13, State$12, AnimApi, {
1610
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"anim", Config$10, State$9, AnimApi, {
1598
1611
  "anim:mark": {
1599
1612
  animation: string;
1600
1613
  mark: string;
@@ -1602,10 +1615,10 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
1602
1615
  "anim:finished": {
1603
1616
  animation: string;
1604
1617
  };
1605
- }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"i18n", Config$10, State$9, I18nApi, {
1618
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"i18n", Config$14, State$14, I18nApi, {
1606
1619
  "i18n:locale-changed": {
1607
1620
  locale: string;
1608
1621
  };
1609
- }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"text", Config$12, State$11, TextApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"ui", Config$15, State$15, UiApi, {}> & Record<never, never>];
1622
+ }> & Record<never, never>, import("@moku-labs/core").PluginInstance<"text", Config$16, State$16, TextApi, {}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"ui", Config$12, State$12, UiApi, {}> & Record<never, never>];
1610
1623
  //#endregion
1611
1624
  export { uiPlugin as $, Pointer as A, Layer as B, defineScene as C, label as Ct, DropTarget as D, Draggable as E, Tappable as F, resource as G, Tree as H, Touchable as I, guide as J, system as K, Traceable as L, Pressable as M, Pressed as N, Held as O, Swipeable as P, worldPlugin as Q, Traced as R, Frames as S, defineTextStyles as St, load as T, component as U, Order as V, mut as W, schedule as X, hint as Y, defineFeature as Z, defineStyle as _, animPlugin as _t, defineEmitter as a, BackResult as at, AnimPlayer as b, Text as bt, Alpha as c, modelPlugin as ct, Displacement as d, i18nPlugin as dt, timePlugin as et, Glow as f, flowPlugin as ft, resolve as g, assetsPlugin as gt, music as h, audioPlugin as ht, screen as i, platformPlugin as it, PointerOver as j, Hovered as k, Blur as l, lifecyclePlugin as lt, Outline as m, clockPlugin as mt, createPlugin as n, scenesPlugin as nt, Emitter as o, PlatformProvider as ot, Noise as p, effectsPlugin as pt, tag as q, defineGame as r, rendererPlugin as rt, defineFilter as s, types_d_exports as st, createApp as t, textPlugin as tt, ColorMatrix as u, inputPlugin as ut, tr as v, projection as vt, defineBundles as w, Animation as x, bind as xt, defineMotion as y, Countdown as yt, Exiting as z };