@moku-labs/game 0.9.0 → 0.10.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.
- package/README.md +81 -22
- package/bin/moku-game.mjs +4 -0
- package/dist/app/page.d.mts +41 -0
- package/dist/app/page.mjs +376 -0
- package/dist/app/system.d.mts +129 -0
- package/dist/app/system.mjs +307 -0
- package/dist/app.d.mts +37 -0
- package/dist/app.mjs +235 -0
- package/dist/assets-BHwTLVta.mjs +7391 -0
- package/dist/assets.d.mts +1 -1
- package/dist/assets.mjs +1 -7390
- package/dist/cli.d.mts +70 -0
- package/dist/cli.mjs +1650 -0
- package/dist/{commands-DuwCxK5t.d.mts → commands-C4QlqijJ.d.mts} +5 -5
- package/dist/config-CdARrrhU.mjs +81 -0
- package/dist/control.d.mts +11 -10
- package/dist/control.mjs +3 -3
- package/dist/{define-B6pHpdBi.d.mts → define-CCc0ozKe.d.mts} +2 -2
- package/dist/{dev-wKLgOw7c.mjs → dev-Cc50Dk7H.mjs} +1 -1
- package/dist/drafts-BawpSaVp.mjs +156 -0
- package/dist/fake-MpzwZhIZ.mjs +87 -0
- package/dist/{headless-SCQXJxQf.d.mts → headless-G4nuZ-xz.d.mts} +1 -1
- package/dist/hot.d.mts +4 -6
- package/dist/hot.mjs +8 -9
- package/dist/{index-BO9kZzY3.d.mts → index-xxSxKJJW.d.mts} +17 -17
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/inspect-la_KkjCS.mjs +1 -0
- package/dist/inspect.d.mts +7 -7
- package/dist/inspect.mjs +3 -2
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/lint.mjs +10 -6
- package/dist/memory-CWlqhS7l.mjs +136 -0
- package/dist/memory-DwDhfFWK.d.mts +50 -0
- package/dist/project.mjs +3 -3
- package/dist/{run-CofJEdYx.mjs → run-wYty_Lwo.mjs} +2 -2
- package/dist/{runtime-CdNJ0UBm.d.mts → runtime-DOBYM6g4.d.mts} +1 -1
- package/dist/{input-z_35Jwfn.mjs → session-DgukhR_7.mjs} +20 -20
- package/dist/{sources-DNnzyCYf.mjs → sources-Duzj6lhV.mjs} +2 -2
- package/dist/{src-536arXjV.mjs → src-DSl1eNB1.mjs} +14 -292
- package/dist/testing.d.mts +4 -50
- package/dist/testing.mjs +3 -87
- package/dist/{types-VZVWwqCg.d.mts → types-BM4shh8K.d.mts} +259 -259
- package/dist/{types-xXfyk56Q.d.mts → types-BUwDHKXm.d.mts} +1377 -1377
- package/dist/{types-CQueODzo.d.mts → types-CefgYzFt.d.mts} +2 -2
- package/dist/{types-DUMoYPYe.d.mts → types-DAdwEJMp.d.mts} +2 -2
- package/dist/{types-B0v5VVa3.d.mts → types-ZQLYXo5X.d.mts} +304 -304
- package/dist/types-hQgI_wrq.d.mts +544 -0
- package/dist/visual.d.mts +6 -6
- package/dist/visual.mjs +5 -5
- package/llms.txt +119 -43
- package/package.json +30 -2
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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 };
|
package/dist/control.d.mts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { t as HeadlessApp } from "./headless-
|
|
2
|
-
import { i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-
|
|
3
|
-
import { t as commands } from "./commands-
|
|
4
|
-
import { t as defineCommand } from "./define-
|
|
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:
|
|
10
|
-
* `globalThis.__MOKU_GAME_DEV__` before the engine runs;
|
|
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:
|
|
20
|
-
* `
|
|
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
|
-
* //
|
|
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
|
|
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-
|
|
2
|
-
import { s as defineCommand } from "./
|
|
3
|
-
import { n as commands, t as run } from "./run-
|
|
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-
|
|
2
|
-
import { c as Source, n as Command, o as InputSchema } from "./types-
|
|
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
|
|
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 {
|
|
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
|
|
76
|
-
* //
|
|
77
|
-
* //
|
|
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,
|
|
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.
|
|
15
|
-
* `
|
|
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
|
|
77
|
-
* //
|
|
78
|
-
* //
|
|
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,
|
|
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
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
/**
|
|
@@ -552,7 +552,7 @@ declare const rendererPlugin: import("@moku-labs/core").PluginInstance<"renderer
|
|
|
552
552
|
* export const awaitIntent = defineNode({ scene: "board", rest: true, outcomes: { merge: type() } });
|
|
553
553
|
* ```
|
|
554
554
|
*/
|
|
555
|
-
declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$
|
|
555
|
+
declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$11, ScenesApi, {
|
|
556
556
|
"scenes:changed": {
|
|
557
557
|
from: string | undefined;
|
|
558
558
|
to: string;
|
|
@@ -577,7 +577,7 @@ declare const scenesPlugin: import("@moku-labs/core").PluginInstance<"scenes", R
|
|
|
577
577
|
* app.text.styles(); // ["body", "digits", "hud.digits"]
|
|
578
578
|
* ```
|
|
579
579
|
*/
|
|
580
|
-
declare const textPlugin: import("@moku-labs/core").PluginInstance<"text", Config$
|
|
580
|
+
declare const textPlugin: import("@moku-labs/core").PluginInstance<"text", Config$16, State$16, TextApi, {}> & Record<never, never>;
|
|
581
581
|
//#endregion
|
|
582
582
|
//#region src/plugins/time/index.d.ts
|
|
583
583
|
/**
|
|
@@ -609,7 +609,7 @@ declare const timePlugin: import("@moku-labs/core").PluginInstance<"time", Confi
|
|
|
609
609
|
* app.input.tap(app.ui.find("claim") ?? 0);
|
|
610
610
|
* ```
|
|
611
611
|
*/
|
|
612
|
-
declare const uiPlugin: import("@moku-labs/core").PluginInstance<"ui", Config$
|
|
612
|
+
declare const uiPlugin: import("@moku-labs/core").PluginInstance<"ui", Config$12, State$12, UiApi, {}> & Record<never, never>;
|
|
613
613
|
//#endregion
|
|
614
614
|
//#region src/plugins/world/index.d.ts
|
|
615
615
|
/**
|
|
@@ -1569,7 +1569,7 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
|
|
|
1569
1569
|
kind: "webgpu" | "webgl";
|
|
1570
1570
|
reason: string;
|
|
1571
1571
|
};
|
|
1572
|
-
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"input", Config$
|
|
1572
|
+
}> & 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
1573
|
"assets:bundle-loaded": {
|
|
1574
1574
|
bundle: string;
|
|
1575
1575
|
tier: Tier;
|
|
@@ -1588,13 +1588,13 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
|
|
|
1588
1588
|
reason: "budget" | "request";
|
|
1589
1589
|
keys: readonly string[];
|
|
1590
1590
|
};
|
|
1591
|
-
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$
|
|
1591
|
+
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"scenes", Record<string, never>, State$11, ScenesApi, {
|
|
1592
1592
|
"scenes:changed": {
|
|
1593
1593
|
from: string | undefined;
|
|
1594
1594
|
to: string;
|
|
1595
1595
|
music: string | undefined;
|
|
1596
1596
|
};
|
|
1597
|
-
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"anim", Config$
|
|
1597
|
+
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"anim", Config$10, State$9, AnimApi, {
|
|
1598
1598
|
"anim:mark": {
|
|
1599
1599
|
animation: string;
|
|
1600
1600
|
mark: string;
|
|
@@ -1602,10 +1602,10 @@ declare const screen: readonly [import("@moku-labs/core").PluginInstance<"world"
|
|
|
1602
1602
|
"anim:finished": {
|
|
1603
1603
|
animation: string;
|
|
1604
1604
|
};
|
|
1605
|
-
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"i18n", Config$
|
|
1605
|
+
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"i18n", Config$14, State$14, I18nApi, {
|
|
1606
1606
|
"i18n:locale-changed": {
|
|
1607
1607
|
locale: string;
|
|
1608
1608
|
};
|
|
1609
|
-
}> & Record<never, never>, import("@moku-labs/core").PluginInstance<"text", Config$
|
|
1609
|
+
}> & 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
1610
|
//#endregion
|
|
1611
1611
|
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 };
|