@moku-labs/game 0.4.5 → 0.5.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 +36 -0
- package/dist/assets.d.mts +1 -1
- package/dist/{commands-C83eQ5B9.d.mts → commands-mzE9PX0H.d.mts} +14 -6
- package/dist/control.d.mts +3 -3
- package/dist/control.mjs +1 -1
- package/dist/{define-BKWjWgyQ.d.mts → define-BbgXv4cy.d.mts} +1 -1
- package/dist/hot.d.mts +85 -0
- package/dist/hot.mjs +108 -0
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +624 -71
- package/dist/inspect.d.mts +6 -6
- package/dist/jsx-dev-runtime.d.mts +1 -1
- package/dist/jsx-runtime.d.mts +1 -1
- package/dist/lint.d.mts +79 -0
- package/dist/lint.mjs +360 -0
- package/dist/{run-D_R0oJ6g.mjs → run-Bcyg32M5.mjs} +40 -11
- package/dist/{runtime-Bhu442L8.d.mts → runtime-HTlmRb81.d.mts} +1 -1
- package/dist/testing.d.mts +5 -5
- package/dist/testing.mjs +19 -4
- package/dist/{types-Dy1xB68L.d.mts → types-BMEn0tSC.d.mts} +39 -4
- package/dist/{types-VErbiEXy.d.mts → types-BaUUcIaN.d.mts} +59 -6
- package/dist/{types-C-4f2jD_.d.mts → types-CXyipKhE.d.mts} +291 -249
- package/dist/{types-DtmFVCHf.d.mts → types-CYYKjvLn.d.mts} +1 -1
- package/dist/{types-w646dMeA.d.mts → types-ClF8say0.d.mts} +7 -3
- package/llms.txt +52 -6
- package/package.json +10 -1
package/llms.txt
CHANGED
|
@@ -11,6 +11,8 @@ Facts: Bun only. ESM only. `"sideEffects": false`. `pixi.js` `^8.0.0` is a peer
|
|
|
11
11
|
- `@moku-labs/game/assets` (node and bun only): the asset key scanner, the production packer and the string compiler. The package bin `moku-game-assets` runs it.
|
|
12
12
|
- `@moku-labs/game/inspect`: the read door of the editor, safe in a production build. See "Doors for the editor".
|
|
13
13
|
- `@moku-labs/game/control`: the write door of the editor, dev builds only. See "Doors for the editor".
|
|
14
|
+
- `@moku-labs/game/hot` (the Bun dev server only): the `bunfig.toml` plugin that hot swaps views in a running page. See "Hot swap".
|
|
15
|
+
- `@moku-labs/game/lint`: the oxlint JS plugin `moku-game` with the game lint rules. See "Lint for games".
|
|
14
16
|
- `@moku-labs/game/fonts/*`: the shipped MSDF body font and its licence. See "Fonts".
|
|
15
17
|
- `@moku-labs/game/jsx-runtime` and `@moku-labs/game/jsx-dev-runtime`: what `"jsxImportSource": "@moku-labs/game"` resolves to. A game never imports them by hand.
|
|
16
18
|
|
|
@@ -130,6 +132,33 @@ type NodeContext = {
|
|
|
130
132
|
- A node never calls `app.model`, `app.clock` or `app.time`. Everything outside logic goes through `fx`.
|
|
131
133
|
- A timer is a moment stored in player state. How long is left is computed from `now`.
|
|
132
134
|
|
|
135
|
+
## Lint for games
|
|
136
|
+
|
|
137
|
+
`.oxlintrc.json` of a game (oxlint 1.86.0 or later):
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"jsPlugins": ["@moku-labs/game/lint"],
|
|
142
|
+
"rules": {
|
|
143
|
+
"moku-game/lazy-imports": "error",
|
|
144
|
+
"moku-game/native-imports": "error",
|
|
145
|
+
"moku-game/dev-imports": "error",
|
|
146
|
+
"moku-game/no-module-state": "error",
|
|
147
|
+
"moku-game/determinism": "error",
|
|
148
|
+
"moku-game/rules-siblings": "error"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- `moku-game/lazy-imports` (L2): no static value import of `pixi.js` or `yoga-layout`. `import type` and `import()` pass. Default files `**`.
|
|
154
|
+
- `moku-game/native-imports` (L13): no `@moku-labs/system`, `@moku-labs/native`, `@tauri-apps/*` in the logic, `kit.ts` and `game.ts`. Use `platform-bridge.ts`.
|
|
155
|
+
- `moku-game/dev-imports`: `@moku-labs/editor` and `@moku-labs/game/control` only in `web/main.ts`, `web/dev*.ts`, `web/editor*.ts`, `*.dev.ts(x)` and tests.
|
|
156
|
+
- `moku-game/no-module-state` (L5): no module-scope `let` or `var`, no module-scope `new Map/Set/WeakMap/WeakSet`. Default files `**`.
|
|
157
|
+
- `moku-game/determinism` (L3): no `Math.random`, `Date.now`, `performance.now`, `new Date()`, `setTimeout`, `setInterval` in the logic. `new Date(now)` passes.
|
|
158
|
+
- `moku-game/rules-siblings` (L4): a file under `rules/` imports only `./` siblings.
|
|
159
|
+
- Logic = `**/state.ts`, `**/tables.ts`, `**/{nodes,flows,rules,features}/**`. Tests (`tests/**`, `**/__tests__/**`, `*.test.ts`) are skipped by every rule.
|
|
160
|
+
- Every rule takes `["error", { "files": [...], "ignores": [...] }]`. Globs are relative to the directory oxlint runs in; a key given replaces the default.
|
|
161
|
+
|
|
133
162
|
## Minimal game
|
|
134
163
|
|
|
135
164
|
```ts
|
|
@@ -355,13 +384,15 @@ The graph. Modules `runner`, `gate`, `inbox`, `fx`, `features`. Runner methods s
|
|
|
355
384
|
- `fx.handle(kind, handler, options?: { runInFast?: boolean }): () => void` registers the single handler of one effect kind. `fx.dispatch(descriptor)` is fire-and-forget. An awaited effect without a handler resolves `undefined` at once.
|
|
356
385
|
- `features.register(name, description)`, `features.all()`, `features.contributions(slotName)`.
|
|
357
386
|
- `FlowState` is `{ running, path, stack, pending: { fx?, gate? }, mode }`.
|
|
358
|
-
- `Bookmark` is `{ path, input, player, session, rng, graph }`. Serialisable. `restore` needs a running graph and the path of a rest node. A checkpoint is always accepted. Any other rest node is accepted only when the graph did not change since the bookmark.
|
|
387
|
+
- `Bookmark` is `{ path, input, player, session, rng, graph, scene? }`. Serialisable. `scene` is written by the `game.bookmark` door and read by `game.restore`; `flow.restore` ignores it. `restore` needs a running graph and the path of a rest node. A checkpoint is always accepted. Any other rest node is accepted only when the graph did not change since the bookmark.
|
|
359
388
|
- Events: `flow:edge` `{ flow, node, outcome, payload, next, patches: { doc, session }, index, now }` after the commit of an edge. `flow:rest` `{ path, checkpoint }`. `flow:error` `{ path, error, rolledBackTo, retry }`.
|
|
360
389
|
- Config: `mainFlow` default `undefined` and required before `run()`, `safeNode: string | undefined` default `undefined` which means the main flow's `start`, `retries` default `1`, `settleTimeoutMs` default `2000`, `journalLimit` default `500`.
|
|
361
390
|
|
|
362
391
|
### Screen plugins (V2, built)
|
|
363
392
|
|
|
364
393
|
`world`, `renderer`, `input`, `assets`, `scenes`. Each has a README at `src/plugins/<name>/README.md`; the root README lists their APIs, events and config. Rendering: WebGPU preferred, with Pixi's WebGL fallback.
|
|
394
|
+
- `app.scenes`: `current()` the mounted scene id, `expect(id)` the scene the next rest node without its own `scene` mounts (the restore door calls it; an unknown id throws).
|
|
395
|
+
- `app.world.projection.replace(spec)` swaps the spec of a registered projection; a mounted one runs its new `view` on the next frame. An unknown name throws. The caller is the dev hot swap.
|
|
365
396
|
|
|
366
397
|
### Interface plugins (V3, built)
|
|
367
398
|
|
|
@@ -370,11 +401,11 @@ A screen is a projection whose `view` returns JSX. The game's `tsconfig.json` se
|
|
|
370
401
|
- Tags: `screen`, `layer`, `row`, `column`, `stack`, `spacer`, `panel` (swallows taps), `image` (`texture`, `fit?: "contain" | "cover" | "fill"`), `icon` (`name`, `fit?`), `text` (`content: string | Message`, or `bind={bind(Counter, "value")}` with `components={[Counter({ value })]}`; formats `int`, `mm:ss`, `h:mm:ss`, `duration`; `Countdown({ until })` with `bind(Countdown, "left", { format: "mm:ss" })` counts down to a `clock` moment; `style` is a text style key), `button` (`intent` + `payload`, or `local`, never both), `scroll` (`axis: "y"`; children for a short list, or the windowed form `rows`, `rowHeight`, `overscan?` (5), `row: index => node`; row state belongs in the model, a row that leaves the window loses its local state), `input` (a text field; its text lives in the component's `local`, see the ui README). Common props: `key`, `style`, `state: { active?, disabled?, selected? }`, `motion`, `components` (extra component values for the element's entity, such as a filter: `components={[Glow({ strength: 2 })]}`); containers take `hosts?: string[]` (projections drawn inside the element). Style adds `nineSlice`, `tint`, `offsetX`, `offsetY`, `scale`, `origin`, `fit: "contain"`; state variants `is: { hover, pressed, disabled, active, selected, covered }`. `popup(Component, props, { over })` stacks a popup over a kept one.
|
|
371
402
|
- Style vocabulary (`defineStyle`, plain data): flow `direction`, `wrap`, `justify`, `align`, `alignSelf`, `gap`, `grow`, `shrink`; box `padding`, `margin`, `width`, `height`, `minWidth`, `minHeight`, `maxWidth`, `maxHeight`, `aspect`, `overflow`; position `position`, `left`, `top`, `right`, `bottom`, `reason`; visual `fill`, `stroke`, `strokeWidth`, `radius`, `alpha`; variants `is: { pressed?, disabled?, active?, selected? }` and `when: { portrait?, landscape?, tall?, wide? }`, each a partial style merged in when the flag holds. Lengths are reference units or `"safeArea.top" | "safeArea.right" | "safeArea.bottom" | "safeArea.left"`. No font size here: it comes from the text style.
|
|
372
403
|
- `defineComponent(name, { local?, outcomes?, view(props, local) })`. A component with `outcomes` is a popup: `await fx(popup(Component, props))` opens the gate for its outcomes and resolves with `{ intent, payload }` of the pressed button. A button with `local` writes into the nearest component instance; it never reaches the graph.
|
|
373
|
-
- `tr(key, params?)` returns a frozen `Message` `{ key, params }`; no locale is read at the call site. Strings live in `features/<feature>/strings/<locale>.json` as ICU MessageFormat 1 (`{n, plural, one {# order} other {# orders}}`, `select`, `selectordinal`, `number`, `date`, `time`; no skeletons, no `currency`). `bun run assets:keys` compiles them. `app.i18n`: `locale()`, `setLocale(locale)` (async, emits `i18n:locale-changed`), `format(message, locale?)` returns `Part[]`, `plain(message)` returns a string, `has(key)`, `locales()
|
|
374
|
-
- Text: `Text` component, `label({ text, style, at, anchor? })` for board text in a projection view, `defineTextStyles({ "hud.digits": { font, size, fill, bold?, italic?, stroke?, strokeWidth?, letterSpacing?, align?, wrap?, digits? } })` under the feature's `textStyles` key. Tags inside a string: `<b>`, `<i>`, `<color=#rrggbb>`, `<icon=key>`, `\<`, `\n`. Built-in styles `body` and `digits` from config `fonts: { body: "ui.font-body", digits: "ui.font-digits" }`. `app.text.measure(content, style)` gives the same numbers in a browser and in Bun.
|
|
375
|
-
- Animation is data: `defineAnimation(id, { slots: { name: type<Anim.Target>() }, build: (slots, { at }) => Step })`. Steps: `sequence(...)`, `parallel(...)`, `stagger(items, ms, item => Step)`, `tween(target, Component, to, { ms, ease?, delayMs?, additive? })`, `set(target, Component, patch)`, `wait(ms)`, `mark(name)`, `frames(target, { keys, fps, loop? })`, `sfx(key, { bus? })`, `haptic(kind)`, `use(animation, slots, tools?)`, `spawn` (a temporary entity, flying coins, that the timeline despawns when it ends; see the anim README). `Frames({ keys, fps, loop, playing })` is a component that walks a sprite through its textures for the entity's whole life. A target is `{ projection, key }` or an entity. A node plays it with `await fx(play(animation, slots))`; in fast mode it finishes at once. `defineMotion({ states, transition?, on: { enter?, exit?, change? } })` builds the enter, exit and change hooks for a projection or the `motion` prop of a tag. Events `anim:mark` `{ animation, mark }` and `anim:finished` `{ animation }`. Config `maxTracks: 2000`.
|
|
404
|
+
- `tr(key, params?)` returns a frozen `Message` `{ key, params }`; no locale is read at the call site. Strings live in `features/<feature>/strings/<locale>.json` as ICU MessageFormat 1 (`{n, plural, one {# order} other {# orders}}`, `select`, `selectordinal`, `number`, `date`, `time`; no skeletons, no `currency`). `bun run assets:keys` compiles them. `app.i18n`: `locale()`, `setLocale(locale)` (async, emits `i18n:locale-changed`), `format(message, locale?)` returns `Part[]`, `plain(message)` returns a string, `has(key)`, `locales()`, `replace(locale, messages)` (dev hot swap of a generated strings file). Config: `locale: "en"`, `fallback: "en"`, `locales: {}`.
|
|
405
|
+
- Text: `Text` component, `label({ text, style, at, anchor? })` for board text in a projection view, `defineTextStyles({ "hud.digits": { font, size, fill, bold?, italic?, stroke?, strokeWidth?, letterSpacing?, align?, wrap?, digits? } })` under the feature's `textStyles` key. Tags inside a string: `<b>`, `<i>`, `<color=#rrggbb>`, `<icon=key>`, `\<`, `\n`. Built-in styles `body` and `digits` from config `fonts: { body: "ui.font-body", digits: "ui.font-digits" }`. `app.text.measure(content, style)` gives the same numbers in a browser and in Bun. `app.text.replaceStyles(styles)` is the dev hot swap of a saved `defineTextStyles` value.
|
|
406
|
+
- Animation is data: `defineAnimation(id, { slots: { name: type<Anim.Target>() }, build: (slots, { at }) => Step })`. Steps: `sequence(...)`, `parallel(...)`, `stagger(items, ms, item => Step)`, `tween(target, Component, to, { ms, ease?, delayMs?, additive? })`, `set(target, Component, patch)`, `wait(ms)`, `mark(name)`, `frames(target, { keys, fps, loop? })`, `sfx(key, { bus? })`, `haptic(kind)`, `use(animation, slots, tools?)`, `spawn` (a temporary entity, flying coins, that the timeline despawns when it ends; see the anim README). `Frames({ keys, fps, loop, playing })` is a component that walks a sprite through its textures for the entity's whole life. A target is `{ projection, key }` or an entity. A node plays it with `await fx(play(animation, slots))`; in fast mode it finishes at once. `defineMotion({ states, transition?, on: { enter?, exit?, change? } })` builds the enter, exit and change hooks for a projection or the `motion` prop of a tag. Events `anim:mark` `{ animation, mark }` and `anim:finished` `{ animation }`. `app.anim.replace(definition)` is the dev hot swap: the next `play` builds the new tree. Config `maxTracks: 2000`.
|
|
376
407
|
- Audio is opt-in: `plugins: [...screen, audioPlugin]`. `fx(sfx("orders.complete"))` resolves when the sound started. `await fx(music("board.theme"))` cross-fades; `music(null)` fades out; a scene's `music` key switches the track on `scenes:changed`. Buses `master`, `music`, `sfx`. Volumes come from the committed player: config `volumes: player => (player as Player).settings.audio`, applied on every `model:committed`. `app.audio`: `setVolume(bus, value)`, `volume(bus)`, `mute(bus, on)`, `muted(bus)`, `unlocked()`, `journal()` (the last started sounds, empty unless config `journal` is above 0). Config: `buses: { master: 1, music: 0.6, sfx: 1 }`, `musicFadeMs: 600`, `volumes`, `context`, `journal: 0`, `music: "decode" | "stream"` default `"decode"` (gapless buffer; `"stream"` is an `<audio>` element, less memory, a gap at every loop), `session: "ambient" | "playback" | "auto"` default `"ambient"` (iOS only: ambient mixes with other apps and obeys the silent switch). Without `audio` every `sfx` and `music` effect resolves `undefined` at once, so the same node runs in a game with no sound.
|
|
377
|
-
- `app.ui`: `tree()` the live screen as plain data, `find(key)` the entity of a keyed element, `fill(key, value)` types into the text field with that `key`, `lint()` tap targets under `tapTargetPt` (default `44`), text that overflows in some locale, absolute elements without `reason`. Config: `tapTargetPt: 44`, `breakpoints: { tall: 2, wide: 1.5 }`. `ui` emits
|
|
408
|
+
- `app.ui`: `tree()` the live screen as plain data (a text node carries `content`, the words it draws after `tr()` and `bind`; a text field carries `value`), `find(key)` the entity of a keyed element, `fill(key, value)` types into the text field with that `key`, `lint()` tap targets under `tapTargetPt` (default `44`), text that overflows in some locale, absolute elements without `reason`. Config: `tapTargetPt: 44`, `breakpoints: { tall: 2, wide: 1.5 }`. `ui` emits only the dev global event `ui:hot-swap`, see "Hot swap".
|
|
378
409
|
|
|
379
410
|
```tsx
|
|
380
411
|
// features/hud/view.tsx
|
|
@@ -469,9 +500,24 @@ Two subpaths, neither on the root (V4). An editor, MCP tools and e2e scripts rea
|
|
|
469
500
|
- `@moku-labs/game/inspect`: `read(app, source, input?)` reads once; `watch(app, source, input, fn)` reads on the `signals` phase when the source changes and returns the stop function; `defineSource`; the frozen catalogue `sources`; the types `Source`, `InputSchema`, `InputOf`. Every source only reads, so the door is safe in a production build. Keys of `sources`: `graph`, `position`, `history`, `tainted`, `cheats`, `model`, `entities`, `projections`, `ui`, `locate`, `render`, `sounds`, `audioMuted`, `effects`, `assets`, `log`, `explain`, `diff`, `schema`, `at`; their ids are `game.<key>`. `locate` takes `{ key }` (a ui element) or `{ target }` (a view by projection key) and answers its box in CSS px, the numbers Playwright clicks. `at` takes `{ x, y }` in CSS px and lists the views under that point, topmost first. `explain` takes `{ entity }` and answers that entity in full, `undefined` for a stale id. `diff` takes `{ from, to }` frames and answers what changed per entity; it throws in a production build and for a frame outside the last 120. `schema` lists every component and tag type the world met, with the JSON kind of each field. `audioMuted` answers whether the master bus is muted. `sounds` throws `[game] The source game.sounds needs audioPlugin.`, `audioMuted` throws `[game] The source game.audioMuted needs audioPlugin.` and `effects` throws `[game] The source game.effects needs effectsPlugin.` when that plugin is not installed; the editor lists such a source as not installed.
|
|
470
501
|
- `@moku-labs/game/control`: `run(app, command, input?)` resolves `{ value, state }`, `state` being `{ path, frame, tainted }`; `defineCommand`; `controlRefused`; the frozen catalogue `commands`; the types `Command`, `Ran`. Keys of `commands`: `answer`, `tap`, `drag`, `key`, `fill`, `walk`, `bookmark`, `restore`, `step`, `pause`, `resume`, `capture`, `debug`, `reducedMotion`, `timeScale`, `mute`, `trace`. `capture` resolves `{ png }`, and `{ png, legend }` with `legend: true`; `undefined` while the renderer is inert. `timeScale` takes `{ scale }`; 0 freezes game time while the frames keep running. `mute` takes `{ muted }`, mutes the master bus and answers the flag. Without `audioPlugin` it throws `[game] The command game.mute needs audioPlugin.` `trace` takes `{ path }`, a list of views by projection key, and answers whether the gate took the answer.
|
|
471
502
|
- Commands run in dev builds only. Only `__MOKU_GAME_DEV__ === true` turns the dev build on: a bundler `define` (`true` in dev, `false` in production), `globalThis.__MOKU_GAME_DEV__ = true` in the dev entry, or `vi.stubGlobal("__MOKU_GAME_DEV__", true)` in a test. Otherwise `run` throws `[game] Control commands run in dev builds only.`
|
|
472
|
-
- A command's effect is `read`, `route`, `cosmetic`, `cheat` or `raw`. A `cheat` or `raw` command taints the session and is journaled; read it with `sources.tainted` and `sources.cheats`. Of the base commands only `restore` is `raw`.
|
|
503
|
+
- A command's effect is `read`, `route`, `cosmetic`, `cheat` or `raw`. A `cheat` or `raw` command taints the session and is journaled; read it with `sources.tainted` and `sources.cheats`. Of the base commands only `restore` is `raw`. `bookmark` answers `flow.bookmark()` plus `scene`, the mounted scene; `restore` calls `scenes.expect(scene)` before `flow.restore`, so a bookmark at a popup restores the scene under it.
|
|
473
504
|
- A game's own: `defineSource({ id, title, input, changes, read })` with `changes` `"frame"`, `"commit"` or `"edge"`, and `defineCommand({ id, title, input, effect, run })`. The id is camelCase words joined by dots, at least two: `dice.rolls`. An input schema maps field names to `"string"`, `"number"`, `"boolean"` or `"json"`; a trailing `?` makes one optional. A command body starts with `if (typeof __MOKU_GAME_DEV__ === "undefined" || !__MOKU_GAME_DEV__) throw controlRefused();`. Keep them in `.dev` modules that only the dev entry and the tests import.
|
|
474
505
|
|
|
506
|
+
## Hot swap
|
|
507
|
+
|
|
508
|
+
Dev only. A save of a view file swaps the module in the running page: same state, same node, no reload. The game's `bunfig.toml` lists the plugin; the dev server runs in the game root, and a production `Bun.build` never loads it:
|
|
509
|
+
|
|
510
|
+
```toml
|
|
511
|
+
[serve.static]
|
|
512
|
+
plugins = ["@moku-labs/game/hot"]
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
- View files swap: `.tsx`, `styles.ts`, `view.ts`, `animations.ts`, `effects.ts`, `generated/strings.<locale>.ts`. Styles, components, function components, projections of a registered name, animations, text styles, emitters and strings. After a strings JSON edit run `bun run assets:keys`; its output is what swaps.
|
|
516
|
+
- Logic files reload the page: `rules/`, `nodes/`, `flows/`, `state.ts`, `tables.ts`, `kit.ts`, a feature `index.ts`. A view file that exports a scene, system, ECS component, filter, flow, node, feature, plugin, a new projection or a new animation is refused: `ui` logs `ui:hot-refused` and throws, and Bun reloads the page.
|
|
517
|
+
- A swap logs `ui:hot-swap` and emits the global event `ui:hot-swap` `{ file, module }`.
|
|
518
|
+
- Another layout: `export default hot({ include, exclude })` from the game's own `hot.ts`, listed as `plugins = ["./hot.ts"]`.
|
|
519
|
+
- Keep a view helper in the module of the projection that uses it. A function another module stored into an object at load time keeps the old version.
|
|
520
|
+
|
|
475
521
|
## Fonts
|
|
476
522
|
|
|
477
523
|
The package ships one MSDF body font: `fonts/font-body.fnt` and `fonts/font-body.png` (Pangolin Regular) with `fonts/LICENSE.txt` (SIL Open Font License 1.1). Copy it into the game; a path in `node_modules` is not an asset key, the scanner reads keys only from `features/<feature>/assets/`. The licence goes next to `assets/`, not inside it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moku-labs/game",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "2D puzzle game engine on TypeScript and PixiJS, built on @moku-labs/core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -27,6 +27,14 @@
|
|
|
27
27
|
"types": "./dist/control.d.mts",
|
|
28
28
|
"default": "./dist/control.mjs"
|
|
29
29
|
},
|
|
30
|
+
"./hot": {
|
|
31
|
+
"types": "./dist/hot.d.mts",
|
|
32
|
+
"default": "./dist/hot.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./lint": {
|
|
35
|
+
"types": "./dist/lint.d.mts",
|
|
36
|
+
"default": "./dist/lint.mjs"
|
|
37
|
+
},
|
|
30
38
|
"./jsx-runtime": {
|
|
31
39
|
"types": "./dist/jsx-runtime.d.mts",
|
|
32
40
|
"default": "./dist/jsx-runtime.mjs"
|
|
@@ -124,6 +132,7 @@
|
|
|
124
132
|
"globals": "17.4.0",
|
|
125
133
|
"jiti": "2.6.1",
|
|
126
134
|
"lefthook": "2.1.1",
|
|
135
|
+
"oxlint": "1.86.0",
|
|
127
136
|
"pixi.js": "8.21.0",
|
|
128
137
|
"playwright-core": "1.60.0",
|
|
129
138
|
"publint": "0.3.21",
|