@alpaca-software/40kdc-data 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/bundle-schemas.d.ts.map +1 -1
- package/dist/bundle-schemas.js +17 -0
- package/dist/bundle-schemas.js.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -1
- package/dist/codegen-data.js +1 -0
- package/dist/codegen-data.js.map +1 -1
- package/dist/commands/populate-base-sizes.d.ts +2 -0
- package/dist/commands/populate-base-sizes.d.ts.map +1 -0
- package/dist/commands/populate-base-sizes.js +158 -0
- package/dist/commands/populate-base-sizes.js.map +1 -0
- package/dist/convert-faction.d.ts +3 -1
- package/dist/convert-faction.d.ts.map +1 -1
- package/dist/convert-faction.js +49 -16
- package/dist/convert-faction.js.map +1 -1
- package/dist/converters/base-size-bridge.d.ts +122 -0
- package/dist/converters/base-size-bridge.d.ts.map +1 -0
- package/dist/converters/base-size-bridge.js +198 -0
- package/dist/converters/base-size-bridge.js.map +1 -0
- package/dist/converters/base-size-guide-extract.d.ts +11 -0
- package/dist/converters/base-size-guide-extract.d.ts.map +1 -0
- package/dist/converters/base-size-guide-extract.js +59 -0
- package/dist/converters/base-size-guide-extract.js.map +1 -0
- package/dist/converters/option-bridge.d.ts +36 -0
- package/dist/converters/option-bridge.d.ts.map +1 -0
- package/dist/converters/option-bridge.js +72 -0
- package/dist/converters/option-bridge.js.map +1 -0
- package/dist/converters/option-parser.d.ts +56 -0
- package/dist/converters/option-parser.d.ts.map +1 -0
- package/dist/converters/option-parser.js +209 -0
- package/dist/converters/option-parser.js.map +1 -0
- package/dist/converters/wargear-options.d.ts +55 -0
- package/dist/converters/wargear-options.d.ts.map +1 -0
- package/dist/converters/wargear-options.js +187 -0
- package/dist/converters/wargear-options.js.map +1 -0
- package/dist/data/bundle.generated.js +1 -1
- package/dist/data/bundle.generated.js.map +1 -1
- package/dist/data/dataset.d.ts +9 -1
- package/dist/data/dataset.d.ts.map +1 -1
- package/dist/data/dataset.js +14 -0
- package/dist/data/dataset.js.map +1 -1
- package/dist/data/entities.d.ts +3 -1
- package/dist/data/entities.d.ts.map +1 -1
- package/dist/data/entities.js +4 -0
- package/dist/data/entities.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/loadout.d.ts +60 -0
- package/dist/data/loadout.d.ts.map +1 -0
- package/dist/data/loadout.js +135 -0
- package/dist/data/loadout.js.map +1 -0
- 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/export/rosterizer.js +1 -1
- package/dist/export/rosterizer.js.map +1 -1
- package/dist/gen-conformance.js +20 -0
- package/dist/gen-conformance.js.map +1 -1
- package/dist/generated.d.ts +112 -55
- package/dist/generated.d.ts.map +1 -1
- package/dist/generated.js.map +1 -1
- package/dist/import/rosterizer.d.ts +1 -1
- package/dist/import/rosterizer.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runner.d.ts +16 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +54 -0
- package/dist/runner.js.map +1 -1
- package/dist/translate/condition.d.ts.map +1 -1
- package/dist/translate/condition.js +4 -0
- package/dist/translate/condition.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +13 -5
- package/dist/validate.js.map +1 -1
- package/package.json +4 -4
- package/schemas/$defs/common.schema.json +14 -0
- package/schemas/core/unit-composition.schema.json +5 -1
- package/schemas/core/unit.schema.json +2 -10
- package/schemas/core/wargear-option.schema.json +32 -6
- package/schemas/core/wargear.schema.json +24 -0
- package/schemas/enrichment/ability-dsl/condition.schema.json +3 -2
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * NDJSON conformance runner — the TypeScript implementation of the wire\n * protocol in `conformance/RUNNER_PROTOCOL.md`. Each line on stdin is a JSON\n * request `{op, args?}`; each line on stdout is a JSON response\n * `{ok: true, value}` or `{ok: false, error_kind, error_payload?}`.\n *\n * This file exports {@link processRequest} so vitest can drive the runner\n * in-process without spawning a child; the bottom `if (isCliMain())` block\n * wires the same dispatcher to real stdin/stdout for the cross-impl differ.\n *\n * The runner is a *thin* wrapper over the existing public API — it is not the\n * canonical way to use the package. Library consumers should import the\n * functions directly; the runner exists to give the cross-implementation\n * differ a uniform interface across language ports.\n */\nimport { createInterface } from \"node:readline\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\n\nimport { Dataset } from \"./data/dataset.js\";\nimport { normalizeName } from \"./data/normalize.js\";\nimport { exportRoster, type ExportFormat } from \"./export/index.js\";\nimport { importRoster, tryImportRoster, REGISTERED_ADAPTERS } from \"./import/import-roster.js\";\nimport { selectAdapter } from \"./import/adapter.js\";\nimport { createValidator } from \"./schema-loader.js\";\nimport { attributeStages, crunch, type Buff, type EngineContext, type EngineInput } from \"./cruncher/index.js\";\nimport { describeScoringCard } from \"./translate/index.js\";\nimport { resolveLayout, TerrainResolveError } from \"./terrain/index.js\";\nimport type { TerrainTemplate, TerrainLayout } from \"./terrain/resolve.js\";\nimport type Ajv from \"ajv\";\n\n// -----------------------------------------------------------------------------\n// Constants — spec version and implementation identity.\n// -----------------------------------------------------------------------------\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadSpecVersion(): number {\n // The corpus lives at repo-root/conformance/. Walk up from tools/src/ until\n // we find it so the runner works both from source (tsx) and from dist/.\n for (const candidate of [\n join(__dirname, \"../../conformance/SPEC_VERSION\"),\n join(__dirname, \"../../../conformance/SPEC_VERSION\"),\n ]) {\n try {\n return Number.parseInt(readFileSync(candidate, \"utf8\").trim(), 10);\n } catch {\n // try next\n }\n }\n throw new Error(\"could not locate conformance/SPEC_VERSION\");\n}\n\nfunction loadImplVersion(): string {\n for (const candidate of [\n join(__dirname, \"../package.json\"),\n join(__dirname, \"../../package.json\"),\n ]) {\n try {\n return (JSON.parse(readFileSync(candidate, \"utf8\")) as { version: string }).version;\n } catch {\n // try next\n }\n }\n return \"unknown\";\n}\n\nconst SPEC_VERSION = loadSpecVersion();\nconst IMPL_VERSION = loadImplVersion();\nconst IMPL_NAME = \"ts\";\n\n// -----------------------------------------------------------------------------\n// Error envelope helpers and the closed `error_kind` enum.\n// -----------------------------------------------------------------------------\n\nexport type RunnerResponse =\n | { ok: true; value: unknown }\n | { ok: false; error_kind: ErrorKind; error_payload?: unknown };\n\nconst ERROR_KINDS = [\n \"INVALID_INPUT\",\n \"UNKNOWN_OP\",\n \"UNKNOWN_ENTITY\",\n \"IMPORT_FAILED\",\n \"EXPORT_FAILED\",\n \"VALIDATION_ERROR\",\n \"CRUNCH_ERROR\",\n \"INTERNAL_ERROR\",\n] as const;\ntype ErrorKind = (typeof ERROR_KINDS)[number];\n\nfunction ok(value: unknown): RunnerResponse {\n return { ok: true, value };\n}\nfunction err(kind: ErrorKind, payload?: unknown): RunnerResponse {\n return payload === undefined\n ? { ok: false, error_kind: kind }\n : { ok: false, error_kind: kind, error_payload: payload };\n}\n\n// -----------------------------------------------------------------------------\n// Runner state — init must be the first request; subsequent ops fail with\n// INVALID_INPUT until init succeeds. Dataset and validator are lazy.\n// -----------------------------------------------------------------------------\n\nexport interface RunnerState {\n initialized: boolean;\n locale: string;\n tz: string;\n seed: number;\n dataset?: Dataset;\n validator?: Ajv;\n}\n\nexport function createRunnerState(): RunnerState {\n return { initialized: false, locale: \"C\", tz: \"UTC\", seed: 0 };\n}\n\nfunction getDataset(state: RunnerState): Dataset {\n if (!state.dataset) state.dataset = Dataset.embedded();\n return state.dataset;\n}\n\nfunction getValidator(state: RunnerState): Ajv {\n if (!state.validator) state.validator = createValidator();\n return state.validator;\n}\n\n// -----------------------------------------------------------------------------\n// Op handlers.\n// -----------------------------------------------------------------------------\n\nfunction handleInit(state: RunnerState, args: unknown): RunnerResponse {\n if (state.initialized) {\n return err(\"INVALID_INPUT\", { detail: \"init called twice\" });\n }\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"init args must be an object\" });\n }\n const a = args as { spec_version?: unknown; locale?: unknown; tz?: unknown; seed?: unknown };\n if (a.spec_version !== SPEC_VERSION) {\n return err(\"INVALID_INPUT\", {\n detail: `spec_version mismatch: runner=${SPEC_VERSION}, request=${String(a.spec_version)}`,\n });\n }\n if (a.locale !== \"C\") {\n return err(\"INVALID_INPUT\", { detail: `unsupported locale: ${String(a.locale)} (only \"C\")` });\n }\n if (a.tz !== \"UTC\") {\n return err(\"INVALID_INPUT\", { detail: `unsupported tz: ${String(a.tz)} (only \"UTC\")` });\n }\n if (typeof a.seed !== \"number\") {\n return err(\"INVALID_INPUT\", { detail: \"seed must be a number\" });\n }\n state.initialized = true;\n state.locale = a.locale;\n state.tz = a.tz;\n state.seed = a.seed;\n return ok({ impl: IMPL_NAME, spec_version: SPEC_VERSION, impl_version: IMPL_VERSION });\n}\n\nfunction handleNormalize(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"normalize args must be an object\" });\n }\n const a = args as { input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"normalize.input must be a string\" });\n }\n return ok(normalizeName(a.input));\n}\n\nfunction handleImport(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"import args must be an object\" });\n }\n const a = args as { format?: unknown; input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"import.input must be a string\" });\n }\n try {\n // The wire protocol carries every input as a string: JSON payloads come\n // through as the JSON text, text payloads come through as-is. The import\n // pipeline decides which by attempting to parse JSON first.\n const trimmed = a.input.trimStart();\n let decoded: unknown;\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n decoded = JSON.parse(a.input);\n } catch {\n decoded = a.input;\n }\n } else {\n decoded = a.input;\n }\n const roster = importRoster(decoded, { dataset: getDataset(state) });\n return ok(roster);\n } catch (e) {\n return err(\"IMPORT_FAILED\", { detail: (e as Error).message, format: a.format ?? null });\n }\n}\n\nfunction handleTryImport(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"try_import args must be an object\" });\n }\n const a = args as { input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"try_import.input must be a string\" });\n }\n const result = tryImportRoster(a.input, { dataset: getDataset(state) });\n if (!result.ok) {\n return err(\"IMPORT_FAILED\", { reason: result.reason, message: result.message });\n }\n return ok({ format: result.format, roster: result.roster });\n}\n\nconst EXPORT_FORMATS: ExportFormat[] = [\n \"newrecruit-json\",\n \"newrecruit-wtc-compact\",\n \"newrecruit-wtc-full\",\n \"newrecruit-simple\",\n \"roster-json\",\n \"rosterizer\",\n];\n\nfunction handleExport(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"export args must be an object\" });\n }\n const a = args as { format?: unknown; roster?: unknown };\n if (typeof a.format !== \"string\" || !EXPORT_FORMATS.includes(a.format as ExportFormat)) {\n return err(\"INVALID_INPUT\", { detail: `unknown export format: ${String(a.format)}` });\n }\n if (typeof a.roster !== \"object\" || a.roster === null) {\n return err(\"INVALID_INPUT\", { detail: \"export.roster must be an object\" });\n }\n try {\n // We assume the caller passes the canonical resolved Roster shape; if they\n // pass something the serializer can't handle, this throws.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(exportRoster(a.roster as any, a.format as ExportFormat));\n } catch (e) {\n return err(\"EXPORT_FAILED\", { detail: (e as Error).message });\n }\n}\n\nfunction handleLinkedQuery(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"linked_query args must be an object\" });\n }\n const a = args as { query?: unknown; input?: unknown };\n if (typeof a.query !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"linked_query.query must be a string\" });\n }\n const ds = getDataset(state);\n const input = (a.input ?? {}) as Record<string, string>;\n try {\n switch (a.query) {\n case \"find_unit\":\n return ok(ds.units.find(input.query ?? \"\")?.id ?? null);\n case \"find_weapon\":\n return ok(ds.weapons.find(input.query ?? \"\")?.id ?? null);\n case \"find_faction\":\n return ok(ds.factions.find(input.query ?? \"\")?.id ?? null);\n case \"find_ability\":\n return ok(ds.abilities.find(input.query ?? \"\")?.id ?? null);\n case \"abilities_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.abilities.map((x) => x.id));\n }\n case \"weapons_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.weapons.map((x) => x.id));\n }\n case \"phases_of\": {\n const ab = ds.abilities.get(input.abilityId);\n if (!ab) return err(\"UNKNOWN_ENTITY\", { kind: \"ability\", id: input.abilityId });\n return ok([...ab.phases]);\n }\n case \"faction_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.faction?.id ?? null);\n }\n case \"abilities_of_faction\":\n return ok(ds.abilities.byFaction(input.factionId).map((x) => x.id));\n case \"weapons_of_faction\": {\n const f = ds.factions.get(input.factionId);\n if (!f) return err(\"UNKNOWN_ENTITY\", { kind: \"faction\", id: input.factionId });\n return ok(f.weapons.map((x) => x.id));\n }\n default:\n return err(\"INVALID_INPUT\", { detail: `unknown linked_query: ${a.query}` });\n }\n } catch (e) {\n return err(\"INTERNAL_ERROR\", { detail: (e as Error).message });\n }\n}\n\nconst VALIDATOR_TARGETS: Record<string, string> = {\n unit: \"https://40kdc.dev/schemas/core/unit.schema.json\",\n weapon: \"https://40kdc.dev/schemas/core/weapon.schema.json\",\n faction: \"https://40kdc.dev/schemas/core/faction.schema.json\",\n ability: \"https://40kdc.dev/schemas/enrichment/ability-dsl/ability.schema.json\",\n};\n\nfunction ajvKeywordToCode(keyword: string): string {\n switch (keyword) {\n case \"required\":\n return \"REQUIRED_MISSING\";\n case \"type\":\n return \"TYPE_MISMATCH\";\n case \"enum\":\n return \"ENUM_VIOLATION\";\n case \"pattern\":\n case \"format\":\n return \"PATTERN_MISMATCH\";\n case \"minimum\":\n case \"maximum\":\n case \"exclusiveMinimum\":\n case \"exclusiveMaximum\":\n case \"minLength\":\n case \"maxLength\":\n case \"minItems\":\n case \"maxItems\":\n return \"RANGE_VIOLATION\";\n case \"additionalProperties\":\n return \"ADDITIONAL_PROPERTY\";\n case \"uniqueItems\":\n return \"UNIQUE_VIOLATION\";\n default:\n return `UNMAPPED:${keyword}`;\n }\n}\n\nfunction handleValidate(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"validate args must be an object\" });\n }\n const a = args as { target?: unknown; value?: unknown };\n if (typeof a.target !== \"string\" || !(a.target in VALIDATOR_TARGETS)) {\n return err(\"INVALID_INPUT\", { detail: `unknown validator target: ${String(a.target)}` });\n }\n let validate;\n try {\n validate = getValidator(state).getSchema(VALIDATOR_TARGETS[a.target]);\n } catch (e) {\n return err(\"VALIDATION_ERROR\", { detail: (e as Error).message });\n }\n if (!validate) {\n return err(\"VALIDATION_ERROR\", { detail: `schema not loaded: ${a.target}` });\n }\n validate(a.value);\n const raw = validate.errors ?? [];\n const seen = new Set<string>();\n const out: { path: string; code: string }[] = [];\n for (const e of raw) {\n const code = ajvKeywordToCode(e.keyword);\n if (code.startsWith(\"UNMAPPED:\")) continue;\n const path =\n e.keyword === \"required\"\n ? `${e.instancePath}/${(e.params as { missingProperty?: string }).missingProperty ?? \"\"}`\n : e.instancePath;\n const key = `${path}|${code}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ path, code });\n }\n return ok(out);\n}\n\ninterface CrunchArgs {\n attacker?: { weaponId?: string; profileIndex?: number };\n modelsFiring?: number;\n target?: { unitId?: string; profileIndex?: number; modelCount?: number };\n context?: EngineContext;\n buffs?: Buff[];\n}\n\n/**\n * Validate the wire-shape `crunch`/`attribution` args and assemble the\n * {@link EngineInput} both ops share, or return a typed runner error. The two\n * ops have identical inputs — separating this lets each handler stay tiny and\n * keeps the validation contract in one place.\n */\nfunction buildEngineInput(\n state: RunnerState,\n a: CrunchArgs,\n opName: string,\n): { ok: true; input: EngineInput } | { ok: false; response: RunnerResponse } {\n if (!a.attacker?.weaponId || typeof a.attacker.profileIndex !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.attacker.weaponId/profileIndex required` }) };\n }\n if (!a.target?.unitId || typeof a.target.profileIndex !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.target.unitId/profileIndex required` }) };\n }\n if (typeof a.modelsFiring !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.modelsFiring required` }) };\n }\n if (!a.context) {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.context required` }) };\n }\n const ds = getDataset(state);\n const weapon = ds.weapons.get(a.attacker.weaponId);\n if (!weapon) return { ok: false, response: err(\"UNKNOWN_ENTITY\", { kind: \"weapon\", id: a.attacker.weaponId }) };\n const unit = ds.units.get(a.target.unitId);\n if (!unit) return { ok: false, response: err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: a.target.unitId }) };\n const input: EngineInput = {\n attacker: { weapon: weapon.raw, profileIndex: a.attacker.profileIndex },\n target: {\n unit: unit.raw,\n profileIndex: a.target.profileIndex,\n ...(a.target.modelCount !== undefined ? { modelCount: a.target.modelCount } : {}),\n },\n modelsFiring: a.modelsFiring,\n buffs: a.buffs ?? [],\n context: a.context,\n };\n return { ok: true, input };\n}\n\nfunction handleCrunch(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"crunch args must be an object\" });\n }\n const built = buildEngineInput(state, args as CrunchArgs, \"crunch\");\n if (!built.ok) return built.response;\n try {\n // Canonical wire shape: stages array only. `resolved` is impl-internal\n // (TS/Rust shape diverges); per-stage `detail` strings aren't byte-equal\n // across impls. The differ compares per-stage `expected` with 5e-4\n // tolerance — that's the cross-impl contract.\n const out = crunch(built.input, getDataset(state));\n return ok({ stages: out.stages.map((s) => ({ name: s.name, expected: s.expected })) });\n } catch (e) {\n return err(\"CRUNCH_ERROR\", { detail: (e as Error).message });\n }\n}\n\nfunction handleAttribution(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"attribution args must be an object\" });\n }\n const a = args as CrunchArgs & { epsilon?: number };\n const built = buildEngineInput(state, a, \"attribution\");\n if (!built.ok) return built.response;\n try {\n const opts = typeof a.epsilon === \"number\" ? { epsilon: a.epsilon } : undefined;\n // Drop `detail` from the wire (impl-specific formatting). Keep all\n // numeric fields and the kind-tagged BuffSource shape that's already\n // serde-compatible.\n const stages = attributeStages(built.input, getDataset(state), opts);\n return ok(\n stages.map((s) => ({\n name: s.name,\n expected: s.expected,\n baseline: s.baseline,\n lifts: s.lifts,\n residual: s.residual,\n intrinsics: s.intrinsics,\n })),\n );\n } catch (e) {\n return err(\"CRUNCH_ERROR\", { detail: (e as Error).message });\n }\n}\n\nfunction handleTranslateScoring(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"translate_scoring args must be an object\" });\n }\n const a = args as { cardId?: unknown };\n if (typeof a.cardId !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"translate_scoring.cardId must be a string\" });\n }\n const card = getDataset(state).missionCards.get(a.cardId);\n if (!card) return err(\"UNKNOWN_ENTITY\", { kind: \"secondary-card\", id: a.cardId });\n return ok({ awards: describeScoringCard(card) });\n}\n\nfunction handleResolveTerrain(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain args must be an object\" });\n }\n const a = args as { layout?: unknown; templates?: unknown };\n if (typeof a.layout !== \"object\" || a.layout === null) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain.layout must be an object\" });\n }\n const templates = a.templates ?? [];\n if (!Array.isArray(templates)) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain.templates must be an array\" });\n }\n try {\n const pieces = resolveLayout(a.layout as TerrainLayout, templates as TerrainTemplate[]);\n return ok({ pieces });\n } catch (e) {\n if (e instanceof TerrainResolveError) {\n return err(\"INVALID_INPUT\", { detail: e.message });\n }\n return err(\"INTERNAL_ERROR\", { detail: (e as Error).message });\n }\n}\n\n// -----------------------------------------------------------------------------\n// Dispatcher and per-line entry point.\n// -----------------------------------------------------------------------------\n\n/**\n * Apply one decoded request to the runner state and return the response. Used\n * directly by tests; the CLI loop wraps it with line parsing.\n */\nexport function dispatch(state: RunnerState, req: { op: string; args?: unknown }): RunnerResponse {\n if (!state.initialized && req.op !== \"init\") {\n return err(\"INVALID_INPUT\", { detail: \"must init before any other op\" });\n }\n switch (req.op) {\n case \"init\":\n return handleInit(state, req.args);\n case \"version\":\n return ok({ impl: IMPL_NAME, spec_version: SPEC_VERSION, impl_version: IMPL_VERSION });\n case \"normalize\":\n return handleNormalize(req.args);\n case \"import\":\n return handleImport(state, req.args);\n case \"try_import\":\n return handleTryImport(state, req.args);\n case \"export\":\n return handleExport(req.args);\n case \"linked_query\":\n return handleLinkedQuery(state, req.args);\n case \"validate\":\n return handleValidate(state, req.args);\n case \"crunch\":\n return handleCrunch(state, req.args);\n case \"attribution\":\n return handleAttribution(state, req.args);\n case \"translate_scoring\":\n return handleTranslateScoring(state, req.args);\n case \"resolve_terrain\":\n return handleResolveTerrain(req.args);\n case \"shutdown\":\n return ok(null);\n default:\n return err(\"UNKNOWN_OP\", { op: req.op });\n }\n}\n\n/**\n * Process one line of stdin (one NDJSON request) and return the line that\n * should be written to stdout (one NDJSON response). Returns `null` only on\n * fully-empty input lines, which should be silently ignored.\n */\nexport function processRequest(state: RunnerState, line: string): string | null {\n const trimmed = line.trim();\n if (trimmed === \"\") return null;\n let req: { op?: unknown; args?: unknown };\n try {\n req = JSON.parse(trimmed);\n } catch (e) {\n return JSON.stringify(err(\"INVALID_INPUT\", { detail: `not valid JSON: ${(e as Error).message}` }));\n }\n if (typeof req.op !== \"string\") {\n return JSON.stringify(err(\"INVALID_INPUT\", { detail: \"request must have a string `op` field\" }));\n }\n const response = dispatch(state, { op: req.op, args: req.args });\n return JSON.stringify(response);\n}\n\n// -----------------------------------------------------------------------------\n// CLI: wire stdin/stdout. Only runs when this file is the process entry point.\n// -----------------------------------------------------------------------------\n\nfunction isCliMain(): boolean {\n // process.argv[1] is the path the runtime invoked. With tsx and node both,\n // it points at this file (or its compiled .js form). Comparing absolute\n // paths is the most robust portable check.\n if (!process.argv[1]) return false;\n const entry = process.argv[1];\n const here = fileURLToPath(import.meta.url);\n return entry === here || entry.endsWith(\"/runner.js\") || entry.endsWith(\"/runner.ts\");\n}\n\nasync function runCli(): Promise<void> {\n const state = createRunnerState();\n const rl = createInterface({ input: process.stdin });\n // We must respond on the same tick the request arrives — the differ\n // pipelines requests and expects responses in order. readline preserves\n // line ordering, so processing inside `line` handler is sufficient.\n for await (const line of rl) {\n const out = processRequest(state, line);\n if (out !== null) {\n process.stdout.write(out + \"\\n\");\n }\n // `shutdown` returns `ok(null)`; honor it by exiting after the response\n // is flushed.\n try {\n const req = JSON.parse(line);\n if (req && req.op === \"shutdown\") {\n process.exit(0);\n }\n } catch {\n // already handled above; not a shutdown\n }\n }\n}\n\nif (isCliMain()) {\n runCli().catch((e) => {\n // Last-ditch INTERNAL_ERROR so the differ sees a typed failure rather\n // than an opaque crash.\n process.stdout.write(\n JSON.stringify({\n ok: false,\n error_kind: \"INTERNAL_ERROR\",\n error_payload: { detail: (e as Error).message },\n }) + \"\\n\",\n );\n process.exit(1);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n/**\n * NDJSON conformance runner — the TypeScript implementation of the wire\n * protocol in `conformance/RUNNER_PROTOCOL.md`. Each line on stdin is a JSON\n * request `{op, args?}`; each line on stdout is a JSON response\n * `{ok: true, value}` or `{ok: false, error_kind, error_payload?}`.\n *\n * This file exports {@link processRequest} so vitest can drive the runner\n * in-process without spawning a child; the bottom `if (isCliMain())` block\n * wires the same dispatcher to real stdin/stdout for the cross-impl differ.\n *\n * The runner is a *thin* wrapper over the existing public API — it is not the\n * canonical way to use the package. Library consumers should import the\n * functions directly; the runner exists to give the cross-implementation\n * differ a uniform interface across language ports.\n */\nimport { createInterface } from \"node:readline\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport process from \"node:process\";\n\nimport { Dataset } from \"./data/dataset.js\";\nimport { normalizeName } from \"./data/normalize.js\";\nimport { maximalLoadout } from \"./data/loadout.js\";\nimport { exportRoster, type ExportFormat } from \"./export/index.js\";\nimport { importRoster, tryImportRoster, REGISTERED_ADAPTERS } from \"./import/import-roster.js\";\nimport { selectAdapter } from \"./import/adapter.js\";\nimport { createValidator } from \"./schema-loader.js\";\nimport { attributeStages, crunch, type Buff, type EngineContext, type EngineInput } from \"./cruncher/index.js\";\nimport { describeScoringCard } from \"./translate/index.js\";\nimport { resolveLayout, TerrainResolveError } from \"./terrain/index.js\";\nimport type { TerrainTemplate, TerrainLayout } from \"./terrain/resolve.js\";\nimport type Ajv from \"ajv\";\n\n// -----------------------------------------------------------------------------\n// Constants — spec version and implementation identity.\n// -----------------------------------------------------------------------------\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadSpecVersion(): number {\n // The corpus lives at repo-root/conformance/. Walk up from tools/src/ until\n // we find it so the runner works both from source (tsx) and from dist/.\n for (const candidate of [\n join(__dirname, \"../../conformance/SPEC_VERSION\"),\n join(__dirname, \"../../../conformance/SPEC_VERSION\"),\n ]) {\n try {\n return Number.parseInt(readFileSync(candidate, \"utf8\").trim(), 10);\n } catch {\n // try next\n }\n }\n throw new Error(\"could not locate conformance/SPEC_VERSION\");\n}\n\nfunction loadImplVersion(): string {\n for (const candidate of [\n join(__dirname, \"../package.json\"),\n join(__dirname, \"../../package.json\"),\n ]) {\n try {\n return (JSON.parse(readFileSync(candidate, \"utf8\")) as { version: string }).version;\n } catch {\n // try next\n }\n }\n return \"unknown\";\n}\n\nconst SPEC_VERSION = loadSpecVersion();\nconst IMPL_VERSION = loadImplVersion();\nconst IMPL_NAME = \"ts\";\n\n// -----------------------------------------------------------------------------\n// Error envelope helpers and the closed `error_kind` enum.\n// -----------------------------------------------------------------------------\n\nexport type RunnerResponse =\n | { ok: true; value: unknown }\n | { ok: false; error_kind: ErrorKind; error_payload?: unknown };\n\nconst ERROR_KINDS = [\n \"INVALID_INPUT\",\n \"UNKNOWN_OP\",\n \"UNKNOWN_ENTITY\",\n \"IMPORT_FAILED\",\n \"EXPORT_FAILED\",\n \"VALIDATION_ERROR\",\n \"CRUNCH_ERROR\",\n \"INTERNAL_ERROR\",\n] as const;\ntype ErrorKind = (typeof ERROR_KINDS)[number];\n\nfunction ok(value: unknown): RunnerResponse {\n return { ok: true, value };\n}\nfunction err(kind: ErrorKind, payload?: unknown): RunnerResponse {\n return payload === undefined\n ? { ok: false, error_kind: kind }\n : { ok: false, error_kind: kind, error_payload: payload };\n}\n\n// -----------------------------------------------------------------------------\n// Runner state — init must be the first request; subsequent ops fail with\n// INVALID_INPUT until init succeeds. Dataset and validator are lazy.\n// -----------------------------------------------------------------------------\n\nexport interface RunnerState {\n initialized: boolean;\n locale: string;\n tz: string;\n seed: number;\n dataset?: Dataset;\n validator?: Ajv;\n}\n\nexport function createRunnerState(): RunnerState {\n return { initialized: false, locale: \"C\", tz: \"UTC\", seed: 0 };\n}\n\nfunction getDataset(state: RunnerState): Dataset {\n if (!state.dataset) state.dataset = Dataset.embedded();\n return state.dataset;\n}\n\nfunction getValidator(state: RunnerState): Ajv {\n if (!state.validator) state.validator = createValidator();\n return state.validator;\n}\n\n// -----------------------------------------------------------------------------\n// Op handlers.\n// -----------------------------------------------------------------------------\n\nfunction handleInit(state: RunnerState, args: unknown): RunnerResponse {\n if (state.initialized) {\n return err(\"INVALID_INPUT\", { detail: \"init called twice\" });\n }\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"init args must be an object\" });\n }\n const a = args as { spec_version?: unknown; locale?: unknown; tz?: unknown; seed?: unknown };\n if (a.spec_version !== SPEC_VERSION) {\n return err(\"INVALID_INPUT\", {\n detail: `spec_version mismatch: runner=${SPEC_VERSION}, request=${String(a.spec_version)}`,\n });\n }\n if (a.locale !== \"C\") {\n return err(\"INVALID_INPUT\", { detail: `unsupported locale: ${String(a.locale)} (only \"C\")` });\n }\n if (a.tz !== \"UTC\") {\n return err(\"INVALID_INPUT\", { detail: `unsupported tz: ${String(a.tz)} (only \"UTC\")` });\n }\n if (typeof a.seed !== \"number\") {\n return err(\"INVALID_INPUT\", { detail: \"seed must be a number\" });\n }\n state.initialized = true;\n state.locale = a.locale;\n state.tz = a.tz;\n state.seed = a.seed;\n return ok({ impl: IMPL_NAME, spec_version: SPEC_VERSION, impl_version: IMPL_VERSION });\n}\n\nfunction handleNormalize(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"normalize args must be an object\" });\n }\n const a = args as { input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"normalize.input must be a string\" });\n }\n return ok(normalizeName(a.input));\n}\n\nfunction handleImport(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"import args must be an object\" });\n }\n const a = args as { format?: unknown; input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"import.input must be a string\" });\n }\n try {\n // The wire protocol carries every input as a string: JSON payloads come\n // through as the JSON text, text payloads come through as-is. The import\n // pipeline decides which by attempting to parse JSON first.\n const trimmed = a.input.trimStart();\n let decoded: unknown;\n if (trimmed.startsWith(\"{\") || trimmed.startsWith(\"[\")) {\n try {\n decoded = JSON.parse(a.input);\n } catch {\n decoded = a.input;\n }\n } else {\n decoded = a.input;\n }\n const roster = importRoster(decoded, { dataset: getDataset(state) });\n return ok(roster);\n } catch (e) {\n return err(\"IMPORT_FAILED\", { detail: (e as Error).message, format: a.format ?? null });\n }\n}\n\nfunction handleTryImport(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"try_import args must be an object\" });\n }\n const a = args as { input?: unknown };\n if (typeof a.input !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"try_import.input must be a string\" });\n }\n const result = tryImportRoster(a.input, { dataset: getDataset(state) });\n if (!result.ok) {\n return err(\"IMPORT_FAILED\", { reason: result.reason, message: result.message });\n }\n return ok({ format: result.format, roster: result.roster });\n}\n\nconst EXPORT_FORMATS: ExportFormat[] = [\n \"newrecruit-json\",\n \"newrecruit-wtc-compact\",\n \"newrecruit-wtc-full\",\n \"newrecruit-simple\",\n \"roster-json\",\n \"rosterizer\",\n];\n\nfunction handleExport(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"export args must be an object\" });\n }\n const a = args as { format?: unknown; roster?: unknown };\n if (typeof a.format !== \"string\" || !EXPORT_FORMATS.includes(a.format as ExportFormat)) {\n return err(\"INVALID_INPUT\", { detail: `unknown export format: ${String(a.format)}` });\n }\n if (typeof a.roster !== \"object\" || a.roster === null) {\n return err(\"INVALID_INPUT\", { detail: \"export.roster must be an object\" });\n }\n try {\n // We assume the caller passes the canonical resolved Roster shape; if they\n // pass something the serializer can't handle, this throws.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(exportRoster(a.roster as any, a.format as ExportFormat));\n } catch (e) {\n return err(\"EXPORT_FAILED\", { detail: (e as Error).message });\n }\n}\n\n/**\n * Canonical string encoding of a base size for cross-impl comparison (mirrors the\n * string-encoding used by `maximal_loadout`). Returns null for an absent base.\n * round 32 → \"round:32\"\n * oval 75×42 → \"oval:75x42\"\n * small flyer → \"flying-base:small:draft\"\n * hull (draft) → \"hull:draft\"\n */\nexport function encodeBase(\n b:\n | { shape: string; diameter?: number; width?: number; length?: number; size?: string; draft?: boolean }\n | null\n | undefined,\n): string | null {\n if (!b) return null;\n const parts: string[] = [b.shape];\n if (b.shape === \"round\" && b.diameter != null) parts.push(String(b.diameter));\n else if (b.shape === \"oval\" && b.width != null && b.length != null) parts.push(`${b.width}x${b.length}`);\n else if (b.shape === \"flying-base\" && b.size) parts.push(b.size);\n if (b.draft) parts.push(\"draft\");\n return parts.join(\":\");\n}\n\nfunction handleLinkedQuery(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"linked_query args must be an object\" });\n }\n const a = args as { query?: unknown; input?: unknown };\n if (typeof a.query !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"linked_query.query must be a string\" });\n }\n const ds = getDataset(state);\n const input = (a.input ?? {}) as Record<string, string>;\n try {\n switch (a.query) {\n case \"find_unit\":\n return ok(ds.units.find(input.query ?? \"\")?.id ?? null);\n case \"find_weapon\":\n return ok(ds.weapons.find(input.query ?? \"\")?.id ?? null);\n case \"find_faction\":\n return ok(ds.factions.find(input.query ?? \"\")?.id ?? null);\n case \"find_ability\":\n return ok(ds.abilities.find(input.query ?? \"\")?.id ?? null);\n case \"abilities_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.abilities.map((x) => x.id));\n }\n case \"weapons_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.weapons.map((x) => x.id));\n }\n case \"wargear_options_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.wargearOptions.map((x) => x.id));\n }\n case \"maximal_loadout\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n const modelCount = Number(input.modelCount);\n const lo = maximalLoadout(u.raw, modelCount, ds.wargearOptionsOf(u.raw));\n // Encode the id→count map as sorted \"id:count\" strings for set compare.\n return ok([...lo.counts].map(([id, n]) => `${id}:${n}`).sort());\n }\n case \"phases_of\": {\n const ab = ds.abilities.get(input.abilityId);\n if (!ab) return err(\"UNKNOWN_ENTITY\", { kind: \"ability\", id: input.abilityId });\n return ok([...ab.phases]);\n }\n case \"faction_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(u.faction?.id ?? null);\n }\n case \"base_size_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n return ok(encodeBase(u.raw.base_size_mm));\n }\n case \"model_bases_of\": {\n const u = ds.units.get(input.unitId);\n if (!u) return err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: input.unitId });\n const comp = ds.unitCompositions.find((c) => c.unit_id === input.unitId);\n // Ordered \"modelName=encodedBase\" pairs in declared model order.\n return ok((comp?.models ?? []).map((m) => `${m.name}=${encodeBase(m.base_size_mm) ?? \"none\"}`));\n }\n case \"abilities_of_faction\":\n return ok(ds.abilities.byFaction(input.factionId).map((x) => x.id));\n case \"weapons_of_faction\": {\n const f = ds.factions.get(input.factionId);\n if (!f) return err(\"UNKNOWN_ENTITY\", { kind: \"faction\", id: input.factionId });\n return ok(f.weapons.map((x) => x.id));\n }\n default:\n return err(\"INVALID_INPUT\", { detail: `unknown linked_query: ${a.query}` });\n }\n } catch (e) {\n return err(\"INTERNAL_ERROR\", { detail: (e as Error).message });\n }\n}\n\nconst VALIDATOR_TARGETS: Record<string, string> = {\n unit: \"https://40kdc.dev/schemas/core/unit.schema.json\",\n weapon: \"https://40kdc.dev/schemas/core/weapon.schema.json\",\n faction: \"https://40kdc.dev/schemas/core/faction.schema.json\",\n ability: \"https://40kdc.dev/schemas/enrichment/ability-dsl/ability.schema.json\",\n wargear: \"https://40kdc.dev/schemas/core/wargear.schema.json\",\n \"wargear-option\": \"https://40kdc.dev/schemas/core/wargear-option.schema.json\",\n};\n\nfunction ajvKeywordToCode(keyword: string): string {\n switch (keyword) {\n case \"required\":\n return \"REQUIRED_MISSING\";\n case \"type\":\n return \"TYPE_MISMATCH\";\n case \"enum\":\n return \"ENUM_VIOLATION\";\n case \"pattern\":\n case \"format\":\n return \"PATTERN_MISMATCH\";\n case \"minimum\":\n case \"maximum\":\n case \"exclusiveMinimum\":\n case \"exclusiveMaximum\":\n case \"minLength\":\n case \"maxLength\":\n case \"minItems\":\n case \"maxItems\":\n return \"RANGE_VIOLATION\";\n case \"additionalProperties\":\n return \"ADDITIONAL_PROPERTY\";\n case \"uniqueItems\":\n return \"UNIQUE_VIOLATION\";\n default:\n return `UNMAPPED:${keyword}`;\n }\n}\n\nfunction handleValidate(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"validate args must be an object\" });\n }\n const a = args as { target?: unknown; value?: unknown };\n if (typeof a.target !== \"string\" || !(a.target in VALIDATOR_TARGETS)) {\n return err(\"INVALID_INPUT\", { detail: `unknown validator target: ${String(a.target)}` });\n }\n let validate;\n try {\n validate = getValidator(state).getSchema(VALIDATOR_TARGETS[a.target]);\n } catch (e) {\n return err(\"VALIDATION_ERROR\", { detail: (e as Error).message });\n }\n if (!validate) {\n return err(\"VALIDATION_ERROR\", { detail: `schema not loaded: ${a.target}` });\n }\n validate(a.value);\n const raw = validate.errors ?? [];\n const seen = new Set<string>();\n const out: { path: string; code: string }[] = [];\n for (const e of raw) {\n const code = ajvKeywordToCode(e.keyword);\n if (code.startsWith(\"UNMAPPED:\")) continue;\n const path =\n e.keyword === \"required\"\n ? `${e.instancePath}/${(e.params as { missingProperty?: string }).missingProperty ?? \"\"}`\n : e.instancePath;\n const key = `${path}|${code}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push({ path, code });\n }\n return ok(out);\n}\n\ninterface CrunchArgs {\n attacker?: { weaponId?: string; profileIndex?: number };\n modelsFiring?: number;\n target?: { unitId?: string; profileIndex?: number; modelCount?: number };\n context?: EngineContext;\n buffs?: Buff[];\n}\n\n/**\n * Validate the wire-shape `crunch`/`attribution` args and assemble the\n * {@link EngineInput} both ops share, or return a typed runner error. The two\n * ops have identical inputs — separating this lets each handler stay tiny and\n * keeps the validation contract in one place.\n */\nfunction buildEngineInput(\n state: RunnerState,\n a: CrunchArgs,\n opName: string,\n): { ok: true; input: EngineInput } | { ok: false; response: RunnerResponse } {\n if (!a.attacker?.weaponId || typeof a.attacker.profileIndex !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.attacker.weaponId/profileIndex required` }) };\n }\n if (!a.target?.unitId || typeof a.target.profileIndex !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.target.unitId/profileIndex required` }) };\n }\n if (typeof a.modelsFiring !== \"number\") {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.modelsFiring required` }) };\n }\n if (!a.context) {\n return { ok: false, response: err(\"INVALID_INPUT\", { detail: `${opName}.context required` }) };\n }\n const ds = getDataset(state);\n const weapon = ds.weapons.get(a.attacker.weaponId);\n if (!weapon) return { ok: false, response: err(\"UNKNOWN_ENTITY\", { kind: \"weapon\", id: a.attacker.weaponId }) };\n const unit = ds.units.get(a.target.unitId);\n if (!unit) return { ok: false, response: err(\"UNKNOWN_ENTITY\", { kind: \"unit\", id: a.target.unitId }) };\n const input: EngineInput = {\n attacker: { weapon: weapon.raw, profileIndex: a.attacker.profileIndex },\n target: {\n unit: unit.raw,\n profileIndex: a.target.profileIndex,\n ...(a.target.modelCount !== undefined ? { modelCount: a.target.modelCount } : {}),\n },\n modelsFiring: a.modelsFiring,\n buffs: a.buffs ?? [],\n context: a.context,\n };\n return { ok: true, input };\n}\n\nfunction handleCrunch(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"crunch args must be an object\" });\n }\n const built = buildEngineInput(state, args as CrunchArgs, \"crunch\");\n if (!built.ok) return built.response;\n try {\n // Canonical wire shape: stages array only. `resolved` is impl-internal\n // (TS/Rust shape diverges); per-stage `detail` strings aren't byte-equal\n // across impls. The differ compares per-stage `expected` with 5e-4\n // tolerance — that's the cross-impl contract.\n const out = crunch(built.input, getDataset(state));\n return ok({ stages: out.stages.map((s) => ({ name: s.name, expected: s.expected })) });\n } catch (e) {\n return err(\"CRUNCH_ERROR\", { detail: (e as Error).message });\n }\n}\n\nfunction handleAttribution(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"attribution args must be an object\" });\n }\n const a = args as CrunchArgs & { epsilon?: number };\n const built = buildEngineInput(state, a, \"attribution\");\n if (!built.ok) return built.response;\n try {\n const opts = typeof a.epsilon === \"number\" ? { epsilon: a.epsilon } : undefined;\n // Drop `detail` from the wire (impl-specific formatting). Keep all\n // numeric fields and the kind-tagged BuffSource shape that's already\n // serde-compatible.\n const stages = attributeStages(built.input, getDataset(state), opts);\n return ok(\n stages.map((s) => ({\n name: s.name,\n expected: s.expected,\n baseline: s.baseline,\n lifts: s.lifts,\n residual: s.residual,\n intrinsics: s.intrinsics,\n })),\n );\n } catch (e) {\n return err(\"CRUNCH_ERROR\", { detail: (e as Error).message });\n }\n}\n\nfunction handleTranslateScoring(state: RunnerState, args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"translate_scoring args must be an object\" });\n }\n const a = args as { cardId?: unknown };\n if (typeof a.cardId !== \"string\") {\n return err(\"INVALID_INPUT\", { detail: \"translate_scoring.cardId must be a string\" });\n }\n const card = getDataset(state).missionCards.get(a.cardId);\n if (!card) return err(\"UNKNOWN_ENTITY\", { kind: \"secondary-card\", id: a.cardId });\n return ok({ awards: describeScoringCard(card) });\n}\n\nfunction handleResolveTerrain(args: unknown): RunnerResponse {\n if (typeof args !== \"object\" || args === null) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain args must be an object\" });\n }\n const a = args as { layout?: unknown; templates?: unknown };\n if (typeof a.layout !== \"object\" || a.layout === null) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain.layout must be an object\" });\n }\n const templates = a.templates ?? [];\n if (!Array.isArray(templates)) {\n return err(\"INVALID_INPUT\", { detail: \"resolve_terrain.templates must be an array\" });\n }\n try {\n const pieces = resolveLayout(a.layout as TerrainLayout, templates as TerrainTemplate[]);\n return ok({ pieces });\n } catch (e) {\n if (e instanceof TerrainResolveError) {\n return err(\"INVALID_INPUT\", { detail: e.message });\n }\n return err(\"INTERNAL_ERROR\", { detail: (e as Error).message });\n }\n}\n\n// -----------------------------------------------------------------------------\n// Dispatcher and per-line entry point.\n// -----------------------------------------------------------------------------\n\n/**\n * Apply one decoded request to the runner state and return the response. Used\n * directly by tests; the CLI loop wraps it with line parsing.\n */\nexport function dispatch(state: RunnerState, req: { op: string; args?: unknown }): RunnerResponse {\n if (!state.initialized && req.op !== \"init\") {\n return err(\"INVALID_INPUT\", { detail: \"must init before any other op\" });\n }\n switch (req.op) {\n case \"init\":\n return handleInit(state, req.args);\n case \"version\":\n return ok({ impl: IMPL_NAME, spec_version: SPEC_VERSION, impl_version: IMPL_VERSION });\n case \"normalize\":\n return handleNormalize(req.args);\n case \"import\":\n return handleImport(state, req.args);\n case \"try_import\":\n return handleTryImport(state, req.args);\n case \"export\":\n return handleExport(req.args);\n case \"linked_query\":\n return handleLinkedQuery(state, req.args);\n case \"validate\":\n return handleValidate(state, req.args);\n case \"crunch\":\n return handleCrunch(state, req.args);\n case \"attribution\":\n return handleAttribution(state, req.args);\n case \"translate_scoring\":\n return handleTranslateScoring(state, req.args);\n case \"resolve_terrain\":\n return handleResolveTerrain(req.args);\n case \"shutdown\":\n return ok(null);\n default:\n return err(\"UNKNOWN_OP\", { op: req.op });\n }\n}\n\n/**\n * Process one line of stdin (one NDJSON request) and return the line that\n * should be written to stdout (one NDJSON response). Returns `null` only on\n * fully-empty input lines, which should be silently ignored.\n */\nexport function processRequest(state: RunnerState, line: string): string | null {\n const trimmed = line.trim();\n if (trimmed === \"\") return null;\n let req: { op?: unknown; args?: unknown };\n try {\n req = JSON.parse(trimmed);\n } catch (e) {\n return JSON.stringify(err(\"INVALID_INPUT\", { detail: `not valid JSON: ${(e as Error).message}` }));\n }\n if (typeof req.op !== \"string\") {\n return JSON.stringify(err(\"INVALID_INPUT\", { detail: \"request must have a string `op` field\" }));\n }\n const response = dispatch(state, { op: req.op, args: req.args });\n return JSON.stringify(response);\n}\n\n// -----------------------------------------------------------------------------\n// CLI: wire stdin/stdout. Only runs when this file is the process entry point.\n// -----------------------------------------------------------------------------\n\nfunction isCliMain(): boolean {\n // process.argv[1] is the path the runtime invoked. With tsx and node both,\n // it points at this file (or its compiled .js form). Comparing absolute\n // paths is the most robust portable check.\n if (!process.argv[1]) return false;\n const entry = process.argv[1];\n const here = fileURLToPath(import.meta.url);\n return entry === here || entry.endsWith(\"/runner.js\") || entry.endsWith(\"/runner.ts\");\n}\n\nasync function runCli(): Promise<void> {\n const state = createRunnerState();\n const rl = createInterface({ input: process.stdin });\n // We must respond on the same tick the request arrives — the differ\n // pipelines requests and expects responses in order. readline preserves\n // line ordering, so processing inside `line` handler is sufficient.\n for await (const line of rl) {\n const out = processRequest(state, line);\n if (out !== null) {\n process.stdout.write(out + \"\\n\");\n }\n // `shutdown` returns `ok(null)`; honor it by exiting after the response\n // is flushed.\n try {\n const req = JSON.parse(line);\n if (req && req.op === \"shutdown\") {\n process.exit(0);\n }\n } catch {\n // already handled above; not a shutdown\n }\n }\n}\n\nif (isCliMain()) {\n runCli().catch((e) => {\n // Last-ditch INTERNAL_ERROR so the differ sees a typed failure rather\n // than an opaque crash.\n process.stdout.write(\n JSON.stringify({\n ok: false,\n error_kind: \"INTERNAL_ERROR\",\n error_payload: { detail: (e as Error).message },\n }) + \"\\n\",\n );\n process.exit(1);\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../../src/translate/condition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,kFAAkF;AAClF,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAWD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"condition.d.ts","sourceRoot":"","sources":["../../src/translate/condition.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,SAAS,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,kFAAkF;AAClF,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAWD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAwItD"}
|
|
@@ -107,6 +107,10 @@ export function describeCondition(c) {
|
|
|
107
107
|
s += " while on an objective";
|
|
108
108
|
return s;
|
|
109
109
|
}
|
|
110
|
+
case "destroyed-in-tagged-terrain": {
|
|
111
|
+
const where = p.at_start_of_turn ? "that started the turn in" : "while in";
|
|
112
|
+
return `${negate}${count(p.count_min ?? 1, "enemy unit")} destroyed ${where} ${dekebab(str(p.tag))} terrain`;
|
|
113
|
+
}
|
|
110
114
|
case "action-completed": {
|
|
111
115
|
let s = `${negate}${count(p.count_min ?? 1, "action")} completed`;
|
|
112
116
|
if (p.action_id != null)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition.js","sourceRoot":"","sources":["../../src/translate/condition.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Humanize an Ability-DSL / scoring `condition` into plain English.\n *\n * Shared by the ability-text CLI (`commands/translate.ts`) and the scoring-card\n * translator (`scoring.ts`). Output is **ASCII-only** with a fixed clause and\n * parameter order: it is pinned byte-for-byte across the TS and Rust ports by\n * the `conformance/scoring-translation` corpus, so any phrasing change here is a\n * semantic corpus change (bump `conformance/SPEC_VERSION`).\n */\n\n/**\n * Minimal structural view of a condition node. Matches both the ability-dsl\n * condition schema and the `secondary-card` award `when` field (a simple node\n * carries `type` + `parameters` + `negated`; a compound node carries\n * `operator` + `operands`).\n */\nexport interface Condition {\n type?: string;\n operator?: \"and\" | \"or\" | \"not\";\n operands?: Condition[];\n parameters?: Record<string, unknown>;\n negated?: boolean;\n}\n\n/** kebab-case → space-separated words (`enemy-territory` → `enemy territory`). */\nexport function dekebab(s: string): string {\n return s.replace(/-/g, \" \");\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : String(v);\n}\n\n/** `2` + `objective` → `2+ objectives`. Nouns here are all regular plurals. */\nfunction count(n: unknown, noun: string): string {\n return `${str(n)}+ ${noun}s`;\n}\n\nexport function describeCondition(c: Condition): string {\n // Compound nodes first — join the operands with lowercase connectives so the\n // result reads naturally inside a \"... when X and Y\" clause.\n if (c.operator === \"and\" && c.operands) {\n return c.operands.map(describeCondition).join(\" and \");\n }\n if (c.operator === \"or\" && c.operands) {\n return c.operands.map(describeCondition).join(\" or \");\n }\n if (c.operator === \"not\" && c.operands) {\n return `not (${c.operands.map(describeCondition).join(\", \")})`;\n }\n\n const negate = c.negated ? \"not \" : \"\";\n const p = c.parameters ?? {};\n\n switch (c.type) {\n // ── Ability-DSL conditions (ported from commands/translate.ts) ──────────\n case \"phase-is\":\n return `${negate}during the ${str(p.phase)} phase`;\n case \"timing-is\":\n return `${negate}at ${dekebab(str(p.timing))}`;\n case \"player-turn-is\":\n return `${negate}in ${p.turn === \"your-turn\" ? \"your\" : p.turn === \"opponent-turn\" ? \"the opponent's\" : \"either player's\"} turn`;\n case \"charged-this-turn\":\n return `${negate}the unit charged this turn`;\n case \"advanced-this-turn\":\n return `${negate}the unit advanced this turn`;\n case \"remained-stationary\":\n return `${negate}the unit remained stationary`;\n case \"unit-below-starting-strength\":\n return `${negate}the unit is below starting strength`;\n case \"unit-below-half-strength\":\n return `${negate}the unit is below half strength`;\n case \"unit-has-keyword\":\n return `${negate}the unit has \"${str(p.keyword)}\"`;\n case \"target-has-keyword\":\n return `${negate}the target has \"${str(p.keyword)}\"`;\n case \"model-is-leader\":\n return `${negate}the model is leading a unit`;\n case \"is-attached\":\n return `${negate}attached to a ${p.keyword ? `${str(p.keyword)} ` : \"\"}unit`;\n case \"attack-is-type\":\n return `${negate}for ${str(p.attack_type)} attacks`;\n case \"is-battle-shocked\":\n return `${negate}the unit is battle-shocked`;\n case \"has-lost-wounds\":\n return `${negate}the model has lost wounds`;\n case \"opponent-unit-within-range\":\n return `${negate}an enemy unit is within ${p.range === \"engagement\" ? \"engagement range\" : `${str(p.range)}\"`}`;\n case \"unit-within-range-of\":\n return `${negate}within ${str(p.range)}\" of ${str(p.target_type ?? \"target\")}${p.keyword ? ` (${str(p.keyword)})` : \"\"}`;\n case \"within-range-of-objective\":\n return `${negate}within range of an objective`;\n case \"has-fought-this-phase\":\n return `${negate}has fought this phase`;\n case \"destroyed-by-attack-type\":\n return `${negate}destroyed by a ${str(p.attack_type)} attack`;\n\n // ── Scoring conditions (secondary-card award `when`) ────────────────────\n case \"objective-majority\":\n return `${negate}you hold more objectives than the ${dekebab(str(p.relative_to ?? \"opponent\"))}`;\n case \"controls-objective\": {\n const noun = p.objective_role ? `${dekebab(str(p.objective_role))} objective` : \"objective\";\n let s = `${negate}you control ${count(p.count_min ?? 1, noun)}`;\n if (p.objective != null) s += ` (${dekebab(str(p.objective))})`;\n if (p.scope != null) s += ` in ${dekebab(str(p.scope))}`;\n if (p.exclude != null) s += ` (excluding ${dekebab(str(p.exclude))})`;\n return s;\n }\n case \"units-destroyed\":\n return `${negate}${count(p.count_min ?? 1, `${str(p.side)} unit`)} destroyed ${dekebab(str(p.window))}`;\n case \"units-destroyed-comparison\": {\n const subj = (p.subject ?? {}) as Record<string, unknown>;\n const ref = (p.reference ?? {}) as Record<string, unknown>;\n const cmp = p.comparator === \"greater-or-equal\" ? \"at least as many\" : \"more\";\n const link = p.comparator === \"greater-or-equal\" ? \"as\" : \"than\";\n return `${negate}you destroyed ${cmp} ${str(subj.side)} units ${dekebab(str(subj.window))} ${link} ${str(ref.side)} units ${dekebab(str(ref.window))}`;\n }\n case \"new-objective-controlled\":\n return `${negate}you newly control ${count(p.count_min ?? 1, \"objective\")} this turn`;\n case \"destroyed-while-on-objective\": {\n let s = `${negate}${count(p.count_min ?? 1, \"enemy unit\")} destroyed`;\n if (p.destroyer_on_objective) s += \" by a unit on an objective\";\n if (p.victim_on_objective) s += \" while on an objective\";\n return s;\n }\n case \"action-completed\": {\n let s = `${negate}${count(p.count_min ?? 1, \"action\")} completed`;\n if (p.action_id != null) s += ` (${dekebab(str(p.action_id))})`;\n if (p.target_kind != null) s += ` on ${dekebab(str(p.target_kind))}`;\n const tf = (p.target_filter ?? {}) as Record<string, unknown>;\n if (tf.objective_role != null) s += ` (${dekebab(str(tf.objective_role))})`;\n if (tf.in_enemy_territory) s += \" in enemy territory\";\n if (tf.exclude != null) s += ` (excluding ${dekebab(str(tf.exclude))})`;\n if (p.window != null) s += ` ${dekebab(str(p.window))}`;\n return s;\n }\n case \"objective-has-tag\": {\n let s = `${negate}${count(p.count_min ?? 1, \"objective\")} tagged ${dekebab(str(p.tag))}`;\n if (p.count_max != null) s += ` (at most ${str(p.count_max)})`;\n if (p.objective != null) s += ` (${dekebab(str(p.objective))})`;\n if (p.scope != null) s += ` in ${dekebab(str(p.scope))}`;\n if (p.last_marked) s += \" (most recently marked)\";\n return s;\n }\n case \"unit-has-tag\": {\n let s = `${negate}${count(p.count_min ?? 1, `${str(p.side)} unit`)} tagged ${dekebab(str(p.tag))}`;\n if (p.window != null) s += ` (${dekebab(str(p.window))})`;\n return s;\n }\n case \"terrain-has-tag\": {\n let s = `${negate}terrain tagged ${dekebab(str(p.tag))}`;\n if (p.friendly_units_min != null) s += ` with ${str(p.friendly_units_min)}+ friendly units`;\n if (p.enemy_units_max != null) s += ` and at most ${str(p.enemy_units_max)} enemy units`;\n if (p.last_marked) s += \" (most recently marked)\";\n if (p.in_enemy_dz) s += \" in the enemy deployment zone\";\n return s;\n }\n case \"terrain-area-control\":\n return `${negate}you control a terrain area with ${str(p.min_models ?? 1)}+ models`;\n case \"territory-control\": {\n let s = `${negate}you control ${dekebab(str(p.territory_ref ?? \"your-territory\"))}`;\n if (p.enemy_units_max != null) s += ` with at most ${str(p.enemy_units_max)} enemy units`;\n return s;\n }\n case \"engagement-fronts\":\n return `${negate}you are engaged on ${str(p.count_min ?? 1)}+ fronts`;\n\n default:\n return `${negate}${dekebab(c.type ?? \"unknown\")}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"condition.js","sourceRoot":"","sources":["../../src/translate/condition.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Humanize an Ability-DSL / scoring `condition` into plain English.\n *\n * Shared by the ability-text CLI (`commands/translate.ts`) and the scoring-card\n * translator (`scoring.ts`). Output is **ASCII-only** with a fixed clause and\n * parameter order: it is pinned byte-for-byte across the TS and Rust ports by\n * the `conformance/scoring-translation` corpus, so any phrasing change here is a\n * semantic corpus change (bump `conformance/SPEC_VERSION`).\n */\n\n/**\n * Minimal structural view of a condition node. Matches both the ability-dsl\n * condition schema and the `secondary-card` award `when` field (a simple node\n * carries `type` + `parameters` + `negated`; a compound node carries\n * `operator` + `operands`).\n */\nexport interface Condition {\n type?: string;\n operator?: \"and\" | \"or\" | \"not\";\n operands?: Condition[];\n parameters?: Record<string, unknown>;\n negated?: boolean;\n}\n\n/** kebab-case → space-separated words (`enemy-territory` → `enemy territory`). */\nexport function dekebab(s: string): string {\n return s.replace(/-/g, \" \");\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v : String(v);\n}\n\n/** `2` + `objective` → `2+ objectives`. Nouns here are all regular plurals. */\nfunction count(n: unknown, noun: string): string {\n return `${str(n)}+ ${noun}s`;\n}\n\nexport function describeCondition(c: Condition): string {\n // Compound nodes first — join the operands with lowercase connectives so the\n // result reads naturally inside a \"... when X and Y\" clause.\n if (c.operator === \"and\" && c.operands) {\n return c.operands.map(describeCondition).join(\" and \");\n }\n if (c.operator === \"or\" && c.operands) {\n return c.operands.map(describeCondition).join(\" or \");\n }\n if (c.operator === \"not\" && c.operands) {\n return `not (${c.operands.map(describeCondition).join(\", \")})`;\n }\n\n const negate = c.negated ? \"not \" : \"\";\n const p = c.parameters ?? {};\n\n switch (c.type) {\n // ── Ability-DSL conditions (ported from commands/translate.ts) ──────────\n case \"phase-is\":\n return `${negate}during the ${str(p.phase)} phase`;\n case \"timing-is\":\n return `${negate}at ${dekebab(str(p.timing))}`;\n case \"player-turn-is\":\n return `${negate}in ${p.turn === \"your-turn\" ? \"your\" : p.turn === \"opponent-turn\" ? \"the opponent's\" : \"either player's\"} turn`;\n case \"charged-this-turn\":\n return `${negate}the unit charged this turn`;\n case \"advanced-this-turn\":\n return `${negate}the unit advanced this turn`;\n case \"remained-stationary\":\n return `${negate}the unit remained stationary`;\n case \"unit-below-starting-strength\":\n return `${negate}the unit is below starting strength`;\n case \"unit-below-half-strength\":\n return `${negate}the unit is below half strength`;\n case \"unit-has-keyword\":\n return `${negate}the unit has \"${str(p.keyword)}\"`;\n case \"target-has-keyword\":\n return `${negate}the target has \"${str(p.keyword)}\"`;\n case \"model-is-leader\":\n return `${negate}the model is leading a unit`;\n case \"is-attached\":\n return `${negate}attached to a ${p.keyword ? `${str(p.keyword)} ` : \"\"}unit`;\n case \"attack-is-type\":\n return `${negate}for ${str(p.attack_type)} attacks`;\n case \"is-battle-shocked\":\n return `${negate}the unit is battle-shocked`;\n case \"has-lost-wounds\":\n return `${negate}the model has lost wounds`;\n case \"opponent-unit-within-range\":\n return `${negate}an enemy unit is within ${p.range === \"engagement\" ? \"engagement range\" : `${str(p.range)}\"`}`;\n case \"unit-within-range-of\":\n return `${negate}within ${str(p.range)}\" of ${str(p.target_type ?? \"target\")}${p.keyword ? ` (${str(p.keyword)})` : \"\"}`;\n case \"within-range-of-objective\":\n return `${negate}within range of an objective`;\n case \"has-fought-this-phase\":\n return `${negate}has fought this phase`;\n case \"destroyed-by-attack-type\":\n return `${negate}destroyed by a ${str(p.attack_type)} attack`;\n\n // ── Scoring conditions (secondary-card award `when`) ────────────────────\n case \"objective-majority\":\n return `${negate}you hold more objectives than the ${dekebab(str(p.relative_to ?? \"opponent\"))}`;\n case \"controls-objective\": {\n const noun = p.objective_role ? `${dekebab(str(p.objective_role))} objective` : \"objective\";\n let s = `${negate}you control ${count(p.count_min ?? 1, noun)}`;\n if (p.objective != null) s += ` (${dekebab(str(p.objective))})`;\n if (p.scope != null) s += ` in ${dekebab(str(p.scope))}`;\n if (p.exclude != null) s += ` (excluding ${dekebab(str(p.exclude))})`;\n return s;\n }\n case \"units-destroyed\":\n return `${negate}${count(p.count_min ?? 1, `${str(p.side)} unit`)} destroyed ${dekebab(str(p.window))}`;\n case \"units-destroyed-comparison\": {\n const subj = (p.subject ?? {}) as Record<string, unknown>;\n const ref = (p.reference ?? {}) as Record<string, unknown>;\n const cmp = p.comparator === \"greater-or-equal\" ? \"at least as many\" : \"more\";\n const link = p.comparator === \"greater-or-equal\" ? \"as\" : \"than\";\n return `${negate}you destroyed ${cmp} ${str(subj.side)} units ${dekebab(str(subj.window))} ${link} ${str(ref.side)} units ${dekebab(str(ref.window))}`;\n }\n case \"new-objective-controlled\":\n return `${negate}you newly control ${count(p.count_min ?? 1, \"objective\")} this turn`;\n case \"destroyed-while-on-objective\": {\n let s = `${negate}${count(p.count_min ?? 1, \"enemy unit\")} destroyed`;\n if (p.destroyer_on_objective) s += \" by a unit on an objective\";\n if (p.victim_on_objective) s += \" while on an objective\";\n return s;\n }\n case \"destroyed-in-tagged-terrain\": {\n const where = p.at_start_of_turn ? \"that started the turn in\" : \"while in\";\n return `${negate}${count(p.count_min ?? 1, \"enemy unit\")} destroyed ${where} ${dekebab(str(p.tag))} terrain`;\n }\n case \"action-completed\": {\n let s = `${negate}${count(p.count_min ?? 1, \"action\")} completed`;\n if (p.action_id != null) s += ` (${dekebab(str(p.action_id))})`;\n if (p.target_kind != null) s += ` on ${dekebab(str(p.target_kind))}`;\n const tf = (p.target_filter ?? {}) as Record<string, unknown>;\n if (tf.objective_role != null) s += ` (${dekebab(str(tf.objective_role))})`;\n if (tf.in_enemy_territory) s += \" in enemy territory\";\n if (tf.exclude != null) s += ` (excluding ${dekebab(str(tf.exclude))})`;\n if (p.window != null) s += ` ${dekebab(str(p.window))}`;\n return s;\n }\n case \"objective-has-tag\": {\n let s = `${negate}${count(p.count_min ?? 1, \"objective\")} tagged ${dekebab(str(p.tag))}`;\n if (p.count_max != null) s += ` (at most ${str(p.count_max)})`;\n if (p.objective != null) s += ` (${dekebab(str(p.objective))})`;\n if (p.scope != null) s += ` in ${dekebab(str(p.scope))}`;\n if (p.last_marked) s += \" (most recently marked)\";\n return s;\n }\n case \"unit-has-tag\": {\n let s = `${negate}${count(p.count_min ?? 1, `${str(p.side)} unit`)} tagged ${dekebab(str(p.tag))}`;\n if (p.window != null) s += ` (${dekebab(str(p.window))})`;\n return s;\n }\n case \"terrain-has-tag\": {\n let s = `${negate}terrain tagged ${dekebab(str(p.tag))}`;\n if (p.friendly_units_min != null) s += ` with ${str(p.friendly_units_min)}+ friendly units`;\n if (p.enemy_units_max != null) s += ` and at most ${str(p.enemy_units_max)} enemy units`;\n if (p.last_marked) s += \" (most recently marked)\";\n if (p.in_enemy_dz) s += \" in the enemy deployment zone\";\n return s;\n }\n case \"terrain-area-control\":\n return `${negate}you control a terrain area with ${str(p.min_models ?? 1)}+ models`;\n case \"territory-control\": {\n let s = `${negate}you control ${dekebab(str(p.territory_ref ?? \"your-territory\"))}`;\n if (p.enemy_units_max != null) s += ` with at most ${str(p.enemy_units_max)} enemy units`;\n return s;\n }\n case \"engagement-fronts\":\n return `${negate}you are engaged on ${str(p.count_min ?? 1)}+ fronts`;\n\n default:\n return `${negate}${dekebab(c.type ?? \"unknown\")}`;\n }\n}\n"]}
|
package/dist/validate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAS3B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAS3B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClD;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAgDD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,MAAM,EACf,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,gBAAgB,CAAC,CAkF3B"}
|
package/dist/validate.js
CHANGED
|
@@ -17,6 +17,7 @@ const SCHEMA_MAP = {
|
|
|
17
17
|
enhancements: "https://40kdc.dev/schemas/core/enhancement.schema.json",
|
|
18
18
|
stratagems: "https://40kdc.dev/schemas/core/stratagem.schema.json",
|
|
19
19
|
"wargear-options": "https://40kdc.dev/schemas/core/wargear-option.schema.json",
|
|
20
|
+
wargear: "https://40kdc.dev/schemas/core/wargear.schema.json",
|
|
20
21
|
"leader-attachments": "https://40kdc.dev/schemas/core/leader-attachment.schema.json",
|
|
21
22
|
"unit-compositions": "https://40kdc.dev/schemas/core/unit-composition.schema.json",
|
|
22
23
|
"force-dispositions": "https://40kdc.dev/schemas/core/force-disposition.schema.json",
|
|
@@ -34,14 +35,17 @@ const SCHEMA_MAP = {
|
|
|
34
35
|
};
|
|
35
36
|
/**
|
|
36
37
|
* Determine which schema $id to use for a given data file path.
|
|
37
|
-
* Convention: the file's base name
|
|
38
|
+
* Convention: the file's base name starts with a SCHEMA_MAP prefix (real data is
|
|
39
|
+
* `<prefix>.json`; test fixtures are `<prefix>-good.json` / `<prefix>-bad.json`).
|
|
40
|
+
* Prefixes are tried longest-first so `wargear-options.json` resolves to the
|
|
41
|
+
* wargear-option schema rather than the shorter `wargear` key (distinct entities).
|
|
38
42
|
*/
|
|
43
|
+
const SCHEMA_PREFIXES = Object.keys(SCHEMA_MAP).sort((a, b) => b.length - a.length);
|
|
39
44
|
function resolveSchemaId(filePath) {
|
|
40
45
|
const base = basename(filePath);
|
|
41
|
-
for (const
|
|
42
|
-
if (base.startsWith(prefix))
|
|
43
|
-
return
|
|
44
|
-
}
|
|
46
|
+
for (const prefix of SCHEMA_PREFIXES) {
|
|
47
|
+
if (base.startsWith(prefix))
|
|
48
|
+
return SCHEMA_MAP[prefix];
|
|
45
49
|
}
|
|
46
50
|
return null;
|
|
47
51
|
}
|
|
@@ -60,6 +64,10 @@ export async function validateFiles(ajv, pattern, cwd) {
|
|
|
60
64
|
errors: [],
|
|
61
65
|
};
|
|
62
66
|
for (const file of files) {
|
|
67
|
+
// Underscore-prefixed files are scratch/reports (e.g. the converter's
|
|
68
|
+
// `_wargear-options.unparsed.json`), not dataset entities — skip them.
|
|
69
|
+
if (basename(file).startsWith("_"))
|
|
70
|
+
continue;
|
|
63
71
|
const schemaId = resolveSchemaId(file);
|
|
64
72
|
if (!schemaId) {
|
|
65
73
|
result.errors.push({
|
package/dist/validate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AAgBnD;;GAEG;AACH,MAAM,UAAU,GAA2B;IACzC,QAAQ,EAAE,oDAAoD;IAC9D,KAAK,EAAE,iDAAiD;IACxD,OAAO,EAAE,mDAAmD;IAC5D,iBAAiB,EAAE,2DAA2D;IAC9E,eAAe,EAAE,yDAAyD;IAC1E,WAAW,EAAE,uDAAuD;IACpE,YAAY,EAAE,wDAAwD;IACtE,UAAU,EAAE,sDAAsD;IAClE,iBAAiB,EAAE,2DAA2D;IAC9E,oBAAoB,EAAE,8DAA8D;IACpF,mBAAmB,EAAE,6DAA6D;IAClF,oBAAoB,EAAE,8DAA8D;IACpF,qBAAqB,EAAE,+DAA+D;IACtF,kBAAkB,EAAE,4DAA4D;IAChF,QAAQ,EAAE,oDAAoD;IAC9D,iBAAiB,EAAE,2DAA2D;IAC9E,mBAAmB,EAAE,6DAA6D;IAClF,iBAAiB,EAAE,2DAA2D;IAC9E,gBAAgB,EAAE,gEAAgE;IAClF,cAAc,EAAE,8DAA8D;IAC9E,mBAAmB,EAAE,mEAAmE;IACxF,SAAS,EAAE,sEAAsE;IACjF,gBAAgB,EAAE,gEAAgE;CACnF,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"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","sourcesContent":["import type Ajv from \"ajv\";\nimport { readFileSync } from \"node:fs\";\nimport { glob } from \"glob\";\nimport { resolve, basename } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\nconst DATA_ROOT = resolve(__dirname, \"../../data\");\n\nexport interface ValidationError {\n file: string;\n index: number;\n errors: Array<{ path: string; message: string }>;\n}\n\nexport interface ValidationResult {\n totalFiles: number;\n totalItems: number;\n passed: number;\n failed: number;\n errors: ValidationError[];\n}\n\n/**\n * Map from data file base-name prefix to schema $id.\n */\nconst SCHEMA_MAP: Record<string, string> = {\n factions: \"https://40kdc.dev/schemas/core/faction.schema.json\",\n units: \"https://40kdc.dev/schemas/core/unit.schema.json\",\n weapons: \"https://40kdc.dev/schemas/core/weapon.schema.json\",\n \"weapon-keywords\": \"https://40kdc.dev/schemas/core/weapon-keyword.schema.json\",\n \"game-versions\": \"https://40kdc.dev/schemas/core/game-version.schema.json\",\n detachments: \"https://40kdc.dev/schemas/core/detachment.schema.json\",\n enhancements: \"https://40kdc.dev/schemas/core/enhancement.schema.json\",\n stratagems: \"https://40kdc.dev/schemas/core/stratagem.schema.json\",\n \"wargear-options\": \"https://40kdc.dev/schemas/core/wargear-option.schema.json\",\n wargear: \"https://40kdc.dev/schemas/core/wargear.schema.json\",\n \"leader-attachments\": \"https://40kdc.dev/schemas/core/leader-attachment.schema.json\",\n \"unit-compositions\": \"https://40kdc.dev/schemas/core/unit-composition.schema.json\",\n \"force-dispositions\": \"https://40kdc.dev/schemas/core/force-disposition.schema.json\",\n \"deployment-patterns\": \"https://40kdc.dev/schemas/core/deployment-pattern.schema.json\",\n \"mission-matchups\": \"https://40kdc.dev/schemas/core/mission-matchup.schema.json\",\n missions: \"https://40kdc.dev/schemas/core/mission.schema.json\",\n \"secondary-cards\": \"https://40kdc.dev/schemas/core/secondary-card.schema.json\",\n \"terrain-templates\": \"https://40kdc.dev/schemas/core/terrain-template.schema.json\",\n \"terrain-layouts\": \"https://40kdc.dev/schemas/core/terrain-layout.schema.json\",\n \"phase-mappings\": \"https://40kdc.dev/schemas/enrichment/phase-mapping.schema.json\",\n \"timing-flags\": \"https://40kdc.dev/schemas/enrichment/timing-flag.schema.json\",\n \"interaction-flags\": \"https://40kdc.dev/schemas/enrichment/interaction-flag.schema.json\",\n abilities: \"https://40kdc.dev/schemas/enrichment/ability-dsl/ability.schema.json\",\n \"resource-pools\": \"https://40kdc.dev/schemas/enrichment/resource-pool.schema.json\",\n};\n\n/**\n * Determine which schema $id to use for a given data file path.\n * Convention: the file's base name starts with a SCHEMA_MAP prefix (real data is\n * `<prefix>.json`; test fixtures are `<prefix>-good.json` / `<prefix>-bad.json`).\n * Prefixes are tried longest-first so `wargear-options.json` resolves to the\n * wargear-option schema rather than the shorter `wargear` key (distinct entities).\n */\nconst SCHEMA_PREFIXES = Object.keys(SCHEMA_MAP).sort((a, b) => b.length - a.length);\nfunction resolveSchemaId(filePath: string): string | null {\n const base = basename(filePath);\n for (const prefix of SCHEMA_PREFIXES) {\n if (base.startsWith(prefix)) return SCHEMA_MAP[prefix];\n }\n return null;\n}\n\n/**\n * Validate all data files matching the given glob pattern.\n * Each data file is expected to be a JSON array; each element is validated individually.\n */\nexport async function validateFiles(\n ajv: Ajv,\n pattern: string,\n cwd?: string,\n): Promise<ValidationResult> {\n const root = cwd ?? DATA_ROOT;\n const files = await glob(pattern, { cwd: root, absolute: true });\n\n const result: ValidationResult = {\n totalFiles: files.length,\n totalItems: 0,\n passed: 0,\n failed: 0,\n errors: [],\n };\n\n for (const file of files) {\n // Underscore-prefixed files are scratch/reports (e.g. the converter's\n // `_wargear-options.unparsed.json`), not dataset entities — skip them.\n if (basename(file).startsWith(\"_\")) continue;\n const schemaId = resolveSchemaId(file);\n if (!schemaId) {\n result.errors.push({\n file,\n index: -1,\n errors: [{ path: \"\", message: `No schema mapping found for file: ${basename(file)}` }],\n });\n result.failed++;\n continue;\n }\n\n const validate = ajv.getSchema(schemaId);\n if (!validate) {\n result.errors.push({\n file,\n index: -1,\n errors: [{ path: \"\", message: `Schema not found: ${schemaId}` }],\n });\n result.failed++;\n continue;\n }\n\n let data: unknown;\n try {\n const raw = readFileSync(file, \"utf-8\");\n data = JSON.parse(raw);\n } catch (err) {\n result.errors.push({\n file,\n index: -1,\n errors: [{ path: \"\", message: `Failed to parse JSON: ${(err as Error).message}` }],\n });\n result.failed++;\n continue;\n }\n\n if (!Array.isArray(data)) {\n result.errors.push({\n file,\n index: -1,\n errors: [{ path: \"\", message: \"Data file must be a JSON array\" }],\n });\n result.failed++;\n continue;\n }\n\n for (let i = 0; i < data.length; i++) {\n result.totalItems++;\n const valid = validate(data[i]);\n if (valid) {\n result.passed++;\n } else {\n result.failed++;\n result.errors.push({\n file,\n index: i,\n errors: (validate.errors ?? []).map((e) => ({\n path: e.instancePath || \"/\",\n message: e.message ?? \"Unknown validation error\",\n })),\n });\n }\n }\n }\n\n return result;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alpaca-software/40kdc-data",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
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": [
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
"json-schema"
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT",
|
|
16
|
-
"homepage": "https://
|
|
16
|
+
"homepage": "https://40kdc.alpacasoft.dev",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/
|
|
19
|
+
"url": "git+https://github.com/wn-mitch/40kdc-data.git",
|
|
20
20
|
"directory": "tools"
|
|
21
21
|
},
|
|
22
22
|
"bugs": {
|
|
23
|
-
"url": "https://github.com/
|
|
23
|
+
"url": "https://github.com/wn-mitch/40kdc-data/issues"
|
|
24
24
|
},
|
|
25
25
|
"main": "./dist/index.js",
|
|
26
26
|
"types": "./dist/index.d.ts",
|
|
@@ -124,6 +124,20 @@
|
|
|
124
124
|
"type": "string",
|
|
125
125
|
"enum": ["obscuring", "hidden", "plunging-fire"],
|
|
126
126
|
"description": "An 11e terrain-area keyword. Confirmed launch set; extend as further keywords publish on dataslate."
|
|
127
|
+
},
|
|
128
|
+
"base-size": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"description": "A model's base. 'round' carries 'diameter'; 'oval' carries 'width'+'length'. 'flying-base' (with 'size': small/large), 'hull', and 'unique' are categories the GW base-size guide gives without standard millimetre dimensions; entries carrying such a category, or any millimetre value not taken from an authoritative source, set 'draft': true to mark them for later hand-authoring.",
|
|
131
|
+
"properties": {
|
|
132
|
+
"shape": { "enum": ["round", "oval", "flying-base", "hull", "unique"] },
|
|
133
|
+
"diameter": { "type": "number", "exclusiveMinimum": 0 },
|
|
134
|
+
"width": { "type": "number", "exclusiveMinimum": 0 },
|
|
135
|
+
"length": { "type": "number", "exclusiveMinimum": 0 },
|
|
136
|
+
"size": { "enum": ["small", "large"], "description": "Flying-base size class, when 'shape' is 'flying-base'." },
|
|
137
|
+
"draft": { "type": "boolean", "default": false, "description": "True when the entry is provisional/guessed (e.g. a category without authoritative dimensions) and should be revisited." }
|
|
138
|
+
},
|
|
139
|
+
"required": ["shape"],
|
|
140
|
+
"additionalProperties": false
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
}
|
|
@@ -24,7 +24,11 @@
|
|
|
24
24
|
"type": "array",
|
|
25
25
|
"items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" }
|
|
26
26
|
},
|
|
27
|
-
"is_leader_model": { "type": "boolean", "default": false }
|
|
27
|
+
"is_leader_model": { "type": "boolean", "default": false },
|
|
28
|
+
"base_size_mm": {
|
|
29
|
+
"description": "This model's base. Absent when no base could be resolved for the model.",
|
|
30
|
+
"$ref": "../defs/common.schema.json#/$defs/base-size"
|
|
31
|
+
}
|
|
28
32
|
},
|
|
29
33
|
"required": ["name", "min", "max"],
|
|
30
34
|
"additionalProperties": false
|
|
@@ -62,17 +62,9 @@
|
|
|
62
62
|
"keywords": { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
|
|
63
63
|
"faction_keywords": { "$ref": "../defs/common.schema.json#/$defs/keyword-list" },
|
|
64
64
|
"base_size_mm": {
|
|
65
|
+
"description": "The unit's representative base (the most-numerous model's base). Mixed-model units carry the full per-model breakdown in unit-composition; this top-level value is a convenience for consumers that need a single base.",
|
|
65
66
|
"oneOf": [
|
|
66
|
-
{
|
|
67
|
-
"type": "object",
|
|
68
|
-
"properties": {
|
|
69
|
-
"shape": { "enum": ["round", "oval"] },
|
|
70
|
-
"diameter": { "type": "number" },
|
|
71
|
-
"width": { "type": "number" },
|
|
72
|
-
"length": { "type": "number" }
|
|
73
|
-
},
|
|
74
|
-
"required": ["shape"]
|
|
75
|
-
},
|
|
67
|
+
{ "$ref": "../defs/common.schema.json#/$defs/base-size" },
|
|
76
68
|
{ "type": "null" }
|
|
77
69
|
]
|
|
78
70
|
},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://40kdc.dev/schemas/core/wargear-option.schema.json",
|
|
4
4
|
"title": "Wargear Option",
|
|
5
|
-
"description": "A
|
|
5
|
+
"description": "A wargear option available to models within a unit: a weapon/wargear swap, a pure add-on, or a choice between alternatives. Models start with the unit's base loadout; an option modifies that loadout for the number of models its `model_constraint` permits.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"properties": {
|
|
8
8
|
"id": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
|
|
@@ -11,10 +11,16 @@
|
|
|
11
11
|
"oneOf": [
|
|
12
12
|
{
|
|
13
13
|
"type": "object",
|
|
14
|
+
"description": "Limits how many models may take this option. The eligible-model count is: `any_number` ? model_count : `per_n_models` ? floor(model_count / per_n_models) : (`max_count` ?? 1); then clamped by `max_count` when both are set. `model_name` scopes the option to a specific model profile (e.g. the unit's champion); omit for single-profile units.",
|
|
14
15
|
"properties": {
|
|
15
16
|
"model_name": { "type": "string", "minLength": 1 },
|
|
16
17
|
"per_n_models": { "type": "integer", "minimum": 1 },
|
|
17
|
-
"max_count": { "type": "integer", "minimum": 1 }
|
|
18
|
+
"max_count": { "type": "integer", "minimum": 1 },
|
|
19
|
+
"any_number": {
|
|
20
|
+
"type": "boolean",
|
|
21
|
+
"default": false,
|
|
22
|
+
"description": "When true, every model in the unit may take the option ('Any number of models can each ...'). Mutually exclusive in spirit with `per_n_models`."
|
|
23
|
+
}
|
|
18
24
|
},
|
|
19
25
|
"additionalProperties": false
|
|
20
26
|
},
|
|
@@ -25,13 +31,23 @@
|
|
|
25
31
|
"type": "array",
|
|
26
32
|
"items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
|
|
27
33
|
"minItems": 1,
|
|
28
|
-
"description": "Weapon IDs
|
|
34
|
+
"description": "Weapon or wargear IDs removed from the model. Omit for a pure add-on (the option only equips new wargear)."
|
|
29
35
|
},
|
|
30
36
|
"replacement": {
|
|
31
37
|
"type": "array",
|
|
32
38
|
"items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
|
|
33
39
|
"minItems": 1,
|
|
34
|
-
"description": "Weapon IDs
|
|
40
|
+
"description": "Weapon or wargear IDs added to the model — all of them. Exactly one of `replacement` / `replacement_choice` is present."
|
|
41
|
+
},
|
|
42
|
+
"replacement_choice": {
|
|
43
|
+
"type": "array",
|
|
44
|
+
"items": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"items": { "$ref": "../defs/common.schema.json#/$defs/entity-id" },
|
|
47
|
+
"minItems": 1
|
|
48
|
+
},
|
|
49
|
+
"minItems": 2,
|
|
50
|
+
"description": "A choice of replacements ('one of the following'): pick exactly one inner group; each group's IDs are all added together. Exactly one of `replacement` / `replacement_choice` is present."
|
|
35
51
|
},
|
|
36
52
|
"is_free": { "type": "boolean", "default": true },
|
|
37
53
|
"additional_cost": {
|
|
@@ -42,6 +58,16 @@
|
|
|
42
58
|
},
|
|
43
59
|
"game_version": { "$ref": "../defs/game-version-ref.schema.json" }
|
|
44
60
|
},
|
|
45
|
-
"required": ["id", "unit_id", "
|
|
46
|
-
"additionalProperties": false
|
|
61
|
+
"required": ["id", "unit_id", "game_version"],
|
|
62
|
+
"additionalProperties": false,
|
|
63
|
+
"allOf": [
|
|
64
|
+
{
|
|
65
|
+
"if": { "required": ["replacement"] },
|
|
66
|
+
"then": { "not": { "required": ["replacement_choice"] } }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"if": { "not": { "required": ["replacement"] } },
|
|
70
|
+
"then": { "required": ["replacement_choice"] }
|
|
71
|
+
}
|
|
72
|
+
]
|
|
47
73
|
}
|