@datamoc/mw_games 0.8.1 → 0.9.0-alpha

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 (47) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/MWG - Best Practices.md +97 -0
  3. package/MWG - Deep Code and Architecture Review.md +238 -0
  4. package/README.md +7 -4
  5. package/dist/core/Sanitize.d.ts +8 -0
  6. package/dist/core/Sanitize.d.ts.map +1 -1
  7. package/dist/core/Sanitize.js +20 -0
  8. package/dist/core/Sanitize.js.map +1 -1
  9. package/dist/core/Save.d.ts +8 -1
  10. package/dist/core/Save.d.ts.map +1 -1
  11. package/dist/core/Save.js +26 -7
  12. package/dist/core/Save.js.map +1 -1
  13. package/dist/mw_games.global.js +107 -91
  14. package/dist/mwl/compiler.d.ts +7 -7
  15. package/dist/mwl/compiler.js +7 -7
  16. package/dist/mwl/content.d.ts +1 -1
  17. package/dist/mwl/content.js +1 -1
  18. package/dist/mwl/grammar.d.ts +18 -8
  19. package/dist/mwl/grammar.d.ts.map +1 -1
  20. package/dist/mwl/grammar.js +227 -92
  21. package/dist/mwl/grammar.js.map +1 -1
  22. package/dist/mwl/persistence.d.ts +4 -1
  23. package/dist/mwl/persistence.d.ts.map +1 -1
  24. package/dist/mwl/persistence.js +6 -1
  25. package/dist/mwl/persistence.js.map +1 -1
  26. package/dist/mwl/readers.d.ts +2 -2
  27. package/dist/mwl/readers.js +2 -2
  28. package/dist/mwl/report.d.ts +2 -2
  29. package/dist/mwl/report.js +2 -2
  30. package/dist/mwl/runtime.d.ts +30 -1
  31. package/dist/mwl/runtime.d.ts.map +1 -1
  32. package/dist/mwl/runtime.js +204 -209
  33. package/dist/mwl/runtime.js.map +1 -1
  34. package/dist/mwl/schema.d.ts +1 -1
  35. package/dist/mwl/schema.js +1 -1
  36. package/dist/two-d/Game.d.ts +10 -4
  37. package/dist/two-d/Game.d.ts.map +1 -1
  38. package/dist/two-d/Game.js.map +1 -1
  39. package/dist/two-d/render/Types2D.d.ts +6 -4
  40. package/dist/two-d/render/Types2D.d.ts.map +1 -1
  41. package/dist/two-d/render/Types2D.js +6 -4
  42. package/dist/two-d/render/Types2D.js.map +1 -1
  43. package/dist/version.d.ts +1 -1
  44. package/dist/version.d.ts.map +1 -1
  45. package/dist/version.js +1 -1
  46. package/dist/version.js.map +1 -1
  47. package/package.json +5 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,62 @@ the public API may still change between minor versions.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.0-alpha] - 2026-09-13
11
+
12
+ ### Changed
13
+
14
+ - MWL authoring syntax is now JSON5 instead of the WML-inspired tag/`key=value`
15
+ form: every `.mwl` file is a top-level array of `{ tag, ...attributes, children }`
16
+ nodes, values are real JSON5 numbers, booleans and strings (stringified back to
17
+ the `Record<string, string>` the schema, compiler and runtime already see), and
18
+ translatable strings are marked `_("...")` via a preprocessor rewrite. The
19
+ `#define`/`#include`/`#ifdef` textual pass is unchanged. The `"""..."""` block
20
+ form (item 317) is superseded by JSON5 `\n` escapes and backslash line
21
+ continuations. Clean cutover: all fixtures, examples, tests and doc examples
22
+ are migrated, the old tag parser is deleted, and `json5` (pure JS, no native
23
+ build) is a runtime dependency.
24
+
25
+ ## [0.8.2] - 2026-09-13
26
+
27
+ ### Added
28
+
29
+ - `tests/builtin-pipes.test.ts`: regression coverage confirming `TiledSprite`/`NinePatch`
30
+ resolve to Pixi's own registered `tilingSprite`/`nineSliceSprite` pipes, a guarantee previously
31
+ verified only by a downstream consumer's own uncommitted script (item 336).
32
+
33
+ ### Changed
34
+
35
+ - `mwl/runtime.ts`'s `executeNode` (a 226-line `switch` over 28 command tags, its own case
36
+ bodies inline) is now a `commandHandlers` dispatch table plus one small, independently-named
37
+ private method per command. No public API or behaviour change: `API_REPORT.md`'s diff is
38
+ purely additive private-method stubs, and all 166 `mwl-*.test.ts` tests pass unmodified
39
+ (item 337).
40
+ - `DEVELOPMENT.md` documents why `coverage`/`coverage:check` exclude `Game.ts`,
41
+ `ColorTransformBatcher.ts`, `Minimap.ts`, `three-d/Vox.ts`, `DialogueStage.ts`,
42
+ `EventDialogue.ts` and `two-d/ui/**` (Pixi rendering/layout, verified by the visual smokes
43
+ instead of a coverage percentage), and why `core` holds optional network-facing clients
44
+ alongside always-needed primitives (item 333, item 334).
45
+ - `GameOptions.extensions`'s doc comment now names `TiledSprite`/`NinePatch` alongside
46
+ `TintedSprite` as self-registering their Pixi render pipe automatically, closing a
47
+ documentation gap that likely explains why a cautious consumer wasn't confident enough to rely
48
+ on it (item 336).
49
+
50
+ ### Fixed
51
+
52
+ - `mwl`'s `validateWorld` rejects a `__proto__`/`constructor`/`prototype` key anywhere in a
53
+ decoded save (item 335): `MwlRuntime.restore` merges a decoded save into its own world with
54
+ `Object.assign`, which honours such a key as a prototype substitution rather than an ordinary
55
+ value. `core/Sanitize.ts` gains `assertNoForbiddenKeys`, the same rejection `validateSchema`
56
+ already applied to fixed-shape data, generalised to `mwl`'s dynamic, arbitrarily-nested world
57
+ shape.
58
+ - `SaveSystem.load` returns `null` for a corrupted or malformed slot instead of throwing an
59
+ uncaught `SyntaxError`, the same outcome a missing slot already produced; `importSlot` throws a
60
+ labelled `SaveSystem.importSlot: payload is not valid save data` error instead of a bare parse
61
+ error (item 331).
62
+ - `Container2D`/`Texture2D` in `two-d/render/Types2D.ts` each carried two adjacent JSDoc blocks,
63
+ so only the generic, less useful one reached generated documentation; merged into one comment
64
+ each (item 332).
65
+
10
66
  ## [0.8.1] - 2026-09-12
