@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,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-record identity for the record files whose identity is NOT a unique
|
|
3
|
+
* string `name`.
|
|
4
|
+
*
|
|
5
|
+
* CURRENT STATE, FIRST, BECAUSE THIS COMMENT USED TO MISLEAD. Every record file
|
|
6
|
+
* but ONE is addressable per record today: 24 by a unique `name`, 19 by the
|
|
7
|
+
* explicit specs in this file, and `history` by nothing - and an op against
|
|
8
|
+
* `history` is REPORTED, never silently dropped. Two independent reviewers read
|
|
9
|
+
* the older wording, took the "the other 20 files do not fit it" paragraph below
|
|
10
|
+
* for the present tense, and filed the same non-existent P1 ("20 files silently
|
|
11
|
+
* discard patch/replace/remove, including object, ego_item, vault, trap, store,
|
|
12
|
+
* brand, slay, projection, constants"). Those nine are precisely the files this
|
|
13
|
+
* table fixes. If you are about to report that bug, run record-key.test.ts.
|
|
14
|
+
*
|
|
15
|
+
* WHY THIS EXISTS
|
|
16
|
+
*
|
|
17
|
+
* composePacks (compose.ts) keys every record by `packRef(pack, slugify(name))`.
|
|
18
|
+
* That is the right identity for the 24 upstream record files whose records
|
|
19
|
+
* carry a unique `name` - monster, object_property, terrain and so on - and it
|
|
20
|
+
* is what makes `patches` / `replaces` / `fieldPatches` / `removes` work there.
|
|
21
|
+
*
|
|
22
|
+
* The other 20 files did not fit it, for two measured reasons (counts taken over
|
|
23
|
+
* packages/content/pack on 2026-07-29). This is the problem statement, not the
|
|
24
|
+
* status:
|
|
25
|
+
*
|
|
26
|
+
* - 14 files have no string `name` at all. Their identity lives somewhere else:
|
|
27
|
+
* `code` (brand, slay, chest_trap, projection), the tval half of a composite
|
|
28
|
+
* (`object_base`), the upstream `name:<name>:<desc>` line (trap), an index
|
|
29
|
+
* (pain, names), a plain other field (store, ui_knowledge, body, world,
|
|
30
|
+
* flavor, hints), or nowhere at all because the file is a single config
|
|
31
|
+
* singleton (constants, visuals).
|
|
32
|
+
* - 6 files DO have `name` but core's own data slugs two records to the same
|
|
33
|
+
* ref, mostly because slugify drops `*` and `+`: object has 5 such pairs
|
|
34
|
+
* ("Acquirement" / "*Acquirement*"), vault 1 ("Little eruption" /
|
|
35
|
+
* "Little eruption+"), and brand / slay / chest_trap name several records
|
|
36
|
+
* after the element rather than the variant. ego_item is the genuine case:
|
|
37
|
+
* "of Acid" exists twice, distinguished only by which item types it applies
|
|
38
|
+
* to - which are the very fields a mod would patch, so they cannot be part of
|
|
39
|
+
* its identity.
|
|
40
|
+
*
|
|
41
|
+
* Before this file existed, a per-record op against any of those 20 was
|
|
42
|
+
* SILENTLY DROPPED (loader.ts stripped the whole contribution before compose saw
|
|
43
|
+
* it). This table is what makes 19 of them addressable, and it is deliberately
|
|
44
|
+
* an EXPLICIT declaration rather than a heuristic: guessing a key would trade a
|
|
45
|
+
* silent drop for a silent mis-merge, which is worse. Every entry below was
|
|
46
|
+
* verified unique over the shipped core pack (see record-key.test.ts, which
|
|
47
|
+
* reads the real pack and fails if a declared key stops being unique).
|
|
48
|
+
*
|
|
49
|
+
* `history` is deliberately absent: a history record is
|
|
50
|
+
* `{chart:{chart,next,roll}, phrase}` and every part of that is a value a mod
|
|
51
|
+
* would legitimately change, so it has no identity to key on. An op against it
|
|
52
|
+
* is REPORTED, not applied - see loader.ts.
|
|
53
|
+
*
|
|
54
|
+
* AMBIGUITY IS NAMED, NEVER GUESSED. A key that two records in the same file
|
|
55
|
+
* claim (object's 5 pairs, ego_item's 25) makes that one ref unaddressable; the
|
|
56
|
+
* records stay in the game and any op naming the ref becomes a reported problem.
|
|
57
|
+
* The rest of the file remains addressable per record.
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* How to derive one record's identity within a file.
|
|
61
|
+
*
|
|
62
|
+
* - `fields`: slugify each dot-path in order and join with "--". Every path must
|
|
63
|
+
* resolve to a string or a number; anything else (absent, object, array) means
|
|
64
|
+
* the record has no derivable key and is simply not addressable.
|
|
65
|
+
* - `singleton`: the file holds exactly one config record, so the FILE is the
|
|
66
|
+
* identity (ref `<pack>:<file>`). A second record in such a file collides with
|
|
67
|
+
* the first and both become unaddressable, which is the correct answer - the
|
|
68
|
+
* host binds one.
|
|
69
|
+
*/
|
|
70
|
+
export type RecordKeySpec = {
|
|
71
|
+
readonly kind: "fields";
|
|
72
|
+
readonly paths: readonly string[];
|
|
73
|
+
} | {
|
|
74
|
+
readonly kind: "singleton";
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* The identity of every record file that is NOT keyed by a unique string `name`.
|
|
78
|
+
*
|
|
79
|
+
* A file absent from this table is keyed by `name`, which is what composePacks
|
|
80
|
+
* already does. Keys here are file stems, exactly as they appear in a pack
|
|
81
|
+
* folder (`brand.json` -> `brand`).
|
|
82
|
+
*/
|
|
83
|
+
export declare const RECORD_KEY_SPECS: Readonly<Record<string, RecordKeySpec>>;
|
|
84
|
+
/**
|
|
85
|
+
* Files with a declared key spec, sorted. Exported so a test can assert the set
|
|
86
|
+
* in BOTH directions (a file wrongly added and a file wrongly removed).
|
|
87
|
+
*/
|
|
88
|
+
export declare const KEYED_RECORD_FILES: readonly string[];
|
|
89
|
+
/** The key spec for a file: the declared one, or `name` by default. */
|
|
90
|
+
export declare function keySpecFor(file: string): RecordKeySpec;
|
|
91
|
+
/**
|
|
92
|
+
* The slug half of a record's ref within `file`, or null when this record has no
|
|
93
|
+
* derivable identity (a missing key field, or a key field that is not a scalar).
|
|
94
|
+
* Null means "not addressable"; it never means "drop the record".
|
|
95
|
+
*/
|
|
96
|
+
export declare function recordKey(file: string, record: unknown, spec?: RecordKeySpec): string | null;
|
|
97
|
+
/** A human phrase for what a file's identity is, for problem messages. */
|
|
98
|
+
export declare function keyDescription(file: string): string;
|
|
99
|
+
//# sourceMappingURL=record-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-key.d.ts","sourceRoot":"","sources":["../src/record-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAKH;;;;;;;;;;GAUG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC9D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC;AAEnC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAsCpE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,MAAM,EACV,CAAC;AAEvC,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAEtD;AAcD;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,IAAI,GAAE,aAAgC,GACrC,MAAM,GAAG,IAAI,CAcf;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAKnD"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-record identity for the record files whose identity is NOT a unique
|
|
3
|
+
* string `name`.
|
|
4
|
+
*
|
|
5
|
+
* CURRENT STATE, FIRST, BECAUSE THIS COMMENT USED TO MISLEAD. Every record file
|
|
6
|
+
* but ONE is addressable per record today: 24 by a unique `name`, 19 by the
|
|
7
|
+
* explicit specs in this file, and `history` by nothing - and an op against
|
|
8
|
+
* `history` is REPORTED, never silently dropped. Two independent reviewers read
|
|
9
|
+
* the older wording, took the "the other 20 files do not fit it" paragraph below
|
|
10
|
+
* for the present tense, and filed the same non-existent P1 ("20 files silently
|
|
11
|
+
* discard patch/replace/remove, including object, ego_item, vault, trap, store,
|
|
12
|
+
* brand, slay, projection, constants"). Those nine are precisely the files this
|
|
13
|
+
* table fixes. If you are about to report that bug, run record-key.test.ts.
|
|
14
|
+
*
|
|
15
|
+
* WHY THIS EXISTS
|
|
16
|
+
*
|
|
17
|
+
* composePacks (compose.ts) keys every record by `packRef(pack, slugify(name))`.
|
|
18
|
+
* That is the right identity for the 24 upstream record files whose records
|
|
19
|
+
* carry a unique `name` - monster, object_property, terrain and so on - and it
|
|
20
|
+
* is what makes `patches` / `replaces` / `fieldPatches` / `removes` work there.
|
|
21
|
+
*
|
|
22
|
+
* The other 20 files did not fit it, for two measured reasons (counts taken over
|
|
23
|
+
* packages/content/pack on 2026-07-29). This is the problem statement, not the
|
|
24
|
+
* status:
|
|
25
|
+
*
|
|
26
|
+
* - 14 files have no string `name` at all. Their identity lives somewhere else:
|
|
27
|
+
* `code` (brand, slay, chest_trap, projection), the tval half of a composite
|
|
28
|
+
* (`object_base`), the upstream `name:<name>:<desc>` line (trap), an index
|
|
29
|
+
* (pain, names), a plain other field (store, ui_knowledge, body, world,
|
|
30
|
+
* flavor, hints), or nowhere at all because the file is a single config
|
|
31
|
+
* singleton (constants, visuals).
|
|
32
|
+
* - 6 files DO have `name` but core's own data slugs two records to the same
|
|
33
|
+
* ref, mostly because slugify drops `*` and `+`: object has 5 such pairs
|
|
34
|
+
* ("Acquirement" / "*Acquirement*"), vault 1 ("Little eruption" /
|
|
35
|
+
* "Little eruption+"), and brand / slay / chest_trap name several records
|
|
36
|
+
* after the element rather than the variant. ego_item is the genuine case:
|
|
37
|
+
* "of Acid" exists twice, distinguished only by which item types it applies
|
|
38
|
+
* to - which are the very fields a mod would patch, so they cannot be part of
|
|
39
|
+
* its identity.
|
|
40
|
+
*
|
|
41
|
+
* Before this file existed, a per-record op against any of those 20 was
|
|
42
|
+
* SILENTLY DROPPED (loader.ts stripped the whole contribution before compose saw
|
|
43
|
+
* it). This table is what makes 19 of them addressable, and it is deliberately
|
|
44
|
+
* an EXPLICIT declaration rather than a heuristic: guessing a key would trade a
|
|
45
|
+
* silent drop for a silent mis-merge, which is worse. Every entry below was
|
|
46
|
+
* verified unique over the shipped core pack (see record-key.test.ts, which
|
|
47
|
+
* reads the real pack and fails if a declared key stops being unique).
|
|
48
|
+
*
|
|
49
|
+
* `history` is deliberately absent: a history record is
|
|
50
|
+
* `{chart:{chart,next,roll}, phrase}` and every part of that is a value a mod
|
|
51
|
+
* would legitimately change, so it has no identity to key on. An op against it
|
|
52
|
+
* is REPORTED, not applied - see loader.ts.
|
|
53
|
+
*
|
|
54
|
+
* AMBIGUITY IS NAMED, NEVER GUESSED. A key that two records in the same file
|
|
55
|
+
* claim (object's 5 pairs, ego_item's 25) makes that one ref unaddressable; the
|
|
56
|
+
* records stay in the game and any op naming the ref becomes a reported problem.
|
|
57
|
+
* The rest of the file remains addressable per record.
|
|
58
|
+
*/
|
|
59
|
+
import { slugify } from "./manifest.js";
|
|
60
|
+
/**
|
|
61
|
+
* The identity of every record file that is NOT keyed by a unique string `name`.
|
|
62
|
+
*
|
|
63
|
+
* A file absent from this table is keyed by `name`, which is what composePacks
|
|
64
|
+
* already does. Keys here are file stems, exactly as they appear in a pack
|
|
65
|
+
* folder (`brand.json` -> `brand`).
|
|
66
|
+
*/
|
|
67
|
+
export const RECORD_KEY_SPECS = {
|
|
68
|
+
/* `code` is the upstream identity; `name` names the element, so ACID_2 and
|
|
69
|
+
* ACID_3 both slug to "acid". */
|
|
70
|
+
brand: { kind: "fields", paths: ["code"] },
|
|
71
|
+
slay: { kind: "fields", paths: ["code"] },
|
|
72
|
+
chest_trap: { kind: "fields", paths: ["code"] },
|
|
73
|
+
/* projection: `code` is on all 56 records, `name` is not. */
|
|
74
|
+
projection: { kind: "fields", paths: ["code"] },
|
|
75
|
+
/* object_base: `name` is the composite {tval, name}; tval is the key upstream
|
|
76
|
+
* looks bases up by. */
|
|
77
|
+
object_base: { kind: "fields", paths: ["name.tval"] },
|
|
78
|
+
/* trap: `name` is the composite {name, desc} - upstream's `name:<name>:<desc>`
|
|
79
|
+
* line. The display half repeats (6 records are "strange rune"); the pair is
|
|
80
|
+
* unique. */
|
|
81
|
+
trap: { kind: "fields", paths: ["name.name", "name.desc"] },
|
|
82
|
+
/* store: the STORE_* code. */
|
|
83
|
+
store: { kind: "fields", paths: ["store"] },
|
|
84
|
+
/* pain: the message-set index, which IS its identity (mon_pain_msg). */
|
|
85
|
+
pain: { kind: "fields", paths: ["type"] },
|
|
86
|
+
ui_knowledge: { kind: "fields", paths: ["monster-category"] },
|
|
87
|
+
/* names: the random-name section index. */
|
|
88
|
+
names: { kind: "fields", paths: ["section"] },
|
|
89
|
+
body: { kind: "fields", paths: ["body"] },
|
|
90
|
+
/* world: the level's name, which is what upstream's up/down links reference. */
|
|
91
|
+
world: { kind: "fields", paths: ["level.name"] },
|
|
92
|
+
/* flavor: one record per object base, keyed by that base's tval. */
|
|
93
|
+
flavor: { kind: "fields", paths: ["kind.tval"] },
|
|
94
|
+
/* hints: the hint text is the whole record, so it is also its identity. */
|
|
95
|
+
hints: { kind: "fields", paths: ["H"] },
|
|
96
|
+
/* Config singletons: one record for the whole file. */
|
|
97
|
+
constants: { kind: "singleton" },
|
|
98
|
+
visuals: { kind: "singleton" },
|
|
99
|
+
/* Files that DO have `name` but need more of the record to be unique. Each is
|
|
100
|
+
* still not fully unique (see the header) - the residual collisions are
|
|
101
|
+
* reported, never guessed. */
|
|
102
|
+
object: { kind: "fields", paths: ["type", "name"] },
|
|
103
|
+
vault: { kind: "fields", paths: ["type", "name"] },
|
|
104
|
+
ego_item: { kind: "fields", paths: ["name"] },
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Files with a declared key spec, sorted. Exported so a test can assert the set
|
|
108
|
+
* in BOTH directions (a file wrongly added and a file wrongly removed).
|
|
109
|
+
*/
|
|
110
|
+
export const KEYED_RECORD_FILES = Object.keys(RECORD_KEY_SPECS).sort();
|
|
111
|
+
/** The key spec for a file: the declared one, or `name` by default. */
|
|
112
|
+
export function keySpecFor(file) {
|
|
113
|
+
return RECORD_KEY_SPECS[file] ?? { kind: "fields", paths: ["name"] };
|
|
114
|
+
}
|
|
115
|
+
function atPath(record, path) {
|
|
116
|
+
let cur = record;
|
|
117
|
+
for (const part of path.split(".")) {
|
|
118
|
+
if (typeof cur !== "object" || cur === null || Array.isArray(cur)) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
cur = cur[part];
|
|
122
|
+
if (cur === undefined)
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
return cur;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* The slug half of a record's ref within `file`, or null when this record has no
|
|
129
|
+
* derivable identity (a missing key field, or a key field that is not a scalar).
|
|
130
|
+
* Null means "not addressable"; it never means "drop the record".
|
|
131
|
+
*/
|
|
132
|
+
export function recordKey(file, record, spec = keySpecFor(file)) {
|
|
133
|
+
if (typeof record !== "object" || record === null || Array.isArray(record)) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (spec.kind === "singleton")
|
|
137
|
+
return slugify(file);
|
|
138
|
+
const parts = [];
|
|
139
|
+
for (const path of spec.paths) {
|
|
140
|
+
const value = atPath(record, path);
|
|
141
|
+
if (typeof value !== "string" && typeof value !== "number")
|
|
142
|
+
return null;
|
|
143
|
+
const slug = slugify(String(value));
|
|
144
|
+
if (slug.length === 0)
|
|
145
|
+
return null;
|
|
146
|
+
parts.push(slug);
|
|
147
|
+
}
|
|
148
|
+
return parts.join("--");
|
|
149
|
+
}
|
|
150
|
+
/** A human phrase for what a file's identity is, for problem messages. */
|
|
151
|
+
export function keyDescription(file) {
|
|
152
|
+
const spec = keySpecFor(file);
|
|
153
|
+
return spec.kind === "singleton"
|
|
154
|
+
? `the whole file (one config record, ref "<pack>:${slugify(file)}")`
|
|
155
|
+
: spec.paths.join(" + ");
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=record-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record-key.js","sourceRoot":"","sources":["../src/record-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic pack load-order resolution.
|
|
3
|
+
*
|
|
4
|
+
* Dependencies load before dependents (topological order); ties break by the
|
|
5
|
+
* caller's INPUT ORDER, so the order is reproducible on every machine given the
|
|
6
|
+
* same pack list. Cycles and missing dependencies are hard errors - a mod set
|
|
7
|
+
* either composes deterministically or fails loudly before play.
|
|
8
|
+
*
|
|
9
|
+
* TIES USED TO BREAK LEXICOGRAPHICALLY BY ID, and that was a defect, not a
|
|
10
|
+
* preference. Determinism was the goal and input order delivers it just as well:
|
|
11
|
+
* the caller's list is itself deterministic (the stored enabled array, an
|
|
12
|
+
* external manager's load-order.json, or `?mods=a,b`). Sorting by id instead
|
|
13
|
+
* DISCARDED the player's choice, while the mod manager went on offering "Move
|
|
14
|
+
* later (loads last, wins conflicts)" (web/src/mods.ts) - a reorder that changed
|
|
15
|
+
* nothing for any two packs with no dependency edge between them, which is most
|
|
16
|
+
* pairs. Vortex/MO2 semantics require the deployed order to decide, so the one
|
|
17
|
+
* thing the resolver must not do is invent an order of its own.
|
|
18
|
+
*
|
|
19
|
+
* Dependency edges still win outright: input order only decides among packs the
|
|
20
|
+
* graph leaves free, which is exactly what a load-order list is for.
|
|
21
|
+
*
|
|
22
|
+
* Beyond hard `dependencies`, MOD_LIFECYCLE.md section 3 defines two more
|
|
23
|
+
* ordering inputs, both soft (their absence is never an error):
|
|
24
|
+
*
|
|
25
|
+
* - `optionalDependencies`: if the named pack is present, it loads first
|
|
26
|
+
* and its version is checked against the declared range exactly like a
|
|
27
|
+
* hard dependency; if it is absent, it is silently skipped.
|
|
28
|
+
* - `loadAfter` / `loadBefore`: pure ordering hints among present packs,
|
|
29
|
+
* with no version semantics. `loadBefore` is implemented as the mirror
|
|
30
|
+
* of `loadAfter` (X.loadBefore = [Y] adds the same edge as Y.loadAfter
|
|
31
|
+
* = [X]).
|
|
32
|
+
*
|
|
33
|
+
* All of these contribute edges to the same topological sort, so a cycle
|
|
34
|
+
* created by mixing dependencies with loadAfter/loadBefore is rejected
|
|
35
|
+
* exactly like a dependency cycle.
|
|
36
|
+
*/
|
|
37
|
+
import type { PackManifest } from "./manifest.js";
|
|
38
|
+
export declare class ResolveError extends Error {
|
|
39
|
+
}
|
|
40
|
+
/** Order manifests so every pack follows all of its dependencies. */
|
|
41
|
+
export declare function resolveLoadOrder(manifests: readonly PackManifest[]): PackManifest[];
|
|
42
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAIlD,qBAAa,YAAa,SAAQ,KAAK;CAAG;AA8B1C,qEAAqE;AACrE,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,YAAY,EAAE,GACjC,YAAY,EAAE,CAoGhB"}
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic pack load-order resolution.
|
|
3
|
+
*
|
|
4
|
+
* Dependencies load before dependents (topological order); ties break by the
|
|
5
|
+
* caller's INPUT ORDER, so the order is reproducible on every machine given the
|
|
6
|
+
* same pack list. Cycles and missing dependencies are hard errors - a mod set
|
|
7
|
+
* either composes deterministically or fails loudly before play.
|
|
8
|
+
*
|
|
9
|
+
* TIES USED TO BREAK LEXICOGRAPHICALLY BY ID, and that was a defect, not a
|
|
10
|
+
* preference. Determinism was the goal and input order delivers it just as well:
|
|
11
|
+
* the caller's list is itself deterministic (the stored enabled array, an
|
|
12
|
+
* external manager's load-order.json, or `?mods=a,b`). Sorting by id instead
|
|
13
|
+
* DISCARDED the player's choice, while the mod manager went on offering "Move
|
|
14
|
+
* later (loads last, wins conflicts)" (web/src/mods.ts) - a reorder that changed
|
|
15
|
+
* nothing for any two packs with no dependency edge between them, which is most
|
|
16
|
+
* pairs. Vortex/MO2 semantics require the deployed order to decide, so the one
|
|
17
|
+
* thing the resolver must not do is invent an order of its own.
|
|
18
|
+
*
|
|
19
|
+
* Dependency edges still win outright: input order only decides among packs the
|
|
20
|
+
* graph leaves free, which is exactly what a load-order list is for.
|
|
21
|
+
*
|
|
22
|
+
* Beyond hard `dependencies`, MOD_LIFECYCLE.md section 3 defines two more
|
|
23
|
+
* ordering inputs, both soft (their absence is never an error):
|
|
24
|
+
*
|
|
25
|
+
* - `optionalDependencies`: if the named pack is present, it loads first
|
|
26
|
+
* and its version is checked against the declared range exactly like a
|
|
27
|
+
* hard dependency; if it is absent, it is silently skipped.
|
|
28
|
+
* - `loadAfter` / `loadBefore`: pure ordering hints among present packs,
|
|
29
|
+
* with no version semantics. `loadBefore` is implemented as the mirror
|
|
30
|
+
* of `loadAfter` (X.loadBefore = [Y] adds the same edge as Y.loadAfter
|
|
31
|
+
* = [X]).
|
|
32
|
+
*
|
|
33
|
+
* All of these contribute edges to the same topological sort, so a cycle
|
|
34
|
+
* created by mixing dependencies with loadAfter/loadBefore is rejected
|
|
35
|
+
* exactly like a dependency cycle.
|
|
36
|
+
*/
|
|
37
|
+
import { ManifestError } from "./manifest.js";
|
|
38
|
+
import { satisfies, SemverError } from "./semver.js";
|
|
39
|
+
export class ResolveError extends Error {
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Verify a present dependency's version range, throwing ResolveError with a
|
|
43
|
+
* plain-language message naming the fix. Used for both hard `dependencies`
|
|
44
|
+
* and `optionalDependencies` that happen to be present.
|
|
45
|
+
*/
|
|
46
|
+
function checkVersionRange(dependentId, depId, range, byId) {
|
|
47
|
+
const dep = byId.get(depId);
|
|
48
|
+
let ok;
|
|
49
|
+
try {
|
|
50
|
+
ok = satisfies(dep.version, range);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const reason = err instanceof SemverError ? err.message : String(err);
|
|
54
|
+
throw new ResolveError(`pack ${dependentId} declares an invalid version range "${range}" for ${depId}: ${reason}`);
|
|
55
|
+
}
|
|
56
|
+
if (!ok) {
|
|
57
|
+
throw new ResolveError(`pack ${dependentId} requires ${depId} ${range} but ${dep.version} is installed`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Order manifests so every pack follows all of its dependencies. */
|
|
61
|
+
export function resolveLoadOrder(manifests) {
|
|
62
|
+
const byId = new Map();
|
|
63
|
+
for (const m of manifests) {
|
|
64
|
+
if (byId.has(m.id)) {
|
|
65
|
+
throw new ManifestError(`duplicate pack id: ${m.id}`);
|
|
66
|
+
}
|
|
67
|
+
byId.set(m.id, m);
|
|
68
|
+
}
|
|
69
|
+
for (const m of manifests) {
|
|
70
|
+
for (const [dep, range] of Object.entries(m.dependencies ?? {})) {
|
|
71
|
+
if (!byId.has(dep)) {
|
|
72
|
+
throw new ResolveError(`pack ${m.id} requires missing pack ${dep}`);
|
|
73
|
+
}
|
|
74
|
+
checkVersionRange(m.id, dep, range, byId);
|
|
75
|
+
}
|
|
76
|
+
for (const [dep, range] of Object.entries(m.optionalDependencies ?? {})) {
|
|
77
|
+
if (!byId.has(dep))
|
|
78
|
+
continue; // absence of an optional dependency is not an error
|
|
79
|
+
checkVersionRange(m.id, dep, range, byId);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Collect, per pack, the full set of ids that must load before it: hard
|
|
83
|
+
// deps, present optional deps, present loadAfter, and the reverse edge
|
|
84
|
+
// for every present pack's loadBefore. Built as per-id Sets so that the
|
|
85
|
+
// same edge declared twice (e.g. both a hard dependency and a loadAfter
|
|
86
|
+
// entry) collapses to one edge instead of a duplicate that would corrupt
|
|
87
|
+
// the Kahn in-degree bookkeeping below.
|
|
88
|
+
const prereqs = new Map();
|
|
89
|
+
for (const m of manifests) {
|
|
90
|
+
prereqs.set(m.id, new Set());
|
|
91
|
+
}
|
|
92
|
+
for (const m of manifests) {
|
|
93
|
+
const set = prereqs.get(m.id);
|
|
94
|
+
for (const dep of Object.keys(m.dependencies ?? {})) {
|
|
95
|
+
set.add(dep);
|
|
96
|
+
}
|
|
97
|
+
for (const dep of Object.keys(m.optionalDependencies ?? {})) {
|
|
98
|
+
if (byId.has(dep))
|
|
99
|
+
set.add(dep);
|
|
100
|
+
}
|
|
101
|
+
for (const after of m.loadAfter ?? []) {
|
|
102
|
+
if (byId.has(after))
|
|
103
|
+
set.add(after);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const m of manifests) {
|
|
107
|
+
for (const before of m.loadBefore ?? []) {
|
|
108
|
+
// m must load before `before`: that is the same edge as
|
|
109
|
+
// `before`.loadAfter including m, so add it to before's prereq set.
|
|
110
|
+
const set = prereqs.get(before);
|
|
111
|
+
if (set !== undefined)
|
|
112
|
+
set.add(m.id);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const remainingDeps = new Map();
|
|
116
|
+
const dependents = new Map();
|
|
117
|
+
for (const m of manifests) {
|
|
118
|
+
remainingDeps.set(m.id, new Set(prereqs.get(m.id)));
|
|
119
|
+
for (const dep of prereqs.get(m.id)) {
|
|
120
|
+
const list = dependents.get(dep) ?? [];
|
|
121
|
+
list.push(m.id);
|
|
122
|
+
dependents.set(dep, list);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/* The caller's position for each id, which is what ties break on. */
|
|
126
|
+
const inputAt = new Map();
|
|
127
|
+
manifests.forEach((m, i) => inputAt.set(m.id, i));
|
|
128
|
+
const at = (id) => inputAt.get(id) ?? 0;
|
|
129
|
+
// Kahn with a frontier kept in input order, so the result is deterministic
|
|
130
|
+
// without the resolver imposing an order the player did not choose.
|
|
131
|
+
const frontier = [...remainingDeps.entries()]
|
|
132
|
+
.filter(([, deps]) => deps.size === 0)
|
|
133
|
+
.map(([id]) => id)
|
|
134
|
+
.sort((a, b) => at(a) - at(b));
|
|
135
|
+
const order = [];
|
|
136
|
+
while (frontier.length > 0) {
|
|
137
|
+
const id = frontier.shift();
|
|
138
|
+
order.push(byId.get(id));
|
|
139
|
+
for (const dependent of dependents.get(id) ?? []) {
|
|
140
|
+
const deps = remainingDeps.get(dependent);
|
|
141
|
+
deps.delete(id);
|
|
142
|
+
if (deps.size === 0) {
|
|
143
|
+
// Insert keeping the frontier in input order.
|
|
144
|
+
const pos = frontier.findIndex((f) => at(f) > at(dependent));
|
|
145
|
+
if (pos === -1)
|
|
146
|
+
frontier.push(dependent);
|
|
147
|
+
else
|
|
148
|
+
frontier.splice(pos, 0, dependent);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (order.length !== manifests.length) {
|
|
153
|
+
const stuck = [...remainingDeps.entries()]
|
|
154
|
+
.filter(([, deps]) => deps.size > 0)
|
|
155
|
+
.map(([id]) => id)
|
|
156
|
+
.sort();
|
|
157
|
+
throw new ResolveError(`dependency cycle among packs: ${stuck.join(", ")}`);
|
|
158
|
+
}
|
|
159
|
+
return order;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../src/resolve.ts"],"names":[],"mappings":"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"}
|
package/dist/semver.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small, dependency-free semver range matcher.
|
|
3
|
+
*
|
|
4
|
+
* This package bundles into a browser build, so it cannot pull in the
|
|
5
|
+
* `semver` npm package; this file implements just enough of the range
|
|
6
|
+
* grammar for pack manifests (MOD_LIFECYCLE.md section 3):
|
|
7
|
+
*
|
|
8
|
+
* - `*` or `x` (any case): matches any version.
|
|
9
|
+
* - an exact version, `1.2.3`: matches only that version.
|
|
10
|
+
* - a partial version used bare, `1.2` or `1`: matches any version with
|
|
11
|
+
* that prefix (`1.2` matches `1.2.0`..`1.2.x`; `1` matches `1.0.0`..`1.x.x`).
|
|
12
|
+
* - caret ranges, `^1.2.3`: compatible-with, following npm's rule that the
|
|
13
|
+
* leftmost nonzero component may not change (`^1.2.3` allows up to but
|
|
14
|
+
* not including `2.0.0`; `^0.2.3` allows up to but not including `0.3.0`;
|
|
15
|
+
* `^0.0.3` allows only `0.0.3`).
|
|
16
|
+
* - tilde ranges, `~1.2.3`: patch-level allowed (up to but not including
|
|
17
|
+
* `1.3.0`); `~1.2` is the same; `~1` allows up to but not including `2.0.0`.
|
|
18
|
+
* - comparator sets: `>=`, `>`, `<=`, `<`, `=`, combined with spaces and
|
|
19
|
+
* ANDed together, e.g. `>=1.0.0 <2.0.0`.
|
|
20
|
+
*
|
|
21
|
+
* Limitation (documented, not fixed): prerelease tags (`1.0.0-beta.2`) are
|
|
22
|
+
* compared naively as a single lexicographic string once the numeric
|
|
23
|
+
* major.minor.patch triple is equal, rather than the full dot-separated,
|
|
24
|
+
* numeric-vs-alphanumeric identifier comparison the semver spec defines.
|
|
25
|
+
* A version with no prerelease is always treated as newer than one with a
|
|
26
|
+
* prerelease at the same major.minor.patch, matching the spec; the ordering
|
|
27
|
+
* among different prerelease strings themselves does not. Pack authors who
|
|
28
|
+
* need exact prerelease ordering should not rely on it here.
|
|
29
|
+
*/
|
|
30
|
+
export declare class SemverError extends Error {
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Does `version` satisfy `range`? Throws SemverError if either string is
|
|
34
|
+
* malformed (an unparseable version, or a range with an unparseable token).
|
|
35
|
+
*/
|
|
36
|
+
export declare function satisfies(version: string, range: string): boolean;
|
|
37
|
+
//# sourceMappingURL=semver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semver.d.ts","sourceRoot":"","sources":["../src/semver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAyLzC;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAWjE"}
|