@alpaca-software/40kdc-data 0.5.3 → 0.5.5

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.
@@ -13,6 +13,11 @@
13
13
  * two-line unit blocks (`[CharN: ]Nx <Unit> (P pts)` then `N with <wargear>`),
14
14
  * `Enhancement: <Name> (+P pts)` on its own line, and per-model-type
15
15
  * breakdowns with `• Nx <ModelType>` + indented `N with <wargear>` lines.
16
+ * Real exports also mix in compact-style lines: single-model units arrive as
17
+ * `[CharN: ]Nx <Unit> (P pts): <wargear>` on one line, and a model-type
18
+ * bullet may inline its loadout after a colon (`• 1x Champion: Chainblades`,
19
+ * `• 5x Eightbound: 5 with Chainblades`). The full-body parser accepts all
20
+ * of these.
16
21
  *
17
22
  * The {@link Roster} pivot stores units at unit granularity — per-model-type
18
23
  * wargear breakdowns and `CharN:` slot numbers aren't modelled, so this adapter
@@ -1 +1 @@
1
- {"version":3,"file":"newrecruit-wtc.d.ts","sourceRoot":"","sources":["../../src/import/newrecruit-wtc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAiXlD,eAAO,MAAM,2BAA2B,EAAE,aAgBzC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,aActC,CAAC"}
1
+ {"version":3,"file":"newrecruit-wtc.d.ts","sourceRoot":"","sources":["../../src/import/newrecruit-wtc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAuYlD,eAAO,MAAM,2BAA2B,EAAE,aAgBzC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,aActC,CAAC"}
@@ -76,7 +76,10 @@ const UNIT_HEADER_COMPACT = /^(?:Char\d+:\s*)?(\d+)x\s+(.+?)\s*\(\s*(\d+)\s*pts?
76
76
  const UNIT_HEADER_FULL = /^(?:Char\d+:\s*)?(\d+)x\s+(.+?)\s*\(\s*(\d+)\s*pts?\s*\)\s*$/i;
77
77
  const ENHANCEMENT_LINE = /^Enhancement:\s*(.+?)\s*\(\+\s*(\d+)\s*pts?\s*\)\s*$/i;
78
78
  const WITH_PREFIX = /^(\d+)\s+with\s+(.*)$/i;
79
- const MODEL_BREAKDOWN = /^\s*•\s*(\d+)x\s+(.+?)(?:\s*\[[^\]]*\])?\s*$/u;
79
+ // Optional trailing `: <wargear>` — NewRecruit inlines a model group's loadout
80
+ // after the model type (`• 1x Champion: Chainblades`, `• 5x Eightbound: 5 with
81
+ // Chainblades`) instead of always breaking it onto `N with` continuation lines.
82
+ const MODEL_BREAKDOWN = /^\s*•\s*(\d+)x\s+([^:]+?)(?:\s*\[[^\]]*\])?\s*(?::\s*(.+))?$/u;
80
83
  const SECTION_HEADER = /^[A-Z][A-Z0-9 \-/&]+$/; // BATTLELINE, ALLIED UNITS, etc.
81
84
  const HEADER_LINE = /^\+/;
82
85
  /**
@@ -233,9 +236,27 @@ function parseFullBody(body) {
233
236
  current = newUnit(name, pts, leading_count, is_character_prefix);
234
237
  continue;
235
238
  }
239
+ // Single-model units (characters, vehicles) appear compact-style even in
240
+ // full exports: `[CharN: ]Nx <Unit> (P pts): <wargear>` on one line.
241
+ // Without this branch they fall through every matcher and vanish.
242
+ const compactMatch = UNIT_HEADER_COMPACT.exec(line);
243
+ if (compactMatch) {
244
+ finalize();
245
+ const leading_count = Number.parseInt(compactMatch[1], 10);
246
+ const name = compactMatch[2].trim();
247
+ const pts = Number.parseInt(compactMatch[3], 10);
248
+ const is_character_prefix = /^Char\d+:/i.test(line);
249
+ current = newUnit(name, pts, leading_count, is_character_prefix);
250
+ applyWithGroup(current, compactMatch[4]);
251
+ continue;
252
+ }
236
253
  const breakdown = MODEL_BREAKDOWN.exec(raw);
237
254
  if (breakdown && current) {
238
255
  breakdownModels += Number.parseInt(breakdown[1], 10);
256
+ // Inline loadout after the model type; `N with` continuation lines for
257
+ // the same group still arrive separately and are handled below.
258
+ if (breakdown[3] !== undefined)
259
+ applyWithGroup(current, breakdown[3]);
239
260
  continue;
240
261
  }
241
262
  if (WITH_PREFIX.test(line) && current) {
@@ -1 +1 @@
1
- {"version":3,"file":"newrecruit-wtc.js","sourceRoot":"","sources":["../../src/import/newrecruit-wtc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * NewRecruit \"wtc-compact\" and \"wtc-full\" text adapters.\n *\n * Both formats open with a `++++++++` summary header carrying FACTION KEYWORD,\n * DETACHMENT, TOTAL ARMY POINTS, WARLORD, ENHANCEMENT(s), NUMBER OF UNITS, and\n * SECONDARY tournament-objective shorthand. The body diverges:\n *\n * - **wtc-compact** — one unit per line:\n * `[CharN: ]Nx <Unit> (P pts): <comma-separated wargear>`\n * followed optionally by `Enhancement: <Name> (+P pts)` on the next line.\n *\n * - **wtc-full** — uppercase section headers (`BATTLELINE`, `ALLIED UNITS`),\n * two-line unit blocks (`[CharN: ]Nx <Unit> (P pts)` then `N with <wargear>`),\n * `Enhancement: <Name> (+P pts)` on its own line, and per-model-type\n * breakdowns with `• Nx <ModelType>` + indented `N with <wargear>` lines.\n *\n * The {@link Roster} pivot stores units at unit granularity — per-model-type\n * wargear breakdowns and `CharN:` slot numbers aren't modelled, so this adapter\n * collapses them: the parsed unit's `model_count` is summed from the breakdown\n * and its `wargear` is the union of every loadout under it. The `WARLORD` /\n * `Houndpack Lance Character` tokens are stripped from the wargear list (and\n * set `is_warlord`/`is_character` instead) so resolution doesn't try to look\n * them up as weapons. Round-trips are at Roster level, not byte-for-byte.\n *\n * Enhancement points (`+15 pts`) are subtracted from the displayed unit total\n * so `ParsedUnit.points` is the *base* unit cost — matching the ListForge\n * convention where the unit's own cost line is base and the enhancement is a\n * sibling cost line. `total_computed` walks every cost line just like ListForge\n * (base unit pts + each enhancement pts).\n *\n * @packageDocumentation\n */\nimport type { FormatAdapter } from \"./adapter.js\";\nimport type { ParsedRoster, ParsedUnit, ParsedWargear } from \"./types.js\";\nimport {\n classifyWargearList,\n factionFromKeyword,\n inferBattleSizeRaw,\n splitWargearList,\n stripParenthetical,\n} from \"./newrecruit-text.js\";\n\nconst WTC_HEADER_PREFIX = \"+ FACTION KEYWORD:\";\n\n// --- header parsing ---------------------------------------------------------\n\ninterface WtcHeader {\n name: string;\n faction_raw_name: string | null;\n detachment_raw_name: string | null;\n declared_limit: number | null;\n total_reported: number | null;\n battle_size_raw: string | null;\n}\n\nconst HEADER_FIELDS = {\n faction: /^\\+\\s*FACTION KEYWORD:\\s*(.+?)\\s*$/i,\n detachment: /^\\+\\s*DETACHMENT:\\s*(.+?)\\s*$/i,\n totalPoints: /^\\+\\s*TOTAL ARMY POINTS:\\s*(\\d+)\\s*pts?\\s*$/i,\n pointsLimit: /^\\+\\s*POINTS LIMIT:\\s*(\\d+)\\s*pts?\\s*$/i,\n listName: /^\\+\\s*LIST NAME:\\s*(.+?)\\s*$/i,\n} as const;\n\n/** Parse the leading `++++ ... ++++` block. Returns `null` if no header is found. */\nfunction parseWtcHeader(text: string): { header: WtcHeader; bodyStart: number } | null {\n const lines = text.split(/\\r?\\n/);\n let faction_raw_name: string | null = null;\n let detachment_raw_name: string | null = null;\n let totalReported: number | null = null;\n let pointsLimit: number | null = null;\n let listName: string | null = null;\n\n // Two `+++++…` fence lines wrap the header. Find them.\n const fenceIndices: number[] = [];\n for (let i = 0; i < lines.length && fenceIndices.length < 2; i += 1) {\n if (/^\\++\\s*$/.test(lines[i])) fenceIndices.push(i);\n }\n let sawFactionKeyword = false;\n for (const line of lines) {\n if (!line.startsWith(\"+\")) continue;\n const factionMatch = HEADER_FIELDS.faction.exec(line);\n if (factionMatch) {\n faction_raw_name = factionFromKeyword(factionMatch[1]);\n sawFactionKeyword = true;\n continue;\n }\n const detMatch = HEADER_FIELDS.detachment.exec(line);\n if (detMatch) {\n detachment_raw_name = stripParenthetical(detMatch[1]);\n continue;\n }\n const ptsMatch = HEADER_FIELDS.totalPoints.exec(line);\n if (ptsMatch) {\n totalReported = Number.parseInt(ptsMatch[1], 10);\n continue;\n }\n const limitMatch = HEADER_FIELDS.pointsLimit.exec(line);\n if (limitMatch) {\n pointsLimit = Number.parseInt(limitMatch[1], 10);\n continue;\n }\n const nameMatch = HEADER_FIELDS.listName.exec(line);\n if (nameMatch) {\n listName = nameMatch[1];\n }\n }\n\n if (!sawFactionKeyword) return null;\n\n const bodyStart = fenceIndices.length >= 2 ? fenceIndices[1] + 1 : 0;\n // POINTS LIMIT — the round-trip-friendly companion to TOTAL ARMY POINTS —\n // is the army's points ceiling. When the source carries only a single\n // figure (the tournament default), fall back to it.\n const declared_limit = pointsLimit ?? totalReported;\n const battle_size_raw = inferBattleSizeRaw(declared_limit);\n\n return {\n header: {\n name: listName ?? \"Imported roster\",\n faction_raw_name,\n detachment_raw_name,\n declared_limit,\n total_reported: totalReported,\n battle_size_raw,\n },\n bodyStart,\n };\n}\n\n// --- shared body helpers ----------------------------------------------------\n\nconst UNIT_HEADER_COMPACT =\n /^(?:Char\\d+:\\s*)?(\\d+)x\\s+(.+?)\\s*\\(\\s*(\\d+)\\s*pts?\\s*\\)\\s*:\\s*(.*)$/i;\nconst UNIT_HEADER_FULL = /^(?:Char\\d+:\\s*)?(\\d+)x\\s+(.+?)\\s*\\(\\s*(\\d+)\\s*pts?\\s*\\)\\s*$/i;\nconst ENHANCEMENT_LINE =\n /^Enhancement:\\s*(.+?)\\s*\\(\\+\\s*(\\d+)\\s*pts?\\s*\\)\\s*$/i;\nconst WITH_PREFIX = /^(\\d+)\\s+with\\s+(.*)$/i;\nconst MODEL_BREAKDOWN = /^\\s*•\\s*(\\d+)x\\s+(.+?)(?:\\s*\\[[^\\]]*\\])?\\s*$/u;\nconst SECTION_HEADER = /^[A-Z][A-Z0-9 \\-/&]+$/; // BATTLELINE, ALLIED UNITS, etc.\nconst HEADER_LINE = /^\\+/;\n\n/**\n * `N with X, Y, Z` means each of `N` models carries the same list — the weapon\n * counts in the list multiply by `N`. Returns `{multiplier:1, list:text}` when\n * the line has no `with` prefix.\n */\nfunction parseWithGroup(text: string): { multiplier: number; list: string } {\n const m = WITH_PREFIX.exec(text);\n if (m) {\n const n = Number.parseInt(m[1], 10);\n return { multiplier: n > 0 ? n : 1, list: m[2] };\n }\n return { multiplier: 1, list: text };\n}\n\ninterface UnitBuilder {\n raw_name: string;\n is_character: boolean;\n is_warlord: boolean;\n enhancement_raw_name: string | null;\n /** Total displayed pts from the header line; base computed once an enhancement is known. */\n displayed_pts: number | null;\n enhancement_pts: number;\n model_count: number;\n wargear: Map<string, number>;\n}\n\nfunction newUnit(name: string, displayed_pts: number, leading_count: number, is_character_prefix: boolean): UnitBuilder {\n return {\n raw_name: name,\n is_character: is_character_prefix,\n is_warlord: false,\n enhancement_raw_name: null,\n displayed_pts,\n enhancement_pts: 0,\n model_count: leading_count > 0 ? leading_count : 1,\n wargear: new Map(),\n };\n}\n\nfunction addWargear(unit: UnitBuilder, items: ParsedWargear[]): void {\n for (const { raw_name, count } of items) {\n unit.wargear.set(raw_name, (unit.wargear.get(raw_name) ?? 0) + count);\n }\n}\n\nfunction applyWithGroup(unit: UnitBuilder, listText: string): void {\n const { multiplier, list } = parseWithGroup(listText);\n const tokens = splitWargearList(list);\n const cls = classifyWargearList(tokens);\n if (cls.is_warlord) unit.is_warlord = true;\n if (cls.is_character) unit.is_character = true;\n // wtc never inlines the enhancement points in the wargear list (that's the\n // simple format) but classifyWargearList silently absorbs it if it shows up;\n // wtc's enhancement is always parsed off the explicit \"Enhancement:\" line.\n const scaled = cls.wargear.map((w) => ({ raw_name: w.raw_name, count: w.count * multiplier }));\n addWargear(unit, scaled);\n}\n\nfunction finishUnit(unit: UnitBuilder): ParsedUnit {\n const displayed = unit.displayed_pts;\n const points = displayed === null ? null : displayed - unit.enhancement_pts;\n const wargear: ParsedWargear[] = [];\n for (const [raw_name, count] of unit.wargear) {\n wargear.push({ raw_name, count });\n }\n return {\n raw_name: unit.raw_name,\n is_character: unit.is_character,\n model_count: unit.model_count,\n points,\n is_warlord: unit.is_warlord,\n enhancement_raw_name: unit.enhancement_raw_name,\n enhancement_points: unit.enhancement_raw_name === null ? null : unit.enhancement_pts,\n wargear,\n };\n}\n\n/** Compute total_computed by walking every parsed unit cost line. */\nfunction computeTotal(units: ParsedUnit[], enhancementPtsByIndex: number[]): number {\n let total = 0;\n for (let i = 0; i < units.length; i += 1) {\n total += units[i].points ?? 0;\n total += enhancementPtsByIndex[i] ?? 0;\n }\n return total;\n}\n\nfunction attachEnhancement(unit: UnitBuilder, raw_name: string, pts: number): void {\n unit.enhancement_raw_name = raw_name.trim();\n unit.enhancement_pts = pts;\n}\n\n// --- compact body parser ----------------------------------------------------\n\nfunction parseCompactBody(body: string): { units: ParsedUnit[]; enhancementPts: number[] } {\n const lines = body.split(/\\r?\\n/);\n const units: ParsedUnit[] = [];\n const enhancementPts: number[] = [];\n let current: UnitBuilder | null = null;\n\n const finalize = (): void => {\n if (current) {\n units.push(finishUnit(current));\n enhancementPts.push(current.enhancement_pts);\n current = null;\n }\n };\n\n for (const raw of lines) {\n const line = raw.trim();\n if (!line || HEADER_LINE.test(line) || /^\\++$/.test(line)) continue;\n\n const enhMatch = ENHANCEMENT_LINE.exec(line);\n if (enhMatch && current) {\n attachEnhancement(current, enhMatch[1], Number.parseInt(enhMatch[2], 10));\n // Emit immediately so subsequent unit lines start fresh.\n finalize();\n continue;\n }\n\n const unitMatch = UNIT_HEADER_COMPACT.exec(line);\n if (unitMatch) {\n finalize();\n const leading_count = Number.parseInt(unitMatch[1], 10);\n const name = unitMatch[2].trim();\n const pts = Number.parseInt(unitMatch[3], 10);\n const is_character_prefix = /^Char\\d+:/i.test(line);\n current = newUnit(name, pts, leading_count, is_character_prefix);\n applyWithGroup(current, unitMatch[4]);\n continue;\n }\n }\n\n finalize();\n return { units, enhancementPts };\n}\n\n// --- full body parser -------------------------------------------------------\n\nfunction parseFullBody(body: string): { units: ParsedUnit[]; enhancementPts: number[] } {\n const lines = body.split(/\\r?\\n/);\n const units: ParsedUnit[] = [];\n const enhancementPts: number[] = [];\n let current: UnitBuilder | null = null;\n let breakdownModels = 0;\n\n const finalize = (): void => {\n if (current) {\n if (breakdownModels > 0) current.model_count = breakdownModels;\n units.push(finishUnit(current));\n enhancementPts.push(current.enhancement_pts);\n current = null;\n breakdownModels = 0;\n }\n };\n\n for (const raw of lines) {\n const line = raw.trim();\n if (!line || HEADER_LINE.test(line) || /^\\++$/.test(line)) continue;\n if (SECTION_HEADER.test(line) && !UNIT_HEADER_FULL.test(line)) {\n finalize();\n continue;\n }\n\n const enhMatch = ENHANCEMENT_LINE.exec(line);\n if (enhMatch && current) {\n attachEnhancement(current, enhMatch[1], Number.parseInt(enhMatch[2], 10));\n continue;\n }\n\n const unitMatch = UNIT_HEADER_FULL.exec(line);\n if (unitMatch) {\n finalize();\n const leading_count = Number.parseInt(unitMatch[1], 10);\n const name = unitMatch[2].trim();\n const pts = Number.parseInt(unitMatch[3], 10);\n const is_character_prefix = /^Char\\d+:/i.test(line);\n current = newUnit(name, pts, leading_count, is_character_prefix);\n continue;\n }\n\n const breakdown = MODEL_BREAKDOWN.exec(raw);\n if (breakdown && current) {\n breakdownModels += Number.parseInt(breakdown[1], 10);\n continue;\n }\n\n if (WITH_PREFIX.test(line) && current) {\n applyWithGroup(current, line);\n continue;\n }\n }\n\n finalize();\n return { units, enhancementPts };\n}\n\n// --- multi-force detection --------------------------------------------------\n\n/** Heuristic for `multi_force`: are there units with \"ALLIED\" decorating\n * the body? wtc-full has an explicit `ALLIED UNITS` section header; compact\n * has no section markers but the user-facing summary header counts every unit\n * together, so detect from explicit section presence. */\nfunction detectMultiForce(text: string, format: \"wtc-compact\" | \"wtc-full\"): boolean {\n if (format === \"wtc-full\") {\n return /^ALLIED UNITS\\s*$/im.test(text);\n }\n // wtc-compact has no section header. Multi-force surfaces only via the\n // primary-faction summary; assume single-force unless we add a richer marker.\n return false;\n}\n\n// --- adapters ---------------------------------------------------------------\n\nfunction isWtcText(decoded: unknown): string | null {\n if (typeof decoded !== \"string\") return null;\n // Both wtc formats begin with the FACTION KEYWORD header line (possibly\n // after some leading whitespace/fence characters).\n if (!decoded.includes(WTC_HEADER_PREFIX)) return null;\n return decoded;\n}\n\n/** Distinguishes wtc-full from wtc-compact: full has a line starting with\n * `\\d+ with ` at the start of a body line (compact only puts `N with` after\n * `:` on the same line as the unit header). */\nfunction isFullFormat(text: string): boolean {\n return /^[\\t ]*\\d+\\s+with\\b/m.test(text);\n}\n\n/** `•`-prefixed body lines. wtc-full uses them for per-model breakdowns; the GW\n * app format uses them for every wargear entry. wtc-compact never emits them,\n * so it's the one matcher that must exclude them to stay disjoint from GW. */\nfunction hasBullets(text: string): boolean {\n return /^[\\t ]*•/mu.test(text);\n}\n\nfunction parseWith(text: string, format: \"wtc-compact\" | \"wtc-full\"): ParsedRoster {\n const parsed = parseWtcHeader(text);\n if (!parsed) {\n throw new Error(`${format}: missing \"+ FACTION KEYWORD:\" header`);\n }\n const { header, bodyStart } = parsed;\n const body = text.split(/\\r?\\n/).slice(bodyStart).join(\"\\n\");\n const { units, enhancementPts } =\n format === \"wtc-full\" ? parseFullBody(body) : parseCompactBody(body);\n\n return {\n name: header.name,\n generated_by: null,\n faction_raw_name: header.faction_raw_name,\n detachment_raw_name: header.detachment_raw_name,\n battle_size_raw: header.battle_size_raw,\n declared_limit: header.declared_limit,\n total_reported: header.total_reported,\n total_computed: computeTotal(units, enhancementPts),\n units,\n multi_force: detectMultiForce(text, format),\n };\n}\n\nexport const newRecruitWtcCompactAdapter: FormatAdapter = {\n id: \"newrecruit-wtc-compact\",\n\n matches(decoded: unknown): boolean {\n const text = isWtcText(decoded);\n if (text === null) return false;\n // wtc-compact has no `N with` lines (that's wtc-full) and no `•` bullets\n // (that's the GW app format) — excluding both keeps the matcher disjoint.\n return !isFullFormat(text) && !hasBullets(text);\n },\n\n parse(decoded: unknown): ParsedRoster {\n const text = isWtcText(decoded);\n if (text === null) throw new Error(\"newrecruit-wtc-compact: input is not a string\");\n return parseWith(text, \"wtc-compact\");\n },\n};\n\nexport const newRecruitWtcFullAdapter: FormatAdapter = {\n id: \"newrecruit-wtc-full\",\n\n matches(decoded: unknown): boolean {\n const text = isWtcText(decoded);\n if (text === null) return false;\n return isFullFormat(text);\n },\n\n parse(decoded: unknown): ParsedRoster {\n const text = isWtcText(decoded);\n if (text === null) throw new Error(\"newrecruit-wtc-full: input is not a string\");\n return parseWith(text, \"wtc-full\");\n },\n};\n"]}
1
+ {"version":3,"file":"newrecruit-wtc.js","sourceRoot":"","sources":["../../src/import/newrecruit-wtc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * NewRecruit \"wtc-compact\" and \"wtc-full\" text adapters.\n *\n * Both formats open with a `++++++++` summary header carrying FACTION KEYWORD,\n * DETACHMENT, TOTAL ARMY POINTS, WARLORD, ENHANCEMENT(s), NUMBER OF UNITS, and\n * SECONDARY tournament-objective shorthand. The body diverges:\n *\n * - **wtc-compact** — one unit per line:\n * `[CharN: ]Nx <Unit> (P pts): <comma-separated wargear>`\n * followed optionally by `Enhancement: <Name> (+P pts)` on the next line.\n *\n * - **wtc-full** — uppercase section headers (`BATTLELINE`, `ALLIED UNITS`),\n * two-line unit blocks (`[CharN: ]Nx <Unit> (P pts)` then `N with <wargear>`),\n * `Enhancement: <Name> (+P pts)` on its own line, and per-model-type\n * breakdowns with `• Nx <ModelType>` + indented `N with <wargear>` lines.\n * Real exports also mix in compact-style lines: single-model units arrive as\n * `[CharN: ]Nx <Unit> (P pts): <wargear>` on one line, and a model-type\n * bullet may inline its loadout after a colon (`• 1x Champion: Chainblades`,\n * `• 5x Eightbound: 5 with Chainblades`). The full-body parser accepts all\n * of these.\n *\n * The {@link Roster} pivot stores units at unit granularity — per-model-type\n * wargear breakdowns and `CharN:` slot numbers aren't modelled, so this adapter\n * collapses them: the parsed unit's `model_count` is summed from the breakdown\n * and its `wargear` is the union of every loadout under it. The `WARLORD` /\n * `Houndpack Lance Character` tokens are stripped from the wargear list (and\n * set `is_warlord`/`is_character` instead) so resolution doesn't try to look\n * them up as weapons. Round-trips are at Roster level, not byte-for-byte.\n *\n * Enhancement points (`+15 pts`) are subtracted from the displayed unit total\n * so `ParsedUnit.points` is the *base* unit cost — matching the ListForge\n * convention where the unit's own cost line is base and the enhancement is a\n * sibling cost line. `total_computed` walks every cost line just like ListForge\n * (base unit pts + each enhancement pts).\n *\n * @packageDocumentation\n */\nimport type { FormatAdapter } from \"./adapter.js\";\nimport type { ParsedRoster, ParsedUnit, ParsedWargear } from \"./types.js\";\nimport {\n classifyWargearList,\n factionFromKeyword,\n inferBattleSizeRaw,\n splitWargearList,\n stripParenthetical,\n} from \"./newrecruit-text.js\";\n\nconst WTC_HEADER_PREFIX = \"+ FACTION KEYWORD:\";\n\n// --- header parsing ---------------------------------------------------------\n\ninterface WtcHeader {\n name: string;\n faction_raw_name: string | null;\n detachment_raw_name: string | null;\n declared_limit: number | null;\n total_reported: number | null;\n battle_size_raw: string | null;\n}\n\nconst HEADER_FIELDS = {\n faction: /^\\+\\s*FACTION KEYWORD:\\s*(.+?)\\s*$/i,\n detachment: /^\\+\\s*DETACHMENT:\\s*(.+?)\\s*$/i,\n totalPoints: /^\\+\\s*TOTAL ARMY POINTS:\\s*(\\d+)\\s*pts?\\s*$/i,\n pointsLimit: /^\\+\\s*POINTS LIMIT:\\s*(\\d+)\\s*pts?\\s*$/i,\n listName: /^\\+\\s*LIST NAME:\\s*(.+?)\\s*$/i,\n} as const;\n\n/** Parse the leading `++++ ... ++++` block. Returns `null` if no header is found. */\nfunction parseWtcHeader(text: string): { header: WtcHeader; bodyStart: number } | null {\n const lines = text.split(/\\r?\\n/);\n let faction_raw_name: string | null = null;\n let detachment_raw_name: string | null = null;\n let totalReported: number | null = null;\n let pointsLimit: number | null = null;\n let listName: string | null = null;\n\n // Two `+++++…` fence lines wrap the header. Find them.\n const fenceIndices: number[] = [];\n for (let i = 0; i < lines.length && fenceIndices.length < 2; i += 1) {\n if (/^\\++\\s*$/.test(lines[i])) fenceIndices.push(i);\n }\n let sawFactionKeyword = false;\n for (const line of lines) {\n if (!line.startsWith(\"+\")) continue;\n const factionMatch = HEADER_FIELDS.faction.exec(line);\n if (factionMatch) {\n faction_raw_name = factionFromKeyword(factionMatch[1]);\n sawFactionKeyword = true;\n continue;\n }\n const detMatch = HEADER_FIELDS.detachment.exec(line);\n if (detMatch) {\n detachment_raw_name = stripParenthetical(detMatch[1]);\n continue;\n }\n const ptsMatch = HEADER_FIELDS.totalPoints.exec(line);\n if (ptsMatch) {\n totalReported = Number.parseInt(ptsMatch[1], 10);\n continue;\n }\n const limitMatch = HEADER_FIELDS.pointsLimit.exec(line);\n if (limitMatch) {\n pointsLimit = Number.parseInt(limitMatch[1], 10);\n continue;\n }\n const nameMatch = HEADER_FIELDS.listName.exec(line);\n if (nameMatch) {\n listName = nameMatch[1];\n }\n }\n\n if (!sawFactionKeyword) return null;\n\n const bodyStart = fenceIndices.length >= 2 ? fenceIndices[1] + 1 : 0;\n // POINTS LIMIT — the round-trip-friendly companion to TOTAL ARMY POINTS —\n // is the army's points ceiling. When the source carries only a single\n // figure (the tournament default), fall back to it.\n const declared_limit = pointsLimit ?? totalReported;\n const battle_size_raw = inferBattleSizeRaw(declared_limit);\n\n return {\n header: {\n name: listName ?? \"Imported roster\",\n faction_raw_name,\n detachment_raw_name,\n declared_limit,\n total_reported: totalReported,\n battle_size_raw,\n },\n bodyStart,\n };\n}\n\n// --- shared body helpers ----------------------------------------------------\n\nconst UNIT_HEADER_COMPACT =\n /^(?:Char\\d+:\\s*)?(\\d+)x\\s+(.+?)\\s*\\(\\s*(\\d+)\\s*pts?\\s*\\)\\s*:\\s*(.*)$/i;\nconst UNIT_HEADER_FULL = /^(?:Char\\d+:\\s*)?(\\d+)x\\s+(.+?)\\s*\\(\\s*(\\d+)\\s*pts?\\s*\\)\\s*$/i;\nconst ENHANCEMENT_LINE =\n /^Enhancement:\\s*(.+?)\\s*\\(\\+\\s*(\\d+)\\s*pts?\\s*\\)\\s*$/i;\nconst WITH_PREFIX = /^(\\d+)\\s+with\\s+(.*)$/i;\n// Optional trailing `: <wargear>` — NewRecruit inlines a model group's loadout\n// after the model type (`• 1x Champion: Chainblades`, `• 5x Eightbound: 5 with\n// Chainblades`) instead of always breaking it onto `N with` continuation lines.\nconst MODEL_BREAKDOWN =\n /^\\s*•\\s*(\\d+)x\\s+([^:]+?)(?:\\s*\\[[^\\]]*\\])?\\s*(?::\\s*(.+))?$/u;\nconst SECTION_HEADER = /^[A-Z][A-Z0-9 \\-/&]+$/; // BATTLELINE, ALLIED UNITS, etc.\nconst HEADER_LINE = /^\\+/;\n\n/**\n * `N with X, Y, Z` means each of `N` models carries the same list — the weapon\n * counts in the list multiply by `N`. Returns `{multiplier:1, list:text}` when\n * the line has no `with` prefix.\n */\nfunction parseWithGroup(text: string): { multiplier: number; list: string } {\n const m = WITH_PREFIX.exec(text);\n if (m) {\n const n = Number.parseInt(m[1], 10);\n return { multiplier: n > 0 ? n : 1, list: m[2] };\n }\n return { multiplier: 1, list: text };\n}\n\ninterface UnitBuilder {\n raw_name: string;\n is_character: boolean;\n is_warlord: boolean;\n enhancement_raw_name: string | null;\n /** Total displayed pts from the header line; base computed once an enhancement is known. */\n displayed_pts: number | null;\n enhancement_pts: number;\n model_count: number;\n wargear: Map<string, number>;\n}\n\nfunction newUnit(name: string, displayed_pts: number, leading_count: number, is_character_prefix: boolean): UnitBuilder {\n return {\n raw_name: name,\n is_character: is_character_prefix,\n is_warlord: false,\n enhancement_raw_name: null,\n displayed_pts,\n enhancement_pts: 0,\n model_count: leading_count > 0 ? leading_count : 1,\n wargear: new Map(),\n };\n}\n\nfunction addWargear(unit: UnitBuilder, items: ParsedWargear[]): void {\n for (const { raw_name, count } of items) {\n unit.wargear.set(raw_name, (unit.wargear.get(raw_name) ?? 0) + count);\n }\n}\n\nfunction applyWithGroup(unit: UnitBuilder, listText: string): void {\n const { multiplier, list } = parseWithGroup(listText);\n const tokens = splitWargearList(list);\n const cls = classifyWargearList(tokens);\n if (cls.is_warlord) unit.is_warlord = true;\n if (cls.is_character) unit.is_character = true;\n // wtc never inlines the enhancement points in the wargear list (that's the\n // simple format) but classifyWargearList silently absorbs it if it shows up;\n // wtc's enhancement is always parsed off the explicit \"Enhancement:\" line.\n const scaled = cls.wargear.map((w) => ({ raw_name: w.raw_name, count: w.count * multiplier }));\n addWargear(unit, scaled);\n}\n\nfunction finishUnit(unit: UnitBuilder): ParsedUnit {\n const displayed = unit.displayed_pts;\n const points = displayed === null ? null : displayed - unit.enhancement_pts;\n const wargear: ParsedWargear[] = [];\n for (const [raw_name, count] of unit.wargear) {\n wargear.push({ raw_name, count });\n }\n return {\n raw_name: unit.raw_name,\n is_character: unit.is_character,\n model_count: unit.model_count,\n points,\n is_warlord: unit.is_warlord,\n enhancement_raw_name: unit.enhancement_raw_name,\n enhancement_points: unit.enhancement_raw_name === null ? null : unit.enhancement_pts,\n wargear,\n };\n}\n\n/** Compute total_computed by walking every parsed unit cost line. */\nfunction computeTotal(units: ParsedUnit[], enhancementPtsByIndex: number[]): number {\n let total = 0;\n for (let i = 0; i < units.length; i += 1) {\n total += units[i].points ?? 0;\n total += enhancementPtsByIndex[i] ?? 0;\n }\n return total;\n}\n\nfunction attachEnhancement(unit: UnitBuilder, raw_name: string, pts: number): void {\n unit.enhancement_raw_name = raw_name.trim();\n unit.enhancement_pts = pts;\n}\n\n// --- compact body parser ----------------------------------------------------\n\nfunction parseCompactBody(body: string): { units: ParsedUnit[]; enhancementPts: number[] } {\n const lines = body.split(/\\r?\\n/);\n const units: ParsedUnit[] = [];\n const enhancementPts: number[] = [];\n let current: UnitBuilder | null = null;\n\n const finalize = (): void => {\n if (current) {\n units.push(finishUnit(current));\n enhancementPts.push(current.enhancement_pts);\n current = null;\n }\n };\n\n for (const raw of lines) {\n const line = raw.trim();\n if (!line || HEADER_LINE.test(line) || /^\\++$/.test(line)) continue;\n\n const enhMatch = ENHANCEMENT_LINE.exec(line);\n if (enhMatch && current) {\n attachEnhancement(current, enhMatch[1], Number.parseInt(enhMatch[2], 10));\n // Emit immediately so subsequent unit lines start fresh.\n finalize();\n continue;\n }\n\n const unitMatch = UNIT_HEADER_COMPACT.exec(line);\n if (unitMatch) {\n finalize();\n const leading_count = Number.parseInt(unitMatch[1], 10);\n const name = unitMatch[2].trim();\n const pts = Number.parseInt(unitMatch[3], 10);\n const is_character_prefix = /^Char\\d+:/i.test(line);\n current = newUnit(name, pts, leading_count, is_character_prefix);\n applyWithGroup(current, unitMatch[4]);\n continue;\n }\n }\n\n finalize();\n return { units, enhancementPts };\n}\n\n// --- full body parser -------------------------------------------------------\n\nfunction parseFullBody(body: string): { units: ParsedUnit[]; enhancementPts: number[] } {\n const lines = body.split(/\\r?\\n/);\n const units: ParsedUnit[] = [];\n const enhancementPts: number[] = [];\n let current: UnitBuilder | null = null;\n let breakdownModels = 0;\n\n const finalize = (): void => {\n if (current) {\n if (breakdownModels > 0) current.model_count = breakdownModels;\n units.push(finishUnit(current));\n enhancementPts.push(current.enhancement_pts);\n current = null;\n breakdownModels = 0;\n }\n };\n\n for (const raw of lines) {\n const line = raw.trim();\n if (!line || HEADER_LINE.test(line) || /^\\++$/.test(line)) continue;\n if (SECTION_HEADER.test(line) && !UNIT_HEADER_FULL.test(line)) {\n finalize();\n continue;\n }\n\n const enhMatch = ENHANCEMENT_LINE.exec(line);\n if (enhMatch && current) {\n attachEnhancement(current, enhMatch[1], Number.parseInt(enhMatch[2], 10));\n continue;\n }\n\n const unitMatch = UNIT_HEADER_FULL.exec(line);\n if (unitMatch) {\n finalize();\n const leading_count = Number.parseInt(unitMatch[1], 10);\n const name = unitMatch[2].trim();\n const pts = Number.parseInt(unitMatch[3], 10);\n const is_character_prefix = /^Char\\d+:/i.test(line);\n current = newUnit(name, pts, leading_count, is_character_prefix);\n continue;\n }\n\n // Single-model units (characters, vehicles) appear compact-style even in\n // full exports: `[CharN: ]Nx <Unit> (P pts): <wargear>` on one line.\n // Without this branch they fall through every matcher and vanish.\n const compactMatch = UNIT_HEADER_COMPACT.exec(line);\n if (compactMatch) {\n finalize();\n const leading_count = Number.parseInt(compactMatch[1], 10);\n const name = compactMatch[2].trim();\n const pts = Number.parseInt(compactMatch[3], 10);\n const is_character_prefix = /^Char\\d+:/i.test(line);\n current = newUnit(name, pts, leading_count, is_character_prefix);\n applyWithGroup(current, compactMatch[4]);\n continue;\n }\n\n const breakdown = MODEL_BREAKDOWN.exec(raw);\n if (breakdown && current) {\n breakdownModels += Number.parseInt(breakdown[1], 10);\n // Inline loadout after the model type; `N with` continuation lines for\n // the same group still arrive separately and are handled below.\n if (breakdown[3] !== undefined) applyWithGroup(current, breakdown[3]);\n continue;\n }\n\n if (WITH_PREFIX.test(line) && current) {\n applyWithGroup(current, line);\n continue;\n }\n }\n\n finalize();\n return { units, enhancementPts };\n}\n\n// --- multi-force detection --------------------------------------------------\n\n/** Heuristic for `multi_force`: are there units with \"ALLIED\" decorating\n * the body? wtc-full has an explicit `ALLIED UNITS` section header; compact\n * has no section markers but the user-facing summary header counts every unit\n * together, so detect from explicit section presence. */\nfunction detectMultiForce(text: string, format: \"wtc-compact\" | \"wtc-full\"): boolean {\n if (format === \"wtc-full\") {\n return /^ALLIED UNITS\\s*$/im.test(text);\n }\n // wtc-compact has no section header. Multi-force surfaces only via the\n // primary-faction summary; assume single-force unless we add a richer marker.\n return false;\n}\n\n// --- adapters ---------------------------------------------------------------\n\nfunction isWtcText(decoded: unknown): string | null {\n if (typeof decoded !== \"string\") return null;\n // Both wtc formats begin with the FACTION KEYWORD header line (possibly\n // after some leading whitespace/fence characters).\n if (!decoded.includes(WTC_HEADER_PREFIX)) return null;\n return decoded;\n}\n\n/** Distinguishes wtc-full from wtc-compact: full has a line starting with\n * `\\d+ with ` at the start of a body line (compact only puts `N with` after\n * `:` on the same line as the unit header). */\nfunction isFullFormat(text: string): boolean {\n return /^[\\t ]*\\d+\\s+with\\b/m.test(text);\n}\n\n/** `•`-prefixed body lines. wtc-full uses them for per-model breakdowns; the GW\n * app format uses them for every wargear entry. wtc-compact never emits them,\n * so it's the one matcher that must exclude them to stay disjoint from GW. */\nfunction hasBullets(text: string): boolean {\n return /^[\\t ]*•/mu.test(text);\n}\n\nfunction parseWith(text: string, format: \"wtc-compact\" | \"wtc-full\"): ParsedRoster {\n const parsed = parseWtcHeader(text);\n if (!parsed) {\n throw new Error(`${format}: missing \"+ FACTION KEYWORD:\" header`);\n }\n const { header, bodyStart } = parsed;\n const body = text.split(/\\r?\\n/).slice(bodyStart).join(\"\\n\");\n const { units, enhancementPts } =\n format === \"wtc-full\" ? parseFullBody(body) : parseCompactBody(body);\n\n return {\n name: header.name,\n generated_by: null,\n faction_raw_name: header.faction_raw_name,\n detachment_raw_name: header.detachment_raw_name,\n battle_size_raw: header.battle_size_raw,\n declared_limit: header.declared_limit,\n total_reported: header.total_reported,\n total_computed: computeTotal(units, enhancementPts),\n units,\n multi_force: detectMultiForce(text, format),\n };\n}\n\nexport const newRecruitWtcCompactAdapter: FormatAdapter = {\n id: \"newrecruit-wtc-compact\",\n\n matches(decoded: unknown): boolean {\n const text = isWtcText(decoded);\n if (text === null) return false;\n // wtc-compact has no `N with` lines (that's wtc-full) and no `•` bullets\n // (that's the GW app format) — excluding both keeps the matcher disjoint.\n return !isFullFormat(text) && !hasBullets(text);\n },\n\n parse(decoded: unknown): ParsedRoster {\n const text = isWtcText(decoded);\n if (text === null) throw new Error(\"newrecruit-wtc-compact: input is not a string\");\n return parseWith(text, \"wtc-compact\");\n },\n};\n\nexport const newRecruitWtcFullAdapter: FormatAdapter = {\n id: \"newrecruit-wtc-full\",\n\n matches(decoded: unknown): boolean {\n const text = isWtcText(decoded);\n if (text === null) return false;\n return isFullFormat(text);\n },\n\n parse(decoded: unknown): ParsedRoster {\n const text = isWtcText(decoded);\n if (text === null) throw new Error(\"newrecruit-wtc-full: input is not a string\");\n return parseWith(text, \"wtc-full\");\n },\n};\n"]}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export type { AbilityScope } from "./generated.js";
6
6
  export { resolveLayout, polygonCentroid, footprintVertices, orientedOffsets, TerrainResolveError, solveCentroid, solveCentroidTriangulated, solveCentroidAttached, TerrainSolveError, keystoneMeasurements, BOARD_INCHES, TerrainKeystoneError, } from "./terrain/index.js";
