@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.
- package/LICENSE.md +43 -0
- package/README.md +72 -0
- package/dist/capabilities.d.ts +116 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +170 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/compose.d.ts +71 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +118 -0
- package/dist/compose.js.map +1 -0
- package/dist/conflicts.d.ts +78 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +160 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +83 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +314 -0
- package/dist/loader.js.map +1 -0
- package/dist/manifest.d.ts +261 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +264 -0
- package/dist/manifest.js.map +1 -0
- package/dist/patch.d.ts +90 -0
- package/dist/patch.d.ts.map +1 -0
- package/dist/patch.js +195 -0
- package/dist/patch.js.map +1 -0
- package/dist/record-key.d.ts +99 -0
- package/dist/record-key.d.ts.map +1 -0
- package/dist/record-key.js +157 -0
- package/dist/record-key.js.map +1 -0
- package/dist/resolve.d.ts +42 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +161 -0
- package/dist/resolve.js.map +1 -0
- package/dist/semver.d.ts +37 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +212 -0
- package/dist/semver.js.map +1 -0
- package/package.json +58 -0
- package/src/capabilities.ts +205 -0
- package/src/compose.ts +186 -0
- package/src/conflicts.ts +242 -0
- package/src/index.ts +73 -0
- package/src/loader.ts +393 -0
- package/src/manifest.ts +523 -0
- package/src/patch.ts +257 -0
- package/src/record-key.ts +180 -0
- package/src/resolve.ts +175 -0
- package/src/semver.ts +231 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-launch conflict report (MOD_LIFECYCLE section 3, P7 phase 6).
|
|
3
|
+
*
|
|
4
|
+
* Before a session starts, the app shows the player every record touched
|
|
5
|
+
* by more than one pack: which fields each pack wrote, who wins, and a
|
|
6
|
+
* plain-language line for anything that actually collides. Nothing is
|
|
7
|
+
* silent, nothing is a surprise at runtime.
|
|
8
|
+
*
|
|
9
|
+
* Two kinds of "touch" feed the report:
|
|
10
|
+
* - Field patches (patch.ts) and the coarse whole-record `patches` merge
|
|
11
|
+
* (compose.ts) both write specific fields. A record is contested when
|
|
12
|
+
* two or more distinct packs write it this way; a field is a collision
|
|
13
|
+
* only when two or more of those packs write the SAME field with an
|
|
14
|
+
* order-dependent op (composeFieldPatches decides that - reused here,
|
|
15
|
+
* not reimplemented).
|
|
16
|
+
* - A whole-record `replaces` or `removes` is always worth reporting,
|
|
17
|
+
* regardless of how many other packs touched the record: it overrides
|
|
18
|
+
* whatever the owning pack (and any patches) established, and that is
|
|
19
|
+
* exactly the kind of surprise this report exists to surface.
|
|
20
|
+
*
|
|
21
|
+
* Pure and deterministic: given the same (already load-ordered) pack list,
|
|
22
|
+
* the report is always the same value.
|
|
23
|
+
*/
|
|
24
|
+
import type { PackContent } from "./compose.js";
|
|
25
|
+
import type { FieldConflict } from "./patch.js";
|
|
26
|
+
/** One field a contested record's contributing packs wrote. */
|
|
27
|
+
export interface FieldTouch {
|
|
28
|
+
/** The dot-path written (as used by FieldOp / touchedFields). */
|
|
29
|
+
path: string;
|
|
30
|
+
/** Packs that wrote this field, in load order. */
|
|
31
|
+
owners: string[];
|
|
32
|
+
/** The pack whose write wins: the last one in load order. */
|
|
33
|
+
winner: string;
|
|
34
|
+
}
|
|
35
|
+
/** A later pack overriding a record's entire body outright. */
|
|
36
|
+
export interface RecordOverride {
|
|
37
|
+
/** The pack that performed the override (last one, if more than one did). */
|
|
38
|
+
pack: string;
|
|
39
|
+
kind: "replace" | "remove";
|
|
40
|
+
}
|
|
41
|
+
/** One contested record: touched by more than one pack. */
|
|
42
|
+
export interface RecordConflict {
|
|
43
|
+
/** The record reference, e.g. "core:kobold". */
|
|
44
|
+
ref: string;
|
|
45
|
+
/** The file the record lives in, e.g. "monster". */
|
|
46
|
+
file: string;
|
|
47
|
+
/**
|
|
48
|
+
* Every pack that contributed a field patch, coarse patch, replace, or
|
|
49
|
+
* remove to this record, in load order. Does not include the owning
|
|
50
|
+
* pack unless the owner is itself one of those contributors.
|
|
51
|
+
*/
|
|
52
|
+
contributingPacks: string[];
|
|
53
|
+
/**
|
|
54
|
+
* Every field any contributing pack wrote (via fieldPatches, or a
|
|
55
|
+
* top-level key of a coarse `patches` body), with who wrote it and who
|
|
56
|
+
* wins. Empty when the record was only touched by a whole-record
|
|
57
|
+
* replace/remove.
|
|
58
|
+
*/
|
|
59
|
+
fields: FieldTouch[];
|
|
60
|
+
/** Same-field collisions among `fields` - empty when none collided. */
|
|
61
|
+
collisions: FieldConflict[];
|
|
62
|
+
/** Present when a pack replaced or removed the record outright. */
|
|
63
|
+
override?: RecordOverride;
|
|
64
|
+
/** One plain-language line per collision and per override. */
|
|
65
|
+
humanLines: string[];
|
|
66
|
+
}
|
|
67
|
+
/** The full pre-launch conflict report: contested records only. */
|
|
68
|
+
export interface ConflictReport {
|
|
69
|
+
records: RecordConflict[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Compute the pre-launch conflict report over an already load-ordered pack
|
|
73
|
+
* list (see resolveLoadOrder). Only contested records get an entry:
|
|
74
|
+
* additive changes - distinct records, or a single pack touching a record -
|
|
75
|
+
* produce none.
|
|
76
|
+
*/
|
|
77
|
+
export declare function computeConflictReport(packs: readonly PackContent[]): ConflictReport;
|
|
78
|
+
//# sourceMappingURL=conflicts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflicts.d.ts","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAgC,WAAW,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAG5D,+DAA+D;AAC/D,MAAM,WAAW,UAAU;IACzB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;OAKG;IACH,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,uEAAuE;IACvE,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,8DAA8D;IAC9D,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,mEAAmE;AACnE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAmFD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,cAAc,CA6EnF"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-launch conflict report (MOD_LIFECYCLE section 3, P7 phase 6).
|
|
3
|
+
*
|
|
4
|
+
* Before a session starts, the app shows the player every record touched
|
|
5
|
+
* by more than one pack: which fields each pack wrote, who wins, and a
|
|
6
|
+
* plain-language line for anything that actually collides. Nothing is
|
|
7
|
+
* silent, nothing is a surprise at runtime.
|
|
8
|
+
*
|
|
9
|
+
* Two kinds of "touch" feed the report:
|
|
10
|
+
* - Field patches (patch.ts) and the coarse whole-record `patches` merge
|
|
11
|
+
* (compose.ts) both write specific fields. A record is contested when
|
|
12
|
+
* two or more distinct packs write it this way; a field is a collision
|
|
13
|
+
* only when two or more of those packs write the SAME field with an
|
|
14
|
+
* order-dependent op (composeFieldPatches decides that - reused here,
|
|
15
|
+
* not reimplemented).
|
|
16
|
+
* - A whole-record `replaces` or `removes` is always worth reporting,
|
|
17
|
+
* regardless of how many other packs touched the record: it overrides
|
|
18
|
+
* whatever the owning pack (and any patches) established, and that is
|
|
19
|
+
* exactly the kind of surprise this report exists to surface.
|
|
20
|
+
*
|
|
21
|
+
* Pure and deterministic: given the same (already load-ordered) pack list,
|
|
22
|
+
* the report is always the same value.
|
|
23
|
+
*/
|
|
24
|
+
import { composeFieldPatches, touchedFields } from "./patch.js";
|
|
25
|
+
/** The pack id a ref's "<owner>:<slug>" prefix names. */
|
|
26
|
+
function ownerOf(ref) {
|
|
27
|
+
const at = ref.indexOf(":");
|
|
28
|
+
return at === -1 ? "" : ref.slice(0, at);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Turn a coarse whole-record patch body into field ops, one per top-level
|
|
32
|
+
* key, so it runs through the same field-conflict engine as fieldPatches:
|
|
33
|
+
* nested objects merge (mirroring mergePatch), everything else - including
|
|
34
|
+
* an explicit null delete - is a straight `set` for reporting purposes.
|
|
35
|
+
*/
|
|
36
|
+
function coarsePatchOps(body) {
|
|
37
|
+
const ops = [];
|
|
38
|
+
for (const [key, value] of Object.entries(body)) {
|
|
39
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
40
|
+
ops.push({ op: "merge", path: key, value });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
ops.push({ op: "set", path: key, value });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return ops;
|
|
47
|
+
}
|
|
48
|
+
function entryFor(byFile, file, ref) {
|
|
49
|
+
let table = byFile.get(file);
|
|
50
|
+
if (!table) {
|
|
51
|
+
table = new Map();
|
|
52
|
+
byFile.set(file, table);
|
|
53
|
+
}
|
|
54
|
+
let entry = table.get(ref);
|
|
55
|
+
if (!entry) {
|
|
56
|
+
entry = { fieldContribs: [], modifiers: new Set(), overrides: [] };
|
|
57
|
+
table.set(ref, entry);
|
|
58
|
+
}
|
|
59
|
+
return entry;
|
|
60
|
+
}
|
|
61
|
+
/** For every field any contributing pack wrote: who wrote it, who wins. */
|
|
62
|
+
function fieldBreakdown(contribs) {
|
|
63
|
+
const writers = new Map();
|
|
64
|
+
for (const { owner, ops } of contribs) {
|
|
65
|
+
for (const path of touchedFields(ops)) {
|
|
66
|
+
const owners = writers.get(path) ?? [];
|
|
67
|
+
if (owners[owners.length - 1] !== owner)
|
|
68
|
+
owners.push(owner);
|
|
69
|
+
writers.set(path, owners);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const out = [];
|
|
73
|
+
for (const [path, owners] of writers) {
|
|
74
|
+
out.push({ path, owners, winner: owners[owners.length - 1] });
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** Two, or several, pack names joined for a human sentence. */
|
|
79
|
+
function joinOwners(owners) {
|
|
80
|
+
if (owners.length <= 1)
|
|
81
|
+
return owners.join("");
|
|
82
|
+
if (owners.length === 2)
|
|
83
|
+
return `${owners[0]} and ${owners[1]}`;
|
|
84
|
+
return `${owners.slice(0, -1).join(", ")} and ${owners[owners.length - 1]}`;
|
|
85
|
+
}
|
|
86
|
+
/** The record name half of a ref, for readable human lines ("core:kobold" -> "kobold"). */
|
|
87
|
+
function refName(ref) {
|
|
88
|
+
const at = ref.indexOf(":");
|
|
89
|
+
return at === -1 ? ref : ref.slice(at + 1);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Compute the pre-launch conflict report over an already load-ordered pack
|
|
93
|
+
* list (see resolveLoadOrder). Only contested records get an entry:
|
|
94
|
+
* additive changes - distinct records, or a single pack touching a record -
|
|
95
|
+
* produce none.
|
|
96
|
+
*/
|
|
97
|
+
export function computeConflictReport(packs) {
|
|
98
|
+
const byFile = new Map();
|
|
99
|
+
for (const pack of packs) {
|
|
100
|
+
const pid = pack.manifest.id;
|
|
101
|
+
for (const [file, contrib] of Object.entries(pack.files)) {
|
|
102
|
+
for (const [ref, ops] of Object.entries(contrib.fieldPatches ?? {})) {
|
|
103
|
+
const entry = entryFor(byFile, file, ref);
|
|
104
|
+
entry.fieldContribs.push({ owner: pid, ops });
|
|
105
|
+
entry.modifiers.add(pid);
|
|
106
|
+
}
|
|
107
|
+
for (const [ref, body] of Object.entries(contrib.patches ?? {})) {
|
|
108
|
+
const entry = entryFor(byFile, file, ref);
|
|
109
|
+
entry.fieldContribs.push({ owner: pid, ops: coarsePatchOps(body) });
|
|
110
|
+
entry.modifiers.add(pid);
|
|
111
|
+
}
|
|
112
|
+
for (const [ref] of Object.entries(contrib.replaces ?? {})) {
|
|
113
|
+
const entry = entryFor(byFile, file, ref);
|
|
114
|
+
entry.modifiers.add(pid);
|
|
115
|
+
entry.overrides.push({ pack: pid, kind: "replace" });
|
|
116
|
+
}
|
|
117
|
+
for (const ref of contrib.removes ?? []) {
|
|
118
|
+
const entry = entryFor(byFile, file, ref);
|
|
119
|
+
entry.modifiers.add(pid);
|
|
120
|
+
entry.overrides.push({ pack: pid, kind: "remove" });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const records = [];
|
|
125
|
+
for (const [file, table] of byFile) {
|
|
126
|
+
for (const [ref, entry] of table) {
|
|
127
|
+
const hasOverride = entry.overrides.length > 0;
|
|
128
|
+
if (entry.modifiers.size < 2 && !hasOverride)
|
|
129
|
+
continue; // additive: not contested
|
|
130
|
+
const { conflicts } = composeFieldPatches({}, entry.fieldContribs);
|
|
131
|
+
const fields = fieldBreakdown(entry.fieldContribs);
|
|
132
|
+
const humanLines = [];
|
|
133
|
+
for (const conflict of conflicts) {
|
|
134
|
+
const winner = conflict.owners[conflict.owners.length - 1];
|
|
135
|
+
const verb = conflict.owners.length === 2 ? "both set" : "all set";
|
|
136
|
+
humanLines.push(`${joinOwners(conflict.owners)} ${verb} ${refName(ref)}.${conflict.path}; ${winner} wins - drag to reorder.`);
|
|
137
|
+
}
|
|
138
|
+
let override;
|
|
139
|
+
if (hasOverride) {
|
|
140
|
+
const last = entry.overrides[entry.overrides.length - 1];
|
|
141
|
+
override = { pack: last.pack, kind: last.kind };
|
|
142
|
+
const verb = last.kind === "replace" ? "replaces" : "removes";
|
|
143
|
+
humanLines.push(`${last.pack} ${verb} ${refName(ref)} outright, overriding ${ownerOf(ref)}'s original.`);
|
|
144
|
+
}
|
|
145
|
+
const record = {
|
|
146
|
+
ref,
|
|
147
|
+
file,
|
|
148
|
+
contributingPacks: [...entry.modifiers],
|
|
149
|
+
fields,
|
|
150
|
+
collisions: conflicts,
|
|
151
|
+
humanLines,
|
|
152
|
+
};
|
|
153
|
+
if (override)
|
|
154
|
+
record.override = override;
|
|
155
|
+
records.push(record);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return { records };
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=conflicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflicts.js","sourceRoot":"","sources":["../src/conflicts.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rpgm-tools/neo-angband-mod-sdk - schemas and tooling for the mod ecosystem.
|
|
3
|
+
*
|
|
4
|
+
* Three pack shapes, one loading pipeline (see docs/MODS.md):
|
|
5
|
+
* - content packs: schema-validated declarative JSON (safe by construction)
|
|
6
|
+
* - tile packs: Linoleum-style manifests with individual images and
|
|
7
|
+
* exact named targets, honest glyph fallback for uncovered targets
|
|
8
|
+
* - scripted plugins: capability-scoped sandboxed scripts (escape hatch)
|
|
9
|
+
*
|
|
10
|
+
* This package holds the pack-agnostic machinery: manifests, the
|
|
11
|
+
* deterministic load-order resolver, and the record composition engine
|
|
12
|
+
* (add/patch/replace/remove with ownership rules and provenance). The
|
|
13
|
+
* base game composes through this exact pipeline as pack zero.
|
|
14
|
+
*/
|
|
15
|
+
export { hasFacet, ManifestError, PACK_SHAPES, packFacets, packRef, slugify, validateManifest, } from "./manifest.js";
|
|
16
|
+
export type { Capability, PackManifest, PackRef, PackRule, PackShape, PackTilePack, } from "./manifest.js";
|
|
17
|
+
export { ResolveError, resolveLoadOrder } from "./resolve.js";
|
|
18
|
+
export { satisfies, SemverError } from "./semver.js";
|
|
19
|
+
export { ComposeError, composePacks, mergePatch } from "./compose.js";
|
|
20
|
+
export { composeContentPacks } from "./loader.js";
|
|
21
|
+
export type { ComposedContent, LoadedPack } from "./loader.js";
|
|
22
|
+
export { KEYED_RECORD_FILES, keyDescription, keySpecFor, RECORD_KEY_SPECS, recordKey, } from "./record-key.js";
|
|
23
|
+
export type { RecordKeySpec } from "./record-key.js";
|
|
24
|
+
export type { ComposedRecord, FileContribution, JsonRecord, JsonValue, PackContent, } from "./compose.js";
|
|
25
|
+
export { applyFieldPatch, composeFieldPatches, PatchError, touchedFields, } from "./patch.js";
|
|
26
|
+
export type { ComposedPatch, FieldConflict, FieldOp, FieldPatch, } from "./patch.js";
|
|
27
|
+
export { computeConflictReport } from "./conflicts.js";
|
|
28
|
+
export type { ConflictReport, FieldTouch, RecordConflict, RecordOverride, } from "./conflicts.js";
|
|
29
|
+
export { CapabilityError, CapabilitySet, parseCapability } from "./capabilities.js";
|
|
30
|
+
export type { ParsedCapability } from "./capabilities.js";
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EACb,WAAW,EACX,UAAU,EACV,OAAO,EACP,OAAO,EACP,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,UAAU,EACV,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,SAAS,EACT,YAAY,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,SAAS,GACV,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AACvD,YAAY,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,cAAc,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rpgm-tools/neo-angband-mod-sdk - schemas and tooling for the mod ecosystem.
|
|
3
|
+
*
|
|
4
|
+
* Three pack shapes, one loading pipeline (see docs/MODS.md):
|
|
5
|
+
* - content packs: schema-validated declarative JSON (safe by construction)
|
|
6
|
+
* - tile packs: Linoleum-style manifests with individual images and
|
|
7
|
+
* exact named targets, honest glyph fallback for uncovered targets
|
|
8
|
+
* - scripted plugins: capability-scoped sandboxed scripts (escape hatch)
|
|
9
|
+
*
|
|
10
|
+
* This package holds the pack-agnostic machinery: manifests, the
|
|
11
|
+
* deterministic load-order resolver, and the record composition engine
|
|
12
|
+
* (add/patch/replace/remove with ownership rules and provenance). The
|
|
13
|
+
* base game composes through this exact pipeline as pack zero.
|
|
14
|
+
*/
|
|
15
|
+
export { hasFacet, ManifestError, PACK_SHAPES, packFacets, packRef, slugify, validateManifest, } from "./manifest.js";
|
|
16
|
+
export { ResolveError, resolveLoadOrder } from "./resolve.js";
|
|
17
|
+
export { satisfies, SemverError } from "./semver.js";
|
|
18
|
+
export { ComposeError, composePacks, mergePatch } from "./compose.js";
|
|
19
|
+
export { composeContentPacks } from "./loader.js";
|
|
20
|
+
export { KEYED_RECORD_FILES, keyDescription, keySpecFor, RECORD_KEY_SPECS, recordKey, } from "./record-key.js";
|
|
21
|
+
export { applyFieldPatch, composeFieldPatches, PatchError, touchedFields, } from "./patch.js";
|
|
22
|
+
export { computeConflictReport } from "./conflicts.js";
|
|
23
|
+
export { CapabilityError, CapabilitySet, parseCapability } from "./capabilities.js";
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EACb,WAAW,EACX,UAAU,EACV,OAAO,EACP,OAAO,EACP,gBAAgB,GACjB,MAAM,eAAe,CAAC;AASvB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,SAAS,GACV,MAAM,iBAAiB,CAAC;AASzB,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAC;AAOpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAOvD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content-pack loading: turn a resolved set of packs into the merged per-file
|
|
3
|
+
* record arrays a host binds into the running game.
|
|
4
|
+
*
|
|
5
|
+
* This is the join MOD_INTEGRATION_PLAN.md (Wave 1, W1.1) calls for. Until now
|
|
6
|
+
* the composition engine (resolveLoadOrder + composePacks) had no runtime
|
|
7
|
+
* caller: the game bound a single hard-coded pack directly. This wraps the
|
|
8
|
+
* engine into one entry point so the base game and every mod flow through the
|
|
9
|
+
* same pipeline, and a mod's records / patches / replaces / removes /
|
|
10
|
+
* fieldPatches actually take effect.
|
|
11
|
+
*
|
|
12
|
+
* The host (web / cli) owns the glue: it reads packs off disk or bundle, calls
|
|
13
|
+
* composeContentPacks, assembles its GamePack from the result, and hands that
|
|
14
|
+
* to core bindCore. Core stays mod-sdk-agnostic (it only ever sees a merged
|
|
15
|
+
* pack), so the layering the audit relied on is preserved.
|
|
16
|
+
*
|
|
17
|
+
* TWO MERGE PHASES, AND WHY
|
|
18
|
+
*
|
|
19
|
+
* composePacks needs a unique string `name` on every added record, because that
|
|
20
|
+
* is the identity it builds refs from. Measured over the shipped core pack, 24
|
|
21
|
+
* of the 44 record files satisfy that and 20 do not (14 carry no string `name`
|
|
22
|
+
* at all; 6 carry names that slug to the same ref). So:
|
|
23
|
+
*
|
|
24
|
+
* 1. COMPOSED FILES (24) go through composePacks, which merges records
|
|
25
|
+
* per-record and appends a later pack's additions after an earlier pack's.
|
|
26
|
+
* 2. PASSTHROUGH FILES (20) keep whole-file semantics for `records` - the last
|
|
27
|
+
* provider in load order wins the file - because a mod that ships
|
|
28
|
+
* `constants.json` means "use mine", not "add a second constants record",
|
|
29
|
+
* and the host binds one. Per-record ops are then applied on top, in load
|
|
30
|
+
* order, against the winning array (applyPassthroughOps below), using the
|
|
31
|
+
* per-file identity declared in record-key.ts.
|
|
32
|
+
*
|
|
33
|
+
* NOTHING IS DROPPED IN SILENCE. This is the invariant the whole file exists to
|
|
34
|
+
* hold. Until 2026-07-29 phase 2 did not exist: a `patches` / `replaces` /
|
|
35
|
+
* `fieldPatches` / `removes` entry aimed at any of the 20 was stripped from the
|
|
36
|
+
* contribution before compose ever saw it, so a mod author shipped a valid op
|
|
37
|
+
* and got no effect and no message - the worst failure mode there is, because a
|
|
38
|
+
* feature that appears to work cannot be found by review. Now every per-record
|
|
39
|
+
* op either takes effect or produces a line in `ComposedContent.problems`
|
|
40
|
+
* naming the pack, the file, the op and the ref. Whole-file replacement of a
|
|
41
|
+
* passthrough file is reported the same way, because it silently discards
|
|
42
|
+
* whatever the previous provider (usually core) put there.
|
|
43
|
+
*
|
|
44
|
+
* `problems` is reported rather than thrown because the web host composes at
|
|
45
|
+
* module scope with no try (packages/web/src/pack.ts), so a throw here is a
|
|
46
|
+
* blank page rather than a message. It is the same shape of channel the pack
|
|
47
|
+
* readers already use (`problems: readonly string[]`), so a host concatenates it
|
|
48
|
+
* into the list it already shows.
|
|
49
|
+
*/
|
|
50
|
+
import type { PackManifest } from "./manifest.js";
|
|
51
|
+
import type { FileContribution } from "./compose.js";
|
|
52
|
+
/**
|
|
53
|
+
* One pack as the host loaded it: its manifest plus its per-file contributions.
|
|
54
|
+
* The base game is the degenerate case where every file is records-only.
|
|
55
|
+
*/
|
|
56
|
+
export interface LoadedPack {
|
|
57
|
+
manifest: PackManifest;
|
|
58
|
+
/** fileName -> that file's contribution (records / patches / ...). */
|
|
59
|
+
files: Record<string, FileContribution>;
|
|
60
|
+
}
|
|
61
|
+
/** The merged content: per-file record arrays, in deterministic order. */
|
|
62
|
+
export interface ComposedContent {
|
|
63
|
+
/** fileName -> composed record array. */
|
|
64
|
+
records: Record<string, unknown[]>;
|
|
65
|
+
/** Files merged per-record through the full FileContribution model. */
|
|
66
|
+
composedFiles: string[];
|
|
67
|
+
/** Files whose `records` pass through last-wins (nameless or name-colliding). */
|
|
68
|
+
passthroughFiles: string[];
|
|
69
|
+
/**
|
|
70
|
+
* Every mod-facing operation that could NOT be honoured, in one line each,
|
|
71
|
+
* naming the pack, the file, the op and the record. Empty for a clean set.
|
|
72
|
+
* A host shows these next to the pack-reading problems it already collects.
|
|
73
|
+
*/
|
|
74
|
+
problems: string[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Compose a set of loaded packs into merged per-file record arrays. With a
|
|
78
|
+
* single pack (the base game alone) the output is record-identical to the
|
|
79
|
+
* input: every record object is preserved by reference and its order is
|
|
80
|
+
* unchanged, so routing the base game through this path is a no-op.
|
|
81
|
+
*/
|
|
82
|
+
export declare function composeContentPacks(packs: readonly LoadedPack[]): ComposedContent;
|
|
83
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAIlD,OAAO,KAAK,EAAE,gBAAgB,EAA2B,MAAM,cAAc,CAAC;AAI9E;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CACzC;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,uEAAuE;IACvE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,iFAAiF;IACjF,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAoND;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,SAAS,UAAU,EAAE,GAC3B,eAAe,CA0FjB"}
|