@rpgm-tools/neo-angband-mod-sdk 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE.md +43 -0
  2. package/README.md +72 -0
  3. package/dist/capabilities.d.ts +116 -0
  4. package/dist/capabilities.d.ts.map +1 -0
  5. package/dist/capabilities.js +170 -0
  6. package/dist/capabilities.js.map +1 -0
  7. package/dist/compose.d.ts +71 -0
  8. package/dist/compose.d.ts.map +1 -0
  9. package/dist/compose.js +118 -0
  10. package/dist/compose.js.map +1 -0
  11. package/dist/conflicts.d.ts +78 -0
  12. package/dist/conflicts.d.ts.map +1 -0
  13. package/dist/conflicts.js +160 -0
  14. package/dist/conflicts.js.map +1 -0
  15. package/dist/index.d.ts +31 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +24 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/loader.d.ts +83 -0
  20. package/dist/loader.d.ts.map +1 -0
  21. package/dist/loader.js +314 -0
  22. package/dist/loader.js.map +1 -0
  23. package/dist/manifest.d.ts +261 -0
  24. package/dist/manifest.d.ts.map +1 -0
  25. package/dist/manifest.js +264 -0
  26. package/dist/manifest.js.map +1 -0
  27. package/dist/patch.d.ts +90 -0
  28. package/dist/patch.d.ts.map +1 -0
  29. package/dist/patch.js +195 -0
  30. package/dist/patch.js.map +1 -0
  31. package/dist/record-key.d.ts +99 -0
  32. package/dist/record-key.d.ts.map +1 -0
  33. package/dist/record-key.js +157 -0
  34. package/dist/record-key.js.map +1 -0
  35. package/dist/resolve.d.ts +42 -0
  36. package/dist/resolve.d.ts.map +1 -0
  37. package/dist/resolve.js +161 -0
  38. package/dist/resolve.js.map +1 -0
  39. package/dist/semver.d.ts +37 -0
  40. package/dist/semver.d.ts.map +1 -0
  41. package/dist/semver.js +212 -0
  42. package/dist/semver.js.map +1 -0
  43. package/package.json +58 -0
  44. package/src/capabilities.ts +205 -0
  45. package/src/compose.ts +186 -0
  46. package/src/conflicts.ts +242 -0
  47. package/src/index.ts +73 -0
  48. package/src/loader.ts +393 -0
  49. package/src/manifest.ts +523 -0
  50. package/src/patch.ts +257 -0
  51. package/src/record-key.ts +180 -0
  52. package/src/resolve.ts +175 -0
  53. package/src/semver.ts +231 -0
