@alpaca-software/40kdc-data 0.5.14 → 0.5.16
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/dist/abilities-resolver/resolver.d.ts.map +1 -1
- package/dist/abilities-resolver/resolver.js +6 -2
- package/dist/abilities-resolver/resolver.js.map +1 -1
- package/dist/audit-coverage.d.ts.map +1 -1
- package/dist/audit-coverage.js +90 -1
- package/dist/audit-coverage.js.map +1 -1
- package/dist/audit-phrasing.d.ts +64 -0
- package/dist/audit-phrasing.d.ts.map +1 -0
- package/dist/audit-phrasing.js +208 -0
- package/dist/audit-phrasing.js.map +1 -0
- package/dist/build-share-registry.d.ts +2 -0
- package/dist/build-share-registry.d.ts.map +1 -0
- package/dist/build-share-registry.js +115 -0
- package/dist/build-share-registry.js.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/codegen-data.js +23 -0
- package/dist/codegen-data.js.map +1 -1
- package/dist/commands/translate.d.ts.map +1 -1
- package/dist/commands/translate.js +2 -5
- package/dist/commands/translate.js.map +1 -1
- package/dist/commands/validate-all.d.ts.map +1 -1
- package/dist/commands/validate-all.js +8 -3
- package/dist/commands/validate-all.js.map +1 -1
- package/dist/data/bundle.generated.js +1 -1
- package/dist/data/bundle.generated.js.map +1 -1
- package/dist/data/dataset.d.ts +33 -1
- package/dist/data/dataset.d.ts.map +1 -1
- package/dist/data/dataset.js +107 -1
- package/dist/data/dataset.js.map +1 -1
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +4 -0
- package/dist/data/index.js.map +1 -1
- package/dist/data/types.d.ts +3 -1
- package/dist/data/types.d.ts.map +1 -1
- package/dist/data/types.js +1 -0
- package/dist/data/types.js.map +1 -1
- package/dist/gen-conformance.js +162 -0
- package/dist/gen-conformance.js.map +1 -1
- package/dist/generated.d.ts +125 -0
- package/dist/generated.d.ts.map +1 -1
- package/dist/generated.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrity.d.ts +32 -0
- package/dist/integrity.d.ts.map +1 -0
- package/dist/integrity.js +120 -0
- package/dist/integrity.js.map +1 -0
- package/dist/report.d.ts +1 -1
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +2 -2
- package/dist/report.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +42 -0
- package/dist/runner.js.map +1 -1
- package/dist/share/codec.d.ts +83 -0
- package/dist/share/codec.d.ts.map +1 -0
- package/dist/share/codec.js +230 -0
- package/dist/share/codec.js.map +1 -0
- package/dist/share/index.d.ts +30 -0
- package/dist/share/index.d.ts.map +1 -0
- package/dist/share/index.js +36 -0
- package/dist/share/index.js.map +1 -0
- package/dist/share/registry.d.ts +42 -0
- package/dist/share/registry.d.ts.map +1 -0
- package/dist/share/registry.generated.d.ts +4 -0
- package/dist/share/registry.generated.d.ts.map +1 -0
- package/dist/share/registry.generated.js +4 -0
- package/dist/share/registry.generated.js.map +1 -0
- package/dist/share/registry.js +68 -0
- package/dist/share/registry.js.map +1 -0
- package/dist/terrain/index.d.ts +2 -2
- package/dist/terrain/index.d.ts.map +1 -1
- package/dist/terrain/index.js +1 -1
- package/dist/terrain/index.js.map +1 -1
- package/dist/terrain/solve.d.ts +76 -0
- package/dist/terrain/solve.d.ts.map +1 -1
- package/dist/terrain/solve.js +138 -0
- package/dist/terrain/solve.js.map +1 -1
- package/dist/translate/condition.d.ts +1 -0
- package/dist/translate/condition.d.ts.map +1 -1
- package/dist/translate/condition.js +101 -7
- package/dist/translate/condition.js.map +1 -1
- package/dist/translate/effect.d.ts +28 -20
- package/dist/translate/effect.d.ts.map +1 -1
- package/dist/translate/effect.js +483 -114
- package/dist/translate/effect.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +1 -0
- package/dist/validate.js.map +1 -1
- package/package.json +3 -1
- package/schemas/core/allied-rule.schema.json +115 -0
- package/schemas/core/detachment.schema.json +38 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { glob } from "glob";
|
|
3
|
+
import { resolve, basename, dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
6
|
+
const DATA_ROOT = resolve(__dirname, "../../data");
|
|
7
|
+
/**
|
|
8
|
+
* Factions whose units carry exactly one bare legion/faction faction_keyword.
|
|
9
|
+
*
|
|
10
|
+
* These are the shared-datasheet factions where contamination has been observed:
|
|
11
|
+
* a unit materialized from a parent template keeps the parent's faction keyword
|
|
12
|
+
* (e.g. a World Eaters Chaos Rhino left carrying `Emperor's Children`, or a Chaos
|
|
13
|
+
* Space Marines unit carrying the full multi-legion union line). For a faction in
|
|
14
|
+
* this map, every unit's `faction_keywords` must be a subset of `{home}`.
|
|
15
|
+
*
|
|
16
|
+
* Factions absent from this map (e.g. Space Marine chapters, which legitimately
|
|
17
|
+
* carry several faction_keywords) are not subject to the membership check, so the
|
|
18
|
+
* guard never produces a false positive on them. Extend this map as other
|
|
19
|
+
* single-token factions adopt the convention.
|
|
20
|
+
*/
|
|
21
|
+
export const FACTION_HOME_KEYWORD = {
|
|
22
|
+
"chaos-space-marines": "Heretic Astartes",
|
|
23
|
+
"world-eaters": "World Eaters",
|
|
24
|
+
"death-guard": "Death Guard",
|
|
25
|
+
"thousand-sons": "Thousand Sons",
|
|
26
|
+
"emperors-children": "Emperor’s Children",
|
|
27
|
+
};
|
|
28
|
+
function readArray(file) {
|
|
29
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
30
|
+
}
|
|
31
|
+
function loadAbilityIds(file, into) {
|
|
32
|
+
try {
|
|
33
|
+
for (const a of readArray(file)) {
|
|
34
|
+
if (a.ability_id)
|
|
35
|
+
into.add(a.ability_id);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// file absent — faction has no enrichment abilities, or no shared core pool
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Cross-entity referential integrity that per-file JSON Schema validation cannot
|
|
44
|
+
* express:
|
|
45
|
+
*
|
|
46
|
+
* - every unit `ability_id` must resolve to an ability defined in that faction's
|
|
47
|
+
* `enrichment/<faction>/abilities.json` (or the shared `enrichment/_core` pool).
|
|
48
|
+
* Same-faction scoping is deliberate — a union check would pass shared-unit
|
|
49
|
+
* contaminants because they happen to be defined in some *other* faction's
|
|
50
|
+
* enrichment.
|
|
51
|
+
* - every unit `faction_keywords` entry must be permitted for the unit's faction
|
|
52
|
+
* (see {@link FACTION_HOME_KEYWORD}).
|
|
53
|
+
*
|
|
54
|
+
* Results reuse {@link ValidationResult} so the CLI reporter can render them.
|
|
55
|
+
*/
|
|
56
|
+
export async function checkReferentialIntegrity(dataRoot) {
|
|
57
|
+
const root = dataRoot ?? DATA_ROOT;
|
|
58
|
+
const result = {
|
|
59
|
+
totalFiles: 0,
|
|
60
|
+
totalItems: 0,
|
|
61
|
+
passed: 0,
|
|
62
|
+
failed: 0,
|
|
63
|
+
errors: [],
|
|
64
|
+
};
|
|
65
|
+
// Shared core ability pool, available to every faction (optional).
|
|
66
|
+
const coreAbilities = new Set();
|
|
67
|
+
loadAbilityIds(resolve(root, "enrichment/_core/abilities.json"), coreAbilities);
|
|
68
|
+
const unitFiles = await glob("core/*/units.json", { cwd: root, absolute: true });
|
|
69
|
+
unitFiles.sort();
|
|
70
|
+
for (const file of unitFiles) {
|
|
71
|
+
const faction = basename(dirname(file));
|
|
72
|
+
if (faction.startsWith("_"))
|
|
73
|
+
continue; // scratch/example/report dirs
|
|
74
|
+
let units;
|
|
75
|
+
try {
|
|
76
|
+
units = readArray(file);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
continue; // structural problems are the AJV pass's job
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(units))
|
|
82
|
+
continue;
|
|
83
|
+
const defined = new Set(coreAbilities);
|
|
84
|
+
loadAbilityIds(resolve(root, `enrichment/${faction}/abilities.json`), defined);
|
|
85
|
+
const home = FACTION_HOME_KEYWORD[faction];
|
|
86
|
+
result.totalFiles++;
|
|
87
|
+
for (let i = 0; i < units.length; i++) {
|
|
88
|
+
const u = units[i];
|
|
89
|
+
result.totalItems++;
|
|
90
|
+
const errs = [];
|
|
91
|
+
for (const aid of u.ability_ids ?? []) {
|
|
92
|
+
if (!defined.has(aid)) {
|
|
93
|
+
errs.push({
|
|
94
|
+
path: `/${i}/ability_ids`,
|
|
95
|
+
message: `unit "${u.id}": ability_id "${aid}" is not defined in ${faction} enrichment`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (home !== undefined) {
|
|
100
|
+
for (const fk of u.faction_keywords ?? []) {
|
|
101
|
+
if (fk !== home) {
|
|
102
|
+
errs.push({
|
|
103
|
+
path: `/${i}/faction_keywords`,
|
|
104
|
+
message: `unit "${u.id}": faction_keyword "${fk}" is not permitted for ${faction} (expected only "${home}")`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (errs.length > 0) {
|
|
110
|
+
result.failed++;
|
|
111
|
+
result.errors.push({ file, index: i, errors: errs });
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
result.passed++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrity.js","sourceRoot":"","sources":["../src/integrity.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { glob } from \"glob\";\nimport { resolve, basename, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ValidationResult } from \"./validate.js\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\nconst DATA_ROOT = resolve(__dirname, \"../../data\");\n\n/**\n * Factions whose units carry exactly one bare legion/faction faction_keyword.\n *\n * These are the shared-datasheet factions where contamination has been observed:\n * a unit materialized from a parent template keeps the parent's faction keyword\n * (e.g. a World Eaters Chaos Rhino left carrying `Emperor's Children`, or a Chaos\n * Space Marines unit carrying the full multi-legion union line). For a faction in\n * this map, every unit's `faction_keywords` must be a subset of `{home}`.\n *\n * Factions absent from this map (e.g. Space Marine chapters, which legitimately\n * carry several faction_keywords) are not subject to the membership check, so the\n * guard never produces a false positive on them. Extend this map as other\n * single-token factions adopt the convention.\n */\nexport const FACTION_HOME_KEYWORD: Record<string, string> = {\n \"chaos-space-marines\": \"Heretic Astartes\",\n \"world-eaters\": \"World Eaters\",\n \"death-guard\": \"Death Guard\",\n \"thousand-sons\": \"Thousand Sons\",\n \"emperors-children\": \"Emperor’s Children\",\n};\n\ninterface UnitLike {\n id?: string;\n ability_ids?: string[];\n faction_keywords?: string[];\n}\ninterface AbilityLike {\n ability_id?: string;\n}\n\nfunction readArray<T>(file: string): T[] {\n return JSON.parse(readFileSync(file, \"utf-8\")) as T[];\n}\n\nfunction loadAbilityIds(file: string, into: Set<string>): void {\n try {\n for (const a of readArray<AbilityLike>(file)) {\n if (a.ability_id) into.add(a.ability_id);\n }\n } catch {\n // file absent — faction has no enrichment abilities, or no shared core pool\n }\n}\n\n/**\n * Cross-entity referential integrity that per-file JSON Schema validation cannot\n * express:\n *\n * - every unit `ability_id` must resolve to an ability defined in that faction's\n * `enrichment/<faction>/abilities.json` (or the shared `enrichment/_core` pool).\n * Same-faction scoping is deliberate — a union check would pass shared-unit\n * contaminants because they happen to be defined in some *other* faction's\n * enrichment.\n * - every unit `faction_keywords` entry must be permitted for the unit's faction\n * (see {@link FACTION_HOME_KEYWORD}).\n *\n * Results reuse {@link ValidationResult} so the CLI reporter can render them.\n */\nexport async function checkReferentialIntegrity(dataRoot?: string): Promise<ValidationResult> {\n const root = dataRoot ?? DATA_ROOT;\n const result: ValidationResult = {\n totalFiles: 0,\n totalItems: 0,\n passed: 0,\n failed: 0,\n errors: [],\n };\n\n // Shared core ability pool, available to every faction (optional).\n const coreAbilities = new Set<string>();\n loadAbilityIds(resolve(root, \"enrichment/_core/abilities.json\"), coreAbilities);\n\n const unitFiles = await glob(\"core/*/units.json\", { cwd: root, absolute: true });\n unitFiles.sort();\n\n for (const file of unitFiles) {\n const faction = basename(dirname(file));\n if (faction.startsWith(\"_\")) continue; // scratch/example/report dirs\n\n let units: UnitLike[];\n try {\n units = readArray<UnitLike>(file);\n } catch {\n continue; // structural problems are the AJV pass's job\n }\n if (!Array.isArray(units)) continue;\n\n const defined = new Set<string>(coreAbilities);\n loadAbilityIds(resolve(root, `enrichment/${faction}/abilities.json`), defined);\n const home = FACTION_HOME_KEYWORD[faction];\n\n result.totalFiles++;\n for (let i = 0; i < units.length; i++) {\n const u = units[i];\n result.totalItems++;\n const errs: Array<{ path: string; message: string }> = [];\n\n for (const aid of u.ability_ids ?? []) {\n if (!defined.has(aid)) {\n errs.push({\n path: `/${i}/ability_ids`,\n message: `unit \"${u.id}\": ability_id \"${aid}\" is not defined in ${faction} enrichment`,\n });\n }\n }\n\n if (home !== undefined) {\n for (const fk of u.faction_keywords ?? []) {\n if (fk !== home) {\n errs.push({\n path: `/${i}/faction_keywords`,\n message: `unit \"${u.id}\": faction_keyword \"${fk}\" is not permitted for ${faction} (expected only \"${home}\")`,\n });\n }\n }\n }\n\n if (errs.length > 0) {\n result.failed++;\n result.errors.push({ file, index: i, errors: errs });\n } else {\n result.passed++;\n }\n }\n }\n\n return result;\n}\n"]}
|
package/dist/report.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ValidationResult } from "./validate.js";
|
|
2
2
|
export type ReporterMode = "pretty" | "json";
|
|
3
|
-
export declare function formatReport(result: ValidationResult, mode: ReporterMode): string;
|
|
3
|
+
export declare function formatReport(result: ValidationResult, mode: ReporterMode, title?: string): string;
|
|
4
4
|
//# sourceMappingURL=report.d.ts.map
|
package/dist/report.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7C,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE7C,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,YAAY,EAClB,KAAK,SAAiC,GACrC,MAAM,CAgCR"}
|
package/dist/report.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
export function formatReport(result, mode) {
|
|
2
|
+
export function formatReport(result, mode, title = "40kdc Data Validation Report") {
|
|
3
3
|
if (mode === "json") {
|
|
4
4
|
return JSON.stringify(result, null, 2);
|
|
5
5
|
}
|
|
6
6
|
const lines = [];
|
|
7
7
|
lines.push("");
|
|
8
|
-
lines.push(chalk.bold(
|
|
8
|
+
lines.push(chalk.bold(title));
|
|
9
9
|
lines.push(chalk.gray("─".repeat(40)));
|
|
10
10
|
lines.push(`Files scanned: ${result.totalFiles}`);
|
|
11
11
|
lines.push(`Items validated: ${result.totalItems}`);
|
package/dist/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,UAAU,YAAY,
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,UAAU,YAAY,CAC1B,MAAwB,EACxB,IAAkB,EAClB,KAAK,GAAG,8BAA8B;IAEtC,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACnD,KAAK,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IACpD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAEpD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAClD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;YAC/C,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["import chalk from \"chalk\";\nimport type { ValidationResult } from \"./validate.js\";\n\nexport type ReporterMode = \"pretty\" | \"json\";\n\nexport function formatReport(\n result: ValidationResult,\n mode: ReporterMode,\n title = \"40kdc Data Validation Report\",\n): string {\n if (mode === \"json\") {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(\"\");\n lines.push(chalk.bold(title));\n lines.push(chalk.gray(\"─\".repeat(40)));\n lines.push(`Files scanned: ${result.totalFiles}`);\n lines.push(`Items validated: ${result.totalItems}`);\n lines.push(chalk.green(`Passed: ${result.passed}`));\n\n if (result.failed > 0) {\n lines.push(chalk.red(`Failed: ${result.failed}`));\n lines.push(\"\");\n\n for (const err of result.errors) {\n const loc = err.index >= 0 ? `[${err.index}]` : \"\";\n lines.push(chalk.red(` ✗ ${err.file}${loc}`));\n for (const e of err.errors) {\n lines.push(chalk.yellow(` ${e.path}: ${e.message}`));\n }\n }\n } else {\n lines.push(chalk.green(`Failed: 0`));\n lines.push(\"\");\n lines.push(chalk.green(\"All validations passed.\"));\n }\n\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n"]}
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";AAsBA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";AAsBA,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAuC5C,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AA8C3B,MAAM,MAAM,cAAc,GACtB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC5B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAElE,QAAA,MAAM,WAAW,oJASP,CAAC;AACX,KAAK,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC;AAgB9C,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C;AAgKD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,CAAC,EACG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GACrG,IAAI,GACJ,SAAS,GACZ,MAAM,GAAG,IAAI,CAQf;AAmnBD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,cAAc,CAsDhG;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAc9E"}
|
package/dist/runner.js
CHANGED
|
@@ -23,6 +23,7 @@ import { Dataset } from "./data/dataset.js";
|
|
|
23
23
|
import { normalizeName } from "./data/normalize.js";
|
|
24
24
|
import { maximalLoadout } from "./data/loadout.js";
|
|
25
25
|
import { exportRoster } from "./export/index.js";
|
|
26
|
+
import { decodeShareToken, encodeShareToken } from "./share/index.js";
|
|
26
27
|
import { importRoster, tryImportRoster } from "./import/import-roster.js";
|
|
27
28
|
import { createValidator } from "./schema-loader.js";
|
|
28
29
|
import { attributeStages, crunch } from "./cruncher/index.js";
|
|
@@ -215,6 +216,34 @@ function handleExport(args) {
|
|
|
215
216
|
return err("EXPORT_FAILED", { detail: e.message });
|
|
216
217
|
}
|
|
217
218
|
}
|
|
219
|
+
function handleShareEncode(args) {
|
|
220
|
+
if (typeof args !== "object" || args === null) {
|
|
221
|
+
return err("INVALID_INPUT", { detail: "share_encode args must be an object" });
|
|
222
|
+
}
|
|
223
|
+
const a = args;
|
|
224
|
+
if (typeof a.list !== "object" || a.list === null) {
|
|
225
|
+
return err("INVALID_INPUT", { detail: "share_encode.list must be an object" });
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
return ok(encodeShareToken(a.list));
|
|
229
|
+
}
|
|
230
|
+
catch (e) {
|
|
231
|
+
// An id absent from the embedded registry is the only expected throw.
|
|
232
|
+
return err("INVALID_INPUT", { detail: e.message });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function handleShareDecode(args) {
|
|
236
|
+
if (typeof args !== "object" || args === null) {
|
|
237
|
+
return err("INVALID_INPUT", { detail: "share_decode args must be an object" });
|
|
238
|
+
}
|
|
239
|
+
const a = args;
|
|
240
|
+
if (typeof a.token !== "string") {
|
|
241
|
+
return err("INVALID_INPUT", { detail: "share_decode.token must be a string" });
|
|
242
|
+
}
|
|
243
|
+
// The DecodeResult ({ ok, list } | { ok, reason }) is the compared value;
|
|
244
|
+
// a malformed/stale token is a normal result, not a protocol error.
|
|
245
|
+
return ok(decodeShareToken(a.token));
|
|
246
|
+
}
|
|
218
247
|
/**
|
|
219
248
|
* Canonical string encoding of a base size for cross-impl comparison (mirrors the
|
|
220
249
|
* string-encoding used by `maximal_loadout`). Returns null for an absent base.
|
|
@@ -318,6 +347,15 @@ function handleLinkedQuery(state, args) {
|
|
|
318
347
|
return err("UNKNOWN_ENTITY", { kind: "faction", id: input.factionId });
|
|
319
348
|
return ok(f.weapons.map((x) => x.id));
|
|
320
349
|
}
|
|
350
|
+
case "units_with_keyword":
|
|
351
|
+
return ok(ds.unitsWithKeyword(input.keyword ?? "").map((u) => u.id));
|
|
352
|
+
case "allies_for": {
|
|
353
|
+
const raw = input.detachmentIds;
|
|
354
|
+
const detachmentIds = Array.isArray(raw) ? raw : [];
|
|
355
|
+
return ok(ds.alliesFor(input.factionId ?? "", detachmentIds).map((r) => r.id));
|
|
356
|
+
}
|
|
357
|
+
case "ally_units_for":
|
|
358
|
+
return ok(ds.allyUnitsFor(input.ruleId ?? "").map((u) => u.id));
|
|
321
359
|
default:
|
|
322
360
|
return err("INVALID_INPUT", { detail: `unknown linked_query: ${a.query}` });
|
|
323
361
|
}
|
|
@@ -856,6 +894,10 @@ export function dispatch(state, req) {
|
|
|
856
894
|
return handleResolveTerrain(req.args);
|
|
857
895
|
case "keystones":
|
|
858
896
|
return handleKeystones(req.args);
|
|
897
|
+
case "share_encode":
|
|
898
|
+
return handleShareEncode(req.args);
|
|
899
|
+
case "share_decode":
|
|
900
|
+
return handleShareDecode(req.args);
|
|
859
901
|
case "shutdown":
|
|
860
902
|
return ok(null);
|
|
861
903
|
default:
|