7
7
  export type { ResolvedPiece, ResolvedVec2, BoardEdge, FeatureRef, Keystone, KeystoneMeasurement, DimensionLine, SolveInput, TriangulationLine, TriangulateInput, AttachLine, AttachPiece, AttachInput, } from "./terrain/index.js";
8
8
  export * from "./scoring/index.js";
9
+ export * from "./cruncher/index.js";
9
10
  export { importListForge, importNewRecruit, importRoster, tryImportRoster, decodeListForge, } from "./import/index.js";
10
11
  export { exportRoster, newRecruitJsonSerializer, newRecruitSimpleSerializer, newRecruitWtcCompactSerializer, newRecruitWtcFullSerializer, rosterJsonSerializer, rosterizerSerializer, } from "./export/index.js";
11
12
  export type { ExportFormat, RosterSerializer } from "./export/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gBAAgB,CAAC;AAI/B,cAAc,sBAAsB,CAAC;AAKrC,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,UAAU,EACV,QAAQ,EACR,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAK5B,cAAc,oBAAoB,CAAC;AAUnC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,MAAM,EACN,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAC;AAGhC,cAAc,gBAAgB,CAAC;AAI/B,cAAc,sBAAsB,CAAC;AAKrC,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAKnD,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,UAAU,EACV,QAAQ,EACR,mBAAmB,EACnB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,oBAAoB,CAAC;AAK5B,cAAc,oBAAoB,CAAC;AAKnC,cAAc,qBAAqB,CAAC;AAUpC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACxE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,MAAM,EACN,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,sBAAsB,EACtB,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAO,EACP,WAAW,EACX,YAAY,EACZ,UAAU,EACV,aAAa,GACd,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -13,6 +13,10 @@ export { resolveLayout, polygonCentroid, footprintVertices, orientedOffsets, Ter