package/LICENSE.md ADDED
@@ -0,0 +1,43 @@
1
+ # License
2
+
3
+ Neo Angband is a derivative work of Angband and retains upstream's dual
4
+ license, as the Angband project asks of its variants (see
5
+ `reference/docs/copying.rst`, which remains the authoritative statement for
6
+ the original code and its assets).
7
+
8
+ Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke, and the
9
+ Angband contributors; port code copyright (c) 2026 the Neo Angband
10
+ contributors.
11
+
12
+ This work is free software; you can redistribute it and/or modify it under
13
+ the terms of either:
14
+
15
+ a) the GNU General Public License as published by the Free Software
16
+ Foundation, version 2, or
17
+
18
+ b) the "Angband licence":
19
+ This software may be copied and distributed for educational, research,
20
+ and not for profit purposes provided that this copyright and statement
21
+ are included in all such copies. Other copyrights may also apply.
22
+
23
+ ## Assets
24
+
25
+ Asset licenses under `reference/` vary and are NOT all dual-licensed as
26
+ above; see `reference/docs/copying.rst` for the full statements, and
27
+ `packages/web/public/tiles/CREDITS.md` for the four tile packs this port
28
+ actually bundles and the terms each one is bundled under.
29
+
30
+ The Adam Bolt tiles (any purpose, with or without modification) and David
31
+ Gervais tiles (CC BY 3.0, attribution given in that CREDITS.md) are available
32
+ to tile packs under their own terms. The Shockbolt tile set is a distinct
33
+ case and is **not** bundled: its license grants fee-free distribution *with*
34
+ Angband but withholds modification without the author's permission, and
35
+ withholds use with "other games or projects" absent explicit permission —
36
+ so whether this port may carry it turns on a question only the author can
37
+ settle. CREDITS.md states the grants and the prohibitions in full.
38
+
39
+ ## Mods
40
+
41
+ Declarative content packs and tile packs are independent works and may be
42
+ licensed as their authors choose. Distributed scripted plugins are safest
43
+ treated as GPLv2 derivatives. See `docs/MODS.md`.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @rpgm-tools/neo-angband-mod-sdk
2
+
3
+ The mod machinery for [Neo Angband](https://github.com/neostryder/neo-angband):
4
+ manifest schema and validation, the deterministic load-order resolver, and the
5
+ record-composition engine that merges packs.
6
+
7
+ ```bash
8
+ npm install @rpgm-tools/neo-angband-mod-sdk
9
+ ```
10
+
11
+ ## Why it is a separate package
12
+
13
+ The base game loads its own content **through this pipeline, as pack zero**. That
14
+ is the point: there is no privileged path a mod cannot take, because the game
15
+ itself does not have one. This package is that pipeline, with no engine attached —
16
+ so a mod's build script or test can validate a manifest, resolve a load order or
17
+ compose records without pulling in a game.
18
+
19
+ ## What is in it
20
+
21
+ | Area | Exports |
22
+ | --- | --- |
23
+ | Manifests | `validateManifest`, `PackManifest`, `PackShape`, `packFacets`, `hasFacet`, `packRef`, `slugify` |
24
+ | Load order | `resolveLoadOrder`, `satisfies` (the semver subset the manifests use) |
25
+ | Composition | `composePacks`, `composeContentPacks`, `mergePatch`, `applyFieldPatch`, `composeFieldPatches` |
26
+ | Conflicts | `computeConflictReport`, `RecordConflict`, `FieldTouch`, `RecordOverride` |
27
+ | Record identity | `recordKey`, `keySpecFor`, `KEYED_RECORD_FILES`, `RECORD_KEY_SPECS` |
28
+ | Capabilities | `CapabilitySet`, `parseCapability` — what a scripted plugin is allowed to reach |
29
+
30
+ ```ts
31
+ import { validateManifest, ManifestError } from "@rpgm-tools/neo-angband-mod-sdk";
32
+
33
+ try {
34
+ const manifest = validateManifest(JSON.parse(text));
35
+ console.log(manifest.id, manifest.shape, manifest.modApi);
36
+ } catch (e) {
37
+ if (e instanceof ManifestError) console.error(`bad manifest: ${e.message}`);
38
+ }
39
+ ```
40
+
41
+ ## The three pack shapes
42
+
43
+ - **content** — declarative JSON validated against the record schemas. Safe by
44
+ construction: it cannot execute anything.
45
+ - **tiles** — a tile pack, either a tilesheet re-skin or a loose Linoleum pack.
46
+ - **plugin** — a scripted mod. It default-exports a `ModPlugin` and receives the
47
+ running engine as `ctx.core`, because a module loaded out of a mod folder cannot
48
+ resolve a bare specifier and a bundled copy of the engine would give the plugin
49
+ its own registries while the game ran on another set.
50
+
51
+ A single mod may declare several of these as **facets**; `packFacets` is what
52
+ reads them.
53
+
54
+ ## Related
55
+
56
+ - [`@rpgm-tools/neo-angband-core`](https://www.npmjs.com/package/@rpgm-tools/neo-angband-core) — the engine itself
57
+ - [docs/MODS.md](https://github.com/neostryder/neo-angband/blob/master/docs/MODS.md) — the full modding guide
58
+ - First-party mods: [qol](https://github.com/neostryder/neo-angband-mod-qol) ·
59
+ [bug-fixes](https://github.com/neostryder/neo-angband-mod-bug-fixes) ·
60
+ [neo-linoleum](https://github.com/neostryder/neo-angband-mod-linoleum) ·
61
+ [borg](https://github.com/neostryder/neo-angband-mod-borg)
62
+
63
+ ## Versioning
64
+
65
+ `0.9.x` is the pre-release line and the API can change inside it; `1.0.0` is
66
+ reserved for the game's public release.
67
+
68
+ ## Licence
69
+
70
+ GNU GPL v2, or the Angband licence, at your option — Angband's dual licence, kept
71
+ as the Angband project asks of its variants. npm carries one SPDX identifier so the
72
+ manifest says `GPL-2.0-only`; both texts are in [LICENSE.md](LICENSE.md).
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Capability model for scripted plugins (MOD_LIFECYCLE.md section 4, P7
3
+ * phase 5).
4
+ *
5
+ * Only `shape: plugin` packs may request capabilities; content and tile
6
+ * packs are validated data that cannot execute, so they request none
7
+ * (docs/MODS.md trust tiers). A plugin's `capabilities` list in its
8
+ * manifest is the consent surface: the installer shows each one in plain
9
+ * language, the user approves, and the runtime grants exactly that set -
10
+ * nothing a plugin did not ask for and the user did not see. The
11
+ * perceive/act facades (a later P7 phase) call `CapabilitySet.check()`
12
+ * before honoring a request; an ungranted capability throws a clear
13
+ * author-facing error rather than silently doing nothing or diverging.
14
+ *
15
+ * Vocabulary (four forms, MOD_LIFECYCLE section 4 / the frost example in
16
+ * section 2):
17
+ * - "command:add" - register commands on the act facade.
18
+ * - "event:<name>" - subscribe to an engine event, e.g.
19
+ * "event:turn-start".
20
+ * - "state:<domain>.read" - read one perceive-facade domain, e.g.
21
+ * "state:party.read"; or the wildcard
22
+ * "state:*.read" for any domain.
23
+ * - "network:<host>" - outbound network to one host; or "network:*"
24
+ * for any host. Not in the section-4 examples
25
+ * verbatim, but named there ("network access to
26
+ * api.example.com"); "*" is this module's
27
+ * extension for a plugin that genuinely needs
28
+ * unrestricted egress, and reads the same way
29
+ * the other wildcards do.
30
+ * - "registry:<domain>" - override a game SYSTEM registry from a TRUSTED
31
+ * in-process plugin (W2.2, core/mod/registry-host.ts):
32
+ * "registry:effect" | "registry:room" |
33
+ * "registry:command" | "registry:monster" |
34
+ * "registry:vocab"; or the wildcard "registry:*"
35
+ * for all of them. "registry:vocab" (W2.3) declares
36
+ * NEW vocabulary (flags/stats/any kind). Distinct
37
+ * from "command:add": that adds a command via the
38
+ * act facade, this replaces what a command DOES (and
39
+ * the effect/room/AI logic behind the game).
40
+ * (The named-core-rule flags the bundled qol /
41
+ * bug-fixes mods use, GameState.modRules, are a
42
+ * DECLARATIVE manifest field - PackManifest.rules -
43
+ * applied by the host, so they need no capability.)
44
+ *
45
+ * This module only surfaces `nondeterministic` from the manifest. The
46
+ * save's determinism ratchet itself - flipping a save from DETERMINISTIC to
47
+ * NONDETERMINISTIC the first time such a mod is enabled, once and
48
+ * irreversibly - lives in core/save (decisions 4/18/22), not here.
49
+ */
50
+ import { type PackManifest } from "./manifest.js";
51
+ export declare class CapabilityError extends Error {
52
+ }
53
+ /** A capability string parsed into its structured form. */
54
+ export type ParsedCapability = {
55
+ kind: "command";
56
+ action: "add";
57
+ } | {
58
+ kind: "event";
59
+ name: string;
60
+ } | {
61
+ kind: "state";
62
+ domain: string;
63
+ access: "read";
64
+ } | {
65
+ kind: "network";
66
+ host: string;
67
+ } | {
68
+ kind: "registry";
69
+ domain: string;
70
+ };
71
+ /**
72
+ * Parse and validate a capability string against the vocabulary above,
73
+ * returning its structured form. Throws CapabilityError on anything
74
+ * malformed or outside the recognized patterns - an unknown capability is
75
+ * a hard error, not a silent no-op, since a typo'd request should fail
76
+ * loudly at install rather than quietly never matching a grant.
77
+ */
78
+ export declare function parseCapability(cap: string): ParsedCapability;
79
+ /**
80
+ * The capabilities a plugin was granted, built from its manifest. Facades
81
+ * hold one of these per loaded plugin and call `check()` before honoring
82
+ * any request that touches commands, events, state reads, or the network.
83
+ */
84
+ export declare class CapabilitySet {
85
+ private readonly grants;
86
+ private readonly nondeterministic;
87
+ private readonly affectsGameplay;
88
+ private constructor();
89
+ /**
90
+ * Build a CapabilitySet from a pack manifest. Only `shape: plugin` packs
91
+ * may request capabilities (MOD_LIFECYCLE section 4): a content or tile
92
+ * pack with a non-empty `capabilities` list throws CapabilityError, since
93
+ * that shape cannot execute and so has nothing to grant capabilities to -
94
+ * the request signals author confusion or an upstream validation bug,
95
+ * not something to silently ignore.
96
+ */
97
+ static fromManifest(manifest: PackManifest): CapabilitySet;
98
+ /**
99
+ * True if `cap` is covered by a grant in this set, honoring the
100
+ * "state:*.read" and "network:*" wildcards. Throws CapabilityError if
101
+ * `cap` itself is not a recognized capability string.
102
+ */
103
+ has(cap: string): boolean;
104
+ /**
105
+ * Throws CapabilityError, naming the missing capability and how to fix
106
+ * it, unless `cap` is granted. This is the guard the perceive/act facades
107
+ * call before honoring a plugin's request - an author-facing error, not
108
+ * a silent divergence.
109
+ */
110
+ check(cap: string): void;
111
+ /** True if the manifest declared `nondeterministic: true` (section 4). */
112
+ isNondeterministic(): boolean;
113
+ /** True if the manifest declared `affectsGameplay: true`. */
114
+ isAffectsGameplay(): boolean;
115
+ }
116
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,EAAY,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE5D,qBAAa,eAAgB,SAAQ,KAAK;CAAG;AAE7C,2DAA2D;AAC3D,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,KAAK,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAQzC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAqB7D;AAgCD;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C,OAAO;IAUP;;;;;;;OAOG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa;IAgB1D;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAKzB;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAOxB,0EAA0E;IAC1E,kBAAkB,IAAI,OAAO;IAI7B,6DAA6D;IAC7D,iBAAiB,IAAI,OAAO;CAG7B"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Capability model for scripted plugins (MOD_LIFECYCLE.md section 4, P7
3
+ * phase 5).
4
+ *
5
+ * Only `shape: plugin` packs may request capabilities; content and tile
6
+ * packs are validated data that cannot execute, so they request none
7
+ * (docs/MODS.md trust tiers). A plugin's `capabilities` list in its
8
+ * manifest is the consent surface: the installer shows each one in plain
9
+ * language, the user approves, and the runtime grants exactly that set -
10
+ * nothing a plugin did not ask for and the user did not see. The
11
+ * perceive/act facades (a later P7 phase) call `CapabilitySet.check()`
12
+ * before honoring a request; an ungranted capability throws a clear
13
+ * author-facing error rather than silently doing nothing or diverging.
14
+ *
15
+ * Vocabulary (four forms, MOD_LIFECYCLE section 4 / the frost example in
16
+ * section 2):
17
+ * - "command:add" - register commands on the act facade.
18
+ * - "event:<name>" - subscribe to an engine event, e.g.
19
+ * "event:turn-start".
20
+ * - "state:<domain>.read" - read one perceive-facade domain, e.g.
21
+ * "state:party.read"; or the wildcard
22
+ * "state:*.read" for any domain.
23
+ * - "network:<host>" - outbound network to one host; or "network:*"
24
+ * for any host. Not in the section-4 examples
25
+ * verbatim, but named there ("network access to
26
+ * api.example.com"); "*" is this module's
27
+ * extension for a plugin that genuinely needs
28
+ * unrestricted egress, and reads the same way
29
+ * the other wildcards do.
30
+ * - "registry:<domain>" - override a game SYSTEM registry from a TRUSTED
31
+ * in-process plugin (W2.2, core/mod/registry-host.ts):
32
+ * "registry:effect" | "registry:room" |
33
+ * "registry:command" | "registry:monster" |
34
+ * "registry:vocab"; or the wildcard "registry:*"
35
+ * for all of them. "registry:vocab" (W2.3) declares
36
+ * NEW vocabulary (flags/stats/any kind). Distinct
37
+ * from "command:add": that adds a command via the
38
+ * act facade, this replaces what a command DOES (and
39
+ * the effect/room/AI logic behind the game).
40
+ * (The named-core-rule flags the bundled qol /
41
+ * bug-fixes mods use, GameState.modRules, are a
42
+ * DECLARATIVE manifest field - PackManifest.rules -
43
+ * applied by the host, so they need no capability.)
44
+ *
45
+ * This module only surfaces `nondeterministic` from the manifest. The
46
+ * save's determinism ratchet itself - flipping a save from DETERMINISTIC to
47
+ * NONDETERMINISTIC the first time such a mod is enabled, once and
48
+ * irreversibly - lives in core/save (decisions 4/18/22), not here.
49
+ */
50
+ import { hasFacet } from "./manifest.js";
51
+ export class CapabilityError extends Error {
52
+ }
53
+ const EVENT_RE = /^event:([a-z][a-z0-9-]*)$/;
54
+ const STATE_RE = /^state:(\*|[a-z][a-z0-9-]*)\.read$/;
55
+ const NETWORK_RE = /^network:(\*|[a-zA-Z0-9.-]+)$/;
56
+ /** The override domains ModRegistryHost gates, plus the "*" wildcard. */
57
+ const REGISTRY_RE = /^registry:(\*|effect|room|command|monster|vocab)$/;
58
+ /**
59
+ * Parse and validate a capability string against the vocabulary above,
60
+ * returning its structured form. Throws CapabilityError on anything
61
+ * malformed or outside the recognized patterns - an unknown capability is
62
+ * a hard error, not a silent no-op, since a typo'd request should fail
63
+ * loudly at install rather than quietly never matching a grant.
64
+ */
65
+ export function parseCapability(cap) {
66
+ if (cap === "command:add") {
67
+ return { kind: "command", action: "add" };
68
+ }
69
+ const event = EVENT_RE.exec(cap);
70
+ if (event) {
71
+ return { kind: "event", name: event[1] };
72
+ }
73
+ const state = STATE_RE.exec(cap);
74
+ if (state) {
75
+ return { kind: "state", domain: state[1], access: "read" };
76
+ }
77
+ const network = NETWORK_RE.exec(cap);
78
+ if (network) {
79
+ return { kind: "network", host: network[1] };
80
+ }
81
+ const registry = REGISTRY_RE.exec(cap);
82
+ if (registry) {
83
+ return { kind: "registry", domain: registry[1] };
84
+ }
85
+ throw new CapabilityError(`unrecognized capability: "${cap}"`);
86
+ }
87
+ /**
88
+ * True if `grant` covers `request`. Exact match for command/event, plus the
89
+ * two documented wildcards: a "state:*.read" grant covers a read of any
90
+ * domain, and a "network:*" grant covers egress to any host.
91
+ */
92
+ function grantCovers(grant, request) {
93
+ switch (request.kind) {
94
+ case "command":
95
+ return grant.kind === "command";
96
+ case "event":
97
+ return grant.kind === "event" && grant.name === request.name;
98
+ case "state":
99
+ return (grant.kind === "state" &&
100
+ grant.access === request.access &&
101
+ (grant.domain === "*" || grant.domain === request.domain));
102
+ case "network":
103
+ return (grant.kind === "network" &&
104
+ (grant.host === "*" || grant.host === request.host));
105
+ case "registry":
106
+ return (grant.kind === "registry" &&
107
+ (grant.domain === "*" || grant.domain === request.domain));
108
+ }
109
+ }
110
+ /**
111
+ * The capabilities a plugin was granted, built from its manifest. Facades
112
+ * hold one of these per loaded plugin and call `check()` before honoring
113
+ * any request that touches commands, events, state reads, or the network.
114
+ */
115
+ export class CapabilitySet {
116
+ grants;
117
+ nondeterministic;
118
+ affectsGameplay;
119
+ constructor(grants, nondeterministic, affectsGameplay) {
120
+ this.grants = grants;
121
+ this.nondeterministic = nondeterministic;
122
+ this.affectsGameplay = affectsGameplay;
123
+ }
124
+ /**
125
+ * Build a CapabilitySet from a pack manifest. Only `shape: plugin` packs
126
+ * may request capabilities (MOD_LIFECYCLE section 4): a content or tile
127
+ * pack with a non-empty `capabilities` list throws CapabilityError, since
128
+ * that shape cannot execute and so has nothing to grant capabilities to -
129
+ * the request signals author confusion or an upstream validation bug,
130
+ * not something to silently ignore.
131
+ */
132
+ static fromManifest(manifest) {
133
+ const requested = manifest.capabilities ?? [];
134
+ if (!hasFacet(manifest, "plugin") && requested.length > 0) {
135
+ throw new CapabilityError(`pack ${manifest.id}: only shape "plugin" packs may request capabilities ` +
136
+ `(this pack is shape "${manifest.shape}")`);
137
+ }
138
+ const grants = requested.map((cap) => parseCapability(cap));
139
+ return new CapabilitySet(grants, manifest.nondeterministic ?? false, manifest.affectsGameplay ?? false);
140
+ }
141
+ /**
142
+ * True if `cap` is covered by a grant in this set, honoring the
143
+ * "state:*.read" and "network:*" wildcards. Throws CapabilityError if
144
+ * `cap` itself is not a recognized capability string.
145
+ */
146
+ has(cap) {
147
+ const request = parseCapability(cap);
148
+ return this.grants.some((grant) => grantCovers(grant, request));
149
+ }
150
+ /**
151
+ * Throws CapabilityError, naming the missing capability and how to fix
152
+ * it, unless `cap` is granted. This is the guard the perceive/act facades
153
+ * call before honoring a plugin's request - an author-facing error, not
154
+ * a silent divergence.
155
+ */
156
+ check(cap) {
157
+ if (this.has(cap))
158
+ return;
159
+ throw new CapabilityError(`this plugin needs capability "${cap}"; add it to pack.json capabilities`);
160
+ }
161
+ /** True if the manifest declared `nondeterministic: true` (section 4). */
162
+ isNondeterministic() {
163
+ return this.nondeterministic;
164
+ }
165
+ /** True if the manifest declared `affectsGameplay: true`. */
166
+ isAffectsGameplay() {
167
+ return this.affectsGameplay;
168
+ }
169
+ }
170
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Record composition: how a stack of packs becomes one game.
3
+ *
4
+ * Every record in the composed game is identified by a PackRef
5
+ * ("<owner-pack>:<slug>"). Packs may:
6
+ * - add records (they become the owner),
7
+ * - patch records owned by packs they declare as dependencies
8
+ * (deep merge: objects merge per key, arrays and scalars replace,
9
+ * an explicit null deletes the key),
10
+ * - replace such records wholesale, or
11
+ * - remove them.
12
+ *
13
+ * The base game is pack zero ("core") and gets no special treatment:
14
+ * a total conversion is just a pack that replaces or removes core
15
+ * records. Composition is deterministic given the resolved load order,
16
+ * and every record carries provenance (owner plus every pack that
17
+ * modified it) for savefiles and debugging.
18
+ */
19
+ import type { PackManifest, PackRef } from "./manifest.js";
20
+ import type { FieldPatch } from "./patch.js";
21
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
22
+ [key: string]: JsonValue;
23
+ };
24
+ export type JsonRecord = {
25
+ [key: string]: JsonValue;
26
+ };
27
+ /** One pack's contribution to one record file (e.g. "monster"). */
28
+ export interface FileContribution {
29
+ /** New records; this pack becomes their owner. Each needs a name. */
30
+ records?: JsonRecord[];
31
+ /** Deep-merge patches onto records owned by declared dependencies. */
32
+ patches?: Record<string, JsonRecord>;
33
+ /** Wholesale replacements (owner and ref are preserved). */
34
+ replaces?: Record<string, JsonRecord>;
35
+ /** Refs to delete from the composed game. */
36
+ removes?: string[];
37
+ /**
38
+ * Field-level patches (see patch.ts): ordered field ops per target ref.
39
+ * composePacks applies these in load order after the coarse `patches`/
40
+ * `replaces` for the same pack (each pack's ops fold onto the running
41
+ * value, which is identical to composeFieldPatches over the ordered
42
+ * list). The pre-launch conflict report (P7 phase 6) reads the same data
43
+ * to find same-field collisions without the false-positive whole-record
44
+ * conflicts `patches` produces.
45
+ */
46
+ fieldPatches?: Record<string, FieldPatch>;
47
+ }
48
+ export interface PackContent {
49
+ manifest: PackManifest;
50
+ /** Contributions keyed by record file: "monster", "object", ... */
51
+ files: Record<string, FileContribution>;
52
+ }
53
+ export interface ComposedRecord {
54
+ ref: PackRef;
55
+ /** The pack that added the record. */
56
+ owner: string;
57
+ /** Every pack that patched or replaced it, in load order. */
58
+ modifiedBy: string[];
59
+ value: JsonRecord;
60
+ }
61
+ export declare class ComposeError extends Error {
62
+ }
63
+ /** Deep merge per the pack patch rules. Returns a new object. */
64
+ export declare function mergePatch(base: JsonRecord, patch: JsonRecord): JsonRecord;
65
+ /**
66
+ * Compose packs (already in resolved load order) into per-file record
67
+ * maps. Iteration order of each map is deterministic: records appear
68
+ * in the order their owning packs added them.
69
+ */
70
+ export declare function composePacks(packs: readonly PackContent[]): Map<string, Map<PackRef, ComposedRecord>>;
71
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,MAAM,SAAS,GACjB,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACjC,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACtC,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,qBAAa,YAAa,SAAQ,KAAK;CAAG;AAE1C,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,GAAG,UAAU,CAkB1E;AAWD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CA4E3C"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Record composition: how a stack of packs becomes one game.
3
+ *
4
+ * Every record in the composed game is identified by a PackRef
5
+ * ("<owner-pack>:<slug>"). Packs may:
6
+ * - add records (they become the owner),
7
+ * - patch records owned by packs they declare as dependencies
8
+ * (deep merge: objects merge per key, arrays and scalars replace,
9
+ * an explicit null deletes the key),
10
+ * - replace such records wholesale, or
11
+ * - remove them.
12
+ *
13
+ * The base game is pack zero ("core") and gets no special treatment:
14
+ * a total conversion is just a pack that replaces or removes core
15
+ * records. Composition is deterministic given the resolved load order,
16
+ * and every record carries provenance (owner plus every pack that
17
+ * modified it) for savefiles and debugging.
18
+ */
19
+ import { packRef } from "./manifest.js";
20
+ import { applyFieldPatch } from "./patch.js";
21
+ export class ComposeError extends Error {
22
+ }
23
+ /** Deep merge per the pack patch rules. Returns a new object. */
24
+ export function mergePatch(base, patch) {
25
+ const out = { ...base };
26
+ for (const [key, val] of Object.entries(patch)) {
27
+ if (val === null) {
28
+ delete out[key];
29
+ }
30
+ else if (typeof val === "object" &&
31
+ !Array.isArray(val) &&
32
+ typeof out[key] === "object" &&
33
+ out[key] !== null &&
34
+ !Array.isArray(out[key])) {
35
+ out[key] = mergePatch(out[key], val);
36
+ }
37
+ else {
38
+ out[key] = val;
39
+ }
40
+ }
41
+ return out;
42
+ }
43
+ function mayModify(m, ownerPack) {
44
+ return ownerPack === m.id || (m.dependencies ?? {})[ownerPack] !== undefined;
45
+ }
46
+ function ownerOf(ref) {
47
+ const at = ref.indexOf(":");
48
+ return at === -1 ? "" : ref.slice(0, at);
49
+ }
50
+ /**
51
+ * Compose packs (already in resolved load order) into per-file record
52
+ * maps. Iteration order of each map is deterministic: records appear
53
+ * in the order their owning packs added them.
54
+ */
55
+ export function composePacks(packs) {
56
+ const game = new Map();
57
+ for (const pack of packs) {
58
+ const pid = pack.manifest.id;
59
+ for (const [file, contrib] of Object.entries(pack.files)) {
60
+ let table = game.get(file);
61
+ if (!table) {
62
+ table = new Map();
63
+ game.set(file, table);
64
+ }
65
+ for (const rec of contrib.records ?? []) {
66
+ const name = rec["name"];
67
+ if (typeof name !== "string" || name.length === 0) {
68
+ throw new ComposeError(`${pid}/${file}: record without a name`);
69
+ }
70
+ const ref = packRef(pid, name);
71
+ if (table.has(ref)) {
72
+ throw new ComposeError(`${pid}/${file}: duplicate record ${ref}`);
73
+ }
74
+ table.set(ref, { ref, owner: pid, modifiedBy: [], value: rec });
75
+ }
76
+ for (const kind of ["patches", "replaces"]) {
77
+ for (const [refStr, body] of Object.entries(contrib[kind] ?? {})) {
78
+ const ref = refStr;
79
+ const existing = table.get(ref);
80
+ if (!existing) {
81
+ const verb = kind === "patches" ? "patch" : "replace";
82
+ throw new ComposeError(`${pid}/${file}: ${verb} target ${ref} does not exist`);
83
+ }
84
+ if (!mayModify(pack.manifest, ownerOf(ref))) {
85
+ throw new ComposeError(`${pid}/${file}: cannot modify ${ref} without declaring ${ownerOf(ref)} as a dependency`);
86
+ }
87
+ existing.value =
88
+ kind === "patches" ? mergePatch(existing.value, body) : body;
89
+ existing.modifiedBy.push(pid);
90
+ }
91
+ }
92
+ for (const [refStr, ops] of Object.entries(contrib.fieldPatches ?? {})) {
93
+ const ref = refStr;
94
+ const existing = table.get(ref);
95
+ if (!existing) {
96
+ throw new ComposeError(`${pid}/${file}: fieldPatch target ${ref} does not exist`);
97
+ }
98
+ if (!mayModify(pack.manifest, ownerOf(ref))) {
99
+ throw new ComposeError(`${pid}/${file}: cannot modify ${ref} without declaring ${ownerOf(ref)} as a dependency`);
100
+ }
101
+ existing.value = applyFieldPatch(existing.value, ops);
102
+ existing.modifiedBy.push(pid);
103
+ }
104
+ for (const refStr of contrib.removes ?? []) {
105
+ const ref = refStr;
106
+ if (!table.has(ref)) {
107
+ throw new ComposeError(`${pid}/${file}: remove target ${ref} does not exist`);
108
+ }
109
+ if (!mayModify(pack.manifest, ownerOf(ref))) {
110
+ throw new ComposeError(`${pid}/${file}: cannot remove ${ref} without declaring ${ownerOf(ref)} as a dependency`);
111
+ }
112
+ table.delete(ref);
113
+ }
114
+ }
115
+ }
116
+ return game;
117
+ }
118
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"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"}