@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
@@ -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"}
@@ -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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"}
@@ -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"}