13
13
  // track per-round, per-player scoring. Mirrored by the Rust `wh40kdc::scoring`
14
14
  // module and pinned by the `conformance/scoring` corpus.
15
15
  export * from "./scoring/index.js";
16
+ // Damage-projection engine (expected-value math over weapon/unit profiles).
17
+ // Mirrored by the Rust `wh40kdc::cruncher` module and pinned by the
18
+ // `conformance/cruncher` corpus. Pure functions — universal (Node + browser).
19
+ export * from "./cruncher/index.js";
16
20
  // Schema access + AJV validation lives behind the `./validate` subpath export
17
21
  // (`@alpaca-software/40kdc-data/validate`), NOT the root barrel: it reads
18
22
  // schema files from disk at module load (node:fs/node:url), which breaks
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,cAAc,iBAAiB,CAAC;AAEhC,mDAAmD;AACnD,cAAc,gBAAgB,CAAC;AAE/B,0EAA0E;AAC1E,6EAA6E;AAC7E,cAAc,sBAAsB,CAAC;AAWrC,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAiB5B,6EAA6E;AAC7E,+EAA+E;AAC/E,yDAAyD;AACzD,cAAc,oBAAoB,CAAC;AAEnC,8EAA8E;AAC9E,0EAA0E;AAC1E,yEAAyE;AACzE,qEAAqE;AAErE,4EAA4E;AAC5E,+EAA+E;AAC/E,uCAAuC;AACvC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAE3B,+EAA+E;AAC/E,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC","sourcesContent":["// The linked, typed dataset — the primary entry point.\nexport * from \"./data/index.js\";\n\n// Generated types for every entity in the dataset.\nexport * from \"./generated.js\";\n\n// Plain-English translation of structured data (scoring-card awards + the\n// shared Ability-DSL condition humanizer). Cross-impl pinned by conformance.\nexport * from \"./translate/index.js\";\n\n// `ScoringTrigger` is emitted by both ./generated.js (schema-derived) and\n// ./translate (hand-authored). They are structurally identical; disambiguate the\n// two wildcard re-exports in favour of the generated, schema-canonical type.\nexport type { ScoringTrigger } from \"./generated.js\";\n\n// `AbilityScope` likewise: ./translate/effect.ts hand-authors a looser view\n// for the describer; prefer the generated, schema-canonical type.\nexport type { AbilityScope } from \"./generated.js\";\n\n// Terrain geometry resolver (template-anchored layout → board-space vertices).\n// Curated (not wildcard) so its internal type aliases don't clash with the\n// generated Footprint/TerrainTemplate/TerrainLayout types. Cross-impl pinned.\nexport {\n resolveLayout,\n polygonCentroid,\n footprintVertices,\n orientedOffsets,\n TerrainResolveError,\n solveCentroid,\n solveCentroidTriangulated,\n solveCentroidAttached,\n TerrainSolveError,\n keystoneMeasurements,\n BOARD_INCHES,\n TerrainKeystoneError,\n} from \"./terrain/index.js\";\nexport type {\n ResolvedPiece,\n ResolvedVec2,\n BoardEdge,\n FeatureRef,\n Keystone,\n KeystoneMeasurement,\n DimensionLine,\n SolveInput,\n TriangulationLine,\n TriangulateInput,\n AttachLine,\n AttachPiece,\n AttachInput,\n} from \"./terrain/index.js\";\n\n// Card-driven secondary-mission scoring: compute VP from asserted awards and\n// track per-round, per-player scoring. Mirrored by the Rust `wh40kdc::scoring`\n// module and pinned by the `conformance/scoring` corpus.\nexport * from \"./scoring/index.js\";\n\n// Schema access + AJV validation lives behind the `./validate` subpath export\n// (`@alpaca-software/40kdc-data/validate`), NOT the root barrel: it reads\n// schema files from disk at module load (node:fs/node:url), which breaks\n// browser bundles. The root barrel stays universal (Node + browser).\n\n// Army-list importer (ListForge → resolved 40kdc roster). Types are curated\n// rather than re-exported wholesale to avoid name clashes with generated types\n// (e.g. BattleSize, LeaderAttachment).\nexport {\n importListForge,\n importNewRecruit,\n importRoster,\n tryImportRoster,\n decodeListForge,\n} from \"./import/index.js\";\n\n// Army-list exporter (Roster → text or JSON for any of the supported formats).\nexport {\n exportRoster,\n newRecruitJsonSerializer,\n newRecruitSimpleSerializer,\n newRecruitWtcCompactSerializer,\n newRecruitWtcFullSerializer,\n rosterJsonSerializer,\n rosterizerSerializer,\n} from \"./export/index.js\";\nexport type { ExportFormat, RosterSerializer } from \"./export/index.js\";\nexport type { FormatAdapter } from \"./import/index.js\";\nexport type {\n ImportOptions,\n ImportResult,\n ImportFailureReason,\n AdapterTrial,\n Roster,\n RosterUnit,\n RosterWargear,\n RosterSource,\n RosterFormat,\n RosterPoints,\n RosterLeaderAttachment,\n ResolvedRef,\n Candidate,\n Diagnostics,\n Warning,\n WarningCode,\n ParsedRoster,\n ParsedUnit,\n ParsedWargear,\n} from \"./import/index.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,cAAc,iBAAiB,CAAC;AAEhC,mDAAmD;AACnD,cAAc,gBAAgB,CAAC;AAE/B,0EAA0E;AAC1E,6EAA6E;AAC7E,cAAc,sBAAsB,CAAC;AAWrC,+EAA+E;AAC/E,2EAA2E;AAC3E,8EAA8E;AAC9E,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAiB5B,6EAA6E;AAC7E,+EAA+E;AAC/E,yDAAyD;AACzD,cAAc,oBAAoB,CAAC;AAEnC,4EAA4E;AAC5E,oEAAoE;AACpE,8EAA8E;AAC9E,cAAc,qBAAqB,CAAC;AAEpC,8EAA8E;AAC9E,0EAA0E;AAC1E,yEAAyE;AACzE,qEAAqE;AAErE,4EAA4E;AAC5E,+EAA+E;AAC/E,uCAAuC;AACvC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAE3B,+EAA+E;AAC/E,OAAO,EACL,YAAY,EACZ,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC","sourcesContent":["// The linked, typed dataset — the primary entry point.\nexport * from \"./data/index.js\";\n\n// Generated types for every entity in the dataset.\nexport * from \"./generated.js\";\n\n// Plain-English translation of structured data (scoring-card awards + the\n// shared Ability-DSL condition humanizer). Cross-impl pinned by conformance.\nexport * from \"./translate/index.js\";\n\n// `ScoringTrigger` is emitted by both ./generated.js (schema-derived) and\n// ./translate (hand-authored). They are structurally identical; disambiguate the\n// two wildcard re-exports in favour of the generated, schema-canonical type.\nexport type { ScoringTrigger } from \"./generated.js\";\n\n// `AbilityScope` likewise: ./translate/effect.ts hand-authors a looser view\n// for the describer; prefer the generated, schema-canonical type.\nexport type { AbilityScope } from \"./generated.js\";\n\n// Terrain geometry resolver (template-anchored layout → board-space vertices).\n// Curated (not wildcard) so its internal type aliases don't clash with the\n// generated Footprint/TerrainTemplate/TerrainLayout types. Cross-impl pinned.\nexport {\n resolveLayout,\n polygonCentroid,\n footprintVertices,\n orientedOffsets,\n TerrainResolveError,\n solveCentroid,\n solveCentroidTriangulated,\n solveCentroidAttached,\n TerrainSolveError,\n keystoneMeasurements,\n BOARD_INCHES,\n TerrainKeystoneError,\n} from \"./terrain/index.js\";\nexport type {\n ResolvedPiece,\n ResolvedVec2,\n BoardEdge,\n FeatureRef,\n Keystone,\n KeystoneMeasurement,\n DimensionLine,\n SolveInput,\n TriangulationLine,\n TriangulateInput,\n AttachLine,\n AttachPiece,\n AttachInput,\n} from \"./terrain/index.js\";\n\n// Card-driven secondary-mission scoring: compute VP from asserted awards and\n// track per-round, per-player scoring. Mirrored by the Rust `wh40kdc::scoring`\n// module and pinned by the `conformance/scoring` corpus.\nexport * from \"./scoring/index.js\";\n\n// Damage-projection engine (expected-value math over weapon/unit profiles).\n// Mirrored by the Rust `wh40kdc::cruncher` module and pinned by the\n// `conformance/cruncher` corpus. Pure functions — universal (Node + browser).\nexport * from \"./cruncher/index.js\";\n\n// Schema access + AJV validation lives behind the `./validate` subpath export\n// (`@alpaca-software/40kdc-data/validate`), NOT the root barrel: it reads\n// schema files from disk at module load (node:fs/node:url), which breaks\n// browser bundles. The root barrel stays universal (Node + browser).\n\n// Army-list importer (ListForge → resolved 40kdc roster). Types are curated\n// rather than re-exported wholesale to avoid name clashes with generated types\n// (e.g. BattleSize, LeaderAttachment).\nexport {\n importListForge,\n importNewRecruit,\n importRoster,\n tryImportRoster,\n decodeListForge,\n} from \"./import/index.js\";\n\n// Army-list exporter (Roster → text or JSON for any of the supported formats).\nexport {\n exportRoster,\n newRecruitJsonSerializer,\n newRecruitSimpleSerializer,\n newRecruitWtcCompactSerializer,\n newRecruitWtcFullSerializer,\n rosterJsonSerializer,\n rosterizerSerializer,\n} from \"./export/index.js\";\nexport type { ExportFormat, RosterSerializer } from \"./export/index.js\";\nexport type { FormatAdapter } from \"./import/index.js\";\nexport type {\n ImportOptions,\n ImportResult,\n ImportFailureReason,\n AdapterTrial,\n Roster,\n RosterUnit,\n RosterWargear,\n RosterSource,\n RosterFormat,\n RosterPoints,\n RosterLeaderAttachment,\n ResolvedRef,\n Candidate,\n Diagnostics,\n Warning,\n WarningCode,\n ParsedRoster,\n ParsedUnit,\n ParsedWargear,\n} from \"./import/index.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpaca-software/40kdc-data",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "type": "module",
5
5
  "description": "The 40kdc Warhammer 40K dataset behind a linked, typed API — find units, follow them to their weapons, abilities, phases, and factions. Also validates data against the canonical JSON Schemas.",
6
6
  "keywords": [