11
67
 
12
68
  ### Added
@@ -0,0 +1,97 @@
1
+ # MWG: Best Practices
2
+
3
+ Practical guidance for someone starting a real project with `mwg` today: how to work with the
4
+ grain of the framework instead of against it. Pair this with
5
+ [MWG: Deep Code and Architecture Review](MWG%20-%20Deep%20Code%20and%20Architecture%20Review.md)
6
+ for the *why* behind each of these; this document is the *how*.
7
+
8
+ ## Start from a working example, not a blank page
9
+
10
+ The shipped examples (colour-transform, interface, dialogue/stage, roguelike dungeon) are
11
+ real, buildable, `file://`-verified projects, not snippets. Copy the one closest to your game
12
+ and grow it, rather than assembling a project from the API reference alone. Decide early which
13
+ of the two install paths in the getting-started guide you actually need (a bundler-based
14
+ project with npm, or a plain `<script>` tag with no build step at all) and verify your own
15
+ project reaches a working `file://` page under that path before you build much on top of it.
16
+
17
+ ## Reach for the typed facade first, the escape hatch second
18
+
19
+ Ordinary sprites, shapes, text, and containers go through `Sprite2D`/`Shape2D`/`Text2D`/
20
+ `TiledSprite`/`Gradient`. None of that requires your own code to name `pixi.js` anywhere. Only
21
+ reach for `two-d/pixi-interop` when you need a specific Pixi capability the facade doesn't
22
+ cover, and keep that usage confined to as few files as you can, the same way `mwg` itself
23
+ confines all Pixi-internal knowledge to one file. Two concrete benefits, not just tidiness:
24
+ your project stays portable if you ever need the Babylon path instead, and `pixi.js` is an
25
+ optional peer dependency, so code that never touches the escape hatch never has to install it.
26
+
27
+ ## Keep game logic renderer-free where you can
28
+
29
+ Model state and rules in plain data and `core`/`simulation`-level terms (stable entity ids via
30
+ `EntityRegistry`, not live object references) rather than reaching into a sprite or a scene
31
+ from inside game logic. Code written this way can be tested with Node's own test runner with
32
+ no renderer, no browser, and no ticker in the loop at all, the same separation that lets `mwg`
33
+ run the identical scene-lifecycle and save-system code under either a Pixi or a Babylon game.
34
+
35
+ ## Load once per scene, then stay synchronous
36
+
37
+ Call `load(paths)` up front for a scene and treat everything after that as synchronous code.
38
+ Scattering `await` through per-frame or per-action logic is a sign an asset load has drifted
39
+ into the wrong place; move it back to scene setup, using the `optional`/`onMissing` path if a
40
+ particular asset is allowed to be missing.
41
+
42
+ ## Trust your own data, validate everything else
43
+
44
+ Use `SaveSystem.load()` for a save this same process previously wrote. For anything that
45
+ crossed a device or a network boundary, a pasted save code, a cloud sync, a value a server
46
+ handed back, use `importSlot`/`importExternal` instead, and validate the *shape* of what comes
47
+ back, not just that it parsed as JSON. Apply the same split in your own persistence code even
48
+ outside `SaveSystem`: "this process wrote it a moment ago" and "this arrived from somewhere
49
+ else" are different trust levels, and worth two different code paths, not one path with an
50
+ optimistic cast.
51
+
52
+ ## Test the way `mwg` tests itself
53
+
54
+ Plain `.ts` sources under Node's built-in test runner, no framework, is a deliberate choice
55
+ here: it keeps the dependency list short and the tests portable. A green test suite and a
56
+ clean typecheck are necessary, not sufficient, verify anything visual (layout, a window's
57
+ position, whether choice text is legible against its background) by building an example and
58
+ actually looking at it. If you can't check a UI change visually yourself, say so plainly rather
59
+ than calling it done on the strength of the typechecker alone.
60
+
61
+ ## Verify from `file://`, not only from a dev server
62
+
63
+ The entire point of this framework is a game that opens by double-clicking a local file, no
64
+ server. A feature that only works against a bundler's dev server has not been verified against
65
+ the target it's actually meant to ship to. Build, then open the built page directly, before
66
+ calling a feature finished.
67
+
68
+ ## Only pull in the renderer you actually use
69
+
70
+ `three-d` is kept off the root barrel specifically so a 2D project never pays for Babylon; a
71
+ Babylon project likewise never needs to pull in Pixi. Import `two-d` or `3d`, not both, unless
72
+ your game genuinely mixes both kinds of scenes.
73
+
74
+ ## Keep your own vocabulary out of your own shared code
75
+
76
+ `mwg` holds no rule, value, or vocabulary belonging to any single reference game, only the
77
+ generic mechanic shape; your own game's specific numbers, names, and content stay in your own
78
+ game layer, supplied to `mwg`'s generic primitives as data. If your project grows multiple
79
+ internal systems, campaigns, or a modding surface, apply the same discipline one level up: keep
80
+ the genuinely generic mechanics generic, and keep any one system's specific content as data
81
+ that mechanic consumes, rather than baking it into the mechanic itself.
82
+
83
+ ## Don't build for the reuse you don't have yet
84
+
85
+ Favour plain data and composition over an early abstraction layer, a manager class, an event
86
+ bus, a plugin system, before you have a second real call site that actually needs it. `mwg`'s
87
+ own modules lean this way on purpose (`StatBlock`'s fixed modifier order over a rule engine,
88
+ `EntityTemplate`'s plain content rows over a generic component system) and it's a good default
89
+ for your own game code too: an abstraction with exactly one caller costs a future reader a hop
90
+ and buys nothing until a second caller actually shows up.
91
+
92
+ ## Verify an API claim before building on it
93
+
94
+ `mwg` is pre-alpha and still adding surface between releases. Check REFERENCE.md (or the
95
+ generated API docs) for the actual current name and signature of something before assuming it
96
+ matches a similar-sounding API from a different framework you've used before, especially for
97
+ anything not yet covered by the API-stability guarantee that starts at 1.0.
@@ -0,0 +1,238 @@
1
+ # MWG: Deep Code and Architecture Review
2
+
3
+ This is an architecture deep-dive for developers building with `mwg`: not what exists
4
+ (that's [REFERENCE.md](https://github.com/datamoc/mwg/blob/main/REFERENCE.md)'s job) or what
5
+ it's for (that's `README.md`'s), but how the pieces fit together and why they're shaped the
6
+ way they are. Read it once before you structure a non-trivial project, then use it as a map
7
+ when you need to decide where new code of your own belongs.
8
+
9
+ `mwg` is pre-alpha (`0.y.z`): the public API documented in REFERENCE.md and API_REPORT.md is
10
+ meant to be stable from 1.0 on, and anything not yet there carries an `@experimental` tag.
11
+ Nothing below hides that status behind confident prose; where a design choice is a deliberate
12
+ trade-off rather than a finished guarantee, it's said so.
13
+
14
+ ## The constraint that shapes everything: `file://`
15
+
16
+ A game built with `mwg` is meant to run by double-clicking a local HTML file. No server, no
17
+ install step, nothing to download at runtime. That single requirement cascades into most of
18
+ the framework's less obvious decisions:
19
+
20
+ - `fetch()`, XHR, and `<script type="module">` are all blocked from `file://`, and Chrome
21
+ treats even a same-folder `<img>` as cross-origin for WebGL. So every asset a build touches
22
+ is compiled into a `data:` URI ahead of time, not fetched at runtime.
23
+ - The standalone library build is a classic IIFE, not ES modules, so it can load from a plain
24
+ `<script>` tag with no bundler at all.
25
+ - A build's last step rewrites the bundler's `<script type="module">` entry tag to a classic
26
+ deferred script and inlines the compiled asset data before it, in document order.
27
+
28
+ If you only take one thing from this document: verify a change by opening the *built* page
29
+ from `file://`, not just by trusting a dev server. A feature that only works under Vite's dev
30
+ server is not finished.
31
+
32
+ ## Module map and dependency direction
33
+
34
+ Every module is its own barrel (`import { X } from '@datamoc/mw_games/core'`, or the matching
35
+ subpath), plus a root re-export. Two rendering worlds sit side by side over one renderer-free
36
+ core, and nothing in that core knows either exists:
37
+
38
+ ```
39
+ +------------------------------------------+
40
+ | core (renderer-free, imports nothing) |
41
+ | Scene lifecycle, SceneStack, Signal, |
42
+ | Random, Input, SaveSystem, Session |
43
+ +------------------------------------------+
44
+ ^ ^
45
+ | |
46
+ +---------------+----------------+ +----------+---------------+
47
+ | two-d (PixiJS) | | three-d (Babylon.js, |
48
+ | Game, Scene2D, Camera, | | optional, off the root |
49
+ | TileMap, Sprite2D/Shape2D/ | | barrel so a 2D game |
50
+ | Text2D facade, ui, stage | | never pays for it) |
51
+ +----------------------------------+ +--------------------------+
52
+
53
+ renderer-free alongside core: i18n, actors, world, battle, simulation,
54
+ roguelike, board, audio, rpg, ai, mwl, assets/paths
55
+ ```
56
+
57
+ `core` importing no other module (and no renderer) is what lets the same scene-lifecycle and
58
+ save-system code run under a Babylon game as under a Pixi one. This isn't a documentation
59
+ claim taken on faith: `tests/renderer-isolation.test.ts` walks the real import graph in both
60
+ directions (nothing under `two-d` reaches Babylon, nothing under `three-d` reaches Pixi, no
61
+ example names `pixi.js`/`@babylonjs/*` directly) and fails the build if the boundary slips.
62
+ `roguelike` adds only `rot-js`; `pixi.js`, `@babylonjs/core`/`@babylonjs/loaders`, and
63
+ `@capacitor/core` are all optional peer dependencies rather than installed ones, so a project
64
+ brings in exactly the renderer it draws with, no more.
65
+
66
+ ## Execution model
67
+
68
+ `Game` (`two-d`) owns one Pixi `Application`, a `SceneStack<Scene2D>`, and the frame loop,
69
+ which normally runs off Pixi's own ticker. `core.Scene` is lifecycle only
70
+ (`create`/`update`/`resize`/`onSuspend`/`onResume`/`destroy`) and owns no display node at all;
71
+ `two-d.Scene2D` adds exactly one field, a `stage` container, on top of it. `SceneStack` is
72
+ generic over that lifecycle type alone, which is what lets it be reused unchanged on the
73
+ Babylon side.
74
+
75
+ Scene changes go through `switchScene`, which queues a request and drains it at the start of
76
+ the *next* frame, so a frame never updates a scene that was just asked to leave. Only the top
77
+ of the stack receives `update`, but every scene in the stack still renders, which is what
78
+ makes a translucent pause menu over live gameplay work without special-casing it.
79
+
80
+ `Game.step(dt)` drives exactly one frame synchronously, bypassing the ticker. Reach for it in
81
+ headless contexts (tests) and when a backgrounded browser tab has throttled
82
+ `requestAnimationFrame` down to a rate that makes a game look frozen for reasons that have
83
+ nothing to do with your code.
84
+
85
+ ## Rendering model
86
+
87
+ `Camera` never moves "the camera": it moves a `world` container under a fixed viewport,
88
+ including for rotation, which turns the whole layer once about the view centre rather than
89
+ transforming tiles individually. Grid maps render through a chunked `TileMap`, so off-screen
90
+ chunks can be culled as a block instead of tile by tile.
91
+
92
+ Per-sprite additive colour (a poison tint, a hit-flash) needs both a multiply *and* an add in
93
+ the same pass, which Pixi's built-in `tint` (multiply only) can't express. `mwg` implements
94
+ that as a custom batcher, `ColorTransformBatcher`, extending Pixi's own `Batcher` so the extra
95
+ math rides in the same draw call rather than costing a separate filter pass per sprite. This
96
+ is also the one place in the entire framework allowed to import Pixi's batcher or shader
97
+ internals; nothing else does. You don't opt into any of this by hand: importing `TintedSprite`
98
+ (directly, or via `TileMap`/`DialogueStage`/`AnimatedSprite`, all built on it) registers the
99
+ colour-transform pipe at module load time, before your own code runs.
100
+
101
+ ## The PixiJS boundary
102
+
103
+ The design intent (recorded as ADR-001, "a game consumes `mwg`, not PixiJS or Babylon.js
104
+ directly, in ordinary use") is a genuine constraint, not a suggestion, and it's enforced the
105
+ same way the module boundary is: by a test that scans the real API surface, not by convention.
106
+ It has three tiers:
107
+
108
+ 1. **The typed facade** - `Container2D`/`Texture2D`/`Rect`/`TextureRegion`
109
+ (`two-d/render/Types2D.ts`) and `Node2D`/`Shape2D`/`Text2D`/`Sprite2D`/`TiledSprite`/
110
+ `Gradient` (`two-d/render/Shape2D.ts`). Ordinary sprites, shapes, text, and containers all
111
+ go through here, and none of it requires your code to name `pixi.js` anywhere, in a type
112
+ position or a value position.
113
+ 2. **One documented escape hatch** - `two-d/pixi-interop.ts`, published at
114
+ `@datamoc/mw_games/two-d/pixi-interop`, a small, explicit re-export of the handful of Pixi
115
+ symbols the facade doesn't (yet) cover. If you genuinely need a Pixi capability the facade
116
+ doesn't expose, this is where that dependency belongs, confined to as few files in your own
117
+ project as you can manage, the same discipline `mwg` holds `ColorTransformBatcher.ts` to
118
+ internally.
119
+ 3. **`pixi.js` itself is an optional peer dependency**, not a direct one. A project that never
120
+ touches the escape hatch never installs Pixi's actual package; only code that imports
121
+ `pixi-interop` does.
122
+
123
+ Why go to this trouble: it's what makes the Babylon path (`three-d`) possible as more than a
124
+ parallel rewrite, and it's what keeps a 2D-only project from paying for a 3D engine it never
125
+ loads, or vice versa. Prefer the facade by default. Reach for the escape hatch deliberately,
126
+ not reflexively, and keep that usage visible and narrow rather than scattered through your
127
+ rendering code.
128
+
129
+ ## Simulation and time
130
+
131
+ Two renderer-free authorities sit underneath both rendering paths:
132
+
133
+ - **`roguelike.Scheduler`** is the sole authority on logical, turn-based time (ADR-003):
134
+ energy-cost ordering, fractional costs, a documented tie-break, `postpone` for delaying an
135
+ actor other than the current one, and a deterministic `toJSON`/`restore` snapshot.
136
+ - **`simulation.SimulationRuntime`** is renderer-free and transactional (ADR-002): a command
137
+ produces its complete logical result, and charges the scheduler, synchronously, *before* any
138
+ presentation plays. `core.Presentation`'s `PresentationQueue` then lets rendering consume the
139
+ resulting events at its own pace, decoupled from when the logic actually happened. This is
140
+ proven, not just structured that way: running the same commands against three different
141
+ presentation-duration functions produces identical simulation state and events every time.
142
+
143
+ A simulation's logical snapshot is deliberately a different thing from `core.SaveSystem`'s
144
+ storage format (ADR-006): `SimulationSnapshot<State>` is plain JSON-safe data that `SaveSystem`
145
+ stores as an opaque payload, never the reverse. Compose the two rather than reinventing either.
146
+
147
+ ## Entity identity and "something happened"
148
+
149
+ Logical entities are referenced by a stable id, not held by object reference (ADR-004,
150
+ `core.Entity`'s `EntityRegistry`). That id is what you pass into `Scheduler.toJSON`'s or
151
+ `SimulationRuntime.snapshot`'s `actorId`, so identity composes cleanly with both authorities
152
+ above without either needing to know about the other.
153
+
154
+ Three distinct shapes exist for "something happened," on purpose, so one word doesn't have to
155
+ mean three things depending on context (ADR-005):
156
+
157
+ - **`GameEvent`** - an ordered *output* of a transition. Something a rule produced, never
158
+ something a global bus dispatches to drive other rules.
159
+ - **`Hook`** (`HookRegistry`) - a synchronous, mid-calculation modification point.
160
+ - **`Signal`** - a plain notification with no bearing on any rule at all.
161
+
162
+ ## Persistence
163
+
164
+ `core.SaveSystem` gives named, versioned save slots over `localStorage`, with an in-memory
165
+ fallback where storage isn't available, scoped deliberately to plain JSON-serialisable state
166
+ rather than arbitrary object graphs. The API draws a real line between two kinds of input:
167
+ `load()` trusts data this same process already wrote, while `importSlot`/`importExternal` are
168
+ the path for anything that crossed a device or a network boundary, and validate size and
169
+ content before accepting it. Follow that same split in your own save-adjacent code: trust what
170
+ your own process just wrote, validate anything a player pastes in, syncs from elsewhere, or
171
+ receives from a server, and don't assume "it's valid JSON" already means "it's valid data."
172
+
173
+ `Session` reuses the same storage abstraction for exactly one counter, how many times a game
174
+ has launched, so a native wrapper can decide when to ask for a rating. `mwg` counts; it never
175
+ prompts on its own.
176
+
177
+ ## Assets
178
+
179
+ `assets/paths.ts` resolves a path with no renderer in reach at all, which is what lets `two-d`
180
+ and `three-d` share one compiled-asset map. `assets/loader.ts` is the half that actually needs
181
+ Pixi. In development, paths are served normally; in a compiled build, resolution looks paths up
182
+ in a global asset map that the build's own compile step populates. Call `load(paths)` once per
183
+ scene; everything after that is meant to be synchronous, so don't scatter `await` through
184
+ per-frame or per-action code. `load` also accepts an `optional` path list with a per-path
185
+ `onMissing` callback, so one missing decal doesn't have to abort an otherwise-successful batch.
186
+
187
+ ## Framework scope discipline
188
+
189
+ Two decisions worth understanding even though they don't change any API you call directly,
190
+ because they set correct expectations for whether `mwg` will fit *your* game:
191
+
192
+ - **No rule, value, or vocabulary specific to any one reference game lives in `mwg`** (ADR-007).
193
+ Every primitive is generic over a game-supplied type parameter; hunger values, weapon
194
+ formulas, faction rules, and the like stay in the game that needs them. If you come to `mwg`
195
+ from studying one of its reference games (Shattered Pixel Dungeon, ADOM, RPG Maker-style
196
+ projects, and others README.md names as capability targets), expect the mechanic's *shape*
197
+ to be there and the specific numbers not to be.
198
+ - **`mwg` provides the mechanism for semantic messaging and localisation; your game supplies
199
+ the vocabulary** (ADR-008). `i18n.SemanticMessage`/`MessageChannel`/`MessageFormatter` build
200
+ on the existing catalog/plural/Fluent machinery; `mwg` holds no message type name, catalog
201
+ key, or translated string of its own anywhere in that path.
202
+
203
+ In practice this means `mwg` will feel intentionally unopinionated about *what* your game says
204
+ and *how much* damage a sword does, while still being opinionated about *how* time, identity,
205
+ events, and persistence work underneath it.
206
+
207
+ ## Extension points
208
+
209
+ `GameOptions.extensions` takes an array of registration functions, run once before Pixi's
210
+ `app.init()` builds the renderer, for registering *your own* Pixi extension the same way `mwg`
211
+ registers its colour-transform pipe internally. You won't need this for ordinary rendering
212
+ work; it exists specifically for the case where your game needs its own custom Pixi-level
213
+ behaviour alongside what `mwg` already provides.
214
+
215
+ ## Testing and verification philosophy
216
+
217
+ `mwg`'s own test suite runs directly under Node's built-in test runner against `.ts` sources,
218
+ no test framework, which is a stated feature, not an accident: the dependency list staying
219
+ short is treated as part of the product. Worth adopting the same discipline in your own tests
220
+ where it fits.
221
+
222
+ Typechecking and unit tests verify correctness, not what a player actually sees. A window
223
+ placed off-screen or choices rendered over text will not show up in `npm run check` or a green
224
+ test suite; building an example and actually looking at it will. If you can't visually verify a
225
+ UI change yourself, say so explicitly rather than reporting it done.
226
+
227
+ ## Status and where to go next
228
+
229
+ - **REFERENCE.md** - the API index: every export, one line each, plus the generated
230
+ parameter-level documentation.
231
+ - **The example games** (colour-transform, interface, dialogue/stage, roguelike dungeon) -
232
+ working, buildable, `file://`-verified starting points; closer to how to structure a real
233
+ project than any written description can be.
234
+ - **`MWG: Best Practices.md`** - the companion to this document: concrete guidance for
235
+ structuring a new project well from day one, rather than how the framework itself is built.
236
+ - **ROADMAP.md and ADR.md** (on the project's GitHub) - the full, numbered build history and
237
+ the reasoning behind the architectural decisions summarised above, for anyone who wants the
238
+ complete record rather than the digest.
package/README.md CHANGED
@@ -402,9 +402,12 @@ The optional 3D group is now implemented without changing the existing 2D entry
402
402
 
403
403
  ## MWL
404
404
 
405
- MWL is the framework's declarative, WML-inspired authoring layer. It describes
406
- game data and commands while `mwg` remains responsible for execution, rendering,
407
- input, audio, and saves. MWL is a new language, not a WML compatibility layer.
405
+ MWL is the framework's declarative, WML-inspired authoring layer. Content files
406
+ are JSON5: a top-level array of `{ tag, ...attributes, children }` nodes, with
407
+ real numbers and booleans for scalar values and `_("...")` marking translatable
408
+ strings. It describes game data and commands while `mwg` remains responsible for
409
+ execution, rendering, input, audio, and saves. MWL is a new language, not a WML
410
+ compatibility layer.
408
411
  The Wesnoth port can nevertheless use its native `.cfg` files as build inputs:
409
412
  the port-owned WML front end parses them, the adapter converts the supported
410
413
  nodes to MWL, and the normal MWL compiler emits the runtime module.
@@ -461,7 +464,7 @@ reported as warnings. The output directory is safe to regenerate: its extracted
461
464
  CSS `url(data:...)` values and HTML `srcset` candidates are handled too, including
462
465
  their density descriptors.
463
466
 
464
- Translation messages are the values marked `_ "..."` in the source; the
467
+ Translation messages are the values marked `_("...")` in the source; the
465
468
  extracted catalog matches `@datamoc/mw_games/i18n`. Hooks are referenced as
466
469
  `type:name`, declared in a manifest, and bundled with esbuild, which a game
467
470
  project installs itself (`npm install --save-dev esbuild`). The framework does
@@ -69,6 +69,14 @@ export type Schema = {
69
69
  items: Schema;
70
70
  maxLength?: number;
71
71
  };
72
+ /**
73
+ * Recursively rejects `__proto__`/`constructor`/`prototype` as an own key anywhere in `value`,
74
+ * for data too dynamic for `validateSchema`'s fixed field list (a free-form variable bag, a
75
+ * dictionary keyed by content-chosen ids, both common in a save format). Reuses the same
76
+ * `FORBIDDEN_KEYS` set `validateSchema` enforces for fixed-shape objects; use this one instead
77
+ * when the shape itself isn't known ahead of time.
78
+ */
79
+ export declare function assertNoForbiddenKeys(value: unknown, path?: string): void;
72
80
  /** throws with a path-qualified message at the first field that does not match `schema` */
73
81
  export declare function validateSchema(value: unknown, schema: Schema, path?: string): void;
74
82
  //# sourceMappingURL=Sanitize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sanitize.d.ts","sourceRoot":"","sources":["../../src/core/Sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,gBAAgB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,2GAA2G;AAC3G,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAIzF;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU3D;AAED,sGAAsG;AACtG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAIxF;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAI5E,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,IAAI,CAsC/E"}
1
+ {"version":3,"file":"Sanitize.d.ts","sourceRoot":"","sources":["../../src/core/Sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,MAAM,WAAW,gBAAgB;IAChC,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,2GAA2G;AAC3G,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,gBAAqB,GAAG,IAAI,CAIzF;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAU3D;AAED,sGAAsG;AACtG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAIxF;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,GACf;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAClE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAI5E;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,SAAM,GAAG,IAAI,CAWtE;AAED,2FAA2F;AAC3F,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,IAAI,CAsC/E"}
@@ -56,6 +56,26 @@ export function sanitizeInboundText(text, options = {}) {
56
56
  return text;
57
57
  }
58
58
  const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
59
+ /**
60
+ * Recursively rejects `__proto__`/`constructor`/`prototype` as an own key anywhere in `value`,
61
+ * for data too dynamic for `validateSchema`'s fixed field list (a free-form variable bag, a
62
+ * dictionary keyed by content-chosen ids, both common in a save format). Reuses the same
63
+ * `FORBIDDEN_KEYS` set `validateSchema` enforces for fixed-shape objects; use this one instead
64
+ * when the shape itself isn't known ahead of time.
65
+ */
66
+ export function assertNoForbiddenKeys(value, path = '$') {
67
+ if (Array.isArray(value)) {
68
+ value.forEach((item, index) => assertNoForbiddenKeys(item, `${path}[${index}]`));
69
+ return;
70
+ }
71
+ if (value !== null && typeof value === 'object') {
72
+ for (const key of Object.keys(value)) {
73
+ if (FORBIDDEN_KEYS.has(key))
74
+ throw new Error(`${path} contains a forbidden key "${key}"`);
75
+ assertNoForbiddenKeys(value[key], `${path}.${key}`);
76
+ }
77
+ }
78
+ }
59
79
  /** throws with a path-qualified message at the first field that does not match `schema` */
60
80
  export function validateSchema(value, schema, path = '$') {
61
81
  if (value === undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"Sanitize.js","sourceRoot":"","sources":["../../src/core/Sanitize.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Sanitize.js","sourceRoot":"","sources":["../../src/core/Sanitize.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,14 @@ export declare class SaveSystem<T> {
33
33
  constructor(options: SaveSystemOptions);
34
34
  private key;
35
35
  save(slot: string, state: T, preview?: unknown): void;
36
- /** reads a slot, migrating it up to the current version if it was saved at an older one */
36
+ /**
37
+ * Reads a slot, migrating it up to the current version if it was saved at an older one.
38
+ * Returns `null` for a corrupted or malformed slot the same way it already does for a
39
+ * missing one - a slot this `SaveSystem` wrote itself is trusted, but a truncated write
40
+ * (an interrupted browser storage flush, a quota eviction, a hand-edited value) is still
41
+ * possible, and a save-select screen should see "no usable save here", not an uncaught
42
+ * `SyntaxError`.
43
+ */
37
44
  load(slot: string): SaveData<T> | null;
38
45
  /**
39
46
  * Adopts a save this `SaveSystem` never wrote: `normalize` runs once on `externalBytes`,
@@ -1 +1 @@
1
- {"version":3,"file":"Save.d.ts","sourceRoot":"","sources":["../../src/core/Save.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAEhB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,CAAC,CAAC;CACT;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACjC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,IAAI,MAAM,EAAE,CAAC;CACjB;AAuBD,qGAAqG;AACrG,wBAAgB,cAAc,IAAI,WAAW,CAS5C;AAED,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAEhB,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,qBAAa,UAAU,CAAC,CAAC;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAAc;IAE7B,YAAY,OAAO,EAAE,iBAAiB,EAKrC;IAED,OAAO,CAAC,GAAG;IAIX,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAGpD;IAED,2FAA2F;IAC3F,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAWrC;IAED;;;;;;;;;;;OAWG;IACH,cAAc,CACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,UAAU,EACzB,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,EACzC,OAAO,CAAC,EAAE,OAAO,GACf,IAAI,CASN;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEzB;IAED;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAWpE;IAED,0EAA0E;IAC1E,IAAI,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAY9C;CACD"}
1
+ {"version":3,"file":"Save.d.ts","sourceRoot":"","sources":["../../src/core/Save.ts"],"names":[],"mappings":"AAuBA,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAEhB,6FAA6F;IAC7F,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,CAAC,CAAC;CACT;AAED,iGAAiG;AACjG,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACjC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,IAAI,IAAI,MAAM,EAAE,CAAC;CACjB;AAuBD,qGAAqG;AACrG,wBAAgB,cAAc,IAAI,WAAW,CAS5C;AAED,MAAM,WAAW,iBAAiB;IACjC,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAEhB,8FAA8F;IAC9F,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;IACzD,OAAO,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,qBAAa,UAAU,CAAC,CAAC;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,OAAO,CAAc;IAE7B,YAAY,OAAO,EAAE,iBAAiB,EAKrC;IAED,OAAO,CAAC,GAAG;IAIX,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAGpD;IAED;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAcrC;IAED;;;;;;;;;;;OAWG;IACH,cAAc,CACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,UAAU,EACzB,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,EACzC,OAAO,CAAC,EAAE,OAAO,GACf,IAAI,CASN;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEzB;IAED;;;;;;OAMG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAiBpE;IAED,0EAA0E;IAC1E,IAAI,IAAI,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAY9C;CACD"}
package/dist/core/Save.js CHANGED
@@ -63,17 +63,29 @@ export class SaveSystem {
63
63
  const data = { meta: { version: this.version, savedAt: Date.now(), preview }, state };
64
64
  this.storage.write(this.key(slot), JSON.stringify(data));
65
65
  }
66
- /** reads a slot, migrating it up to the current version if it was saved at an older one */
66
+ /**
67
+ * Reads a slot, migrating it up to the current version if it was saved at an older one.
68
+ * Returns `null` for a corrupted or malformed slot the same way it already does for a
69
+ * missing one - a slot this `SaveSystem` wrote itself is trusted, but a truncated write
70
+ * (an interrupted browser storage flush, a quota eviction, a hand-edited value) is still
71
+ * possible, and a save-select screen should see "no usable save here", not an uncaught
72
+ * `SyntaxError`.
73
+ */
67
74
  load(slot) {
68
75
  const raw = this.storage.read(this.key(slot));
69
76
  if (!raw)
70
77
  return null;
71
- const data = JSON.parse(raw);
72
- let state = data.state;
73
- for (let v = data.meta.version; v < this.version; v++) {
74
- state = (this.migrations[v] ?? ((s) => s))(state);
78
+ try {
79
+ const data = JSON.parse(raw);
80
+ let state = data.state;
81
+ for (let v = data.meta.version; v < this.version; v++) {
82
+ state = (this.migrations[v] ?? ((s) => s))(state);
83
+ }
84
+ return { meta: { ...data.meta, version: this.version }, state: state };
85
+ }
86
+ catch {
87
+ return null;
75
88
  }
76
- return { meta: { ...data.meta, version: this.version }, state: state };
77
89
  }
78
90
  /**
79
91
  * Adopts a save this `SaveSystem` never wrote: `normalize` runs once on `externalBytes`,
@@ -124,7 +136,14 @@ export class SaveSystem {
124
136
  importSlot(slot, payload, scrambleKey) {
125
137
  checkSize(payload);
126
138
  const raw = scrambleKey ? unscramble(payload, scrambleKey) : payload;
127
- const data = JSON.parse(sanitizeInboundText(raw));
139
+ const sanitized = sanitizeInboundText(raw);
140
+ let data;
141
+ try {
142
+ data = JSON.parse(sanitized);
143
+ }
144
+ catch {
145
+ throw new Error('SaveSystem.importSlot: payload is not valid save data (not parseable JSON)');
146
+ }
128
147
  let state = data.state;
129
148
  for (let v = data.meta.version; v < this.version; v++) {
130
149
  state = (this.migrations[v] ?? ((s) => s))(state);
@@ -1 +1 @@
1
- {"version":3,"file":"Save.js","sourceRoot":"","sources":["../../src/core/Save.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAuB/D,uFAAuF;AACvF,MAAM,aAAa;IAAnB;QACS,SAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAiB1C,CAAC;IAfA,IAAI,CAAC,GAAW;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,GAAW,EAAE,KAAa;QAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,GAAW;QACjB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;IAED,IAAI;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9B,CAAC;CACD;AAED,qGAAqG;AACrG,MAAM,UAAU,cAAc;IAC7B,IAAI,OAAO,YAAY,KAAK,WAAW;QAAE,OAAO,IAAI,aAAa,EAAE,CAAC;IAEpE,OAAO;QACN,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;QACxC,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;QACvD,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC;QAC7C,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;KACrC,CAAC;AACH,CAAC;AAYD,MAAM,OAAO,UAAU;IAMtB,YAAY,OAA0B;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;IACpD,CAAC;IAEO,GAAG,CAAC,IAAY;QACvB,OAAO,YAAY,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,KAAQ,EAAE,OAAiB;QAC7C,MAAM,IAAI,GAAgB,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC;QACnG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,2FAA2F;IAC3F,IAAI,CAAC,IAAY;QAChB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QAEtB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;QAC5C,IAAI,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;YACvD,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAU,EAAE,CAAC;IAC7E,CAAC;IAED;;;;;;;;;;;OAWG;IACH,cAAc,CACb,IAAY,EACZ,aAAyB,EACzB,SAAyC,EACzC,OAAiB;QAEjB,SAAS,CAAC,aAAa,CAAC,CAAC;QACzB,IAAI,KAAK,GAAY,SAAS,CAAC,aAAa,CAAC,CAAC;QAC9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,IAAI,GAAgB,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,KAAU,EAAE,CAAC;QAC/G,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,IAAY;QAClB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACrC,CAAC;IAED;;;;;;OAMG;IACH,UAAU,CAAC,IAAY,EAAE,WAAoB;QAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9C,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACvD,CAAC;IAED;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAY,EAAE,OAAe,EAAE,WAAoB;QAC7D,SAAS,CAAC,OAAO,CAAC,CAAC;QACnB,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACrE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAsB,CAAC;QACvE,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;YACvD,KAAK,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;QAED,MAAM,UAAU,GAAgB,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAU,EAAE,CAAC;QACrG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,0EAA0E;IAC1E,IAAI;QACH,MAAM,MAAM,GAAG,YAAY,IAAI,CAAC,SAAS,GAAG,CAAC;QAC7C,MAAM,GAAG,GAA4C,EAAE,CAAC;QAExD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,SAAS;YACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,CAAC,GAAG;gBAAE,SAAS;YAEnB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;CACD"}
1
+ {"version":3,"file":"Save.js","sourceRoot":"","sources":["../../src/core/Save.ts"],"names":[],"mappings":"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"}