@moku-labs/game 0.0.3 → 0.1.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 +32 -24
- package/dist/assets.d.mts +1 -1
- package/dist/{commands-BOxqlv7w.d.mts → commands-C0BljsM5.d.mts} +4 -4
- package/dist/control.d.mts +3 -3
- package/dist/{define-BMo0pHe_.d.mts → define-CjP_4H0G.d.mts} +1 -1
- package/dist/index.d.mts +189 -318
- package/dist/index.mjs +7015 -6404
- package/dist/inspect.d.mts +7 -7
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/{runtime-BstIkrhH.d.mts → runtime-Bn5TXNxP.d.mts} +1 -1
- package/dist/testing.d.mts +44 -21
- package/dist/testing.mjs +113 -43
- package/dist/{types-Cjf3ywai.d.mts → types-BZ1OLf24.d.mts} +20 -3
- package/dist/{types-DgwbzDLP.d.mts → types-C47fKNcn.d.mts} +7 -2
- package/dist/{types-KlE_GL1z.d.mts → types-CqkrEY06.d.mts} +296 -6
- package/dist/{types-BTycq8_Z.d.mts → types-CxZy7Pi4.d.mts} +31 -17
- package/dist/{types-D0uYTTC3.d.mts → types-DmhTHRVW.d.mts} +1 -1
- package/dist/{types-C0SLQsx0.d.mts → types-X9jXjka3.d.mts} +1 -1
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
**A 2D puzzle game engine where the game is a deterministic graph of business logic.**
|
|
4
4
|
|
|
5
|
-
`@moku-labs/game` is a Layer-2 framework on [`@moku-labs/core`](https://github.com/moku-labs/core), written in TypeScript, with PixiJS v8 as a peer dependency. You write small nodes and edge tables. The engine runs them, commits state on the edges, saves at rest points and replays the same game without a screen. It is not a general-purpose engine and it ships no genre rules: no match-3, no merge, no physics. V1 is the logic half; V2 adds the screen: an own small ECS, projections from committed state, a Pixi v8 renderer loaded lazily, gestures as data components, typed asset keys and scenes as declarations. V3 adds the interface: choreographies as data, strings as data, text from MSDF fonts, screens written in JSX and laid out by Yoga, and sound as an effect a node awaits. V4 adds two doors for the editor: sources that read a running game and dev-only commands that drive it. V5 adds cosmetic effects as data: particles and WGSL filters as components, a frame loop for sprites, and the render numbers that say what they cost; then the production pack of the art, icons that wrap with their words, a text field, and visual tests.
|
|
5
|
+
`@moku-labs/game` is a Layer-2 framework on [`@moku-labs/core`](https://github.com/moku-labs/core), written in TypeScript, with PixiJS v8 as a peer dependency. You write small nodes and edge tables. The engine runs them, commits state on the edges, saves at rest points and replays the same game without a screen. It is not a general-purpose engine and it ships no genre rules: no match-3, no merge, no physics. V1 is the logic half; V2 adds the screen: an own small ECS, projections from committed state, a Pixi v8 renderer loaded lazily, gestures as data components, typed asset keys and scenes as declarations. V3 adds the interface: choreographies as data, strings as data, text from MSDF fonts, screens written in JSX and laid out by Yoga, and sound as an effect a node awaits. V4 adds two doors for the editor: sources that read a running game and dev-only commands that drive it. V5 adds cosmetic effects as data: particles and WGSL filters as components, a frame loop for sprites, and the render numbers that say what they cost; then the production pack of the art, icons that wrap with their words, a text field, and visual tests. V6 adds the platform seam: the Back chain, haptics and keep-awake, filters that also draw on WebGL, and the game as a Tauri app on iOS and Android.
|
|
6
6
|
|
|
7
7
|
<br/>
|
|
8
8
|
|
|
9
|
-
[](#status)
|
|
10
10
|
[](#requirements)
|
|
11
11
|
[](#requirements)
|
|
12
12
|
[](#requirements)
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
## Status
|
|
32
32
|
|
|
33
|
-
V1, V2 and V3 are built. V4 has its engine side: the two doors described in [Doors for the editor](#doors-for-the-editor). The editor that uses them is a separate package. V5 is built: the `effects` plugin, the `Frames` component, the `components` prop of the JSX tags and the render numbers; the production pack of `assets` (`--pack`, a v2 manifest with WebP atlas pages); icons inside wrapped text; the `input` tag, a text field; and visual tests in `@moku-labs/game/testing`.
|
|
33
|
+
V1, V2 and V3 are built. V4 has its engine side: the two doors described in [Doors for the editor](#doors-for-the-editor). The editor that uses them is a separate package. V5 is built: the `effects` plugin, the `Frames` component, the `components` prop of the JSX tags and the render numbers; the production pack of `assets` (`--pack`, a v2 manifest with WebP atlas pages); icons inside wrapped text; the `input` tag, a text field; and visual tests in `@moku-labs/game/testing`. V6 is built: the `platform` plugin with its provider seam, the Back chain, haptics and keep-awake; GLSL twins for custom filters, so they draw on WebGL; the Tauri fallback in `assets`; and a WebGL leg of the visual tests. `0.1.0` is the first working release. The API may still change before `1.0`.
|
|
34
34
|
|
|
35
35
|
| Milestone | State | Scope | Exit criterion |
|
|
36
36
|
|---|---|---|---|
|
|
@@ -39,7 +39,7 @@ V1, V2 and V3 are built. V4 has its engine side: the two doors described in [Doo
|
|
|
39
39
|
| V3 | built | `anim`, `i18n`, `text`, `ui`, `audio`, the `@moku-labs/game/jsx-runtime` entry | Popup, HUD and buttons with sound; the same game still plays to the end headless |
|
|
40
40
|
| V4 | doors built | `/inspect` and `/control` entries | External tools can read and drive a game |
|
|
41
41
|
| V5 | built | `effects`, `Frames`, render passes and draw calls; the production pack of `assets`, icons in wrapped text, the `input` tag, visual tests in `/testing` | Not defined yet |
|
|
42
|
-
| V6 |
|
|
42
|
+
| V6 | built | `platform` (provider seam, Back chain, haptics, keep-awake), custom filters on WebGL, the Tauri fallback in `assets`, a WebGL leg of the visual tests | The fixture game runs as a Tauri app on the iOS simulator and an Android emulator; real phones are not checked yet |
|
|
43
43
|
|
|
44
44
|
Rendering: WebGPU is preferred, with Pixi's WebGL fallback and an honest "unsupported device" screen when neither exists.
|
|
45
45
|
|
|
@@ -50,7 +50,7 @@ bun add @moku-labs/game pixi.js
|
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
> [!NOTE]
|
|
53
|
-
> **Status: `0.
|
|
53
|
+
> **Status: `0.1.0` on npm.** The first working release; the API may still change before `1.0`. `pixi.js` `^8.0.0` is a peer dependency.
|
|
54
54
|
|
|
55
55
|
> [!IMPORTANT]
|
|
56
56
|
> Bun only. ESM only. `"sideEffects": false`. There is no CJS build. `yoga-layout` is a dependency the `ui` plugin loads lazily; nothing imports it before `onStart`.
|
|
@@ -225,7 +225,7 @@ flowchart LR
|
|
|
225
225
|
|
|
226
226
|
## Plugins
|
|
227
227
|
|
|
228
|
-
Five logic plugins are on every app; the nine screen plugins are the list `screen` a game spreads in; `audio` and `
|
|
228
|
+
Five logic plugins are on every app; the nine screen plugins are the list `screen` a game spreads in; `effects`, `audio` and `platform` are opt-in, `[...screen, effectsPlugin, audioPlugin, platformPlugin]`. `log` and `env` come from [`@moku-labs/common`](https://github.com/moku-labs/common) and sit on every plugin context as `ctx.log` and `ctx.env`.
|
|
229
229
|
|
|
230
230
|
### Built
|
|
231
231
|
|
|
@@ -246,7 +246,8 @@ Five logic plugins are on every app; the nine screen plugins are the list `scree
|
|
|
246
246
|
| [`text`](./src/plugins/text/README.md) | Complex | The `Text` component, `label()`, `defineTextStyles()`, the tags `<b> <i> <color=#hex> <icon=key>`, measurement from the font's advance table, BitmapText from the MSDF fonts of a bundle | `measure(content, style)`, `styles()` |
|
|
247
247
|
| [`ui`](./src/plugins/ui/README.md) | Very Complex | A screen is a projection whose `view` returns JSX; the tree is reconciled by identity into entities, laid out by one Yoga solve per change, `Box` is the rest pose; `defineComponent` with `local` and `outcomes`, `popup` as an effect, `defineStyle`, `defineTokens`; every tag takes `components`, extra component values such as a filter; the `input` tag is a text field whose text lives in `local` | `tree()`, `find(key)`, `lint()`, `fill(key, value)` |
|
|
248
248
|
| [`audio`](./src/plugins/audio/README.md) | Standard | Opt-in. Buses `master`, `music`, `sfx`; `sfx()` descriptors of `anim` and `music()` descriptors handled here; the scene's `music`; volumes read from the committed player through `volumes` | `setVolume(bus, value)`, `volume(bus)`, `mute(bus, on)`, `unlocked()` |
|
|
249
|
-
| [`effects`](./src/plugins/effects/README.md) | Complex | Opt-in. Particles: `defineEmitter` and the `Emitter` component, one Pixi `ParticleContainer` per instance stepped by the engine clock. Filters: `defineFilter` turns a WGSL fragment body into a flat component that `tween` drives; `Glow`, `Outline`, `Blur`, `ColorMatrix`, `Noise`, `Displacement`, `Alpha` ship built in. Budgets warn once per crossing; a dev build compiles each
|
|
249
|
+
| [`effects`](./src/plugins/effects/README.md) | Complex | Opt-in. Particles: `defineEmitter` and the `Emitter` component, one Pixi `ParticleContainer` per instance stepped by the engine clock. Filters: `defineFilter` turns a WGSL fragment body and its GLSL twin into a flat component that `tween` drives, so a custom filter draws on WebGPU and on WebGL; `Glow`, `Outline`, `Blur`, `ColorMatrix`, `Noise`, `Displacement`, `Alpha` ship built in. Budgets warn once per crossing; a dev build compiles each kind before its first instance, WGSL on WebGPU and GLSL on WebGL. Headless it draws nothing | `stats()` |
|
|
250
|
+
| [`platform`](./src/plugins/platform/README.md) | Standard | Opt-in, last in the array. The phone as a provider: the game passes a `PlatformProvider`; its pause and resume become the `"background"` reason of `lifecycle`, its Back press runs the Back chain (Escape, then the intent `back`, then `exit()`), the `haptic` effect reaches `provider.haptic`, and `keepAwake` keeps the screen on while the game runs. Inert without a provider | `back()` |
|
|
250
251
|
|
|
251
252
|
```mermaid
|
|
252
253
|
flowchart LR
|
|
@@ -290,6 +291,10 @@ flowchart LR
|
|
|
290
291
|
EF --> W
|
|
291
292
|
EF --> R
|
|
292
293
|
EF --> A
|
|
294
|
+
EF --> AN
|
|
295
|
+
PL["platform"] --> L
|
|
296
|
+
PL --> F
|
|
297
|
+
PL --> I
|
|
293
298
|
classDef u fill:#0b7285,stroke:#08525f,color:#fff;
|
|
294
299
|
classDef m fill:#1864ab,stroke:#0d3d6e,color:#fff;
|
|
295
300
|
classDef s fill:#5c940d,stroke:#3d6208,color:#fff;
|
|
@@ -297,18 +302,14 @@ flowchart LR
|
|
|
297
302
|
class G u
|
|
298
303
|
class F,T,L,M,C m
|
|
299
304
|
class W,R,I,A,S s
|
|
300
|
-
class AN,N,X,U,AU,EF v
|
|
305
|
+
class AN,N,X,U,AU,EF,PL v
|
|
301
306
|
```
|
|
302
307
|
|
|
303
|
-
An arrow means "depends on";
|
|
308
|
+
An arrow means "depends on"; from `renderer` on, most edges to `time` and `clock` and most edges a nearer plugin already implies are left out for space, the plugin READMEs list them in full. `time`, `model` and `clock` depend on nothing. The logic plugins are registered in this order: `time`, `lifecycle`, `model`, `clock`, `flow`; the screen set `screen` follows as `world`, `renderer`, `input`, `assets`, `scenes`, `anim`, `i18n`, `text`, `ui`; a game that wants particles and filters appends `effectsPlugin`, one that wants sound appends `audioPlugin`, and one in a native shell appends `platformPlugin` last. A game without `effectsPlugin` carries none of its systems, filters or WGSL in its bundle. Without a document the screen plugins are inert: the same app starts in plain Bun, Yoga included.
|
|
304
309
|
|
|
305
310
|
### Planned
|
|
306
311
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
| Plugin | Milestone | Tier | Depends on | Will own |
|
|
310
|
-
|---|---|---|---|---|
|
|
311
|
-
| `platform` | V6 | Standard | `lifecycle`, `flow` | The native provider: background, system dialogs |
|
|
312
|
+
None at the moment: `platform`, the last planned plugin, is built. `defineFeature` refuses every engine plugin name as a feature name.
|
|
312
313
|
|
|
313
314
|
### Root exports
|
|
314
315
|
|
|
@@ -330,9 +331,10 @@ Not built. Names are reserved: `defineFeature` refuses them as feature names. Sc
|
|
|
330
331
|
| `Text`, `label`, `defineTextStyles` | component and functions | Words on the screen and the text styles a feature registers |
|
|
331
332
|
| `defineComponent`, `popup`, `defineStyle`, `defineTokens`, `resolve`, `Box`, `LocalWrite` | functions and components | Interface components, the popup effect, the style vocabulary, the rect of an element |
|
|
332
333
|
| `music` | function | `music(key \| null, { fadeMs? })`, the awaited effect that switches the music track |
|
|
333
|
-
| `defineEmitter`, `Emitter`, `defineFilter`, `Glow`, `Outline`, `Blur`, `ColorMatrix`, `Noise`, `Displacement`, `Alpha` | functions and components | Effects as data, drawn by `effectsPlugin`. `defineEmitter(id, config)` describes a particle effect that a feature lists under `emitters`; `Emitter({ effect, active })` runs it on its entity. `defineFilter(id, { wgsl, uniforms, passes?, padding? })` returns a filter component type that a feature lists under `filters`; a filter on a ui element goes in its `components` prop |
|
|
334
|
-
| `
|
|
335
|
-
| `
|
|
334
|
+
| `defineEmitter`, `Emitter`, `defineFilter`, `Glow`, `Outline`, `Blur`, `ColorMatrix`, `Noise`, `Displacement`, `Alpha` | functions and components | Effects as data, drawn by `effectsPlugin`. `defineEmitter(id, config)` describes a particle effect that a feature lists under `emitters`; `Emitter({ effect, active })` runs it on its entity. `defineFilter(id, { wgsl, glsl, uniforms, passes?, padding? })` returns a filter component type that a feature lists under `filters`; a filter on a ui element goes in its `components` prop |
|
|
335
|
+
| `PlatformProvider`, `BackResult`, `HapticKind`, `HAPTIC_KINDS` | types and a constant | The seam a game fills for `platformPlugin`, what one Back press ends in, and the seven haptic kinds as a type and as a frozen list |
|
|
336
|
+
| `timePlugin`, `lifecyclePlugin`, `modelPlugin`, `clockPlugin`, `flowPlugin`, `worldPlugin`, `rendererPlugin`, `inputPlugin`, `assetsPlugin`, `scenesPlugin`, `animPlugin`, `i18nPlugin`, `textPlugin`, `uiPlugin`, `audioPlugin`, `effectsPlugin`, `platformPlugin` | plugin instances | For `depends` and `ctx.require` in game plugins; `screen` is the list of the nine screen plugins |
|
|
337
|
+
| `Time`, `Lifecycle`, `Model`, `Clock`, `Flow`, `World`, `Renderer`, `Input`, `Assets`, `Scenes`, `Anim`, `I18n`, `TextTypes`, `Ui`, `Audio`, `Effects`, `Platform` | type namespaces | All public types of one plugin |
|
|
336
338
|
|
|
337
339
|
### Other entries
|
|
338
340
|
|
|
@@ -356,9 +358,9 @@ Not built. Names are reserved: `defineFeature` refuses them as feature names. Sc
|
|
|
356
358
|
| `fakeClock` | `(start = 0) => FakeClock` | A `ClockSource` with `advance(ms)` and `set(moment)` |
|
|
357
359
|
| `memory` | `(fixture?: { state: SaveDoc; version: number }) => PlayerStateProvider & { calls: ProviderCall[] }` | In-memory save provider. It keeps what it was committed and records every call |
|
|
358
360
|
| `saveOf` | `(player: Json, seed?: number) => SaveDoc` | Builds a save document for a fixture |
|
|
359
|
-
| `defineVisualTest` | `(name: string, test: { start: VisualStart; steps: readonly VisualStep[] }) => VisualTest` | A visual test as frozen data: where it starts
|
|
361
|
+
| `defineVisualTest` | `(name: string, test: { start: VisualStart; steps: readonly VisualStep[]; webgl?: boolean }) => VisualTest` | A visual test as frozen data: where it starts, its steps, and whether it also runs in the WebGL leg |
|
|
360
362
|
| `runVisualTests` | `(setup: VisualSetup, tests: readonly VisualTest[], options?: VisualOptions) => Promise<VisualReport>` | Plays the tests and compares every checkpoint with its baseline files |
|
|
361
|
-
| `parseVisualArgv` | `(argv: readonly string[]) => { update?; pixels?; only?; dir? }` | Reads `--update`, `--no-pixels`, `--only <name>` and `--dir <path>` from a command line |
|
|
363
|
+
| `parseVisualArgv` | `(argv: readonly string[]) => { update?; pixels?; only?; dir?; renderer? }` | Reads `--update`, `--no-pixels`, `--webgl`, `--only <name>` and `--dir <path>` from a command line. `--webgl` runs only the tests with `webgl: true`, on the page with `?renderer=webgl`: such a test writes its picture to `screen.webgl.webp` and shares `state.json` and `describe.json` with the WebGPU leg |
|
|
362
364
|
|
|
363
365
|
A `HeadlessGame` has `walk(route)`, `answer(answer)`, `state()`, `history()` and `stop()`.
|
|
364
366
|
|
|
@@ -384,14 +386,14 @@ const report = await runVisualTests({ app: () => createScreenGame().app, page: {
|
|
|
384
386
|
process.exitCode = report.ok ? 0 : 1;
|
|
385
387
|
```
|
|
386
388
|
|
|
387
|
-
A checkpoint settles the motions and saves three baseline files next to the test: `<dir>/<test>/<checkpoint>/state.json`, `describe.json` and `screen.webp`. A missing file is written; `--update` rewrites them; any other file is compared. The headless leg plays every test in plain Bun and compares `state.json` and `describe.json` exactly, so it runs in `bun run test`. The pixel leg plays the same steps on the dev page in Chrome with WebGPU, on a Mac only, and compares `screen.webp` with a tolerance; a pixel difference with the same state and describe is reported as a rendering regression. The page is the contract: a dev build that sets `globalThis.game` to the app and `globalThis.doors` to `{ read, watch, sources, run, commands }`. No CI job runs pixels.
|
|
389
|
+
A checkpoint settles the motions and saves three baseline files next to the test: `<dir>/<test>/<checkpoint>/state.json`, `describe.json` and `screen.webp`. A missing file is written; `--update` rewrites them; any other file is compared. The headless leg plays every test in plain Bun and compares `state.json` and `describe.json` exactly, so it runs in `bun run test`. The pixel leg plays the same steps on the dev page in Chrome with WebGPU, or with WebGL under `--webgl`, on a Mac only, and compares `screen.webp` with a tolerance; a pixel difference with the same state and describe is reported as a rendering regression. The page is the contract: a dev build that sets `globalThis.game` to the app and `globalThis.doors` to `{ read, watch, sources, run, commands }`. No CI job runs pixels.
|
|
388
390
|
|
|
389
391
|
#### Pixels
|
|
390
392
|
|
|
391
393
|
The pixel leg runs on a Mac only. `pixels` is on by default when the setup has a `page` and the platform is `darwin`, and off everywhere else. No CI job runs it: a Linux runner has no WebGPU worth trusting.
|
|
392
394
|
|
|
393
395
|
- **`playwright-core`** is an optional peer dependency. The leg loads it with a dynamic import, so a game that never runs pixels never installs it. Add it with `bun add -d playwright-core`. Without it the run stops with "The pixel leg needs playwright-core"; `--no-pixels` runs the headless leg alone.
|
|
394
|
-
- **The browser.** `page.browser` when the setup names one (`{ channel }` or `{ executablePath }`). Otherwise Playwright's own Chromium, whose version `playwright-core` pins, so a Chrome update never moves the baselines: install it with `bunx playwright-core install chromium`. Otherwise the system Google Chrome. Chrome starts with WebGPU on Metal; a page that draws with
|
|
396
|
+
- **The browser.** `page.browser` when the setup names one (`{ channel }` or `{ executablePath }`). Otherwise Playwright's own Chromium, whose version `playwright-core` pins, so a Chrome update never moves the baselines: install it with `bunx playwright-core install chromium`. Otherwise the system Google Chrome. Chrome starts with WebGPU on Metal; a page that draws with a renderer other than the requested one fails the leg.
|
|
395
397
|
- **The page.** 390 CSS px wide at a device scale of 2, as tall as the game's inert frame: 520 for a 4:3 portrait game, so `screen.webp` is 780 × 1040.
|
|
396
398
|
- **Game time.** The leg drives the clock itself, so two runs draw the same pixels: a dash phase, a pulse or a particle reads the same time. It waits until the page has loaded what it asked for (no request for half a second), runs `pause`, then steps the game to 5 s of game time, the last frame shorter so it lands exactly. From there every frame of the restore, of every settle and of a checkpoint is a `step` of 1000/60 ms, the frame of the headless leg; the browser's own frames move no game time. The picture is the stage after the last stepped frame. A `resume` step hands the clock back to the browser.
|
|
397
399
|
- **The picture.** Lossless WebP: the page encodes it with `convertToBlob({ type: "image/webp", quality: 1 })`, which Chrome encodes without loss, so the baseline holds the captured pixels exactly.
|
|
@@ -722,7 +724,7 @@ Global events are empty: every event belongs to a plugin. `time` and `clock` emi
|
|
|
722
724
|
| `anim:finished` | `anim` | `{ animation }` | A timeline ended or was finished. Never on `cancel()` |
|
|
723
725
|
| `i18n:locale-changed` | `i18n` | `{ locale }` | The module of the new locale is loaded; `text` re-resolves. Never at start |
|
|
724
726
|
|
|
725
|
-
`text`, `ui` and `
|
|
727
|
+
`text`, `ui`, `audio`, `effects` and `platform` emit nothing; the `effects` budgets are log warnings.
|
|
726
728
|
|
|
727
729
|
```ts
|
|
728
730
|
import { createPlugin, flowPlugin } from "@moku-labs/game";
|
|
@@ -801,6 +803,8 @@ Set with `createApp({ pluginConfigs: { <plugin>: { ... } } })`.
|
|
|
801
803
|
| `effects` | `maxPasses` | `number` | `24` | Render passes per frame above which `effects:pass-budget` warns once per crossing |
|
|
802
804
|
| `effects` | `phone` | `boolean \| "auto"` | `"auto"` | Whether the device is a phone. `"auto"`: a coarse pointer and a short side of at most 820 CSS px, read once at start |
|
|
803
805
|
| `effects` | `blur` | `{ quality, phoneResolution }` | `{ quality: 2, phoneResolution: 0.5 }` | What a `Blur` with `quality: 0` and `resolution: 0` resolves to |
|
|
806
|
+
| `platform` | `provider` | `PlatformProvider \| undefined` | `undefined` | The provider the game builds, usually from `@moku-labs/system`. Absent: the plugin is inert and `back()` answers `"none"` |
|
|
807
|
+
| `platform` | `keepAwake` | `boolean` | `false` | Keep the screen on while the game runs; released while it is paused and on stop |
|
|
804
808
|
|
|
805
809
|
## Development
|
|
806
810
|
|
|
@@ -818,7 +822,8 @@ bun run test:integration # vitest project "integration"
|
|
|
818
822
|
bun run test:coverage # both projects with coverage, 90% thresholds
|
|
819
823
|
bun run validate # publint and attw with the esm-only profile
|
|
820
824
|
bun run fixture:pack # pack the fixture game into tests/integration/merge-game/dist/assets
|
|
821
|
-
bun run fixture:visual # the fixture's visual tests, both legs: --url <dev page>, --update, --only <name>, --no-pixels
|
|
825
|
+
bun run fixture:visual # the fixture's visual tests, both legs: --url <dev page>, --update, --only <name>, --no-pixels, --webgl
|
|
826
|
+
bun run fixture:native # the fixture as a native app on @moku-labs/native: <target> [--simulator] [--page <html>]
|
|
822
827
|
bun run release:setup # moku-release setup
|
|
823
828
|
bun run release:doctor # moku-release doctor
|
|
824
829
|
bun run release # moku-release
|
|
@@ -838,7 +843,7 @@ bun run release # moku-release
|
|
|
838
843
|
|
|
839
844
|
Plugin tests never go into the root `tests/` folder. Coverage thresholds are 90% for lines, functions, branches and statements.
|
|
840
845
|
|
|
841
|
-
### Lint rules
|
|
846
|
+
### Lint rules
|
|
842
847
|
|
|
843
848
|
The project rules live in [`eslint.config.ts`](./eslint.config.ts).
|
|
844
849
|
|
|
@@ -853,6 +858,7 @@ The project rules live in [`eslint.config.ts`](./eslint.config.ts).
|
|
|
853
858
|
| L7 | The public contract carries the docs: every member of a `…Api` type in `types.ts` has JSDoc and a scenario `@example` (when it is called, literal arguments, the result). A member another plugin calls is shown from that plugin's point of view; there is no private tier and no exemption. The implementation of an API method has no JSDoc. Elsewhere an example is allowed, never required | `src/plugins/**/types.ts` |
|
|
854
859
|
| L8 | No signature echo: an `@example` whose whole body is one call with bare identifiers is an error | `src/**` |
|
|
855
860
|
| L9 | The JSX runtime module is reached only through `src/jsx-runtime.ts` and `src/jsx-dev-runtime.ts`, and those two import nothing else | `src/**` outside `ui` |
|
|
861
|
+
| L13 | No import of `@moku-labs/system`, `@moku-labs/native` or `@tauri-apps/*`, type imports included. The game builds the `PlatformProvider` in its own layer | every file under `src/`, tests included |
|
|
856
862
|
|
|
857
863
|
## Requirements
|
|
858
864
|
|
|
@@ -875,6 +881,8 @@ The project rules live in [`eslint.config.ts`](./eslint.config.ts).
|
|
|
875
881
|
- [`text`](./src/plugins/text/README.md): styles, tags, measurement, fonts
|
|
876
882
|
- [`ui`](./src/plugins/ui/README.md): the JSX runtime, components, styles, layout, popups
|
|
877
883
|
- [`audio`](./src/plugins/audio/README.md): buses, the unlock, pause, memory
|
|
884
|
+
- [`effects`](./src/plugins/effects/README.md): particles, filters, WGSL and GLSL twins, budgets
|
|
885
|
+
- [`platform`](./src/plugins/platform/README.md): the provider seam, the Back chain, haptics, keep-awake
|
|
878
886
|
- [`llms.txt`](./llms.txt): overview for an LLM that writes a game on this engine
|
|
879
887
|
- [Moku Core specification](https://github.com/moku-labs/core/tree/main/specification)
|
|
880
888
|
|
package/dist/assets.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Tt as Time, Ut as Api, ft as FlowState, lt as Bookmark, t as Api$1, u as DebugSwitches } from "./types-
|
|
2
|
-
import { d as HeadlessApp, n as Command, r as ControlApp } from "./types-
|
|
3
|
-
import { H as InputApi } from "./types-
|
|
4
|
-
import {
|
|
1
|
+
import { Tt as Time, Ut as Api, ft as FlowState, lt as Bookmark, t as Api$1, u as DebugSwitches } from "./types-BZ1OLf24.mjs";
|
|
2
|
+
import { d as HeadlessApp, n as Command, r as ControlApp } from "./types-DmhTHRVW.mjs";
|
|
3
|
+
import { H as InputApi } from "./types-X9jXjka3.mjs";
|
|
4
|
+
import { A as UiApi, t as AnimApi } from "./types-CqkrEY06.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/plugins/flow/doors/commands.d.ts
|
|
7
7
|
/**
|
package/dist/control.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { d as HeadlessApp, i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-
|
|
2
|
-
import { t as commands } from "./commands-
|
|
3
|
-
import { t as defineCommand } from "./define-
|
|
1
|
+
import { d as HeadlessApp, i as InputArguments, n as Command, o as InputSchema, s as Ran } from "./types-DmhTHRVW.mjs";
|
|
2
|
+
import { t as commands } from "./commands-C0BljsM5.mjs";
|
|
3
|
+
import { t as defineCommand } from "./define-CjP_4H0G.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/plugins/flow/doors/dev.d.ts
|
|
6
6
|
/**
|