@hawkeyexl/manni 4.5.0 → 4.6.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 +1 -1
- package/dist/batch-OZOACN4G.js +41 -0
- package/dist/build-Z5VOVADH.js +32 -0
- package/dist/calibrate-TZYT42DR.js +36 -0
- package/dist/capture-SL4MNDDP.js +34 -0
- package/dist/check-5OMA42Y6.js +26 -0
- package/dist/{check-ESNS7ZBC.js → check-OAJWC3WD.js} +7 -5
- package/dist/{check-RQIIIURI.js → check-PKY7KTQE.js} +13 -14
- package/dist/check-WB2QDHJQ.js +880 -0
- package/dist/check-WB2QDHJQ.js.map +1 -0
- package/dist/{chunk-F2OVHJNC.js → chunk-2JFPBZJK.js} +11 -11
- package/dist/{chunk-CP4ZM43V.js → chunk-2KRB3EG7.js} +2 -2
- package/dist/{chunk-QLUMKWQQ.js → chunk-2M6DVNI5.js} +10 -6
- package/dist/{chunk-QLUMKWQQ.js.map → chunk-2M6DVNI5.js.map} +1 -1
- package/dist/{chunk-HQFVJDEN.js → chunk-3NWX34ZI.js} +19 -19
- package/dist/{chunk-TCC737IS.js → chunk-5G53XATW.js} +4 -4
- package/dist/{chunk-B7XUEQF3.js → chunk-6KKZXB2O.js} +7 -5
- package/dist/{chunk-B7XUEQF3.js.map → chunk-6KKZXB2O.js.map} +1 -1
- package/dist/{chunk-SNEHX4VE.js → chunk-733Y2STG.js} +2 -2
- package/dist/{chunk-ELZ2IIOF.js → chunk-73TC4DYH.js} +2 -2
- package/dist/{chunk-QY3DOZMS.js → chunk-7WRP5FFP.js} +4 -4
- package/dist/{chunk-3R5Y3XOL.js → chunk-APKH2T6E.js} +29 -591
- package/dist/chunk-APKH2T6E.js.map +1 -0
- package/dist/{chunk-FLF6AECQ.js → chunk-B3TS4WRM.js} +17 -15
- package/dist/{chunk-FLF6AECQ.js.map → chunk-B3TS4WRM.js.map} +1 -1
- package/dist/chunk-BWCWTHYS.js +146 -0
- package/dist/chunk-BWCWTHYS.js.map +1 -0
- package/dist/{chunk-C6HZ6C5T.js → chunk-C3B2VJNC.js} +2 -2
- package/dist/{chunk-NN4ASVTY.js → chunk-C73RSUIC.js} +4 -4
- package/dist/chunk-CU57HT2E.js +116 -0
- package/dist/chunk-CU57HT2E.js.map +1 -0
- package/dist/chunk-CWXZ2MRT.js +222 -0
- package/dist/chunk-CWXZ2MRT.js.map +1 -0
- package/dist/{chunk-GAZFKZHM.js → chunk-DC5DDJVE.js} +6 -6
- package/dist/{chunk-52PDIZKV.js → chunk-ECVQNGTH.js} +3 -3
- package/dist/chunk-FLAO4ZS3.js +502 -0
- package/dist/chunk-FLAO4ZS3.js.map +1 -0
- package/dist/chunk-FQGLRJGX.js +174 -0
- package/dist/chunk-FQGLRJGX.js.map +1 -0
- package/dist/{chunk-XWFNO5PO.js → chunk-GAJIJKET.js} +3 -3
- package/dist/{chunk-QCBICNKC.js → chunk-GREQKO6M.js} +202 -54
- package/dist/chunk-GREQKO6M.js.map +1 -0
- package/dist/chunk-HX44GB4Z.js +30 -0
- package/dist/chunk-HX44GB4Z.js.map +1 -0
- package/dist/{chunk-7ZUCEMCY.js → chunk-IY7NYNPZ.js} +9 -11
- package/dist/{chunk-7ZUCEMCY.js.map → chunk-IY7NYNPZ.js.map} +1 -1
- package/dist/{chunk-527HHQPS.js → chunk-KCOFEWV5.js} +6 -4
- package/dist/{chunk-527HHQPS.js.map → chunk-KCOFEWV5.js.map} +1 -1
- package/dist/chunk-M2RLHZ4J.js +849 -0
- package/dist/chunk-M2RLHZ4J.js.map +1 -0
- package/dist/{chunk-K3U5F2QT.js → chunk-M6GSS72T.js} +12 -12
- package/dist/{chunk-UK2I3ACY.js → chunk-MHM4QEBV.js} +4 -4
- package/dist/{chunk-EL2BZTDL.js → chunk-MOWMCEOJ.js} +13 -11
- package/dist/{chunk-EL2BZTDL.js.map → chunk-MOWMCEOJ.js.map} +1 -1
- package/dist/{chunk-OIZ3XMKB.js → chunk-MWBJ3KTF.js} +4 -4
- package/dist/chunk-N7JSEDSC.js +233 -0
- package/dist/chunk-N7JSEDSC.js.map +1 -0
- package/dist/{chunk-MZC62HPZ.js → chunk-NBMPHD4I.js} +2 -2
- package/dist/{chunk-YA63PVWI.js → chunk-NHHOPZRA.js} +12 -10
- package/dist/{chunk-YA63PVWI.js.map → chunk-NHHOPZRA.js.map} +1 -1
- package/dist/{chunk-DX32P2DT.js → chunk-NTWW7J76.js} +2 -2
- package/dist/{chunk-6XTQDFGP.js → chunk-PCFG7E6B.js} +3 -3
- package/dist/{chunk-6XTQDFGP.js.map → chunk-PCFG7E6B.js.map} +1 -1
- package/dist/{chunk-SJO2FTPG.js → chunk-PL4UAF4S.js} +73 -1304
- package/dist/chunk-PL4UAF4S.js.map +1 -0
- package/dist/chunk-QCHVR4ZM.js +286 -0
- package/dist/chunk-QCHVR4ZM.js.map +1 -0
- package/dist/{chunk-JQU7FHYH.js → chunk-QW77LCOU.js} +10 -10
- package/dist/{chunk-IA7BMIPE.js → chunk-SM6NOMJS.js} +2 -2
- package/dist/{chunk-DE7JSWXV.js → chunk-SWSTHZBI.js} +8 -6
- package/dist/{chunk-DE7JSWXV.js.map → chunk-SWSTHZBI.js.map} +1 -1
- package/dist/{chunk-NGK3N6F2.js → chunk-T6SYGLWR.js} +3 -3
- package/dist/chunk-TH4JVQPF.js +133 -0
- package/dist/chunk-TH4JVQPF.js.map +1 -0
- package/dist/{chunk-TVIX653Q.js → chunk-TLXE7LYK.js} +4 -4
- package/dist/{chunk-HVFR66GQ.js → chunk-TUAAR3MM.js} +3280 -283
- package/dist/chunk-TUAAR3MM.js.map +1 -0
- package/dist/{chunk-TA42STP7.js → chunk-UAKNZVCD.js} +4 -4
- package/dist/{chunk-C3KI45RF.js → chunk-UJSGS2Y5.js} +4 -4
- package/dist/{chunk-ZX43SRZR.js → chunk-UWQ2UVHE.js} +8 -6
- package/dist/{chunk-ZX43SRZR.js.map → chunk-UWQ2UVHE.js.map} +1 -1
- package/dist/chunk-WQWKYAAK.js +86 -0
- package/dist/chunk-WQWKYAAK.js.map +1 -0
- package/dist/{chunk-LJ2RDH3F.js → chunk-XG6PVB5S.js} +1 -23
- package/dist/chunk-XG6PVB5S.js.map +1 -0
- package/dist/{chunk-Q35YIDBV.js → chunk-XKBEHKWO.js} +29 -264
- package/dist/chunk-XKBEHKWO.js.map +1 -0
- package/dist/chunk-XPEWUK7J.js +532 -0
- package/dist/chunk-XPEWUK7J.js.map +1 -0
- package/dist/{chunk-BOIF2XGG.js → chunk-XXDXIIQ4.js} +2 -2
- package/dist/chunk-YLK2J5YK.js +610 -0
- package/dist/chunk-YLK2J5YK.js.map +1 -0
- package/dist/{chunk-7OQAU4EA.js → chunk-Z4IZGEM3.js} +16 -12
- package/dist/{chunk-7OQAU4EA.js.map → chunk-Z4IZGEM3.js.map} +1 -1
- package/dist/{cli-E3H6ZP4I.js → cli-6EWVMEN6.js} +5 -3
- package/dist/{cli-E3H6ZP4I.js.map → cli-6EWVMEN6.js.map} +1 -1
- package/dist/{cli-MHTYPVOD.js → cli-6H7HVDMQ.js} +113 -20
- package/dist/{cli-MHTYPVOD.js.map → cli-6H7HVDMQ.js.map} +1 -1
- package/dist/{cli-HUIMNJT6.js → cli-EPVZHAZS.js} +19 -20
- package/dist/{cli-HUIMNJT6.js.map → cli-EPVZHAZS.js.map} +1 -1
- package/dist/{cli-6PGVHP7Y.js → cli-LUCSGTVI.js} +17 -19
- package/dist/{cli-6PGVHP7Y.js.map → cli-LUCSGTVI.js.map} +1 -1
- package/dist/{cli-PRZHQ3QB.js → cli-NIG4N3XM.js} +14 -15
- package/dist/{cli-PRZHQ3QB.js.map → cli-NIG4N3XM.js.map} +1 -1
- package/dist/{cli-TYGOHJOJ.js → cli-PSKNB66T.js} +225 -58
- package/dist/cli-PSKNB66T.js.map +1 -0
- package/dist/{cli-QKIJOXGW.js → cli-UO4NHIB3.js} +17 -17
- package/dist/{cli-AAG3N4NB.js → cli-UQ7A3YA2.js} +17 -18
- package/dist/{cli-AAG3N4NB.js.map → cli-UQ7A3YA2.js.map} +1 -1
- package/dist/{cli-YW6VWBHX.js → cli-YHYDLQAF.js} +10 -10
- package/dist/cli.js +11 -11
- package/dist/config-P4HDNTYD.js +26 -0
- package/dist/conformance-ZFCMKM6M.js +18 -0
- package/dist/docmeta.js +9 -9
- package/dist/github-7ZCH5PSO.js +26 -0
- package/dist/github-POT6724K.js +31 -0
- package/dist/host-6GURH5HE.js +23 -0
- package/dist/index.d.ts +72 -4
- package/dist/index.js +176 -166
- package/dist/index.js.map +1 -1
- package/dist/{json-4DCKDGUV.js → json-KT2K62L5.js} +12 -13
- package/dist/lint-5AQ5C2BV.js +29 -0
- package/dist/list-5JZKX2NK.js +28 -0
- package/dist/list-SWPFXQMU.js +30 -0
- package/dist/{page-QEDB7EVU.js → page-UPSXRP75.js} +11 -12
- package/dist/prepare-6GNEWCNH.js +155 -0
- package/dist/prepare-6GNEWCNH.js.map +1 -0
- package/dist/{pretty-SQX33UM2.js → pretty-Q6FQAOK4.js} +13 -14
- package/dist/{pretty-5DGEBZSV.js → pretty-SQRPFPSQ.js} +12 -13
- package/dist/provider-AMGRDY6V.js +34 -0
- package/dist/release-IECYCLTD.js +36 -0
- package/dist/release-IECYCLTD.js.map +1 -0
- package/dist/{reporters-OHSI3RMV.js → reporters-45MMC2ID.js} +11 -12
- package/dist/{reporters-YELDDYJD.js → reporters-XUV4MM64.js} +7 -5
- package/dist/reporters-XUV4MM64.js.map +1 -0
- package/dist/run-KMWY2IAN.js +32 -0
- package/dist/run-KMWY2IAN.js.map +1 -0
- package/dist/{term-2WMDZ4PY.js → term-PCJUCWNF.js} +16 -17
- package/dist/{term-2WMDZ4PY.js.map → term-PCJUCWNF.js.map} +1 -1
- package/dist/validate-IMNCZNWG.js +17 -0
- package/dist/validate-IMNCZNWG.js.map +1 -0
- package/package.json +2 -2
- package/plugin/manni/.claude-plugin/plugin.json +1 -1
- package/plugin/manni/hooks/hooks.json +47 -0
- package/dist/batch-GOE3UXOD.js +0 -36
- package/dist/build-M7BMEHL2.js +0 -33
- package/dist/calibrate-6RRAILUY.js +0 -31
- package/dist/capture-RJPQ6B4N.js +0 -32
- package/dist/check-BQD64X3N.js +0 -27
- package/dist/chunk-3R5Y3XOL.js.map +0 -1
- package/dist/chunk-3YHAYXBC.js +0 -164
- package/dist/chunk-3YHAYXBC.js.map +0 -1
- package/dist/chunk-HVFR66GQ.js.map +0 -1
- package/dist/chunk-LJ2RDH3F.js.map +0 -1
- package/dist/chunk-NAZRLJAK.js +0 -3090
- package/dist/chunk-NAZRLJAK.js.map +0 -1
- package/dist/chunk-Q35YIDBV.js.map +0 -1
- package/dist/chunk-QCBICNKC.js.map +0 -1
- package/dist/chunk-SJO2FTPG.js.map +0 -1
- package/dist/chunk-TU2WUBVZ.js +0 -2
- package/dist/cli-TYGOHJOJ.js.map +0 -1
- package/dist/github-3WMSTNTK.js +0 -32
- package/dist/github-OXSJQ4PS.js +0 -27
- package/dist/lint-YDHVVZHT.js +0 -30
- package/dist/list-3ZO2NIYO.js +0 -31
- package/dist/list-EJH6VAOW.js +0 -29
- package/dist/run-IP6NZHAG.js +0 -33
- package/dist/validate-G6HH3YXL.js +0 -16
- /package/dist/{batch-GOE3UXOD.js.map → batch-OZOACN4G.js.map} +0 -0
- /package/dist/{build-M7BMEHL2.js.map → build-Z5VOVADH.js.map} +0 -0
- /package/dist/{calibrate-6RRAILUY.js.map → calibrate-TZYT42DR.js.map} +0 -0
- /package/dist/{capture-RJPQ6B4N.js.map → capture-SL4MNDDP.js.map} +0 -0
- /package/dist/{check-BQD64X3N.js.map → check-5OMA42Y6.js.map} +0 -0
- /package/dist/{check-ESNS7ZBC.js.map → check-OAJWC3WD.js.map} +0 -0
- /package/dist/{check-RQIIIURI.js.map → check-PKY7KTQE.js.map} +0 -0
- /package/dist/{chunk-F2OVHJNC.js.map → chunk-2JFPBZJK.js.map} +0 -0
- /package/dist/{chunk-CP4ZM43V.js.map → chunk-2KRB3EG7.js.map} +0 -0
- /package/dist/{chunk-HQFVJDEN.js.map → chunk-3NWX34ZI.js.map} +0 -0
- /package/dist/{chunk-TCC737IS.js.map → chunk-5G53XATW.js.map} +0 -0
- /package/dist/{chunk-SNEHX4VE.js.map → chunk-733Y2STG.js.map} +0 -0
- /package/dist/{chunk-ELZ2IIOF.js.map → chunk-73TC4DYH.js.map} +0 -0
- /package/dist/{chunk-QY3DOZMS.js.map → chunk-7WRP5FFP.js.map} +0 -0
- /package/dist/{chunk-C6HZ6C5T.js.map → chunk-C3B2VJNC.js.map} +0 -0
- /package/dist/{chunk-NN4ASVTY.js.map → chunk-C73RSUIC.js.map} +0 -0
- /package/dist/{chunk-GAZFKZHM.js.map → chunk-DC5DDJVE.js.map} +0 -0
- /package/dist/{chunk-52PDIZKV.js.map → chunk-ECVQNGTH.js.map} +0 -0
- /package/dist/{chunk-XWFNO5PO.js.map → chunk-GAJIJKET.js.map} +0 -0
- /package/dist/{chunk-K3U5F2QT.js.map → chunk-M6GSS72T.js.map} +0 -0
- /package/dist/{chunk-UK2I3ACY.js.map → chunk-MHM4QEBV.js.map} +0 -0
- /package/dist/{chunk-OIZ3XMKB.js.map → chunk-MWBJ3KTF.js.map} +0 -0
- /package/dist/{chunk-MZC62HPZ.js.map → chunk-NBMPHD4I.js.map} +0 -0
- /package/dist/{chunk-DX32P2DT.js.map → chunk-NTWW7J76.js.map} +0 -0
- /package/dist/{chunk-JQU7FHYH.js.map → chunk-QW77LCOU.js.map} +0 -0
- /package/dist/{chunk-IA7BMIPE.js.map → chunk-SM6NOMJS.js.map} +0 -0
- /package/dist/{chunk-NGK3N6F2.js.map → chunk-T6SYGLWR.js.map} +0 -0
- /package/dist/{chunk-TVIX653Q.js.map → chunk-TLXE7LYK.js.map} +0 -0
- /package/dist/{chunk-TA42STP7.js.map → chunk-UAKNZVCD.js.map} +0 -0
- /package/dist/{chunk-C3KI45RF.js.map → chunk-UJSGS2Y5.js.map} +0 -0
- /package/dist/{chunk-BOIF2XGG.js.map → chunk-XXDXIIQ4.js.map} +0 -0
- /package/dist/{cli-QKIJOXGW.js.map → cli-UO4NHIB3.js.map} +0 -0
- /package/dist/{chunk-TU2WUBVZ.js.map → cli-YHYDLQAF.js.map} +0 -0
- /package/dist/{cli-YW6VWBHX.js.map → config-P4HDNTYD.js.map} +0 -0
- /package/dist/{github-3WMSTNTK.js.map → conformance-ZFCMKM6M.js.map} +0 -0
- /package/dist/{github-OXSJQ4PS.js.map → github-7ZCH5PSO.js.map} +0 -0
- /package/dist/{json-4DCKDGUV.js.map → github-POT6724K.js.map} +0 -0
- /package/dist/{lint-YDHVVZHT.js.map → host-6GURH5HE.js.map} +0 -0
- /package/dist/{list-3ZO2NIYO.js.map → json-KT2K62L5.js.map} +0 -0
- /package/dist/{list-EJH6VAOW.js.map → lint-5AQ5C2BV.js.map} +0 -0
- /package/dist/{page-QEDB7EVU.js.map → list-5JZKX2NK.js.map} +0 -0
- /package/dist/{pretty-5DGEBZSV.js.map → list-SWPFXQMU.js.map} +0 -0
- /package/dist/{pretty-SQX33UM2.js.map → page-UPSXRP75.js.map} +0 -0
- /package/dist/{reporters-OHSI3RMV.js.map → pretty-Q6FQAOK4.js.map} +0 -0
- /package/dist/{reporters-YELDDYJD.js.map → pretty-SQRPFPSQ.js.map} +0 -0
- /package/dist/{run-IP6NZHAG.js.map → provider-AMGRDY6V.js.map} +0 -0
- /package/dist/{validate-G6HH3YXL.js.map → reporters-45MMC2ID.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lint/commands/lint.ts","../src/lint/core/template-registry.ts","../schemas/lint/template.json","../templates/lint/tgdp/manifest.json","../src/lint/core/resolve-template.ts","../src/lint/core/validator.ts","../src/lint/core/config.ts","../schemas/lint/config.json","../src/lint/tools/dita-ot.ts","../src/lint/tools/index.ts"],"sourcesContent":["/**\n * `lint` command core. Resolves targets, picks a parser per file, resolves a\n * template per file, and returns structured results. Free of CLI/IO plumbing so\n * it can be driven directly from tests and from the programmatic API.\n *\n * Template selection is per file, not per run: a page declares what it is\n * (`type: how-to`) and the template follows. `--template` still exists and\n * still overrides everything, but it is no longer required, which is what lets\n * one invocation lint a whole tree of mixed doctypes.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { extname, relative, resolve } from \"node:path\";\nimport { errorMessage, ToolError } from \"../../shared/errors.js\";\nimport {\n LintError,\n type DocumentParser,\n type DocumentTree,\n type FileResult,\n type Finding,\n type Position,\n} from \"../types.js\";\nimport {\n parserByName,\n parserForExtension,\n unsupportedFormatMessage,\n} from \"../parsers/index.js\";\nimport type { Template, TemplateFile } from \"../core/template.js\";\nimport {\n classifyRef,\n refRelativeTo,\n listBuiltins,\n loadResolvedTemplate,\n loadTemplateFile,\n} from \"../core/template-registry.js\";\nimport {\n buildTypeIndex,\n knownTypes,\n resolveTemplateRef,\n FILE_TEMPLATE_KEY,\n type Resolution,\n type TemplateOverride,\n type TypeIndexEntry,\n} from \"../core/resolve-template.js\";\nimport { validateDocument } from \"../core/validator.js\";\nimport {\n resolveStructureTool,\n structureTools,\n type StructureToolDescriptor,\n} from \"../tools/index.js\";\nimport {\n ditaOt,\n runDitaOtValidate,\n type DitaOtMessage,\n} from \"../tools/dita-ot.js\";\nimport { ditaOtHome } from \"../../shared/tools.js\";\nimport {\n assertNonEmpty,\n gitignoreOptions,\n resolveTargetSet,\n STDIN_TOKEN,\n} from \"../../meta/internal.js\";\nimport { isErrorSeverity } from \"../../meta/index.js\";\nimport { resolveLintRun, type LintConfig } from \"../core/config.js\";\n\n/** File label used for a document read from stdin. */\nexport const STDIN_LABEL = \"<stdin>\";\n\nexport interface LintOptions {\n /**\n * Positional inputs: files, directories, or globs. `-` reads stdin. Empty\n * falls back to the `collections:` the config declares (proposal 0041).\n */\n inputs: string[];\n /** `--collection <name>`, repeatable: the collections this run covers. */\n collection?: string[];\n /**\n * `--tool`: which tool performs the structure job. Overrides the config's\n * own `lint.structure.tool`, and defaults to manni's engine.\n */\n tool?: string;\n /** `--template`: a built-in id, a path, or a name inside `templates`. */\n template?: string;\n /**\n * `--templates`: template files whose `types:` join the routing table. A bare\n * `--template <name>` is resolved as a name inside the *last* of them, which\n * is the pairing the manni docevals adapter invokes. Last, not first, to\n * agree with `buildTypeIndex`: a later file overrides an earlier one there,\n * so resolving a bare name against the first would make one name mean two\n * different templates depending on how it was written.\n */\n templates?: string | string[];\n /** `--as`: force an input format, by parser name. */\n as?: string;\n /** `--ext`: extensions kept during directory and glob expansion. */\n exts?: string[];\n /** `--exclude`: globs removed from directory/glob expansion. */\n exclude?: string[];\n cwd?: string;\n /** Content for the `-` input, injected by the CLI and by tests. */\n stdinContent?: string;\n /** `--explain`: record how each file's template was chosen. */\n explain?: boolean;\n /** Repo policy: first matching glob wins. Overrides the config's own. */\n overrides?: TemplateOverride[];\n /** Applied when a page declares no doctype. Overrides the config's own. */\n defaultTemplate?: string;\n /** Explicit doctype -> template ref map. Overrides the config's own. */\n types?: Record<string, string>;\n /** `--allow-empty`: zero matched files is a success, not an error. */\n allowEmpty?: boolean;\n /** `--no-gitignore`: only an explicit `false` travels, so config can decide. */\n respectGitignore?: boolean;\n /** `-c/--config`. */\n configPath?: string;\n /** `--no-config`: skip discovery and run on the built-in defaults. */\n noConfig?: boolean;\n /** Told which config governed the run, and where it came from. */\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n /** Told what the run had to say beside its findings. */\n onNotice?: (message: string) => void;\n /**\n * The DITA-OT seam, injected by tests, as `term lint` injects Vale's.\n *\n * There is no JVM and no DITA-OT on a CI runner of ours, so the only way to\n * exercise this branch end to end is to hand it the process boundary. The\n * shipped default is the real one.\n */\n runDitaOt?: typeof runDitaOtValidate;\n}\n\n/** Extensions that make a bare `--template` value a filename, not a name. */\nconst TEMPLATE_FILE_EXTENSIONS = [\".yaml\", \".yml\", \".json\"];\n\n/**\n * Run a family helper, rethrowing the error it raises as this tool's.\n *\n * The walker and its guards are meta's, and they throw `DocmetaError` - a\n * sibling of `LintError`, not a parent of it. `runLint` is exported from\n * `src/index.ts` and called in process by manni docevals, so a caller doing\n * the documented thing, `catch (err) { if (err instanceof LintError) … }`,\n * missed the two most ordinary operational failures there are: a mistyped path\n * and a pattern matching nothing. The message is the walker's own, which is\n * the part the user reads; only the class changes.\n */\nasync function asLintError<T>(run: () => T | Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (err) {\n if (err instanceof LintError) throw err;\n // A `ToolError` from any sibling is operational and already worded for a\n // user. Anything else is a bug and keeps its class and its stack.\n if (err instanceof ToolError) throw new LintError(err.message);\n throw err;\n }\n}\n\n/** `--templates` accepts one path or several; normalize to a list. */\nfunction templateFiles(value: string | string[] | undefined): string[] {\n if (value == null) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * The options a tool can own, in the order the guard reports them, each paired\n * with how to tell that the caller passed it.\n *\n * `--templates` reads `opts` alone. A `templates:` key in the config describes\n * the repo rather than the invocation, so a repo that has written one must not\n * be refused the moment it points a different tool at the same tree.\n */\nconst TOOL_OPTIONS: {\n spelling: string;\n given: (opts: LintOptions) => boolean;\n}[] = [\n { spelling: \"--template\", given: (o) => o.template !== undefined },\n {\n spelling: \"--templates\",\n given: (o) => templateFiles(o.templates).length > 0,\n },\n { spelling: \"--explain\", given: (o) => o.explain === true },\n { spelling: \"--as\", given: (o) => o.as !== undefined },\n];\n\n/**\n * The options some other registered tool owns and this one does not, each with\n * the tool that does own it.\n *\n * Derived rather than written out per tool, because a hand-written chain is a\n * list the next tool has to remember to join - and the failure mode of\n * forgetting is a silently ignored option, which is a run that reports success\n * having done something other than what was asked.\n */\nfunction foreignOptions(\n tool: StructureToolDescriptor,\n): Map<string, StructureToolDescriptor> {\n const foreign = new Map<string, StructureToolDescriptor>();\n for (const other of structureTools()) {\n // By identity, not by name: the registry holds one descriptor per name, so\n // the tool this run resolved *is* its entry, and comparing the names is a\n // tautology to the compiler while `LintTool` carries one of them.\n if (other === tool) continue;\n for (const spelling of other.ownedOptions) {\n if (tool.ownedOptions.includes(spelling)) continue;\n if (!foreign.has(spelling)) foreign.set(spelling, other);\n }\n }\n return foreign;\n}\n\n/**\n * Refuse an option that belongs to a tool this run is not performing the job\n * with. One message, naming the first offending option.\n *\n * Ignoring it instead would be the quietest kind of wrong answer: the flag\n * reads as applied, the run exits 0, and nothing says the template, the format\n * override or the explanation was dropped on the floor.\n */\nfunction assertOptionsOwned(\n opts: LintOptions,\n tool: StructureToolDescriptor,\n usingStdin: boolean,\n): void {\n const foreign = foreignOptions(tool);\n if (foreign.size === 0) return;\n\n for (const { spelling, given } of TOOL_OPTIONS) {\n const owner = foreign.get(spelling);\n if (owner === undefined || !given(opts)) continue;\n throw new LintError(\n `${spelling} is an option of the ${owner.name} tool; ` +\n `the structure job's tool is ${tool.name}.`,\n );\n }\n\n // Stdin last, and in its own words: \"option\" is the wrong noun for a pipe,\n // and the advice a reader needs is what to pass instead.\n if (usingStdin && foreign.has(STDIN_TOKEN)) {\n throw new LintError(`${tool.name} cannot read stdin. Name files or a map.`);\n }\n}\n\nexport interface LintSummary {\n /** Files actually linted. Always `passed + failed`; skips are counted apart. */\n checked: number;\n passed: number;\n failed: number;\n skipped: number;\n}\n\n/**\n * A `FileResult` plus the human-readable reason a file was skipped.\n *\n * `types.ts` pins the fields manni docevals reads off the JSON reporter, so\n * this diagnostic lives here instead: it exists for pretty output, and the\n * JSON reporter never emits it.\n */\nexport interface LintFileResult extends FileResult {\n /** Why the file was skipped, in words. Set only alongside `skipped`. */\n reason?: string;\n /** How the template was chosen. Present when `--explain` asked for it. */\n resolution?: Resolution;\n /**\n * What the alignment block is rendered from. Present when `--explain` asked\n * for it, and only for a file that routed to a template.\n *\n * Nested rather than flat, because `FileResult.template` is already taken by\n * the template's *ref* - the string a reader sees on the routing line. A\n * loaded `Template` beside it under the same name would be two things called\n * one thing, and the reporter would read whichever it got.\n */\n alignment?: { tree: DocumentTree; template: Template };\n}\n\nexport interface LintRun {\n results: LintFileResult[];\n summary: LintSummary;\n}\n\n/** Zero-width span at the top of a file, for findings with nowhere better. */\nfunction origin(): Position {\n return {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n };\n}\n\nfunction skip(\n file: string,\n reason: string,\n cause: FileResult[\"skipped\"] = \"unsupported-format\",\n): LintFileResult {\n return {\n file,\n // `success` means \"linted and produced no findings\". A file that was never\n // linted is neither passing nor failing, and calling it a pass would hide\n // the gap from anyone reading CI output.\n success: false,\n findings: [],\n template: null,\n skipped: cause,\n reason,\n };\n}\n\nfunction parseFinding(message: string): Finding {\n return {\n type: \"parse_error\",\n heading: null,\n message,\n position: origin(),\n severity: \"error\",\n };\n}\n\n/**\n * A template ref, loaded once per run however many pages route to it.\n *\n * The registry deliberately leaves `extends` unresolved, so the merge happens\n * here. Skipping it would make inheritance silently inert: a template that\n * inherits every one of its sections would check nothing and report a clean\n * pass. `resolveExtends` is a no-op on a template without `extends`.\n */\nfunction templateLoader(): (ref: string) => Promise<Template> {\n const cache = new Map<string, Promise<Template>>();\n return (ref) => {\n let pending = cache.get(ref);\n if (!pending) {\n pending = loadResolvedTemplate(ref);\n cache.set(ref, pending);\n }\n return pending;\n };\n}\n\n/**\n * Normalize `--template` into a ref the registry understands.\n *\n * `--templates t.yaml --template how-to` is the pairing the manni docevals\n * adapter invokes, and it means \"the template named how-to inside t.yaml\".\n * A ref that already names a file, a URL, or a built-in is passed through.\n */\nfunction cliTemplateRef(\n template: string | undefined,\n templatePaths: string[],\n): string | undefined {\n if (!template) return undefined;\n // A ref that names itself is passed through. Sniffing for `#`, `/` and `\\`\n // missed built-in ids, which contain none of them, so `-t tgdp:how-to:1.6`\n // silently became a lookup for a template of that name inside the first\n // `--templates` file the moment one was given.\n if (template.includes(\"#\")) return template;\n if (classifyRef(template).kind !== \"file\") return template;\n if (template.includes(\"/\") || template.includes(\"\\\\\")) return template;\n // A bare filename names a file even without a separator: `--template\n // custom.yaml` means that file, not a template called \"custom.yaml\" inside\n // some other one.\n if (TEMPLATE_FILE_EXTENSIONS.some((e) => template.toLowerCase().endsWith(e))) {\n return template;\n }\n if (templatePaths.length === 0) return template;\n\n // A bare name is a name inside a `--templates` file. Search them last-first,\n // matching `buildTypeIndex`, so the file that wins routing also wins here.\n return `${templatePaths[templatePaths.length - 1] ?? \"\"}#${template}`;\n}\n\n/**\n * Which file a template came from and what it is called there.\n *\n * The validator and the matcher both name the template in messages - the\n * state-cap failure, and the warning about a rule a format cannot answer - and\n * neither can work either out from a `Template` object, which carries no\n * record of where it was loaded from. The ref does: `./templates.yaml#how-to`\n * is a file and a name, and a built-in id is both at once.\n */\nfunction templateIdentity(ref: string): { source: string; template: string } {\n const hash = ref.lastIndexOf(\"#\");\n if (hash === -1) return { source: ref, template: ref };\n return { source: ref.slice(0, hash), template: ref.slice(hash + 1) };\n}\n\n/** The finding a page gets when it declares a doctype nothing serves. */\nfunction unknownTypeFinding(\n resolution: Resolution,\n typeIndex: Map<string, TypeIndexEntry>,\n position: Position,\n): Finding {\n const suggestions = resolution.suggestions ?? [];\n const known = resolution.knownTypes ?? knownTypes(typeIndex);\n const hint = suggestions.length\n ? ` Did you mean ${suggestions.map((s) => `\"${s}\"`).join(\", \")}?`\n : ` Known doctypes: ${known.join(\", \") || \"(none)\"}.`;\n // `unknownType` is set whenever the cause is `unknown-type`, but the type\n // says `string | undefined` and only the resolver enforces the pairing. The\n // fallback keeps a future break from printing `type \"undefined\"`, which\n // would send the reader looking for a doctype they never wrote.\n const declared = resolution.unknownType ?? \"(none recorded)\";\n return {\n type: \"unknown_type\",\n heading: null,\n message:\n `No template serves type \"${declared}\".${hint} ` +\n `Declare it on a template with \"types:\", then pass that file with --templates.`,\n position,\n severity: \"error\",\n };\n}\n\ninterface LintContext {\n getTemplate: (ref: string) => Promise<Template>;\n /** Absolute, forward-slashed form of a display path, for override globs. */\n absoluteOf?: (file: string) => string;\n typeIndex: Map<string, TypeIndexEntry>;\n cliTemplate?: string;\n overrides?: TemplateOverride[];\n defaultTemplate?: string;\n explain: boolean;\n}\n\nasync function lintOne(\n label: string,\n content: string,\n parser: DocumentParser,\n ctx: LintContext,\n): Promise<LintFileResult> {\n let tree: DocumentTree;\n try {\n tree = parser.parse(content, label);\n } catch (err) {\n // A file that will not parse is that file's problem, not the run's: it is\n // reported and the run continues, so one broken page cannot hide the\n // findings in the other 199.\n return {\n file: label,\n success: false,\n findings: [parseFinding(errorMessage(err))],\n template: null,\n };\n }\n\n // Routing needs the frontmatter, so it happens after the parse rather than\n // before the read - which is also what makes `--explain` able to say what the\n // page actually declared.\n const resolution = resolveTemplateRef({\n filePath: label,\n absolutePath: ctx.absoluteOf?.(label),\n frontmatter: tree.frontmatter,\n cliTemplate: ctx.cliTemplate,\n overrides: ctx.overrides,\n typeIndex: ctx.typeIndex,\n defaultTemplate: ctx.defaultTemplate,\n });\n const withResolution = <T extends LintFileResult>(result: T): T =>\n ctx.explain ? { ...result, resolution } : result;\n\n // Anchor anything said about the frontmatter at the frontmatter.\n const metaPosition = tree.frontmatterPosition ?? origin();\n\n if (resolution.ref === null) {\n if (resolution.cause === \"unknown-type\") {\n // A declared type that resolves to nothing is a typo or a gap. Skipping\n // would mean a page silently stops being checked, which is the one\n // outcome worse than a false positive.\n return withResolution({\n file: label,\n success: false,\n findings: [unknownTypeFinding(resolution, ctx.typeIndex, metaPosition)],\n template: null,\n });\n }\n // An untyped page is manni meta's complaint, not this tool's - its OKF\n // schema already requires `type`. Skipping is what lets manni lint be\n // pointed at a whole tree on day one.\n return withResolution(\n skip(\n label,\n \"no type in frontmatter and no template resolved\",\n \"no-template\",\n ),\n );\n }\n\n /** A broken template, reported against this page rather than thrown. */\n const brokenTemplate = (err: unknown): LintFileResult =>\n withResolution({\n file: label,\n success: false,\n findings: [\n {\n type: \"template_error\",\n heading: null,\n message: errorMessage(err),\n position: metaPosition,\n severity: \"error\",\n },\n ],\n template: resolution.ref,\n });\n\n // A page may not send the linter to a URL of its own choosing.\n //\n // `type` comes out of the document too, but it only *selects* from the\n // routing table the operator assembled - a page can ask for a template on\n // offer, not introduce one. `$template` names a reference directly, which\n // makes it the one stage where a document says where a template comes from,\n // and documents are exactly what this tool is pointed at untrusted: a docset\n // from a fork, a contributor's branch, a vendored copy.\n // Left open, one line of frontmatter makes the linting host issue an\n // arbitrary request, which on CI means from inside the build network. The\n // `$ref` hole this mirrors was closed in the template loader; this is the\n // same hole one level up, where the ref itself is attacker-chosen.\n //\n // Refused rather than ignored, because silently falling through to `type`\n // would lint the page against something other than what it asked for and say\n // nothing. An operator who does want a remote template still has every other\n // stage: name it in `templates:`, map it under `types:`, or pass --template.\n if (\n resolution.stage === \"frontmatter-template\" &&\n classifyRef(resolution.ref).kind === \"url\"\n ) {\n return brokenTemplate(\n new LintError(\n `${FILE_TEMPLATE_KEY} may not name a URL (\"${resolution.ref}\"): a document ` +\n `must not choose what the linter fetches. Declare it in manni.config.yaml ` +\n `under \"templates:\" or \"types:\", or pass it with --template.`,\n ),\n );\n }\n\n // A relative `$template` means \"beside this page\", not \"beside wherever the\n // tool happened to be invoked from\". `extends` already follows that rule, and\n // for the same reason: resolved against the process cwd, `$template:\n // ./house.yaml` in `docs/guide.md` found the file only when the run started\n // in `docs/` - so the page routed correctly from one directory and reported\n // \"Template file not found\" from every other, including from CI.\n const ref =\n resolution.stage === \"frontmatter-template\"\n ? refRelativeTo(ctx.absoluteOf?.(label) ?? label, resolution.ref)\n : resolution.ref;\n\n let template: Template;\n try {\n template = await ctx.getTemplate(ref);\n } catch (err) {\n // One unloadable template must not abort a run over a whole tree; it is\n // reported against the pages that route to it.\n return brokenTemplate(err);\n }\n\n // `--explain` stops here, having promised to \"lint nothing\".\n //\n // The line is drawn after loading rather than before it, because loading is\n // part of the answer the flag exists to give: \"which template, and why\" is\n // not answered by naming a ref that does not resolve, and a broken\n // `--templates` path is exactly the surprise someone runs `--explain` to\n // find. Loading is also cheap in the way validation is not - templates are\n // cached, so a tree of one doctype loads once, while `validateDocument` runs\n // per file and its findings are then discarded unrendered.\n if (ctx.explain) {\n return withResolution({\n file: label,\n success: true,\n findings: [],\n template: resolution.ref,\n // The tree and the loaded template, for the alignment block. Both are\n // already in hand here, and neither survives this function otherwise.\n alignment: { tree, template },\n });\n }\n\n let findings: Finding[];\n try {\n findings = validateDocument(tree, template, {\n ...templateIdentity(ref),\n kinds: parser.kinds,\n });\n } catch (err) {\n // Validation can raise for the same reason loading can: a template the\n // author got wrong - an uncompilable `heading.pattern` is the live case.\n // Without this the raise escapes `runLint` and kills the run, so one bad\n // pattern hides the findings in every other page, which is the outcome the\n // load path above exists to prevent.\n if (err instanceof LintError) return brokenTemplate(err);\n throw err;\n }\n\n return withResolution({\n file: label,\n // \"Linted and found nothing wrong\", which is not the same as \"produced no\n // findings\". A `warning` - today, a rule this file's format cannot answer -\n // says something about the run rather than about the document, and the\n // exit code follows `success`, so counting it as a failure would turn a\n // docset of mixed formats permanently red. The family's rule, and the one\n // meta and cite already apply: `ok` is \"no error-severity finding\".\n success: !findings.some(isErrorSeverity),\n findings,\n template: resolution.ref,\n });\n}\n\n/** Everything manni's branch needs, settled once by the shared prefix. */\ninterface ManniRun {\n opts: LintOptions;\n config: LintConfig;\n /** The directory the resolved files are relative to. */\n cwd: string;\n /** The files the shared target resolution produced. */\n files: string[];\n /** The parser `--as` forced, when it did. */\n forcedParser?: DocumentParser;\n /** Whether `-` rode among the inputs. */\n usingStdin: boolean;\n}\n\n/**\n * Lint with manni's own engine.\n *\n * Everything below this line is manni's and nobody else's: the template files,\n * the doctype index, the parser registry, and the guard that fires when a run\n * checks nothing. The loading in particular has to sit here rather than in the\n * shared prefix, because it is file I/O a tool that reads no templates must\n * not be made to pay - and a missing `templates:` entry is then the loader's\n * error under manni and nothing at all under anybody else.\n */\nasync function lintWithManni(run: ManniRun): Promise<LintFileResult[]> {\n const { opts, config, cwd, files, forcedParser, usingStdin } = run;\n\n // The doctype -> template map. Built-ins go in first and user templates\n // overwrite them, so overriding `how-to` for a repo is one file with\n // `types: [how-to]` in it - no config entry, no flag.\n const templatePaths = templateFiles(opts.templates ?? config.templates);\n const getTemplate = templateLoader();\n\n const userFiles: { ref: string; file: TemplateFile }[] = [];\n for (const ref of templatePaths) {\n const file = await loadTemplateFile(ref);\n // `extends` is resolved before `types` is read. A template that inherits\n // its doctypes declares none of its own, so reading the raw file routes\n // nothing to it - the override loads, merges, and is silently inert while\n // the page goes to the built-in it meant to replace. The loader is cached,\n // so this costs nothing the lint would not already pay.\n const templates: Record<string, Template> = {};\n for (const name of Object.keys(file.templates ?? {})) {\n templates[name] = await getTemplate(`${ref}#${name}`);\n }\n userFiles.push({ ref, file: { ...file, templates } });\n }\n\n const typeIndex = buildTypeIndex({\n builtins: listBuiltins(),\n userFiles,\n explicitTypes: opts.types ?? config.types,\n });\n\n const ctx: LintContext = {\n getTemplate,\n absoluteOf: (file) => resolve(cwd, file).replace(/\\\\/g, \"/\"),\n typeIndex,\n cliTemplate: cliTemplateRef(opts.template, templatePaths),\n overrides: opts.overrides ?? config.overrides,\n // `template:` in config is the default for a page that declares no type -\n // the bottom of the chain, not the top. `--template` is the top.\n defaultTemplate: opts.defaultTemplate ?? config.template,\n explain: opts.explain === true,\n };\n\n // `--template` applies to every file, so a ref that will not load is bad\n // usage, not a property of any page. Resolved before a single page is linted\n // so it exits 2 with one message, rather than becoming an identical\n // `template_error` finding on all 200 pages and exiting 1 - which reads to\n // CI as \"the docs are wrong\".\n if (ctx.cliTemplate != null) await getTemplate(ctx.cliTemplate);\n\n const results: LintFileResult[] = [];\n\n // `forcedParser` is set whenever `usingStdin` is - the shared prefix refused\n // the run otherwise - but only that guard knows it, so the pair is tested\n // rather than asserted.\n if (usingStdin && forcedParser) {\n results.push(\n await lintOne(STDIN_LABEL, opts.stdinContent ?? \"\", forcedParser, ctx),\n );\n }\n\n for (const file of files) {\n const ext = extname(file);\n const parser = forcedParser ?? parserForExtension(ext);\n // The parser is chosen before the read, not after, because a file nothing\n // can parse must not depend on being readable to be reported. Reading\n // first pulls every skipped file into memory for nothing, and an\n // unreadable one - a denied permission, a file a doc build moved mid-run -\n // throws out of this loop, so a `.xyz` nobody asked us to parse takes the\n // whole run down with it: exit 2 and no verdict for any of the pages after\n // it, instead of one skip line and a report.\n if (!parser) {\n results.push(skip(file, unsupportedFormatMessage(ext || file)));\n continue;\n }\n let content: string;\n try {\n content = await readFile(resolve(cwd, file), \"utf8\");\n } catch (err) {\n // The read is the other thing that can fail per file, and it failed out\n // of the loop: a denied permission, a symlink loop, or a page a doc\n // build moved between `resolveTargetSet` and here threw straight through\n // `runLint` to `fail()`. Exit 2, no report, and every result already\n // gathered discarded - which is exactly what moving the parser lookup\n // before the read was meant to prevent, one step further along.\n //\n // The OS message travels verbatim: \"permission denied\" and \"no such\n // file\" send the reader to different places, and this code cannot tell\n // which it was.\n results.push(\n skip(file, `could not be read: ${errorMessage(err)}`, \"unreadable\"),\n );\n continue;\n }\n results.push(await lintOne(file, content, parser, ctx));\n }\n\n return results;\n}\n\n/** Everything the DITA-OT branch needs, and nothing else. */\ninterface DitaOtRun {\n /** The directory the resolved files are relative to. */\n cwd: string;\n /** The files the shared target resolution produced, as it spelled them. */\n files: string[];\n /** `tools.dita-ot.home`, resolved; undefined means the `dita` on PATH. */\n home?: string;\n onNotice?: (message: string) => void;\n /** The seam. The shipped default starts the real launcher. */\n validate: typeof runDitaOtValidate;\n}\n\n/** One DITA-OT message as a finding. */\nfunction ditaOtFinding(message: DitaOtMessage): Finding {\n // The code is the finding's `type`, so `ruleId` yields\n // `manni:lint/structure/DOTX010E` and the JSON reporter publishes the code\n // DITA-OT's own message reference is indexed by.\n const point =\n message.line === undefined\n ? null\n : {\n line: message.line,\n // The log calls the column `row`; see `readMessage` in the seam.\n column: message.column ?? 1,\n // DITA-OT reports no byte offset, and nothing downstream needs one.\n offset: 0,\n };\n return {\n type: message.code,\n heading: null,\n message: message.message,\n position: point === null ? origin() : { start: point, end: point },\n severity: message.severity,\n };\n}\n\n/**\n * Lint with DITA Open Toolkit.\n *\n * One invocation per target, and the log is the verdict. A map's log speaks\n * about the topics it reached, so the findings are grouped by the file each\n * message named rather than by the file the run was pointed at - which is how\n * one map target produces a result per topic.\n */\nasync function lintWithDitaOt(run: DitaOtRun): Promise<LintFileResult[]> {\n const { cwd, files, onNotice } = run;\n\n /**\n * Only the formats this tool says it reads are handed to it.\n *\n * `manni lint tools` prints that list, and the rule the registry documents\n * is that a listed format is one that is read. Passing anything else on and\n * reporting whatever came back meant a `notes.md` named on the command line\n * came out as a clean pass, because DITA-OT shrugged and logged nothing to\n * disagree with. The manni branch skips a format no parser claims; this is\n * the same answer for the same reason.\n */\n const readable = ditaOt.formats().flatMap((format) => format.extensions);\n const reads = new Set(readable.map((ext) => ext.toLowerCase()));\n const targets: string[] = [];\n const unsupported: LintFileResult[] = [];\n for (const file of files) {\n if (reads.has(extname(file).toLowerCase())) targets.push(file);\n else {\n unsupported.push(\n skip(\n file,\n `dita-ot does not read \"${extname(file) || file}\". It reads ${readable.join(\", \")}.`,\n ),\n );\n }\n }\n\n // The label the run resolved is what the report says, whatever spelling the\n // absolute path comes back with.\n /**\n * One entry per absolute path, first spelling wins.\n *\n * **Defensive, not a fix for anything reachable today.** `resolveTargetSet`\n * normalizes every input to a cwd-relative posix path and holds them in a\n * `Set`, so two spellings of one file cannot arrive here. Review raised the\n * collision as a hazard and it is a real one in isolation: built naively the\n * later label replaces the earlier, `byFile` keeps both, and findings reach\n * only the survivor while the other reports as a clean pass.\n *\n * Kept because this function starts a JVM per target, so being correct on\n * its own terms rather than on its caller's is worth eight lines. Do not\n * write a test that claims to prove the collision: it cannot be reached\n * from `runLint`, and a test that passes either way proves nothing.\n */\n const labels = new Map<string, string>();\n for (const file of targets) {\n const absolute = resolve(cwd, file);\n if (!labels.has(absolute)) labels.set(absolute, file);\n }\n const unique = [...labels.values()];\n const labelOf = (absolute: string): string => {\n const known = labels.get(absolute);\n if (known !== undefined) return known;\n const near = relative(cwd, absolute).replace(/\\\\/g, \"/\");\n return near === \"\" ? absolute : near;\n };\n\n for (const file of unique) {\n // Checked, not skipped: a topic on its own is validated, it just resolves\n // fewer references than the same topic reached through its map. Calling it\n // a skip would say nothing was checked, which is false.\n if (extname(file).toLowerCase() !== \".ditamap\") {\n onNotice?.(\n `checked ${file} without a map; references outside it are not resolved.`,\n );\n }\n }\n\n // Nothing this tool can read is not a run: starting a JVM per target for an\n // empty list would be work with no answer at the end of it.\n if (unique.length === 0) return unsupported;\n\n const results = await run.validate({\n targets: [...labels.keys()],\n cwd,\n ...(run.home === undefined ? {} : { home: run.home }),\n });\n\n // Seeded with the targets, so a target DITA-OT had nothing to say about is a\n // result that passed rather than a file missing from the report.\n const byFile = new Map<string, Finding[]>(unique.map((file) => [file, []]));\n const findingsFor = (file: string): Finding[] => {\n let list = byFile.get(file);\n if (list === undefined) {\n list = [];\n byFile.set(file, list);\n }\n return list;\n };\n\n // A topic DITA-OT names that nobody targeted becomes a result of its own, so\n // `checked` can exceed the number of paths the user typed. That is the\n // answer, not an inflation of it: pointing at a map is how you ask about the\n // topics it gathers, and a finding against `topic.dita` has to be reported\n // against `topic.dita` for anyone to fix it. One map in, two files checked.\n for (const { target, messages } of results) {\n const invoked = labelOf(target);\n for (const message of messages) {\n // A message with no location belongs to the target the run was invoked\n // on, at a zero-width origin: DITA-OT's semantic messages routinely\n // carry no file, and discarding them would drop real errors.\n const file =\n message.file === undefined\n ? invoked\n : labelOf(resolve(cwd, message.file));\n findingsFor(file).push(ditaOtFinding(message));\n }\n }\n\n return [\n ...unsupported,\n ...[...byFile].map(([file, findings]) => ({\n file,\n // The family's rule, as manni's branch applies it: `ok` is \"no\n // error-severity finding\", so a warning-only file stays green.\n success: !findings.some(isErrorSeverity),\n findings,\n template: null,\n })),\n ];\n}\n\n/**\n * A run that found files and checked none of them is the worst thing this tool\n * can do quietly: it exits 0 and reads as a clean bill of health for a docset\n * nothing looked at. A repo that adopts manni lint in CI before backfilling\n * `type:` keys would get a permanently green job.\n *\n * This replaces a guard that tested `typeIndex.size === 0` before any file was\n * read. That could never fire - the index is always seeded with the built-ins\n * - so it protected nothing. Counting the outcome does.\n *\n * `--explain` is exempt: showing why nothing routed is exactly its job.\n */\nfunction assertSomethingWasChecked(\n results: LintFileResult[],\n explain: boolean,\n tool: StructureToolDescriptor,\n): void {\n const skipped = results.filter((r) => r.skipped != null).length;\n const checked = results.length - skipped;\n if (explain || checked > 0 || skipped === 0) return;\n\n // The advice has to follow the cause. The guard fires on any skip, but it\n // used to describe routing only - so a run over files no parser claims was\n // told to add a `type:` key, which changes nothing about an extension the\n // tool cannot read. Advice the reader cannot act on is only half a guard.\n const unrouted = results.filter((r) => r.skipped === \"no-template\").length;\n const unsupported = results.filter(\n (r) => r.skipped === \"unsupported-format\",\n ).length;\n const unreadable = results.filter((r) => r.skipped === \"unreadable\").length;\n const advice = [\n unrouted > 0\n ? `${unrouted} had no template: give a page a \"type:\" that a template ` +\n `serves, pass -t/--template <ref>, or set \"lint.template\" as a default.`\n : null,\n unsupported > 0\n ? // `--as` belongs to manni's engine, so offering it to someone running\n // another tool is advice that answers with a usage error. The formats\n // line is true for every tool, because every tool declares one.\n `${unsupported} ${unsupported === 1 ? \"is\" : \"are\"} in a format ` +\n `${tool.name} does not read: ` +\n (tool.ownedOptions.includes(\"--as\")\n ? `pass --as <format> to force one, or target files in a format `\n : `target files in a format `) +\n `\"manni lint tools\" lists.`\n : null,\n unreadable > 0\n ? `${unreadable} could not be read: check the permissions on those ` +\n `paths, or drop them from the run with --exclude <glob>.`\n : null,\n ]\n .filter((line): line is string => line !== null)\n .join(\" \");\n\n throw new LintError(\n `Nothing was checked: all ${skipped} file(s) were skipped. ${advice} ` +\n `Run \"manni lint structure <paths> --explain\" to see how each file resolved.`,\n );\n}\n\nexport async function runLint(opts: LintOptions): Promise<LintRun> {\n // Which config governs the run, and what it covers: positional paths, or\n // the `collections:` the family file declares. One base per run, so a\n // collection's globs resolve beside the config that wrote them.\n const run = await resolveLintRun({\n cwd: opts.cwd ?? process.cwd(),\n configPath: opts.configPath,\n noConfig: opts.noConfig,\n inputs: opts.inputs,\n collection: opts.collection,\n onConfigLoaded: opts.onConfigLoaded,\n });\n const config: LintConfig = run.config;\n const cwd = run.base;\n\n if (run.inputs.length === 0) {\n throw new LintError(\n \"No files to check. Pass paths/globs, or declare a collection under `collections:` in manni.config.yaml.\",\n );\n }\n\n // Which tool performs the structure job: `--tool` first, then the config's\n // `lint.structure.tool`, then manni's own engine. Settled before anything is\n // read, because it decides what \"anything\" even is - the extensions a walk\n // collects, and which of the options below the run may carry at all.\n const tool = resolveStructureTool(\n opts.tool ?? config.structure?.tool ?? \"manni\",\n );\n\n const usingStdin = run.inputs.includes(STDIN_TOKEN);\n assertOptionsOwned(opts, tool, usingStdin);\n\n // Resolve `--as` before anything is read: a typo should fail immediately,\n // not after walking a tree of files it was going to mis-parse anyway.\n //\n // A name no parser answers to is unknown, whatever it is meant to name: the\n // formats `manni lint tools` lists are exactly the ones `--as` accepts.\n const forcedParser = opts.as != null ? parserByName(opts.as) : undefined;\n if (opts.as != null && !forcedParser) {\n throw new LintError(\n `Unknown format \"${opts.as}\". Run \"manni lint tools\" to see the formats manni lint reads.`,\n );\n }\n\n // Before the targets are resolved, as cite does: a mistyped path beside `-`\n // was reported as \"File not found\", which names the wrong mistake when the\n // run could not have read stdin either way.\n if (usingStdin && !forcedParser) {\n throw new LintError(\n \"Reading from stdin (-) requires --as <format> to choose a parser.\",\n );\n }\n\n const fileInputs = run.inputs.filter((input) => input !== STDIN_TOKEN);\n // The resolved tool's own walk set is the default, not the walker's. Left\n // undefined, the family walker falls back to the *metadata* tool's extractor\n // extensions, which sweep in every format that tool reads - `.xml` among\n // them, where manni lint's registry honours `walkExtensions` and\n // deliberately walks `.dita` and not `.xml`, so an ordinary `pom.xml` cannot\n // fail a clean tree. Read off the descriptor rather than off that registry\n // directly, so a tool with input formats of its own walks for those.\n // Resolved here rather than at the walk, so `assertNonEmpty` names the same\n // set it filtered with.\n const exts = opts.exts ?? forcedParser?.extensions ?? tool.walkExtensions();\n const allowEmpty = opts.allowEmpty ?? config.allowEmpty;\n // A collection's `exclude:` shapes the collection, so it applies when the\n // inputs came from the collections and never to a path the operator typed.\n // `--exclude` filters either way; the union is deduplicated.\n const exclude = [\n ...new Set([\n ...(opts.exclude ?? []),\n ...(run.fromCollections ? run.collections.flatMap((c) => c.exclude) : []),\n ]),\n ];\n const { files, gitignoreSkipped } = await asLintError(() =>\n resolveTargetSet({\n inputs: fileInputs,\n exts,\n exclude,\n cwd,\n allowEmpty,\n ...gitignoreOptions({\n flag: opts.respectGitignore,\n onNotice: opts.onNotice,\n }),\n }),\n );\n // Resolving zero files is an operational error, not a pass: with no files\n // there is no verdict, and exit 0 would read as a clean bill of health.\n await asLintError(() => {\n assertNonEmpty({\n files,\n inputs: fileInputs,\n usingStdin,\n allowEmpty,\n exclude,\n exts,\n gitignoreSkipped,\n action: \"linted\",\n });\n });\n\n // The branch per tool. Reaching the refusal is an internal fault rather than\n // user error - every name the registry does not carry was refused by\n // `resolveStructureTool` above - and it is a `LintError` for that reason: a\n // bare `Error` escapes the bin runner and exits 1, the code that means \"the\n // documents have findings\".\n const name: string = tool.name;\n let results: LintFileResult[];\n if (name === \"manni\") {\n results = await lintWithManni({\n opts,\n config,\n cwd,\n files,\n forcedParser,\n usingStdin,\n });\n } else if (name === \"dita-ot\") {\n // `tools:` is the family's key, not the `lint:` section's, so the home is\n // resolved against the directory of the config that declared it.\n const home = ditaOtHome(run.tools, run.configDir ?? cwd);\n results = await lintWithDitaOt({\n cwd,\n files,\n ...(home === undefined ? {} : { home }),\n ...(opts.onNotice === undefined ? {} : { onNotice: opts.onNotice }),\n validate: opts.runDitaOt ?? runDitaOtValidate,\n });\n } else {\n throw new LintError(`No implementation is registered for the ${name} tool.`);\n }\n\n // Every tool, not just manni's. A run that resolved files and checked none\n // of them exits 0 and reads as a clean bill of health, which is the worst\n // thing this command can do quietly. It sat inside the manni branch until\n // `dita-ot` started skipping the formats it does not read, at which point\n // `manni lint structure notes.md` reported \"0 files checked, 1 skipped\" and\n // exited 0. One call here is one the next tool cannot forget to make.\n assertSomethingWasChecked(results, opts.explain === true, tool);\n\n const skipped = results.filter((r) => r.skipped != null).length;\n const failed = results.filter((r) => r.skipped == null && !r.success).length;\n const checked = results.length - skipped;\n\n return {\n results,\n summary: { checked, passed: checked - failed, failed, skipped },\n };\n}\n","/**\n * Source of truth for templates. Holds the built-in doctype templates\n * (addressed by `vendor:name:version`-style ids) and knows how to load any\n * template reference - a built-in id, a local `.yaml`/`.yml`/`.json` path, or\n * an `http(s)` URL, optionally naming one template inside a file with a `#`\n * fragment (`./templates.yaml#how-to`).\n *\n * Two ordering decisions are load-bearing:\n *\n * - A file is dereferenced *before* it is validated. `$ref` is how a template\n * file shares one section rule between templates, and a section rule is\n * `additionalProperties: false`, so any `$ref` still standing at validation\n * time would be reported as an unexpected key.\n * - `instructions` is looked for by hand *before* Ajv runs. The schema rejects\n * it too (`not: {required: [\"instructions\"]}`), but a raw schema error says\n * nothing useful about where the feature went; scanning the parsed data\n * directly is both more reliable than reverse-engineering which Ajv error\n * came from the `not` keyword and the only way to quote the author's own\n * instruction back at them in the migration snippet.\n *\n * `extends` is deliberately *not* resolved by `loadTemplate`. Inheritance needs\n * a resolver that knows what a relative ref is relative to, which only the\n * caller knows, so `resolveExtends` takes that resolver as an argument.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { packageRoot } from \"../../shared/package-root.js\";\nimport { parse as parseYaml } from \"yaml\";\nimport { dereference } from \"@apidevtools/json-schema-ref-parser\";\nimport * as AjvNs from \"ajv\";\nimport type { ErrorObject, SchemaObject, ValidateFunction } from \"ajv\";\nimport { LintError } from \"../types.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\nimport { warn } from \"../../shared/warn.js\";\nimport { BLOCK_KINDS, isWildcard, occurrenceRange } from \"./template.js\";\nimport type {\n BlockRule,\n ListItemsRule,\n Occurrences,\n Rule,\n Template,\n TemplateFile,\n} from \"./template.js\";\nimport templateFileSchema from \"../../../schemas/lint/template.json\" with { type: \"json\" };\nimport tgdpManifest from \"../../../templates/lint/tgdp/manifest.json\" with { type: \"json\" };\n\n// ajv is CommonJS with a default export; under NodeNext the constructable\n// value lives on `.default`. Cast through the named default type so tsc sees a\n// constructor.\ntype AjvCtor = typeof import(\"ajv\").default;\nconst Ajv = AjvNs.default as unknown as AjvCtor;\n\n/**\n * Options for both `dereference` calls, named so the two cannot drift apart.\n *\n * `resolve.external: false` confines `$ref` to the file it is written in. A\n * template file is untrusted input - `loadTemplateFile` will fetch one over\n * `http(s)`, and a local one is only as trustworthy as whoever wrote it, who is\n * not necessarily whoever runs the lint. The dereferencer resolves a `$ref` by\n * reading the file or making the request it names, from the linting host and\n * with its privileges. Left on, `$ref: /etc/passwd` or\n * `$ref: http://169.254.169.254/latest/meta-data/` in a template turned a lint\n * run into an arbitrary read, and a remote template into one that reports back\n * what it found.\n *\n * Nothing in the template DSL wants it. `$ref` is documented as how one file\n * shares a section rule between its own templates, and reuse *across* files is\n * what `extends` is for - which goes through `loadTemplate`, is re-based against\n * the declaring file, and is cycle-checked. Same-document (`#/...`) pointers are\n * untouched; an external one is left standing instead, and the schema then\n * rejects it as an unexpected `$ref` key rather than following it.\n */\nconst DEREFERENCE_OPTIONS = { resolve: { external: false } };\n\nexport interface BuiltinInfo {\n id: string;\n title: string;\n types: string[];\n}\n\n/**\n * Built-in doctype templates.\n *\n * The manifest is a JSON import, so it is bundled and `listBuiltins()` stays\n * synchronous. The templates themselves are YAML files beside it, read on\n * demand: they are meant to be opened, read, and copied by anyone writing their\n * own template, and YAML is the format that survives that. They ship at the\n * package root as `templates/lint/**`, listed in `files`, and are found by\n * walking up from this module to `package.json`, which works identically from\n * source and from the built package.\n */\ninterface ManifestEntry {\n id: string;\n file: string;\n title: string;\n types: string[];\n /** Upstream file this was derived from, for provenance. */\n source?: string;\n}\n\ninterface Manifest {\n vendor: string;\n upstream: string;\n pin: string;\n templates: ManifestEntry[];\n}\n\nconst MANIFESTS: Manifest[] = [tgdpManifest];\n\nconst BUILTINS = new Map<string, ManifestEntry>();\nfor (const manifest of MANIFESTS) {\n for (const entry of manifest.templates) BUILTINS.set(entry.id, entry);\n}\n\n/** Parsed built-ins, keyed by id. Populated on first load. */\nconst builtinCache = new Map<string, Template>();\n\nexport function listBuiltins(): BuiltinInfo[] {\n return [...BUILTINS.values()].map((entry) => ({\n id: entry.id,\n title: entry.title,\n types: entry.types,\n }));\n}\n\n/**\n * Locate a built-in's YAML at the package root.\n *\n * The templates ship as `templates/lint/**` beside `package.json`. Resolving\n * from the package root rather than from `import.meta.url` is what keeps one\n * path correct both in the repo, where this module sits under\n * `src/lint/core/`, and in the bundle, where it sits in `dist/`.\n */\nasync function readBuiltinFile(id: string, file: string): Promise<string> {\n const path = join(packageRoot(import.meta.url), \"templates\", \"lint\", file);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n throw new LintError(\n `Built-in template \"${id}\" is registered but its file could not be read (${path}): ${errorMessage(err)}`,\n );\n }\n}\n\n/**\n * Read, validate, and cache one built-in.\n *\n * Built-ins go through exactly the same schema validation as a user's file. A\n * template we ship is not more trustworthy than one you write - it is only\n * better tested - and a shipped template that violates its own schema should\n * fail loudly here rather than behave strangely during matching.\n */\nasync function loadBuiltin(id: string): Promise<Template> {\n const cached = builtinCache.get(id);\n if (cached) return cached;\n\n // Every caller checks `BUILTINS.has` first, so this is an internal\n // invariant rather than a user error - but it fails loudly all the same,\n // because the alternative is a `TypeError` several lines later on a field\n // of `undefined`.\n const entry = BUILTINS.get(id);\n if (!entry) {\n throw new LintError(`No built-in template is registered under \"${id}\".`);\n }\n const raw = await readBuiltinFile(id, entry.file);\n\n // The same guard `loadTemplateFile` applies to an outside file. A built-in\n // is ours and should never fail it, which is the point: if one ever does,\n // this says which file and what it held, rather than letting `dereference`\n // report something opaque about a value it did not expect.\n const parsed: unknown = parseYaml(raw);\n if (!isRecord(parsed)) {\n throw new LintError(\n `Built-in template \"${id}\" (${entry.file}) is not a template file: ` +\n `expected an object at the top level, got ${\n parsed === null ? \"null\" : Array.isArray(parsed) ? \"an array\" : typeof parsed\n }.`,\n );\n }\n\n const file = validateTemplateFile(\n await dereference<Record<string, unknown>>(parsed, DEREFERENCE_OPTIONS),\n entry.file,\n );\n const names = Object.keys(file.templates ?? {});\n const template = file.templates?.[names[0] ?? \"\"];\n if (!template || names.length !== 1) {\n throw new LintError(\n `Built-in template \"${id}\" must define exactly one template, found ${names.length}.`,\n );\n }\n\n // `types` lives in the manifest so the type map can be built without reading\n // every YAML file; mirror it onto the template so both agree.\n const resolved: Template = { types: entry.types, ...template };\n builtinCache.set(id, resolved);\n return resolved;\n}\n\nexport type RefKind = \"builtin\" | \"file\" | \"url\";\n\n/**\n * A built-in id looks like `seg(:seg)+` using only [a-z0-9._-] segments, with\n * no path separators and no template-file extension. This deliberately\n * excludes Windows paths (`C:\\...`), URLs, and `.yaml`/`.yml`/`.json` files so\n * a typo'd built-in is reported as an unknown id rather than silently treated\n * as a missing file.\n */\nconst BUILTIN_ID = /^[a-z0-9][a-z0-9._-]*(?::[a-z0-9][a-z0-9._-]*)+$/i;\n\nconst FILE_EXTENSIONS = [\".yaml\", \".yml\", \".json\"];\n\nexport function classifyRef(ref: string): { kind: RefKind; ref: string } {\n if (/^https?:\\/\\//i.test(ref)) return { kind: \"url\", ref };\n const lower = ref.toLowerCase();\n if (\n !ref.includes(\"/\") &&\n !ref.includes(\"\\\\\") &&\n !FILE_EXTENSIONS.some((ext) => lower.endsWith(ext)) &&\n BUILTIN_ID.test(ref)\n ) {\n return { kind: \"builtin\", ref };\n }\n return { kind: \"file\", ref };\n}\n\n/** Default network timeout for fetching a remote (`http(s)`) template file. */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface LoadTemplateOptions {\n /** Abort a remote fetch after this many ms (default 10_000). */\n timeoutMs?: number;\n}\n\n/* -------------------------------------------------------------------------- *\n * Validation\n * -------------------------------------------------------------------------- */\n\n// `useDefaults` is deliberately off, and the schema declares no `default`\n// anywhere. A default Ajv writes into the data is indistinguishable from a\n// value the author typed, so it beats the parent's real value in an `extends`\n// merge. Every default in this format lives in the code that reads it\n// (`occurrenceRange`), where an absent key stays absent.\nconst ajv = new Ajv();\n\nlet compiled: ValidateFunction | null = null;\n\nfunction fileValidator(): ValidateFunction {\n compiled ??= ajv.compile(templateFileSchema as SchemaObject);\n return compiled;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** One `instructions` key found in a template file, and where it lives. */\ninterface InstructionsHit {\n /** Dotted path to the rule carrying it, e.g. `templates.how-to.sections.title`. */\n path: string;\n /** Suggested eval name, kebab-cased from the path. */\n name: string;\n /** The instruction strings, verbatim. */\n values: string[];\n}\n\n/** The strings in an `instructions:` value, whatever shape it was written in. */\nfunction stringsOf(value: unknown): string[] {\n if (typeof value === \"string\") return [value];\n if (Array.isArray(value)) return value.filter((v): v is string => typeof v === \"string\");\n return [];\n}\n\n/**\n * Name for the suggested eval: the path with the structural keys (`templates`,\n * `sections`) dropped, kebab-cased. `templates.how-to.sections.title` becomes\n * `how-to-title`.\n */\nfunction suggestEvalName(segments: string[]): string {\n const name = segments\n .filter((segment) => segment !== \"templates\" && segment !== \"sections\")\n .join(\"-\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return name || \"section-instructions\";\n}\n\n/**\n * First `instructions` key anywhere in a parsed template file.\n *\n * Dereferencing can make two rules share one object, and a self-referential\n * `$ref` can make the tree cyclic, hence the `seen` set.\n */\nfunction findInstructions(\n node: unknown,\n segments: string[] = [],\n seen = new WeakSet(),\n): InstructionsHit | null {\n if (typeof node !== \"object\" || node === null) return null;\n if (seen.has(node)) return null;\n seen.add(node);\n\n if (Array.isArray(node)) {\n for (const [index, item] of node.entries()) {\n const hit = findInstructions(item, [...segments, String(index)], seen);\n if (hit) return hit;\n }\n return null;\n }\n\n const record = node as Record<string, unknown>;\n\n // Only a section's own `instructions` *property* is the legacy key. Inside a\n // `sections:` map the keys are section names the author chose, and a section\n // legitimately called \"instructions\" - TGDP's README doctype wants one - must\n // not be mistaken for it. So descend into a `sections` map without testing\n // its keys, and test the property everywhere else.\n const insideSectionsMap = segments.at(-1) === \"sections\";\n if (!insideSectionsMap && \"instructions\" in record) {\n return {\n path: segments.join(\".\"),\n name: suggestEvalName(segments),\n values: stringsOf(record.instructions),\n };\n }\n for (const [key, value] of Object.entries(record)) {\n const hit = findInstructions(value, [...segments, key], seen);\n if (hit) return hit;\n }\n return null;\n}\n\n/**\n * The message a template still carrying `instructions` gets: what replaced the\n * feature, and the config to paste, with the author's own instruction in it.\n */\nfunction instructionsMessage(source: string, hit: InstructionsHit): string {\n const assertion = hit.values[0] ?? \"\";\n return [\n `${source}: \"${hit.path}\" uses \\`instructions\\`, which manni lint no longer evaluates — structure checking is deterministic. Move it to a manni docevals assertion eval in manni.config.yaml:`,\n \"\",\n \" docevals:\",\n \" evals:\",\n ` ${hit.name}:`,\n ` assertion: ${assertion}`,\n \" grader: ai\",\n ].join(\"\\n\");\n}\n\n/* -------------------------------------------------------------------------- *\n * Refusing a v1 file\n *\n * v1 never shipped, so there is no migration mode and no alias. What there is\n * instead is a sentence per key naming the v2 spelling, because a v1 file is\n * what anyone who tried the format early still has on disk. Left to the schema\n * these all read as \"must NOT have additional properties\", which says nothing\n * about where the key went.\n *\n * The scan runs before Ajv, for the same reason the `instructions` scan does.\n * -------------------------------------------------------------------------- */\n\n/** v1 keys that are gone outright, and what to write instead. */\nconst V1_KEYS: Record<string, string> = {\n required: '\"required\" is not a template key. A rule is optional with \"min: 0\".',\n additionalSections:\n '\"additionalSections\" is not a template key. v2 has no equivalent, so drop it.',\n code_blocks: '\"code_blocks\" is not a template key. The v2 spelling is \"codeBlocks\".',\n};\n\nconst V1_SECTIONS_MAP =\n '\"sections\" is a list of rules, not a map. Name each rule with \"id\" and list them in order.';\n\nconst V1_REPEAT_BOOLEAN =\n '\"repeat\" is a list of rules, not a boolean. A rule repeats through \"min\" and \"max\".';\n\nconst V1_PARAGRAPH_PATTERNS =\n '\"patterns\" is not a paragraphs key. The v2 spelling is \"pattern\", one regular expression.';\n\n/**\n * The first v1 key anywhere under `templates` or `components`.\n *\n * `info` is skipped: it is free-form by contract, so a key of one of these\n * names in it is the author's own metadata rather than a template rule.\n *\n * A node carrying a `sections` *map* returns before its children are walked.\n * Inside such a map the keys are section names the author chose, and a v1\n * template with a section named `required` must not be reported as using the\n * `required` key.\n */\nfunction findV1Key(node: unknown, seen = new WeakSet()): string | null {\n if (typeof node !== \"object\" || node === null) return null;\n if (seen.has(node)) return null;\n seen.add(node);\n\n if (Array.isArray(node)) {\n for (const item of node) {\n const hit = findV1Key(item, seen);\n if (hit) return hit;\n }\n return null;\n }\n\n const record = node as Record<string, unknown>;\n for (const [key, message] of Object.entries(V1_KEYS)) {\n if (key in record) return message;\n }\n if (isRecord(record.sections)) return V1_SECTIONS_MAP;\n if (typeof record.repeat === \"boolean\") return V1_REPEAT_BOOLEAN;\n if (isRecord(record.paragraphs) && \"patterns\" in record.paragraphs) {\n return V1_PARAGRAPH_PATTERNS;\n }\n\n for (const value of Object.values(record)) {\n const hit = findV1Key(value, seen);\n if (hit) return hit;\n }\n return null;\n}\n\nfunction findV1File(data: unknown): string | null {\n if (!isRecord(data)) return null;\n return findV1Key(data.templates) ?? findV1Key(data.components);\n}\n\n/* -------------------------------------------------------------------------- *\n * Checks JSON Schema cannot express\n *\n * Three of these compare two siblings, which draft-07 has no keyword for, and\n * one compiles a regular expression, which it has no opinion about. The fifth\n * is a warning rather than an error: the template is legal, and only its author\n * can say whether the ambiguity is intended.\n * -------------------------------------------------------------------------- */\n\n/** How a rule is named in a message: its id where it has one, else its path. */\nfunction ruleLabel(rule: Rule, path: string): string {\n return rule.id === undefined ? path : `\"${rule.id}\"`;\n}\n\n/**\n * `max` below `min`, where the author wrote both.\n *\n * Only where both are written. `min` defaults to one, so comparing against the\n * default would refuse `max: 0`, which is how the format spells \"none of\n * these\".\n */\nfunction checkRange(range: Occurrences, source: string, label: string): void {\n const { min, max } = range;\n if (min !== undefined && max !== undefined && max < min) {\n throw new LintError(`${source}: rule ${label} sets max ${max} below min ${min}.`);\n }\n}\n\n/**\n * Whether two rules claim the same occurrence range, the one comparison the\n * ambiguity warning below needs. Compares what `occurrenceRange` resolves\n * `min`/`max` to, not the raw (possibly absent) fields, so a written `min: 1`\n * and an absent `min` - both one occurrence by default - count as the same.\n */\nfunction sameOccurrenceRange(a: Occurrences, b: Occurrences): boolean {\n const rangeA = occurrenceRange(a);\n const rangeB = occurrenceRange(b);\n return rangeA.min === rangeB.min && rangeA.max === rangeB.max;\n}\n\n/**\n * Compile an author's pattern here, so a broken one names the file it is in.\n *\n * \"Broken\" means it does not compile. A pattern that compiles can still hang\n * the run by backtracking, and this does not catch that; `compilePattern` in\n * `../rules/index.ts` carries the full note, including why a length cap is not\n * the mitigation it looks like.\n */\nfunction checkPattern(pattern: string | undefined, source: string): void {\n if (pattern === undefined) return;\n try {\n new RegExp(pattern);\n } catch (err) {\n throw new LintError(\n `Invalid pattern \"${pattern}\" in ${source}: ${errorMessage(err)}.`,\n );\n }\n}\n\nfunction checkListItems(items: ListItemsRule, source: string, path: string): void {\n checkRange(items, source, path);\n if (items.contains) checkBlockRule(items.contains, source, `${path}.contains`);\n items.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.sequence[${index}]`);\n });\n}\n\nfunction checkBlockRule(block: BlockRule, source: string, path: string): void {\n for (const kind of BLOCK_KINDS) {\n const counted: Occurrences | undefined = block[kind];\n if (counted) checkRange(counted, source, `${path}.${kind}`);\n }\n checkPattern(block.paragraphs?.pattern, source);\n if (block.lists?.items) {\n checkListItems(block.lists.items, source, `${path}.lists.items`);\n }\n const element = block.elements;\n if (element?.contains) {\n checkBlockRule(element.contains, source, `${path}.elements.contains`);\n }\n element?.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.elements.sequence[${index}]`);\n });\n}\n\n/** Everything a rule and a template share. A template is a rule. */\nfunction checkRuleBody(rule: Rule, source: string, path: string): void {\n const heading = rule.heading;\n if (typeof heading === \"object\" && !Array.isArray(heading)) {\n checkPattern(heading.pattern, source);\n }\n if (rule.contains) checkBlockRule(rule.contains, source, `${path}.contains`);\n rule.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.sequence[${index}]`);\n });\n checkRuleList(rule.repeat, source, `${path}.repeat`);\n checkRuleList(rule.sections, source, `${path}.sections`);\n}\n\n/**\n * One list of sibling rules: their ids, their ranges, and whether two\n * neighbours are told apart by nothing but their order.\n */\nfunction checkRuleList(rules: Rule[] | undefined, source: string, path: string): void {\n if (!rules) return;\n\n const ids = new Set<string>();\n for (const [index, rule] of rules.entries()) {\n const here = `${path}[${index}]`;\n if (rule.id !== undefined) {\n if (ids.has(rule.id)) {\n throw new LintError(`${source}: two sibling rules share the id \"${rule.id}\".`);\n }\n ids.add(rule.id);\n }\n checkRange(rule, source, ruleLabel(rule, here));\n checkRuleBody(rule, source, here);\n }\n\n // A warning, not an error. Two adjacent wildcards are how TGDP's \"{Task\n // name}\" then \"{Next task}\" is written, and that is legal. It is also how a\n // template accidentally describes one section twice, and nothing in the file\n // distinguishes the two cases - unless their occurrence ranges differ, in\n // which case the matcher can still tell them apart: one claims a fixed count\n // of sections and the other claims however many are left. That is\n // `reference-description` (`max: 1`) next to `structured-entry` (`min: 0`,\n // no `max`) in tgdp:reference - legal and unambiguous, so only a shared range\n // is flagged.\n for (const [index, rule] of rules.entries()) {\n const next = rules[index + 1];\n if (!next) break;\n if (\n isWildcard(rule) &&\n rule.repeat === undefined &&\n isWildcard(next) &&\n next.repeat === undefined &&\n sameOccurrenceRange(rule, next)\n ) {\n warn(\n `${source}: two adjacent rules have no heading and no repeat; only rule order tells them apart.`,\n );\n break;\n }\n }\n}\n\n/** The checks that run over a validated file, template by template. */\nfunction checkTemplateFile(file: TemplateFile, source: string): void {\n for (const [name, template] of Object.entries(file.templates ?? {})) {\n if (template.min !== undefined || template.max !== undefined) {\n throw new LintError(\n `${source}: a template may not set min or max; a page is one page.`,\n );\n }\n checkRuleBody(template, source, `templates.${name}`);\n }\n}\n\n/**\n * What each `not` constraint means, in the words its author needs.\n *\n * Ajv renders every `not` as \"must NOT be valid\", which names the path and\n * then says nothing whatsoever about what is wrong with it. The schema carries\n * a `description` on each of these; the sentence lives here instead because it\n * is user-facing text, and every other message in this file does too.\n */\nconst NOT_CONSTRAINTS: Record<string, string> = {\n oneOfSequenceOrContains:\n \"sets both sequence and contains; a rule says what it holds in order, or in any order, never both\",\n noHeadingOnRepeat:\n \"sets both heading and repeat; a repeat group has no heading of its own, so put the heading on the first rule inside it\",\n noInstructions: \"carries an instructions key, which this tool does not read\",\n};\n\n/** `/templates/how-to/sections/title must NOT have additional properties (foo)`. */\nfunction describeError(error: ErrorObject): string {\n const where = error.instancePath || \"/\";\n\n if (error.keyword === \"not\") {\n for (const [name, why] of Object.entries(NOT_CONSTRAINTS)) {\n if (error.schemaPath.includes(name)) return `${where} ${why}`;\n }\n }\n\n const params = error.params as { additionalProperty?: unknown };\n const extra =\n typeof params.additionalProperty === \"string\" ? ` (\"${params.additionalProperty}\")` : \"\";\n return `${where} ${error.message ?? \"is invalid\"}${extra}`;\n}\n\n/**\n * Validate a parsed, dereferenced template file, filling in rule defaults.\n *\n * Throws `LintError` naming `source` and the offending instance path. The\n * pre-rewrite loader threw a bare \"Template is invalid\" and passed the real\n * detail as `new Error(msg, {message: ...})` - an options bag that only\n * understands `cause` - so every schema error reached the user as four words.\n */\nexport function validateTemplateFile(data: unknown, source: string): TemplateFile {\n const instructions = findInstructions(data);\n if (instructions) throw new LintError(instructionsMessage(source, instructions));\n\n // Before Ajv, so a v1 file is told where its keys went rather than that it\n // has additional properties.\n const v1 = findV1File(data);\n if (v1) throw new LintError(`${source}: ${v1}`);\n\n const validate = fileValidator();\n if (!validate(data)) {\n const errors = validate.errors ?? [];\n const detail = errors.map(describeError).join(\"; \") || \"does not match the template schema\";\n throw new LintError(`${source} is not a valid template file: ${detail}.`);\n }\n const file = data as TemplateFile;\n checkTemplateFile(file, source);\n return file;\n}\n\n/* -------------------------------------------------------------------------- *\n * Loading\n * -------------------------------------------------------------------------- */\n\n/** Split `./templates.yaml#how-to` into its file part and template name. */\nfunction splitFragment(ref: string): { base: string; fragment: string | null } {\n const hash = ref.lastIndexOf(\"#\");\n if (hash === -1) return { base: ref, fragment: null };\n return { base: ref.slice(0, hash), fragment: ref.slice(hash + 1) || null };\n}\n\n/** The part of a ref an extension should be read off - a URL's path, not its query. */\nfunction extensionSource(ref: string, kind: RefKind): string {\n if (kind !== \"url\") return ref;\n try {\n return new URL(ref).pathname;\n } catch {\n return ref;\n }\n}\n\n/**\n * Parse a template file by extension. Anything unrecognized is parsed as YAML,\n * which is a superset of JSON, so an extensionless URL still loads.\n */\nfunction parseTemplateFile(raw: string, source: string, kind: RefKind): unknown {\n const lower = extensionSource(source, kind).toLowerCase();\n if (lower.endsWith(\".json\")) {\n try {\n return JSON.parse(raw) as unknown;\n } catch (err) {\n throw new LintError(`${source} is not valid JSON: ${errorMessage(err)}`);\n }\n }\n try {\n return parseYaml(raw) as unknown;\n } catch (err) {\n throw new LintError(`${source} is not valid YAML: ${errorMessage(err)}`);\n }\n}\n\n/**\n * Read a local template file, saying which way the read failed.\n *\n * Every failure used to be reported as \"not found\", which is the one wording\n * that contradicts `ls -la`: a directory named `templates.yaml` (EISDIR), a\n * file the user cannot open (EACCES), and a process out of descriptors\n * (EMFILE) are three different problems with three different fixes, and the\n * reader was sent after a path that is plainly there. Only ENOENT is a missing\n * file; anything else keeps the OS's own message.\n */\nasync function readText(ref: string): Promise<string> {\n try {\n return await readFile(ref, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | null)?.code;\n if (code === \"ENOENT\") {\n throw new LintError(`Template file not found: \"${ref}\".`);\n }\n throw new LintError(\n `Template file \"${ref}\" could not be read: ${errorMessage(err)}`,\n );\n }\n}\n\nconst urlCache = new Map<string, TemplateFile>();\n\n/**\n * Drop the parsed-template caches.\n *\n * Nothing in the CLI needs this: a process lints one docset and exits, and the\n * caches are what keep a tree of one doctype from re-reading and re-validating\n * the same template per page. It exists for the other caller - a long-lived\n * process using this as a library - where the two behave differently: the\n * memory grows with the number of distinct refs seen, and a `urlCache` entry\n * for a remote template is served for the life of the process, so a template\n * republished upstream is never picked up.\n */\nexport function clearTemplateCaches(): void {\n builtinCache.clear();\n urlCache.clear();\n}\n\nasync function fetchText(ref: string, timeoutMs: number): Promise<string> {\n let res: Response;\n try {\n res = await fetch(ref, { signal: AbortSignal.timeout(timeoutMs) });\n } catch (err) {\n const e = err as Error;\n if (e.name === \"TimeoutError\" || e.name === \"AbortError\") {\n throw new LintError(\n `Failed to fetch template file \"${ref}\": timed out after ${timeoutMs}ms.`,\n );\n }\n throw new LintError(`Failed to fetch template file \"${ref}\": ${e.message}`);\n }\n if (!res.ok) {\n throw new LintError(`Failed to fetch template file \"${ref}\": HTTP ${res.status}.`);\n }\n return await res.text();\n}\n\n/** Resolve every `$ref` in place, so validation never sees one. */\nasync function dereferenceTemplates(\n data: Record<string, unknown>,\n source: string,\n): Promise<Record<string, unknown>> {\n try {\n return await dereference<Record<string, unknown>>(data, DEREFERENCE_OPTIONS);\n } catch (err) {\n throw new LintError(`${source}: could not resolve a \"$ref\": ${errorMessage(err)}`);\n }\n}\n\n/** Load, dereference, and validate a whole template file. */\nexport async function loadTemplateFile(\n ref: string,\n options: LoadTemplateOptions = {},\n): Promise<TemplateFile> {\n const { kind } = classifyRef(ref);\n if (kind === \"builtin\") {\n throw new LintError(\n `\"${ref}\" is a built-in template id, not a template file. Load it with loadTemplate().`,\n );\n }\n\n const cached = kind === \"url\" ? urlCache.get(ref) : undefined;\n if (cached) return cached;\n\n const raw =\n kind === \"url\"\n ? await fetchText(ref, options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n : await readText(ref);\n\n const parsed = parseTemplateFile(raw, ref, kind);\n if (!isRecord(parsed)) {\n throw new LintError(\n `${ref} is not a template file: expected an object at the top level, got ${\n parsed === null ? \"null\" : Array.isArray(parsed) ? \"an array\" : typeof parsed\n }.`,\n );\n }\n\n const file = validateTemplateFile(await dereferenceTemplates(parsed, ref), ref);\n if (kind === \"url\") urlCache.set(ref, file);\n return file;\n}\n\n/**\n * Resolve a single template.\n *\n * A built-in id names one directly. A file or URL may name one inside itself\n * with a `#` fragment; without a fragment a single-template file resolves\n * unambiguously and anything else is an error listing what is on offer.\n *\n * `extends` is left unresolved - see `resolveExtends`.\n */\nexport async function loadTemplate(\n ref: string,\n options: LoadTemplateOptions = {},\n): Promise<Template> {\n const { base, fragment } = splitFragment(ref);\n const { kind } = classifyRef(base);\n\n if (kind === \"builtin\") {\n // A fragment here was silently dropped, so `tgdp:how-to:1.6#typo` loaded\n // `tgdp:how-to:1.6` and succeeded - the same quiet-wrong-answer as the\n // prototype lookup below, where a configuration error selects a different\n // template than the author wrote and nothing says so. A built-in id names\n // exactly one template, so there is nothing a fragment could select.\n if (fragment !== null) {\n throw new LintError(\n `\"${ref}\" adds a \"#${fragment}\" fragment to a built-in template id. ` +\n `A built-in id names one template on its own - drop the fragment and ` +\n `use \"${base}\". Fragments name a template inside a file, as in ` +\n `\"./templates.yaml#how-to\".`,\n );\n }\n if (!BUILTINS.has(base)) {\n const available = [...BUILTINS.keys()].join(\", \");\n throw new LintError(\n `Unknown built-in template \"${base}\". Available: ${available || \"(none)\"}.`,\n );\n }\n return loadBuiltin(base);\n }\n\n const file = await loadTemplateFile(base, options);\n const templates = file.templates ?? {};\n const names = Object.keys(templates);\n\n if (fragment !== null) {\n // An own-property check, not a truthiness test on the lookup. `templates`\n // is a plain object parsed from YAML, so `#constructor` and `#toString`\n // name members it inherits from `Object.prototype`. Those are truthy, so\n // such a fragment sailed past this guard and a function came back as a\n // template - one with no `sections`, which checks the document against\n // nothing. The bad fragment therefore produced silence rather than an\n // error: the page was reported as passing, not as naming a template that\n // does not exist.\n const named = Object.hasOwn(templates, fragment)\n ? templates[fragment]\n : undefined;\n if (named === undefined) {\n throw new LintError(\n `${base} has no template named \"${fragment}\". Available: ${names.join(\", \") || \"(none)\"}.`,\n );\n }\n return named;\n }\n\n const [only] = Object.values(templates);\n if (names.length === 1 && only !== undefined) return only;\n if (names.length === 0) throw new LintError(`${base} defines no templates.`);\n // Past both branches above, `names.length` is at least two, so `names[0]` is\n // there and the example fragment is never empty. The `?? \"\"` is\n // `noUncheckedIndexedAccess` paperwork rather than a case to look for.\n throw new LintError(\n `${base} defines ${names.length} templates; name one with a \"#\" fragment ` +\n `(e.g. \"${base}#${names[0] ?? \"\"}\"). Available: ${names.join(\", \")}.`,\n );\n}\n\n/* -------------------------------------------------------------------------- *\n * Inheritance\n * -------------------------------------------------------------------------- */\n\n/** How `resolveExtends` turns an `extends` ref into a template. */\nexport type TemplateResolver = (ref: string) => Promise<Template>;\n\n/**\n * Merge a child template onto its parent.\n *\n * Recursion follows `sections` and stops there. A rule's own keys are units:\n * overriding `contains` means replacing it, not merging a child's `min` into\n * the parent's `max` and hoping the pair still means something. But `sections`\n * is a container, not a rule. Replacing it wholesale would mean that tightening\n * one nested rule silently discards every sibling the parent declared, which is\n * the opposite of what `extends` is for.\n *\n * `sections` is a list in v2, so \"merges by key\" now means \"merges by `id`\". A\n * child rule carrying an id the parent also uses replaces that rule where the\n * parent had it, keeping the parent's order. A child rule with no id, or with\n * an id the parent does not use, is appended. Every other key the child sets\n * replaces the parent's outright, `repeat` included: a repeated run is one\n * unit, and merging two of them by position would be guesswork.\n */\nfunction mergeRuleLists(\n parent: Rule[] | undefined,\n child: Rule[] | undefined,\n): Rule[] | undefined {\n if (!parent) return child;\n if (!child) return parent;\n\n // Parent order first, so inherited rules keep the document order the parent\n // declared and child-only additions land at the end.\n const merged: Rule[] = [...parent];\n for (const childRule of child) {\n const id = childRule.id;\n const index =\n id === undefined ? -1 : merged.findIndex((rule) => rule.id === id);\n const target = index === -1 ? undefined : merged[index];\n if (target === undefined) merged.push(childRule);\n else merged[index] = mergeRules(target, childRule);\n }\n return merged;\n}\n\nfunction mergeRules(parent: Rule, child: Rule): Rule {\n // `sections` is dropped from both spreads and merged on its own. Every other\n // key replaces outright, which is what a spread does; sections merge by `id`,\n // which it cannot. Leaving the child's raw array in the spread and then\n // overwriting it worked, but it read as though the spread were the whole\n // answer.\n const sections = mergeRuleLists(parent.sections, child.sections);\n const { sections: _parentSections, ...parentRest } = parent;\n const { sections: _childSections, ...childRest } = child;\n const merged: Rule = { ...parentRest, ...childRest };\n // `!== undefined` rather than truthiness. The two are equivalent here, since\n // `mergeRuleLists` returns `Rule[] | undefined` and an empty array is truthy\n // in any case - but a rule may legitimately carry `sections: []`, meaning it\n // allows none, and a reader should not have to know that `[]` survives a\n // truthiness test to be sure that case is kept.\n if (sections !== undefined) merged.sections = sections;\n return merged;\n}\n\nfunction mergeTemplates(parent: Template, child: Template): Template {\n // Shaped like `mergeRules` above, and for the same reasons: merge first,\n // drop `sections` from both spreads, and test for `undefined` rather than\n // truthiness.\n const sections = mergeRuleLists(parent.sections, child.sections);\n const { sections: _parentSections, ...parentRest } = parent;\n const { sections: _childSections, ...childRest } = child;\n const merged: Template = { ...parentRest, ...childRest };\n if (sections !== undefined) merged.sections = sections;\n // The chain is resolved; leaving `extends` on would invite resolving twice.\n delete merged.extends;\n return merged;\n}\n\n/**\n * Resolve a template's `extends` chain, innermost first.\n *\n * `load` is injected because a relative `extends` is relative to the file the\n * child came from, which this function has no way to know. `chain` is internal\n * bookkeeping for cycle detection.\n */\nexport async function resolveExtends(\n template: Template,\n load: TemplateResolver = loadTemplate,\n chain: string[] = [],\n): Promise<Template> {\n const parentRef = template.extends;\n if (parentRef === undefined) return template;\n\n if (chain.includes(parentRef)) {\n throw new LintError(\n `Template \"extends\" cycle: ${[...chain, parentRef].join(\" -> \")}.`,\n );\n }\n\n const parent = await resolveExtends(await load(parentRef), load, [...chain, parentRef]);\n return mergeTemplates(parent, template);\n}\n\n/* -------------------------------------------------------------------------- *\n * Resolution relative to the declaring file\n * -------------------------------------------------------------------------- */\n\n/**\n * Re-base a ref against the file that declared it.\n *\n * A built-in id, a URL, and an absolute path all name themselves and are\n * returned unchanged. A relative path means \"beside the file I am written in\",\n * which is the one reading `loadTemplate` cannot produce on its own - it reads\n * against the process working directory, so `extends: ./base.yaml` in\n * `tpl/house.yaml` looked for `./base.yaml` at the cwd and found either nothing\n * or, worse, an unrelated file of that name.\n */\nexport function refRelativeTo(baseRef: string, ref: string): string {\n const { base, fragment } = splitFragment(ref);\n if (classifyRef(base).kind !== \"file\" || isAbsolute(base)) return ref;\n\n const { base: fromBase } = splitFragment(baseRef);\n const from = classifyRef(fromBase).kind;\n\n // A template fetched over HTTP names its neighbours the same way a local one\n // does, and `./base.yaml` beside it is a URL, not a path. Resolved as a path\n // it became a read of the process working directory - a local file quietly\n // standing in for the remote one, or a \"file not found\" naming a path that\n // appears nowhere in the template.\n if (from === \"url\") {\n const rebased = new URL(base, fromBase).href;\n return fragment === null ? rebased : `${rebased}#${fragment}`;\n }\n\n if (from !== \"file\") return ref;\n\n const rebased = resolvePath(dirname(fromBase), base);\n return fragment === null ? rebased : `${rebased}#${fragment}`;\n}\n\n/**\n * Load a template with its `extends` chain resolved, re-basing each relative\n * ref against the file that declared it.\n *\n * This is what a caller should use. `resolveExtends` stays exported for callers\n * that supply their own resolver, but its default is the cwd-relative\n * `loadTemplate`, and reaching that default silently is the bug this exists to\n * avoid - `.then(resolveExtends)` passes one argument, so the default is\n * exactly what you get.\n */\nexport async function loadResolvedTemplate(\n ref: string,\n options: LoadTemplateOptions = {},\n chain: string[] = [],\n): Promise<Template> {\n // Seeded with this ref when the caller started here, so the reported cycle\n // starts where the reader did. Chained from the parent only, `a -> b -> a`\n // was reported as `b -> a -> b`: the file the run was pointed at never\n // appeared in the cycle it was said to be part of, which is the one name in\n // the message the reader can act on.\n const seen = chain.length === 0 ? [ref] : chain;\n const template = await loadTemplate(ref, options);\n const parentRef = template.extends;\n if (parentRef === undefined) return template;\n\n const absolute = refRelativeTo(ref, parentRef);\n if (seen.includes(absolute)) {\n throw new LintError(\n `Template \"extends\" cycle: ${[...seen, absolute].join(\" -> \")}.`,\n );\n }\n\n const parent = await loadResolvedTemplate(absolute, options, [\n ...seen,\n absolute,\n ]);\n return mergeTemplates(parent, template);\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"manni-lint:template:2\",\n \"title\": \"manni lint template file\",\n \"description\": \"A template file: named doctype templates, plus the reusable fragments they point at with `$ref`. Validated after dereferencing, so no `$ref` survives to be seen here. A template is itself a rule: it describes the page, and it takes every rule key plus `title`, `types` and `extends`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"templates\"],\n \"properties\": {\n \"$schema\": {\n \"description\": \"URL of this schema, so an editor can offer completion. Read by editors, never by the linter.\",\n \"type\": \"string\"\n },\n \"info\": {\n \"description\": \"Free-form metadata about the file (title, version, author). Never read by the linter; declared so a file that carries it is not rejected.\",\n \"type\": \"object\"\n },\n \"templates\": {\n \"description\": \"The doctype templates this file defines, keyed by name.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"patternProperties\": {\n \"^[A-Za-z][A-Za-z0-9-_]*$\": { \"$ref\": \"#/definitions/template\" }\n }\n },\n \"components\": {\n \"description\": \"Reusable fragments, targeted by `$ref` from elsewhere in the file. Left unconstrained: a fragment is validated where it is inlined, not where it is declared.\",\n \"type\": \"object\"\n }\n },\n \"definitions\": {\n \"min\": {\n \"description\": \"Fewest occurrences. Absent means one. Deliberately carries no JSON Schema `default`, and neither does any other key in this file: Ajv's `useDefaults` would write one in at load time, and a written-in value is indistinguishable from a value the author typed, so it wins the `extends` merge against the parent's real value. The code reads `undefined` instead.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"max\": {\n \"description\": \"Most occurrences. Absent is unbounded, so write `max: 1` for exactly once and `max: 0` to forbid. Must be at least `min` where both are written; the loader checks that, because JSON Schema cannot compare two siblings.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"id\": {\n \"description\": \"Handle for this rule, unique among its siblings. It appears in messages, and `extends` targets it to replace one inherited rule in place.\",\n \"type\": \"string\",\n \"pattern\": \"^[A-Za-z][A-Za-z0-9-_]*$\"\n },\n \"description\": {\n \"description\": \"Prose description of what the rule is for. Never checked.\",\n \"type\": \"string\"\n },\n \"heading\": {\n \"description\": \"The heading of the section this rule describes. Absent means any heading, or none. A string is exact text, a list is one-of, `{ pattern }` is an unanchored regular expression, and `false` means the section has no heading of its own.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n },\n {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"pattern\"],\n \"properties\": {\n \"pattern\": { \"type\": \"string\", \"minLength\": 1 }\n }\n },\n { \"const\": false }\n ]\n },\n \"attributes\": {\n \"description\": \"Literal attributes the node must carry. A string is the required value; `true` requires the attribute to be present, and `false` requires it to be absent.\",\n \"type\": \"object\",\n \"additionalProperties\": {\n \"oneOf\": [{ \"type\": \"string\" }, { \"type\": \"boolean\" }]\n }\n },\n \"oneOfSequenceOrContains\": {\n \"description\": \"A rule says what it holds in order (`sequence`) or in any order (`contains`), never both.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"sequence\", \"contains\"] }\n },\n \"noHeadingOnRepeat\": {\n \"description\": \"`repeat` groups sibling rules into one repeated unit, so the group has no heading of its own. Put the heading on the first rule inside it.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"heading\", \"repeat\"] }\n },\n \"noInstructions\": {\n \"description\": \"`instructions` was the language-model hook of doc-structure-lint. manni lint is deterministic and has no model, so a template carrying it is rejected outright rather than silently ignored; the loader turns this into a migration message pointing at manni docevals.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"instructions\"] }\n },\n \"template\": {\n \"description\": \"One doctype template. A template is a rule describing the page itself, so it takes every rule key, plus `title`, `types` and `extends`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [\n { \"$ref\": \"#/definitions/noInstructions\" },\n { \"$ref\": \"#/definitions/oneOfSequenceOrContains\" },\n { \"$ref\": \"#/definitions/noHeadingOnRepeat\" }\n ],\n \"properties\": {\n \"title\": {\n \"description\": \"Human-readable name for the template, shown by `manni lint templates`.\",\n \"type\": \"string\"\n },\n \"types\": {\n \"description\": \"Doctypes this template serves, matched against a page's `type` frontmatter.\",\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n },\n \"extends\": {\n \"description\": \"Template reference (built-in id, path, or URL, optionally with a `#name` fragment) whose rules this template inherits.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"description\": { \"$ref\": \"#/definitions/description\" },\n \"heading\": { \"$ref\": \"#/definitions/heading\" },\n \"repeat\": { \"$ref\": \"#/definitions/repeat\" },\n \"min\": {\n \"description\": \"Refused on a template: a page is one page. Declared here only so the loader can say that in a sentence, rather than leaving Ajv to report an unexpected key.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"max\": {\n \"description\": \"Refused on a template, for the same reason as `min`.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" },\n \"sections\": { \"$ref\": \"#/definitions/rules\" }\n }\n },\n \"rule\": {\n \"description\": \"One rule. It describes a section of the page, or, with `repeat`, a run of sections matched as one unit.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [\n { \"$ref\": \"#/definitions/noInstructions\" },\n { \"$ref\": \"#/definitions/oneOfSequenceOrContains\" },\n { \"$ref\": \"#/definitions/noHeadingOnRepeat\" }\n ],\n \"properties\": {\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"description\": { \"$ref\": \"#/definitions/description\" },\n \"heading\": { \"$ref\": \"#/definitions/heading\" },\n \"repeat\": { \"$ref\": \"#/definitions/repeat\" },\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" },\n \"sections\": { \"$ref\": \"#/definitions/rules\" }\n }\n },\n \"rules\": {\n \"description\": \"Rules for the subsections, in document order.\",\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/rule\" }\n },\n \"repeat\": {\n \"description\": \"Rules matched in order as one unit, repeated by this rule's `min` and `max`.\",\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"$ref\": \"#/definitions/rule\" }\n },\n \"sequence\": {\n \"description\": \"What the section holds, in order. Each entry is one run of one content kind.\",\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": {\n \"allOf\": [\n { \"$ref\": \"#/definitions/blockRules\" },\n { \"type\": \"object\", \"maxProperties\": 1 }\n ]\n }\n },\n \"blockRules\": {\n \"description\": \"Block rules, keyed by content kind. The key is plural because it carries a count: the node model says `codeBlock`, a template says `codeBlocks: { min: 1 }`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"minProperties\": 1,\n \"properties\": {\n \"paragraphs\": { \"$ref\": \"#/definitions/paragraphs\" },\n \"codeBlocks\": { \"$ref\": \"#/definitions/codeBlocks\" },\n \"lists\": { \"$ref\": \"#/definitions/lists\" },\n \"tables\": { \"$ref\": \"#/definitions/tables\" },\n \"admonitions\": { \"$ref\": \"#/definitions/admonitions\" },\n \"images\": { \"$ref\": \"#/definitions/images\" },\n \"blockquotes\": { \"$ref\": \"#/definitions/counted\" },\n \"definitionLists\": { \"$ref\": \"#/definitions/counted\" },\n \"elements\": { \"$ref\": \"#/definitions/elements\" }\n }\n },\n \"counted\": {\n \"description\": \"A block rule that only counts.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" }\n }\n },\n \"paragraphs\": {\n \"description\": \"Paragraph requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"pattern\": {\n \"description\": \"Unanchored regular expression every paragraph in the run must match.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n },\n \"codeBlocks\": {\n \"description\": \"Code block requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"language\": {\n \"description\": \"Language every code block must be tagged with, as the node model spells it. A list is one-of: the block must declare one of them.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n }\n ]\n },\n \"fenceInfo\": {\n \"description\": \"Info-string tail after the language, where the format carries one.\",\n \"type\": \"string\"\n }\n }\n },\n \"lists\": {\n \"description\": \"List requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"ordered\": {\n \"description\": \"Whether the list must be ordered. Absent accepts either.\",\n \"type\": \"boolean\"\n },\n \"items\": { \"$ref\": \"#/definitions/listItems\" }\n }\n },\n \"listItems\": {\n \"description\": \"Requirements applied to every item of every list in the run. Recurses arbitrarily deep.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [{ \"$ref\": \"#/definitions/oneOfSequenceOrContains\" }],\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" }\n }\n },\n \"tables\": {\n \"description\": \"Table requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"columns\": {\n \"description\": \"The header cells, in order.\",\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n }\n }\n },\n \"admonitions\": {\n \"description\": \"Admonition requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"variant\": {\n \"description\": \"Which flavor of admonition. The node model's `variant`, not any one format's spelling.\",\n \"enum\": [\n \"note\",\n \"tip\",\n \"important\",\n \"caution\",\n \"warning\",\n \"danger\"\n ]\n }\n }\n },\n \"images\": {\n \"description\": \"Image requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"url\": {\n \"description\": \"Where the image must point.\",\n \"type\": \"string\"\n },\n \"alt\": {\n \"description\": \"Alternative text the image must carry.\",\n \"type\": \"string\"\n },\n \"attributes\": { \"$ref\": \"#/definitions/attributes\" }\n }\n },\n \"elements\": {\n \"description\": \"Named-wrapper requirements. `sequence` and `contains` here describe what the element holds, which is never the enclosing section's content.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [{ \"$ref\": \"#/definitions/oneOfSequenceOrContains\" }],\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"tag\": {\n \"description\": \"The element's name, as an author of the format would write it. A list is one-of: an element counts when it carries any of them.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n }\n ]\n },\n \"attributes\": { \"$ref\": \"#/definitions/attributes\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" }\n }\n }\n }\n}\n","{\n \"$comment\": \"Built-in doctype templates derived from The Good Docs Project templates, pinned to release v1.6.0 (\\\"Iron\\\"): https://gitlab.com/tgdp/templates/-/tree/v1.6.0. Each entry's `file` is a YAML template beside this manifest; `source` is the upstream Markdown it was derived from, vendored under test/lint/fixtures/tgdp/ and linted against the derived template so the two cannot drift. Run `npm run check:tgdp-pin` to see whether upstream has moved past the pin.\",\n \"vendor\": \"tgdp\",\n \"upstream\": \"https://gitlab.com/tgdp/templates\",\n \"pin\": \"v1.6.0\",\n \"templates\": [\n {\n \"id\": \"tgdp:how-to:1.6\",\n \"file\": \"tgdp/how-to.yaml\",\n \"title\": \"How-to guide (The Good Docs Project)\",\n \"types\": [\"how-to\"],\n \"source\": \"how-to/template_how-to.md\"\n },\n {\n \"id\": \"tgdp:tutorial:1.6\",\n \"file\": \"tgdp/tutorial.yaml\",\n \"title\": \"Tutorial (The Good Docs Project)\",\n \"types\": [\"tutorial\"],\n \"source\": \"tutorial/template_tutorial.md\"\n },\n {\n \"id\": \"tgdp:reference:1.6\",\n \"file\": \"tgdp/reference.yaml\",\n \"title\": \"Reference (The Good Docs Project)\",\n \"types\": [\"reference\"],\n \"source\": \"reference/template_reference.md\"\n },\n {\n \"id\": \"tgdp:concept:1.6\",\n \"file\": \"tgdp/concept.yaml\",\n \"title\": \"Concept (The Good Docs Project)\",\n \"types\": [\"concept\", \"explanation\"],\n \"source\": \"concept/template_concept.md\"\n },\n {\n \"id\": \"tgdp:troubleshooting:1.6\",\n \"file\": \"tgdp/troubleshooting.yaml\",\n \"title\": \"Troubleshooting (The Good Docs Project)\",\n \"types\": [\"troubleshooting\"],\n \"source\": \"troubleshooting/template_troubleshooting.md\"\n },\n {\n \"id\": \"tgdp:release-notes:1.6\",\n \"file\": \"tgdp/release-notes.yaml\",\n \"title\": \"Release notes (The Good Docs Project)\",\n \"types\": [\"release-notes\"],\n \"source\": \"release-notes/template_release-notes.md\"\n },\n {\n \"id\": \"tgdp:readme:1.6\",\n \"file\": \"tgdp/readme.yaml\",\n \"title\": \"README (The Good Docs Project)\",\n \"types\": [\"readme\"],\n \"source\": \"readme/template_readme.md\"\n }\n ]\n}\n","/**\n * Decide which template describes a given page.\n *\n * The headline case is the last one: a page declares what it is in its\n * frontmatter (`type: how-to`), and the template follows from that. It is the\n * same key `manni meta`'s OKF, Diataxis, and TGDP schemas validate, so a repo\n * that already types its pages gets routing for free, and one run can lint a\n * whole tree with each page checked against the doctype it claims.\n *\n * The precedence chain mirrors docmeta's `resolveSchemaSet`: an explicit\n * instruction beats a per-page declaration, which beats repo policy, which\n * beats the page's own vocabulary, which beats a blanket default.\n *\n * 1. `--template` an operator overriding everything, for one run\n * 2. `$template` this page, specifically, is an exception\n * 3. config override repo policy: \"everything under docs/api is a reference\"\n * 4. `type` what the page says it is\n * 5. config default what to assume when a page says nothing\n *\n * Every step is recorded, because \"why was this page linted with that\n * template?\" is otherwise a question only the source can answer. `--explain`\n * prints the record.\n */\nimport picomatch from \"picomatch\";\nimport type { Template, TemplateFile } from \"./template.js\";\n\n/** Where a resolution came from, in precedence order. */\nexport type ResolutionStage =\n | \"cli\"\n | \"frontmatter-template\"\n | \"config-override\"\n | \"type\"\n | \"config-default\";\n\nexport type SkipCause = \"no-type\" | \"unknown-type\";\n\nexport interface ResolutionStep {\n stage: ResolutionStage;\n /** What the stage found, or null when it had nothing to say. */\n ref: string | null;\n /** Human-readable account of what the stage saw. */\n detail: string;\n}\n\nexport interface Resolution {\n /** The template ref to lint with, or null when the page is not linted. */\n ref: string | null;\n /** Which stage decided, when one did. */\n stage: ResolutionStage | null;\n /** Why no template was found. Only set when `ref` is null. */\n cause?: SkipCause;\n /**\n * The `type` the page declared but that resolved to nothing. Set only for\n * `unknown-type`, so the caller can name it and suggest near misses.\n */\n unknownType?: string;\n /** Near-miss doctypes, for an unknown-type message. */\n suggestions?: string[];\n /**\n * Every doctype something serves, for an unknown-type message with no near\n * miss to offer. Carried on the resolution rather than looked up by each\n * reporter, because the type index does not reach them - which is how the\n * pretty reporter came to print the list and `--explain` came to print\n * nothing, for the same page.\n */\n knownTypes?: string[];\n /** The full chain, for `--explain`. */\n steps: ResolutionStep[];\n}\n\n/** Per-glob repo policy, from `manni.config.yaml`'s `lint.overrides`. */\nexport interface TemplateOverride {\n files: string;\n template: string;\n}\n\nexport interface TypeIndexEntry {\n /** Ref that `loadTemplate` understands. */\n ref: string;\n /** Where the mapping came from; later sources outrank earlier ones. */\n source: \"builtin\" | \"user\" | \"config\";\n}\n\n/** The frontmatter key a page uses to override its doctype's template. */\nexport const FILE_TEMPLATE_KEY = \"$template\";\n/** The frontmatter key a page uses to declare its doctype. */\nexport const TYPE_KEY = \"type\";\n\n/**\n * Build the doctype -> template map.\n *\n * Built-ins go in first and user templates overwrite them, so overriding\n * `how-to` for a repo is one file with `types: [how-to]` in it - no config, no\n * flag. Within each source, a later entry wins, which makes the last\n * `--templates` file the one that decides.\n */\nexport function buildTypeIndex(params: {\n builtins?: { id: string; types: string[] }[];\n /** User template files, in the order they were supplied. */\n userFiles?: { ref: string; file: TemplateFile }[];\n /**\n * The config's explicit `types:` map. Highest precedence of the three, because\n * it is the only one where someone wrote down this exact pairing on purpose -\n * `types:` on a template says what that template is for, while this says what\n * this repo does about a doctype.\n */\n explicitTypes?: Record<string, string>;\n}): Map<string, TypeIndexEntry> {\n const index = new Map<string, TypeIndexEntry>();\n\n for (const builtin of params.builtins ?? []) {\n for (const type of builtin.types) {\n index.set(type, { ref: builtin.id, source: \"builtin\" });\n }\n }\n\n for (const { ref, file } of params.userFiles ?? []) {\n for (const [name, template] of Object.entries(file.templates ?? {})) {\n for (const type of template.types ?? []) {\n index.set(type, { ref: `${ref}#${name}`, source: \"user\" });\n }\n }\n }\n\n for (const [type, ref] of Object.entries(params.explicitTypes ?? {})) {\n index.set(type, { ref, source: \"config\" });\n }\n\n return index;\n}\n\nconst matcherCache = new Map<string, (p: string) => boolean>();\n\n/** Drop the compiled override-glob matchers. See `clearCaches`. */\nexport function clearMatcherCache(): void {\n matcherCache.clear();\n}\n\nfunction matchesGlob(glob: string, filePath: string): boolean {\n let m = matcherCache.get(glob);\n if (!m) {\n m = picomatch(glob, { dot: true });\n matcherCache.set(glob, m);\n }\n // Globs are written with forward slashes whatever the platform uses.\n return m(filePath.replace(/\\\\/g, \"/\"));\n}\n\n/**\n * Doctypes close enough to a typo to be worth naming. Edit distance of one or\n * two on short strings, or a shared prefix - enough to catch `how-two` and\n * `refernce` without listing the whole vocabulary at someone.\n */\nfunction suggestTypes(unknown: string, known: Iterable<string>): string[] {\n const target = unknown.toLowerCase();\n const scored: { type: string; distance: number }[] = [];\n\n for (const type of known) {\n const distance = editDistance(target, type.toLowerCase());\n const threshold = target.length <= 4 ? 1 : 3;\n if (distance <= threshold) scored.push({ type, distance });\n }\n\n return scored\n .sort((a, b) => a.distance - b.distance || a.type.localeCompare(b.type))\n .slice(0, 3)\n .map((s) => s.type);\n}\n\n/**\n * Levenshtein, two rows. Inputs here are doctype slugs, so size is not a\n * concern.\n *\n * Each cell needs three neighbours: the one to its left, the one above, and\n * the one above-left. Only the one above is read out of the previous row; the\n * other two are carried in locals from the cell just written and the previous\n * `above`. So the loop reads no array slot back by a computed index, which is\n * what `noUncheckedIndexedAccess` cannot prove is filled.\n *\n * The characters are compared as code units for the same reason: `a[i]` is\n * typed `string | undefined`, so comparing the two would either need an\n * assertion or a guard for a case the loop bounds already rule out.\n * `charCodeAt` returns `NaN` past the end and `NaN !== NaN`, so an index that\n * did escape its bounds costs an edit rather than matching by accident - the\n * invariant expressed rather than asserted.\n */\nfunction editDistance(a: string, b: string): number {\n if (a === b) return 0;\n let prev = Array.from({ length: b.length + 1 }, (_, i) => i);\n // The answer when `a` is empty: every character of `b` is an insertion.\n let last = b.length;\n for (let i = 0; i < a.length; i++) {\n const row = [i + 1];\n let left = i + 1;\n let diagonal = i;\n for (const [j, above] of prev.slice(1).entries()) {\n const cost = a.charCodeAt(i) === b.charCodeAt(j) ? 0 : 1;\n const cell = Math.min(left + 1, above + 1, diagonal + cost);\n row.push(cell);\n diagonal = above;\n left = cell;\n }\n prev = row;\n last = left;\n }\n return last;\n}\n\n/** A frontmatter value that should be a template ref. Non-strings are ignored. */\nfunction refFromFrontmatter(\n frontmatter: Record<string, unknown> | null,\n key: string,\n): string | null {\n const value = frontmatter?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nexport interface ResolveParams {\n /** Path used for override glob matching. */\n filePath: string;\n /**\n * Absolute path of the same file, matched as a fallback.\n *\n * `overrides[].files` globs are written relative to the config file, while\n * `filePath` is relative to the working directory. Those agree only when the\n * tool is run from the config's own directory; anywhere else the glob matched\n * nothing, the page fell through to its own `type`, and the run exited 0 with\n * the repo's policy silently unapplied. The CLI now rebases the globs to\n * absolute, so matching the absolute path too is what makes them land.\n */\n absolutePath?: string;\n /** The page's frontmatter, or null when it carries none. */\n frontmatter: Record<string, unknown> | null;\n /** `--template`: applies to every file in the run. */\n cliTemplate?: string;\n /** Repo policy, first match wins. */\n overrides?: TemplateOverride[];\n /** Doctype -> template, from `buildTypeIndex`. */\n typeIndex?: Map<string, TypeIndexEntry>;\n /** Applied when a page declares no doctype and nothing else matches. */\n defaultTemplate?: string;\n}\n\nexport function resolveTemplateRef(params: ResolveParams): Resolution {\n const steps: ResolutionStep[] = [];\n const typeIndex = params.typeIndex ?? new Map<string, TypeIndexEntry>();\n\n const decide = (stage: ResolutionStage, ref: string): Resolution => ({\n ref,\n stage,\n steps,\n });\n\n if (params.cliTemplate) {\n steps.push({ stage: \"cli\", ref: params.cliTemplate, detail: \"--template\" });\n return decide(\"cli\", params.cliTemplate);\n }\n steps.push({ stage: \"cli\", ref: null, detail: \"--template not given\" });\n\n const fileTemplate = refFromFrontmatter(params.frontmatter, FILE_TEMPLATE_KEY);\n if (fileTemplate) {\n steps.push({\n stage: \"frontmatter-template\",\n ref: fileTemplate,\n detail: `${FILE_TEMPLATE_KEY} in frontmatter`,\n });\n return decide(\"frontmatter-template\", fileTemplate);\n }\n steps.push({\n stage: \"frontmatter-template\",\n ref: null,\n detail: `no ${FILE_TEMPLATE_KEY} in frontmatter`,\n });\n\n for (const override of params.overrides ?? []) {\n if (\n matchesGlob(override.files, params.filePath) ||\n (params.absolutePath !== undefined &&\n matchesGlob(override.files, params.absolutePath))\n ) {\n steps.push({\n stage: \"config-override\",\n ref: override.template,\n detail: `matched override \"${override.files}\"`,\n });\n return decide(\"config-override\", override.template);\n }\n }\n steps.push({\n stage: \"config-override\",\n ref: null,\n detail: (params.overrides ?? []).length\n ? \"no override glob matched\"\n : \"no overrides configured\",\n });\n\n const type = refFromFrontmatter(params.frontmatter, TYPE_KEY);\n if (type) {\n const entry = typeIndex.get(type);\n if (entry) {\n steps.push({\n stage: \"type\",\n ref: entry.ref,\n detail: `type: ${type} -> ${entry.source} template`,\n });\n return decide(\"type\", entry.ref);\n }\n\n // A declared type that resolves to nothing is a typo or a gap, and either\n // way silently not checking the page is the wrong answer.\n steps.push({\n stage: \"type\",\n ref: null,\n detail: `type: ${type} matches no template`,\n });\n return {\n ref: null,\n stage: null,\n cause: \"unknown-type\",\n unknownType: type,\n suggestions: suggestTypes(type, typeIndex.keys()),\n knownTypes: knownTypes(typeIndex),\n steps,\n };\n }\n steps.push({ stage: \"type\", ref: null, detail: \"no type in frontmatter\" });\n\n if (params.defaultTemplate) {\n steps.push({\n stage: \"config-default\",\n ref: params.defaultTemplate,\n detail: \"config default\",\n });\n return decide(\"config-default\", params.defaultTemplate);\n }\n steps.push({ stage: \"config-default\", ref: null, detail: \"no default configured\" });\n\n // An untyped page is manni meta's complaint, not this tool's: its OKF schema\n // already requires `type`. Skipping rather than failing is what lets you\n // point manni lint at a whole tree on day one.\n return { ref: null, stage: null, cause: \"no-type\", steps };\n}\n\n/** The doctypes a resolved index can serve, sorted, for error messages. */\nexport function knownTypes(index: Map<string, TypeIndexEntry>): string[] {\n return [...index.keys()].sort();\n}\n\nexport type { Template };\n","/**\n * Run a template over a parsed document.\n *\n * Matching and checking are deliberately separate: `match.ts` decides which\n * section each rule is talking about, and this module asks the content rules\n * about the pairs it produced. The old code fused the two - it inferred which\n * section a rule meant by running the rules and seeing whether they passed -\n * which made \"does this section match?\" and \"is this section valid?\" the same\n * question, so a section could never be both matched and wrong.\n *\n * Everything here is synchronous. It was async only to await a language model,\n * which this tool no longer has.\n *\n * Two things beyond that wiring live here, and both are about the document as a\n * whole rather than about any one section.\n *\n * **A template is a rule, and its subject is the page.** Its `heading`\n * constrains the page's own title, its `sequence`/`contains` describe what sits\n * before the first heading, and its `sections` describe what follows. See\n * `pageSection` for which section that makes the subject.\n *\n * **A rule the format cannot answer is said out loud.** A parser declares the\n * content kinds it emits (proposal 0065), and a rule about a kind absent from\n * that list is dropped and reported as a `warning` rather than silently passing\n * - or, worse, failing a document for holding none of something its format\n * cannot express.\n */\nimport type {\n ContentKind,\n DocumentTree,\n Finding,\n Position,\n SectionNode,\n} from \"../types.js\";\nimport {\n checkContains,\n checkContainsIn,\n checkHeading,\n checkSequence,\n checkSequenceIn,\n type RuleContext,\n} from \"../rules/index.js\";\nimport { parserByName } from \"../parsers/index.js\";\nimport { matchSections, type MatchOptions } from \"./match.js\";\nimport { BLOCK_KINDS, BLOCK_KIND_NODE } from \"./template.js\";\nimport type {\n BlockKind,\n BlockRule,\n ElementsRule,\n ListItemsRule,\n ListsRule,\n Rule,\n TablesRule,\n Template,\n} from \"./template.js\";\n\n/** What a caller can tell the validator beyond the tree and the template. */\nexport interface ValidateOptions {\n /** Template file the rules came from. Names the state-cap failure. */\n source?: string;\n /** Template within that file. Named in messages about the template. */\n template?: string;\n /**\n * Content kinds the parser that produced this tree emits. Defaults to what\n * the registry declares for `tree.format`, which is what the CLI wants; a\n * caller holding a tree from somewhere else passes its own.\n */\n kinds?: ContentKind[];\n}\n\n/* -------------------------------------------------------------------------- *\n * The page as the template's subject\n * -------------------------------------------------------------------------- */\n\n/** A zero-width span at the top of a file, for a document with no sections. */\nfunction origin(): Position {\n return {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n };\n}\n\n/** First section's start through the last one's end. */\nfunction span(sections: SectionNode[]): Position {\n const first = sections[0];\n const last = sections[sections.length - 1];\n if (!first || !last) return origin();\n return { start: { ...first.position.start }, end: { ...last.position.end } };\n}\n\n/**\n * The section the template describes: the page itself.\n *\n * A page whose body is one `# Title` with everything nested under it *is* that\n * section, and a page whose content starts before any heading is the implicit\n * lead section `sectionize` opens for it - which has no heading of its own, so\n * `heading: false` matches it and `heading: \"Overview\"` does not.\n *\n * Anything else - several top-level headings, or a body that starts at `##`,\n * which is what a page whose title lives in frontmatter looks like - has no one\n * section standing for the page, so one is synthesised. It has no heading and\n * no content of its own: content before the first heading always opens a lead\n * section, so a document that reaches this branch has none.\n */\nfunction pageSection(tree: DocumentTree): SectionNode {\n const [only] = tree.sections;\n if (tree.sections.length === 1 && only && only.level <= 1) return only;\n return {\n slug: \"\",\n title: \"\",\n level: 0,\n order: 0,\n parentSlug: null,\n titlePosition: null,\n position: span(tree.sections),\n children: [],\n sections: tree.sections,\n };\n}\n\n/* -------------------------------------------------------------------------- *\n * Rules the format cannot answer\n * -------------------------------------------------------------------------- */\n\n/** One rule that will not run here, and what the parser does not report. */\ninterface UncheckedRule {\n /** The template key, as the author wrote it. */\n rule: string;\n /** What is missing, in words: `tables`, `table columns`, `list items`. */\n noun: string;\n}\n\n/** What each block-rule key asks about, as a message says it. */\nconst BLOCK_NOUNS: Record<BlockKind, string> = {\n paragraphs: \"paragraphs\",\n codeBlocks: \"code blocks\",\n lists: \"lists\",\n tables: \"tables\",\n admonitions: \"admonitions\",\n images: \"images\",\n blockquotes: \"blockquotes\",\n definitionLists: \"definition lists\",\n elements: \"elements\",\n};\n\n/**\n * A template with the rules this format cannot answer removed, and the list of\n * what was removed.\n *\n * Removing them is the point, not a side effect. A `tables: { min: 1 }` left\n * standing against a parser that never emits a table counts zero tables and\n * fails the page, which is a verdict on the format rather than on the document\n * - and it would contradict, in the same report, the warning saying the rule\n * was not checked.\n */\nclass Pruner {\n private readonly emitted: Set<ContentKind>;\n /** Keyed by message, so one rule asked twice is reported once. */\n private readonly missing = new Map<string, UncheckedRule>();\n\n constructor(kinds: ContentKind[]) {\n this.emitted = new Set(kinds);\n }\n\n get gaps(): UncheckedRule[] {\n return [...this.missing.values()];\n }\n\n private emits(kind: ContentKind): boolean {\n return this.emitted.has(kind);\n }\n\n private drop(rule: string, noun: string): void {\n this.missing.set(`${rule}: ${noun}`, { rule, noun });\n }\n\n /** A table rule without its `columns`, when the parser reports no cells. */\n private tables(rule: TablesRule): TablesRule {\n if (rule.columns === undefined || this.emits(\"tableCell\")) return rule;\n this.drop(\"columns\", \"table columns\");\n const { columns: _columns, ...kept } = rule;\n return kept;\n }\n\n /**\n * A list rule with its `items` pruned.\n *\n * `items` has no capability of its own to check. A parser that reports a\n * list reports its items, since `ListNode.items` is not optional, and\n * `listItem` is not one of the `BLOCK_KINDS` a block rule can target. So\n * the `lists` gap covers the whole rule, and only what the items *hold* can\n * go unchecked.\n *\n * `listItem` is a `ContentKind` and parsers do declare it. That is a fact\n * about the node model rather than about this pruner: a template names\n * `lists`, and reaches an item only through `items`, so there is no rule\n * whose capability would be `listItem` on its own.\n */\n private lists(rule: ListsRule): ListsRule {\n const items = rule.items;\n if (items === undefined) return rule;\n const pruned = this.listItems(items);\n return pruned === items ? rule : { ...rule, items: pruned };\n }\n\n private listItems(rule: ListItemsRule): ListItemsRule {\n return this.holder(rule);\n }\n\n private elements(rule: ElementsRule): ElementsRule {\n return this.holder(rule);\n }\n\n /** The `sequence`/`contains` pair every content holder carries. */\n private holder<T extends { sequence?: BlockRule[]; contains?: BlockRule }>(\n rule: T,\n ): T {\n const kept = { ...rule };\n let changed = false;\n\n if (rule.sequence) {\n const sequence = this.sequence(rule.sequence);\n if (sequence === undefined) {\n delete kept.sequence;\n changed = true;\n } else if (sequence !== rule.sequence) {\n kept.sequence = sequence;\n changed = true;\n }\n }\n\n if (rule.contains) {\n const contains = this.block(rule.contains);\n if (contains === undefined) {\n delete kept.contains;\n changed = true;\n } else if (contains !== rule.contains) {\n kept.contains = contains;\n changed = true;\n }\n }\n\n return changed ? kept : rule;\n }\n\n /**\n * One `sequence`, with the runs this format cannot see taken out.\n *\n * Taking the entry out rather than leaving it empty is what keeps the rest of\n * the sequence meaningful: a run of a kind the parser never emits can never\n * appear among the actual runs, so an entry left standing would report an\n * order error on every file of that format. `undefined` means the key goes.\n */\n private sequence(entries: BlockRule[]): BlockRule[] | undefined {\n const kept: BlockRule[] = [];\n let changed = false;\n for (const entry of entries) {\n const pruned = this.block(entry);\n if (pruned === undefined) {\n changed = true;\n continue;\n }\n if (pruned !== entry) changed = true;\n kept.push(pruned);\n }\n if (!changed) return entries;\n return kept.length > 0 ? kept : undefined;\n }\n\n /** One block rule, or `undefined` when nothing in it can be checked. */\n private block(block: BlockRule): BlockRule | undefined {\n const kept: BlockRule = { ...block };\n let changed = false;\n\n for (const key of BLOCK_KINDS) {\n if (kept[key] === undefined) continue;\n if (this.emits(BLOCK_KIND_NODE[key])) continue;\n this.drop(key, BLOCK_NOUNS[key]);\n // Cleared rather than deleted. A computed `delete` is banned here, and\n // nothing that reads a block rule asks whether the key is present - every\n // one of them tests the value against `undefined`.\n Object.assign(kept, { [key]: undefined });\n changed = true;\n }\n\n const tables = kept.tables;\n if (tables) {\n const pruned = this.tables(tables);\n if (pruned !== tables) {\n kept.tables = pruned;\n changed = true;\n }\n }\n\n const lists = kept.lists;\n if (lists) {\n const pruned = this.lists(lists);\n if (pruned !== lists) {\n kept.lists = pruned;\n changed = true;\n }\n }\n\n const elements = kept.elements;\n if (elements) {\n const pruned = this.elements(elements);\n if (pruned !== elements) {\n kept.elements = pruned;\n changed = true;\n }\n }\n\n if (!changed) return block;\n return BLOCK_KINDS.some((key) => kept[key] !== undefined) ? kept : undefined;\n }\n\n private rules(rules: Rule[]): Rule[] {\n const kept: Rule[] = [];\n let changed = false;\n for (const rule of rules) {\n const pruned = this.rule(rule);\n if (pruned !== rule) changed = true;\n kept.push(pruned);\n }\n return changed ? kept : rules;\n }\n\n rule<T extends Rule>(rule: T): T {\n const kept = this.holder(rule);\n let changed = kept !== rule;\n const out = changed ? kept : { ...rule };\n\n if (rule.repeat) {\n const repeat = this.rules(rule.repeat);\n if (repeat !== rule.repeat) {\n out.repeat = repeat;\n changed = true;\n }\n }\n\n if (rule.sections) {\n const sections = this.rules(rule.sections);\n if (sections !== rule.sections) {\n out.sections = sections;\n changed = true;\n }\n }\n\n return changed ? out : rule;\n }\n}\n\n/** A pruned template and what pruning it took out. */\ninterface Pruned {\n template: Template;\n gaps: UncheckedRule[];\n}\n\n/**\n * Pruned templates, keyed by the template and the format's kinds.\n *\n * The identity matters as much as the saving. `match.ts` caches one compiled\n * automaton per rule *array*, so a fresh copy of `sections` per file would\n * recompile the template for every page of a docset. A template that needs no\n * pruning is therefore returned as-is, and one that does is pruned once per\n * format.\n */\nconst pruneCache = new WeakMap<Template, Map<string, Pruned>>();\n\nfunction prune(template: Template, kinds: ContentKind[]): Pruned {\n const key = [...kinds].sort().join(\",\");\n let byKinds = pruneCache.get(template);\n if (!byKinds) {\n byKinds = new Map<string, Pruned>();\n pruneCache.set(template, byKinds);\n }\n const hit = byKinds.get(key);\n if (hit) return hit;\n\n const pruner = new Pruner(kinds);\n const pruned: Pruned = { template: pruner.rule(template), gaps: pruner.gaps };\n byKinds.set(key, pruned);\n return pruned;\n}\n\n/** The warning one unchecked rule gets, anchored at the top of the file. */\nfunction uncheckedFinding(\n format: string,\n name: string,\n gap: UncheckedRule,\n): Finding {\n return {\n type: \"unsupported_content_kind\",\n heading: null,\n message:\n `The ${format} parser does not report ${gap.noun}, so the ` +\n `\"${gap.rule}\" rule in template \"${name}\" is not checked for this file.`,\n position: origin(),\n // A warning, because nothing about the document is wrong: the rule asks\n // about something this format cannot express. It never changes the exit\n // code - `runLint` counts a file as failed only on an error-severity\n // finding - so a docset of mixed formats stays green while still saying\n // which rules went unchecked, and where.\n severity: \"warning\",\n };\n}\n\n/* -------------------------------------------------------------------------- *\n * The walk\n * -------------------------------------------------------------------------- */\n\n/** Content and heading rules for one matched pair, without recursion. */\nfunction checkSection(section: SectionNode, rule: Rule): Finding[] {\n return [\n ...checkHeading(section, rule.heading),\n ...checkSequence(section, rule.sequence),\n ...checkContains(section, rule.contains),\n ];\n}\n\n/** Match one level of sections, check each pair, then recurse. */\nexport function validateSections(\n sections: SectionNode[],\n rules: Rule[] | undefined,\n options: MatchOptions = {},\n): Finding[] {\n const { matches, findings } = matchSections(sections, rules, options);\n const all = [...findings];\n\n for (const match of matches) {\n all.push(...checkSection(match.section, match.rule));\n all.push(\n ...validateSections(match.section.sections, match.rule.sections, {\n ...options,\n parent: match.section,\n }),\n );\n }\n\n return all;\n}\n\n/** Findings for one document against one template, in document order. */\nexport function validateDocument(\n tree: DocumentTree,\n template: Template,\n options: ValidateOptions = {},\n): Finding[] {\n // An unrecognised format falls back to *no* kinds rather than to no pruning.\n // Every tree the CLI builds carries a registered parser's own name, so this\n // is only reachable through the exported API, with a tree somebody else\n // parsed. Such a format has promised nothing, so no content rule can be said\n // to have run against it, and the alternative is the one 0065 rules out:\n // applying every rule to a format that may answer none of them, which makes\n // a green run mean two different things.\n const kinds = options.kinds ?? parserByName(tree.format)?.kinds ?? [];\n const pruned = prune(template, kinds);\n const rules = pruned.template;\n // The template's own name, for the messages that have to say which template\n // asked. The caller knows it; `title` is the next best thing a file carries.\n const name = options.template ?? template.title ?? \"unnamed\";\n\n const findings: Finding[] = pruned.gaps.map((gap) =>\n uncheckedFinding(tree.format, name, gap),\n );\n\n // The page is the template's subject. Its content is checked through the\n // `*In` rules rather than the section-shaped ones so that a finding about a\n // page with no heading of its own carries no heading either, instead of the\n // empty string a headless section's title is.\n const page = pageSection(tree);\n const ctx: RuleContext = {\n heading: page.title || null,\n position: page.position,\n };\n findings.push(...checkHeading(page, rules.heading));\n findings.push(...checkSequenceIn(page.children, rules.sequence, ctx));\n findings.push(...checkContainsIn(page.children, rules.contains, ctx));\n findings.push(\n ...validateSections(page.sections, rules.sections, {\n parent: page,\n source: options.source,\n template: options.template,\n }),\n );\n\n // Rules fire in rule order, which is not necessarily document order once a\n // template mixes optional sections and subsections. Readers scan a report top\n // to bottom against the file, so sort by where the finding actually is.\n return findings.sort(\n (a, b) => a.position.start.offset - b.position.start.offset,\n );\n}\n","/**\n * The `lint:` section of `manni.config.yaml`.\n *\n * The file is shared across the manni family - manni docevals, manni meta and\n * this tool all read one file per repo, each taking its own top-level key. So\n * the root is a mapping of tool name to that tool's settings, and manni lint\n * reads `lint:` and nothing else: sibling keys are neither read nor validated,\n * which is what keeps the family decoupled - no registry of known tools, no\n * coordination, no version coupling. See agentevals ADR 01009.\n *\n * Finding the file is the family's job (`src/shared/config-file.ts`): it walks\n * up from the working directory to the repository root, reads the `lint:` key\n * of the first family file that has one, and hands this module the slice. What\n * the slice may contain is decided here. The pre-family\n * `doc-structure-lint.config.yaml` is no longer discovered: its whole document\n * was a `lint:` section written in keys this one no longer takes, so reading it\n * could only produce the moved-key refusal from a file the author is not\n * looking at.\n *\n * The document set is not a `lint:` key. Proposal 0041 declares it once for\n * every tool, under the family-level `collections:`, which the shared loader\n * parses and `resolveLintRun` selects from; `paths:` and `exclude:` under\n * `lint:` are refused by name.\n *\n * That leaves the root unvalidatable by any single tool, so the two ways an\n * author can lose a whole config to this convention are caught by shape\n * instead: keys left at the top level, and a wrapper differing only in case. A\n * wrapper misspelled any other way (`lnt:`) is indistinguishable from another\n * tool's section and still yields defaults - the accepted cost of having no\n * registry.\n *\n * Inside the section, `config.json` is the authority and `additionalProperties`\n * is false at every level, so a typo'd key is a loud failure rather than a\n * silent default. Defaults themselves live with the consumers, not here: this\n * returns exactly what the author wrote.\n */\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport * as AjvNs from \"ajv\";\nimport type { ErrorObject, SchemaObject, ValidateFunction } from \"ajv\";\nimport configSchema from \"../../../schemas/lint/config.json\" with { type: \"json\" };\nimport { selectCollections, type CollectionConfig } from \"../../shared/collections.js\";\nimport {\n FAMILY_CONFIG_NAMES,\n findConfigFile,\n readConfigFile,\n type ConfigFile,\n type ConfigFileOptions,\n} from \"../../shared/config-file.js\";\nimport { LintError } from \"../types.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\nimport type { ToolsConfig } from \"../../shared/tools.js\";\nimport type { TemplateOverride } from \"./resolve-template.js\";\nimport { refRelativeTo } from \"./template-registry.js\";\n\n// ajv is CommonJS with a default export; under NodeNext the constructable\n// value is on `.default`, which the namespace import reaches without the\n// esModuleInterop-only call signature TypeScript otherwise complains about.\ntype AjvCtor = typeof import(\"ajv\").default;\nconst Ajv = AjvNs.default as unknown as AjvCtor;\n\n/**\n * Everything the `lint:` section can carry. Every key is optional, and an\n * absent one means \"the consumer's default\", never a value written in here.\n *\n * `overrides[].template` and the `types` values are template refs, left as the\n * strings they were written as: `resolve-template.ts` owns what a ref means.\n */\nexport interface LintConfig {\n /** Zero matched files is a success, not an operational error. */\n allowEmpty?: boolean;\n /** The structure job: what `lint structure` runs and `lint check` includes. */\n structure?: LintJobConfig;\n /** Template files to load; their `types:` join the routing table. */\n templates?: string[];\n /** Template for a page that declares no doctype. */\n template?: string;\n /** Explicit doctype -> template ref map. */\n types?: Record<string, string>;\n /** Repo policy, first matching glob wins. */\n overrides?: TemplateOverride[];\n}\n\nexport type { TemplateOverride };\n\n/** One job's settings. The tool that performs it is all a job configures. */\nexport interface LintJobConfig {\n tool?: LintTool;\n}\n\n/**\n * The tools that can perform a lint job: manni's own engine, and DITA Open\n * Toolkit for a DITA docset.\n */\nexport const LINT_TOOLS = [\"manni\", \"dita-ot\"] as const;\nexport type LintTool = (typeof LINT_TOOLS)[number];\n\n/** The jobs `lint check` runs: `structure`. */\nexport const LINT_JOBS = [\"structure\"] as const;\nexport type LintJob = (typeof LINT_JOBS)[number];\n\n/** The tools each job accepts, in the order its message lists them. */\nexport const TOOLS_BY_JOB: Readonly<Record<LintJob, readonly LintTool[]>> = {\n structure: [\"manni\", \"dita-ot\"],\n};\n\nexport function isLintTool(value: string): value is LintTool {\n return (LINT_TOOLS as readonly string[]).includes(value);\n}\n\n/** The top-level key manni lint owns inside the shared manni config. */\nexport const SECTION_KEY = \"lint\";\n\n/** The family filenames discovery accepts, in the order it tries them. */\nexport const CONFIG_FILENAMES: readonly string[] = FAMILY_CONFIG_NAMES;\n\n/**\n * How the family loader finds and slices this tool's config.\n *\n * `legacyNames` is empty on purpose. `doc-structure-lint.config.yaml` was this\n * tool's file before the family file, and its whole document was the `lint:`\n * section - written in `paths:` and `exclude:`, which the section no longer\n * takes. Reading one now could only refuse it, from a file the author is not\n * looking at, so discovery no longer knows the name at all.\n */\nconst CONFIG_FILE: ConfigFileOptions = {\n section: SECTION_KEY,\n legacyNames: [],\n toError: (message) => new LintError(message),\n};\n\n/**\n * The keys this tool owns, read off the schema rather than restated here.\n *\n * Finding any of them at the root of a manni config means the file was never\n * nested under `lint:`. Deriving the list from `config.json` is what stops it\n * drifting from the real key set the next time a key is added - a restated\n * list that fell behind would let exactly the un-nested config it exists to\n * catch fall through to defaults instead.\n */\nconst SECTION_KEYS: readonly string[] = Object.keys(configSchema.properties);\n\n/** The keys one job takes. Read off the schema for the same reason. */\nconst STRUCTURE_KEYS: readonly string[] = Object.keys(\n configSchema.properties.structure.properties,\n);\n\n/**\n * Where the metadata tool's configuration reference documents `collections:`.\n * The literal cite's `config.ts` carries, repeated for the same reason: that\n * module is meta's own, and sibling tools import only its public and family\n * barrels.\n */\nconst CONFIG_REF = \"https://hawkeyexl.github.io/manni/meta/reference/configuration/\";\n\n/**\n * The two keys proposal 0041 moved out of `lint:` and up to the family level.\n * Refused rather than aliased, as meta and cite refuse them: an alias would be\n * a second place to declare a document set that is meant to be declared once.\n */\nconst MOVED_KEYS: readonly string[] = [\"paths\", \"exclude\"];\n\n/**\n * Refuse a moved key before the unknown-key check, which would otherwise call\n * `paths` a typo. Same sentence as meta's and cite's, with the section renamed.\n */\nfunction assertNoMovedKeys(raw: Record<string, unknown>, source: string): void {\n for (const key of MOVED_KEYS) {\n if (Object.hasOwn(raw, key)) {\n throw new LintError(\n `${source}: \"${key}\" is no longer a ${SECTION_KEY} key. Document sets are declared once for every tool, under a top-level collections: list. See ${CONFIG_REF}#collections`,\n );\n }\n }\n}\n\n/**\n * A key the parser does not know is a typo, not a no-op. Naming every key the\n * mapping takes is what turns `allowEmtpy: true` from a line that reads as\n * configured and does nothing into a failure the author can act on. `where` is\n * the mapping as the user spells it: `lint` or `lint.structure`.\n */\nfunction rejectUnknownKeys(\n obj: Record<string, unknown>,\n allowed: readonly string[],\n where: string,\n source: string,\n): void {\n for (const key of Object.keys(obj)) {\n if (!allowed.includes(key)) {\n throw new LintError(\n `Unknown key \"${key}\" under ${where}: in ${source}. Supported keys: ${allowed.join(\", \")}.`,\n );\n }\n }\n}\n\n/**\n * The job mappings, checked before Ajv so the messages name the key set and\n * the tool list rather than restating a schema keyword. A tool nobody\n * implements is refused by name: silently falling back to manni's engine would\n * make `tool: vale` read as configured and lint with something else.\n */\nfunction checkJobs(section: Record<string, unknown>, source: string): void {\n for (const job of LINT_JOBS) {\n const value = section[job];\n if (value == null || !isRecord(value)) continue;\n rejectUnknownKeys(value, STRUCTURE_KEYS, `${SECTION_KEY}.${job}`, source);\n const tool = value.tool;\n if (tool === undefined) continue;\n if (typeof tool !== \"string\" || !TOOLS_BY_JOB[job].includes(tool as LintTool)) {\n throw new LintError(\n `${source}: ${SECTION_KEY}.${job}.tool must be one of: ${TOOLS_BY_JOB[job].join(\", \")}.`,\n );\n }\n }\n}\n\n// No `useDefaults`: the schema declares no defaults, deliberately. A default\n// written into the parsed object is indistinguishable from a value the author\n// wrote, which is the trap `template.json` documents on its `required` key.\nconst ajv = new Ajv({ allErrors: true });\n\nlet compiled: ValidateFunction<LintConfig> | null = null;\n\nfunction sectionValidator(): ValidateFunction<LintConfig> {\n compiled ??= ajv.compile<LintConfig>(configSchema as SchemaObject);\n return compiled;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * One Ajv error, as a path an author can find in their file.\n *\n * Ajv's `instancePath` is relative to the section, so it is prefixed back to\n * `lint`: a bare `/overrides/0/files` names a path that is not in the file.\n */\nfunction describeError(error: ErrorObject): string {\n const params = error.params as { additionalProperty?: unknown };\n const extra =\n typeof params.additionalProperty === \"string\"\n ? ` (\"${params.additionalProperty}\")`\n : \"\";\n return `${SECTION_KEY}${error.instancePath} ${error.message ?? \"is invalid\"}${extra}`;\n}\n\n/** The un-nested config: our own keys at the root, with no `lint:` wrapper. */\nfunction rejectUnNested(root: Record<string, unknown>, source: string): void {\n const stray = SECTION_KEYS.filter((key) => key in root);\n if (stray.length === 0) return;\n // The convention is named after the shared file, but the message must\n // describe the file the user actually passed: telling someone who ran\n // `-c my-custom.yaml` about `manni.config.yaml` reads as a complaint about a\n // file they never mentioned, and sends them looking for the wrong one.\n throw new LintError(\n `${source}: found ${stray.map((key) => `\"${key}:\"`).join(\", \")} at the top level, ` +\n `but no \"${SECTION_KEY}:\" key. This file is shared across the manni family, ` +\n `so every manni lint setting belongs under a top-level \"${SECTION_KEY}:\" key. ` +\n `Indent the file's contents one level and add that key.`,\n );\n}\n\n/**\n * The miscased wrapper: `Lint:`, which the stray-key check cannot see, because\n * the keys under it are nested rather than at the top level.\n */\nfunction rejectMiscasedWrapper(\n root: Record<string, unknown>,\n source: string,\n): void {\n const miscased = Object.keys(root).find(\n (key) => key !== SECTION_KEY && key.toLowerCase() === SECTION_KEY,\n );\n if (miscased === undefined) return;\n throw new LintError(\n `${source}: found \"${miscased}:\" at the top level, but the key is case-sensitive ` +\n `and manni lint reads \"${SECTION_KEY}:\" exactly. Rename \"${miscased}:\" to \"${SECTION_KEY}:\".`,\n );\n}\n\n/**\n * Validate the `lint:` section itself, whatever file it came from.\n *\n * `source` names the file in every message. `null` is the section written\n * with nothing under it, which an author means as \"defaults\"; it is not a\n * broken section.\n */\nexport function parseConfigSection(section: unknown, source: string): LintConfig {\n if (section == null) return {};\n\n // Before Ajv, which would call a moved key an additional property and an\n // unknown one a schema keyword. Both messages have to name what to do.\n if (isRecord(section)) {\n assertNoMovedKeys(section, source);\n rejectUnknownKeys(section, SECTION_KEYS, SECTION_KEY, source);\n checkJobs(section, source);\n }\n\n const validate = sectionValidator();\n if (!validate(section)) {\n const detail =\n (validate.errors ?? []).map(describeError).join(\"; \") ||\n `does not match the ${SECTION_KEY} config schema`;\n throw new LintError(\n `${source}: invalid \"${SECTION_KEY}\" section: ${detail}.`,\n );\n }\n return section;\n}\n\n/**\n * Parse a whole `manni.config.yaml` and return its `lint:` section.\n *\n * `source` names the file in every message. A file that is empty, or that\n * carries only other tools' sections, returns `{}` rather than throwing - that\n * is the case that keeps one shared file usable by a project which has not\n * adopted this tool yet.\n */\nexport function parseConfig(text: string, source: string): LintConfig {\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (err) {\n throw new LintError(\n `${source}: invalid YAML: ${errorMessage(err)}`,\n );\n }\n\n // An empty file configures nothing; it is not a broken one.\n if (raw == null) return {};\n if (!isRecord(raw)) {\n throw new LintError(\n `${source}: top level must be a mapping of tool name to that tool's settings, ` +\n `with manni lint's under \"${SECTION_KEY}:\".`,\n );\n }\n\n if (!(SECTION_KEY in raw)) {\n rejectUnNested(raw, source);\n rejectMiscasedWrapper(raw, source);\n // Every remaining root key belongs to a sibling tool. Reading none of them\n // is the whole point of the convention.\n return {};\n }\n\n return parseConfigSection(raw[SECTION_KEY], source);\n}\n\n/**\n * Load the config from an explicit path, or by walking up from `cwd`.\n *\n * Discovery is the family's: the nearest `manni.config.yaml` with a `lint:`\n * key or a family key wins, and a family file with neither belongs to a\n * sibling and is passed over. An explicit path skips discovery and errors if\n * it is missing; its `lint:` key is unwrapped when present and the whole\n * document is the section otherwise, so a file passed by hand may be called\n * anything.\n *\n * A wrapper differing from `lint:` only in case is still caught on an\n * un-wrapped document: nothing else in the section's schema would name it.\n *\n * Returns null when discovery finds no config, which is not an error - a repo\n * whose pages all declare their `type` needs no config at all.\n */\nexport async function loadConfig(\n explicitPath?: string,\n cwd: string = process.cwd(),\n): Promise<{ config: LintConfig; path: string } | null> {\n const file = await readLintConfigFile(explicitPath, cwd);\n if (file === null) return null;\n return { config: sectionOf(file), path: file.path };\n}\n\n/** The family file itself, found or named, before its `lint:` slice is read. */\nfunction readLintConfigFile(\n explicitPath: string | undefined,\n cwd: string,\n): Promise<ConfigFile | null> {\n return explicitPath\n ? readConfigFile(explicitPath, cwd, CONFIG_FILE)\n : findConfigFile(cwd, CONFIG_FILE);\n}\n\n/** One found file's `lint:` section, parsed, with the shape checks a file gets. */\nfunction sectionOf(file: ConfigFile): LintConfig {\n if (!file.wrapped && isRecord(file.value)) {\n rejectMiscasedWrapper(file.value, file.source);\n }\n return parseConfigSection(file.value, file.source);\n}\n\n/**\n * Re-base everything a config declares against the config file's own\n * directory.\n *\n * `loadConfig` returns the file's `path` precisely so this can happen, and\n * discarding it made the same config mean different things depending on where\n * the tool was invoked from. Running from a subdirectory turned `templates:`\n * into \"file not found\" - and turned `overrides:` into nothing at all,\n * silently: the glob stopped matching, every page fell through to its own\n * `type`, and the run exited 0 having applied none of the repo's policy.\n *\n * Refs go through `refRelativeTo`, which leaves built-in ids, URLs, and\n * absolute paths alone. Globs become absolute, which is what the run matches\n * them against. Every non-absolute glob is relative to the config, `**\\/*.md`\n * included: exempting a leading `**` left the original bug half-open, because\n * from a subdirectory that pattern expanded against the working directory.\n */\nexport function rebaseConfig(\n found: { config: LintConfig; path: string } | null,\n): LintConfig {\n if (!found) return {};\n const dir = dirname(resolve(found.path));\n const config = found.config;\n\n const ref = (value: string): string => refRelativeTo(found.path, value);\n const glob = (value: string): string =>\n isAbsolute(value) ? value : resolve(dir, value).replace(/\\\\/g, \"/\");\n\n return {\n ...config,\n ...(config.templates ? { templates: config.templates.map(ref) } : {}),\n ...(config.template ? { template: ref(config.template) } : {}),\n ...(config.types\n ? {\n types: Object.fromEntries(\n Object.entries(config.types).map(([type, value]) => [type, ref(value)]),\n ),\n }\n : {}),\n ...(config.overrides\n ? {\n overrides: config.overrides.map((o) => ({\n files: glob(o.files),\n template: ref(o.template),\n })),\n }\n : {}),\n };\n}\n\n/** The stdin token, repeated so this module does not depend on the file loader. */\nconst STDIN = \"-\";\n\nexport interface LintRunOptions {\n cwd: string;\n /** `-c/--config`. */\n configPath?: string;\n /** `--no-config`: skip discovery and run on the built-in defaults. */\n noConfig?: boolean;\n /** Positional inputs; empty means fall back to the selected collections' `paths:`. */\n inputs: string[];\n /**\n * `--collection <name>`, repeatable: the collections this run covers. Empty\n * or absent means every declared one, the same thing, because commander's\n * collector hands `[]` over when the flag was never typed.\n */\n collection?: string[];\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n}\n\n/** What every lint command settles before it touches a file. */\nexport interface ResolvedLintRun {\n /** The `lint:` section, rebased against the config's directory. */\n config: LintConfig;\n /** What to resolve: the positional inputs, or the collections' `paths:`. */\n inputs: string[];\n /** The directory `inputs` resolve from. */\n base: string;\n /** The collections this run covers; `[]` with no config. */\n collections: CollectionConfig[];\n /** Whether `inputs` came from the collections rather than the command line. */\n fromCollections: boolean;\n /**\n * The document's top-level `tools:`, an outside tool's settings (proposal\n * 0052). `{}` with no config. It is not part of the `lint:` section and is\n * not rebased here: a path in it is resolved against `configDir` by the run\n * that needs it, through the helpers in `src/shared/tools.ts`.\n */\n tools: ToolsConfig;\n configDir?: string;\n configPath?: string;\n configSource?: string;\n}\n\n/**\n * Settle which config governs the run and what it covers. The same shape cite\n * uses, for the same reason: positional inputs are typed from a shell and stay\n * cwd-relative, while a collection's `paths:` was written next to the config\n * and resolves there, so one run has exactly one base.\n */\nexport async function resolveLintRun(\n opts: LintRunOptions,\n): Promise<ResolvedLintRun> {\n const cwd = resolve(opts.cwd);\n\n // `--collection` names something only a config can define, and selects a set\n // the operator did not type, so it composes with neither positional paths\n // nor `--no-config`. The same two sentences meta, a11y and cite use. Stdin\n // is one more input rather than a path, so it rides beside the flag.\n const wanted = opts.collection ?? [];\n if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN)) {\n throw new LintError(\n \"--collection selects a configured collection; it cannot be combined with paths.\",\n );\n }\n\n const file = opts.noConfig ? null : await readLintConfigFile(opts.configPath, cwd);\n if (wanted.length > 0 && file === null) {\n throw new LintError(\"--collection needs a config file to select from.\");\n }\n if (file !== null) opts.onConfigLoaded?.({ path: file.path, dir: file.dir });\n\n const config = rebaseConfig(\n file === null ? null : { config: sectionOf(file), path: file.path },\n );\n // Selected whatever the inputs are: a typed file is still a member of the\n // collections that contain it. An unknown name is the shared helper's error.\n const collections =\n file === null\n ? []\n : selectCollections(\n file.collections,\n wanted,\n file.source,\n (message) => new LintError(message),\n );\n\n // `--collection` is a request the run has to honour, as in cite and meta:\n // the named collection's pages are resolved even when stdin rides beside the\n // flag. Counting `-` as a path made `lint check - --as markdown --collection\n // guides` lint the piped document, open not one file of `guides`, and exit 0\n // over a docset nothing read. The implicit fallback is not a request, and a\n // lone `-` cancels it: a piped page is a run of its own.\n const fromCollections = wanted.length > 0 || opts.inputs.length === 0;\n const collectionPaths = fromCollections ? collections.flatMap((c) => c.paths) : [];\n // Stdin keeps its typed position; the globs follow it in declaration order.\n const inputs = fromCollections ? [...opts.inputs, ...collectionPaths] : opts.inputs;\n // Only a path needs a base, so it follows the globs when they contributed\n // and stays at the working directory otherwise, stdin included.\n const base = collectionPaths.length > 0 && file ? file.dir : cwd;\n\n return {\n config,\n inputs,\n base,\n collections,\n fromCollections,\n tools: file?.tools ?? {},\n ...(file === null\n ? {}\n : { configDir: file.dir, configPath: file.path, configSource: file.source }),\n };\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"manni-lint:config:1\",\n \"title\": \"manni lint config section\",\n \"description\": \"The `lint:` section of a shared `manni.config.yaml`. The file's root is a mapping of tool name to that tool's settings, so this schema describes the section only - never the file. The document set is not here: proposal 0041 declares it once for every tool, under the family-level `collections:` key, and `paths`/`exclude` under `lint:` are refused by name. Every key is optional: a repo that configures no manni lint at all is a legitimate config, and so is one that carries only a sibling tool's section. `additionalProperties: false` applies at every level below `lint:`, which is what turns a typo'd key into a loud failure instead of a silent default.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"allowEmpty\": {\n \"description\": \"Treat a run that matched zero files as a success rather than an operational error. The same key, with the same meaning, as `cite.allowEmpty`: for a shared CI template or a pre-commit hook whose file list may legitimately be empty. `--allow-empty` on the command line sets it for one run.\",\n \"type\": \"boolean\"\n },\n \"overrides\": {\n \"description\": \"Repo policy: everything under a glob is linted with one template, whatever the pages say they are. First matching entry wins, and an override outranks a page's own `type`.\",\n \"type\": \"array\",\n \"items\": {\n \"description\": \"One policy entry. Both keys are required: an entry naming no template, or no files, is a line that silently does nothing.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"files\", \"template\"],\n \"properties\": {\n \"files\": {\n \"description\": \"Glob matched against each file's path, written with forward slashes whatever the platform uses.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"template\": {\n \"description\": \"Template ref applied to the files this entry matches.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n }\n },\n \"structure\": {\n \"description\": \"The structure job, which `manni lint structure` and `manni lint check` both run. Declaring it with no keys is how a repo says the job applies. Its one setting is the tool that performs it.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"tool\": {\n \"description\": \"Which tool performs the job. `manni` is manni's own template-structure engine; `dita-ot` is DITA Open Toolkit, which validates a DITA map and the topics it reaches and is located through the top-level `tools.dita-ot.home`. `--tool <name>` on the command line names the same thing.\",\n \"type\": \"string\",\n \"enum\": [\"manni\", \"dita-ot\"]\n }\n }\n },\n \"template\": {\n \"description\": \"Template ref applied to a page that declares no doctype and matches no override. The last stage of the resolution chain, not an override: `--template`, a page's `$template`, an `overrides` entry, and the page's own `type` all outrank it.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"templates\": {\n \"description\": \"Template files to load. Their templates' `types:` join the doctype routing table, outranking the built-ins, so adopting a repo's own how-to shape is one entry here and no flag.\",\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n }\n },\n \"types\": {\n \"description\": \"Explicit doctype -> template ref map, for a doctype no template file claims with its own `types:`. Keys are the vocabulary a page's `type` frontmatter is written in, so they are the author's names rather than this schema's - constrained only to the shape a doctype slug takes, not to a fixed set.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"patternProperties\": {\n \"^[A-Za-z0-9._:-]+$\": {\n \"description\": \"A template ref: a built-in id, a path, or a URL, optionally with a `#name` fragment.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n }\n }\n}\n","/**\n * The DITA Open Toolkit process seam: the only module that starts `dita`.\n *\n * Written in the shape `src/term/core/vale.ts` set, for the same reasons. The\n * launcher is started with an argv and never a shell string, the buffer is\n * raised because a build log is not small, and what comes back is validated\n * here, because it crosses a process boundary.\n *\n * **The test suite starts no DITA-OT, and needs no JVM.** Every unit test\n * injects a stub spawn. What the parser is fed is real output, though: the\n * logs under `test/lint/fixtures/dita-ot/logs/` are captures of DITA-OT 4.4.1\n * runs over the fixtures beside them, with only absolute paths rewritten.\n * Their README records what each one holds and where each field name comes\n * from.\n *\n * The verdict is the log, not the exit code: DITA-OT publishes no exit-code\n * contract, so a non-zero exit with a parseable log is findings, and only a\n * log that is missing or unreadable *and* a non-zero exit is a failure to run.\n */\nimport { execFile } from \"node:child_process\";\nimport { mkdtemp, readFile, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Severity } from \"../../shared/severity.js\";\nimport { ditaOtHome } from \"../../shared/tools.js\";\nimport { LintError } from \"../types.js\";\n// Type-only, and erased at runtime: `./index.js` imports this module to put the\n// descriptor in its initial map, so a value import here would be a cycle.\nimport type { FormatInfo } from \"../commands/tools.js\";\nimport type { StructureToolDescriptor, ToolProbe, ToolProbeContext } from \"./index.js\";\n\n/** What the launcher did. `code` is null when a signal ended it. */\nexport interface DitaOtProcessResult {\n code: number | null;\n /**\n * The signal that ended it, when one did, and `code` is then null. Optional\n * because absent means what it says: the run was not killed.\n */\n signal?: string | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Start `launcher` with `args` and wait for it. Resolves for any exit; rejects\n * when the process could not start, with an error whose `code` is `ENOENT`\n * when there is no such launcher. Tests inject a stub.\n */\nexport type DitaOtSpawn = (\n launcher: string,\n args: string[],\n opts: { cwd?: string },\n) => Promise<DitaOtProcessResult>;\n\nconst MAX_BUFFER = 64 * 1024 * 1024;\n\n/** The two refusals a user reads, worded once. */\nconst NOT_ON_PATH =\n \"dita is not on PATH. Install DITA Open Toolkit to lint structure, or set tools.dita-ot.home.\";\n\nconst noLauncher = (home: string): string =>\n `no DITA Open Toolkit at \"${home}\". tools.dita-ot.home must be a DITA-OT installation directory.`;\n\n/**\n * `<home>/bin/dita`, or the bare name for the one on PATH. Windows has its own\n * launcher, `dita.bat`, which is what makes `ditaOtCommandLine` necessary.\n *\n * `platform` is a parameter so the mapping can be tested on either host.\n */\nexport function ditaOtLauncher(\n home: string | undefined,\n platform: NodeJS.Platform = process.platform,\n): string {\n const name = platform === \"win32\" ? \"dita.bat\" : \"dita\";\n return home === undefined ? name : join(home, \"bin\", name);\n}\n\n/** How a launcher is actually started. */\nexport interface DitaOtCommandLine {\n command: string;\n argv: string[];\n /** Windows: the argv is already one quoted string and must not be re-quoted. */\n verbatim: boolean;\n}\n\nfunction isBatch(launcher: string): boolean {\n const lower = launcher.toLowerCase();\n return lower.endsWith(\".bat\") || lower.endsWith(\".cmd\");\n}\n\n/**\n * Quote one token for `cmd.exe`. A `\"` inside a Windows path is impossible -\n * the character is not legal in a filename - so a value carrying one is a\n * shape this cannot quote safely, and it is refused rather than passed on.\n */\nfunction quoteForCmd(value: string): string {\n if (value.includes('\"')) {\n throw new LintError(\n `DITA Open Toolkit cannot be given an argument containing a quote: ${value}`,\n );\n }\n return `\"${value}\"`;\n}\n\n/**\n * How to start `launcher`, given what it is.\n *\n * Node refuses to `execFile` a `.bat` or `.cmd` without a shell, and\n * `shell: true` joins the argv into one string **with no quoting at all**. Our\n * argv carries user-supplied paths, so that is a command-injection hole, not a\n * convenience. The documented alternative is this one: run `cmd.exe` directly,\n * do the quoting here, and pass the whole command as one verbatim argument.\n * `/s` then strips exactly the outer pair of quotes, which is why the string\n * is wrapped a second time. The next reader will find `shell: true` simpler;\n * it is also the version with the hole in it.\n */\nexport function ditaOtCommandLine(\n launcher: string,\n args: string[],\n): DitaOtCommandLine {\n if (!isBatch(launcher)) {\n return { command: launcher, argv: args, verbatim: false };\n }\n const line = [launcher, ...args].map(quoteForCmd).join(\" \");\n return {\n command: process.env[\"COMSPEC\"] ?? \"cmd.exe\",\n argv: [\"/d\", \"/s\", \"/c\", `\"${line}\"`],\n verbatim: true,\n };\n}\n\nexport const spawnDitaOt: DitaOtSpawn = (launcher, args, opts) =>\n new Promise((settle, reject) => {\n const line = ditaOtCommandLine(launcher, args);\n execFile(\n line.command,\n line.argv,\n {\n cwd: opts.cwd,\n windowsHide: true,\n windowsVerbatimArguments: line.verbatim,\n maxBuffer: MAX_BUFFER,\n encoding: \"utf8\",\n },\n (error, stdout, stderr) => {\n if (!error) {\n settle({ code: 0, stdout, stderr });\n return;\n }\n if (typeof error.code === \"number\") {\n settle({ code: error.code, stdout, stderr });\n return;\n }\n // Killed by a signal. Node reports the exit code as **null** here, not\n // as undefined, so testing for undefined never matched and a killed\n // run rejected as though the launcher had failed to start. The log is\n // still read: a run cut short may have written findings before it died.\n if (error.code == null && typeof error.signal === \"string\") {\n settle({ code: null, signal: error.signal, stdout, stderr });\n return;\n }\n // A start failure (ENOENT) or an output over the buffer.\n const failure: Error = error;\n reject(failure);\n },\n );\n });\n\nfunction isEnoent(err: unknown): boolean {\n return (\n typeof err === \"object\" &&\n err !== null &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n );\n}\n\nfunction isMapping(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a bare launcher name resolves to a file on `PATH`.\n *\n * Asked only after a failure, and only to choose the message. On Windows the\n * launcher is a `.bat` started through `cmd.exe`, so a missing one comes back\n * as cmd's own non-zero exit rather than as an `ENOENT` - and the advice a\n * reader needs is \"install DITA-OT\", not cmd's sentence about a command it did\n * not recognize.\n */\nasync function isOnPath(name: string): Promise<boolean> {\n for (const dir of (process.env[\"PATH\"] ?? \"\").split(delimiter)) {\n if (dir === \"\") continue;\n if (await isFile(join(dir, name))) return true;\n }\n return false;\n}\n\n/**\n * DITA-OT's `MessageBean.Type` onto the family's three-value scale, as\n * `foldValeSeverity` does for Vale's.\n *\n * `null` is \"not a finding\": `DEBUG` and `TRACE` are build chatter even when\n * they carry a code, and a level this does not know is dropped rather than\n * promoted to something it might not be.\n */\nexport function foldDitaOtSeverity(level: string): Severity | null {\n switch (level.toUpperCase()) {\n case \"FATAL\":\n case \"ERROR\":\n return \"error\";\n case \"WARN\":\n return \"warning\";\n case \"INFO\":\n return \"notice\";\n default:\n return null;\n }\n}\n\n/** One diagnostic DITA-OT reported, in the vocabulary the lint branch reads. */\nexport interface DitaOtMessage {\n /** DITA-OT's own message id, e.g. `DOTX010E`. Becomes the finding's `type`. */\n code: string;\n severity: Severity;\n message: string;\n /** The path its `location` URI named, absent when it named none. */\n file?: string;\n line?: number;\n /** The **column**. See `readMessage` for why the log calls it `row`. */\n column?: number;\n}\n\n/**\n * The path a `location` names.\n *\n * DITA-OT writes `file:/x`, one slash and no authority. `new URL` normalizes\n * that to the three-slash form, so the quirk costs nothing. What does cost\n * something is a POSIX-rooted URI read on Windows: `fileURLToPath` refuses\n * `file:///repo/a.dita` outright there, with `ERR_INVALID_FILE_URL_PATH`, and\n * a log written on a Linux CI runner is full of exactly those. The decoded\n * pathname is the answer in that case.\n */\nfunction pathFromFileUri(location: string): string {\n let url: URL;\n try {\n url = new URL(location);\n } catch {\n // Not a URI at all: DITA-OT named a bare path, so it is the path.\n return location;\n }\n if (url.protocol !== \"file:\") return location;\n try {\n return fileURLToPath(url);\n } catch {\n return decodeURIComponent(url.pathname);\n }\n}\n\n/**\n * One log object as a finding, or `null` when it is not one.\n *\n * `code` is the marker. DITA-OT writes hundreds of `INFO` lines that are\n * progress rather than diagnostics, and it is its own account of which is\n * which, so an object without a code is chatter whatever its level.\n */\nfunction readMessage(raw: unknown): DitaOtMessage | null {\n if (!isMapping(raw)) return null;\n const code = raw[\"code\"];\n if (typeof code !== \"string\" || code === \"\") return null;\n const level = raw[\"level\"];\n const severity =\n typeof level === \"string\" ? foldDitaOtSeverity(level) : null;\n if (severity === null) return null;\n\n const msg = raw[\"msg\"];\n const location = raw[\"location\"];\n const line = raw[\"line\"];\n // `row` is the column. The name is the logger's: its regex takes group 2 as\n // the line and group 3 as the column, then writes the column out under this\n // name. Read as a row it puts every finding on the wrong line, so do not\n // \"fix\" it to `line`.\n const row = raw[\"row\"];\n\n return {\n code,\n severity,\n // A coded message with no text is still a finding; the code says what it is.\n message: typeof msg === \"string\" && msg !== \"\" ? msg : code,\n ...(typeof location === \"string\" && location !== \"\"\n ? { file: pathFromFileUri(location) }\n : {}),\n ...(typeof line === \"number\" ? { line } : {}),\n ...(typeof row === \"number\" ? { column: row } : {}),\n };\n}\n\n/**\n * The findings in a `--logger=json` log, or `null` when the text is not one.\n *\n * Null rather than an empty list, because the caller treats the two\n * differently: no findings is a clean target, while no log at all is only a\n * failure when the launcher also exited non-zero.\n */\nexport function parseDitaOtLog(text: string): DitaOtMessage[] | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n if (!Array.isArray(parsed)) return null;\n const messages: DitaOtMessage[] = [];\n for (const raw of parsed as unknown[]) {\n const message = readMessage(raw);\n if (message !== null) messages.push(message);\n }\n return messages;\n}\n\n/** The first version-shaped number in what the launcher printed. */\nexport function parseDitaOtVersion(output: string): string | null {\n const match = /\\b(\\d+(?:\\.\\d+)+(?:[-.][0-9A-Za-z]+)*)/.exec(output);\n return match?.[1] ?? null;\n}\n\n/** What one invocation of DITA-OT had to say about one target. */\nexport interface DitaOtTargetResult {\n /** The absolute path this invocation was given as `--input`. */\n target: string;\n messages: DitaOtMessage[];\n}\n\nexport interface DitaOtRunOptions {\n /** Absolute paths. One invocation each: DITA-OT takes one `--input`. */\n targets: string[];\n /** The directory the run resolves from, and where the launcher is started. */\n cwd: string;\n /** `tools.dita-ot.home`, already resolved to an absolute directory. */\n home?: string;\n spawn?: DitaOtSpawn;\n}\n\n/** The message a failure to run reports, which is the launcher's own. */\nfunction failureText(result: DitaOtProcessResult): string {\n const stderr = result.stderr.trim();\n if (stderr !== \"\") return stderr;\n // \"exit code null\" says nothing a reader can act on, and a signal is the one\n // ending that reliably leaves no message behind.\n if (result.signal != null) return `killed by ${result.signal}`;\n return `exit code ${String(result.code)}`;\n}\n\nasync function readLog(path: string): Promise<DitaOtMessage[] | null> {\n let text: string;\n try {\n text = await readFile(path, \"utf8\");\n } catch {\n return null;\n }\n return parseDitaOtLog(text);\n}\n\n/**\n * DITA-OT over each target, and what its log said.\n *\n * ## Why this is not `dita validate`\n *\n * Because `dita validate` does not answer the question this tool exists for.\n * Run against a map whose topic conrefs an id that is not there, it exits 0 and\n * prints nothing. It parses and checks grammar, and it resolves no references.\n * Its own `--help` lists neither `--logger` nor `--logfile` nor `--temp`, so\n * the obvious invocation is silently accepted and silently useless.\n *\n * `--format=dita` is the DITA-to-DITA transformation. It runs the whole\n * preprocessing chain, which is where conref, keyref and link resolution\n * happen, and it reports what those stages find. Measured against the fixtures\n * beside this file, it catches everything `validate` catches and the references\n * as well: a grammar error is `DOTJ088E` with a line and a column, a missing\n * conref target is `DOTX010E`, an unreadable link target is `DOTX008E`.\n *\n * The cost is that a transformation writes output. It goes to the scratch\n * directory with the logs and the temp trees, and the whole scratch is removed\n * however the run ends, so nothing of ours lands in the user's tree.\n *\n * ## Why verbose\n *\n * Because DITA-OT reports an undefined key reference (`DOTJ047I`) at `INFO`,\n * and the default verbosity drops it. Verbose turns one run of a four-file map\n * into roughly 135 log entries, of which one carries a `code`. That is what\n * makes the `code` filter in `parseDitaOtLog` load-bearing rather than tidy.\n */\nexport async function runDitaOtValidate(\n opts: DitaOtRunOptions,\n): Promise<DitaOtTargetResult[]> {\n const spawn = opts.spawn ?? spawnDitaOt;\n const launcher = ditaOtLauncher(opts.home);\n // A home that holds no launcher is a configuration mistake, and saying so\n // before anything is started names the thing the user can fix.\n if (opts.home !== undefined && !(await isFile(launcher))) {\n throw new LintError(noLauncher(opts.home));\n }\n\n const scratch = await mkdtemp(join(tmpdir(), \"manni-dita-ot-\"));\n try {\n const results: DitaOtTargetResult[] = [];\n for (const [index, target] of opts.targets.entries()) {\n const logfile = join(scratch, `${String(index)}.json`);\n const args = [\n `--input=${target}`,\n \"--format=dita\",\n `--output=${join(scratch, `out-${String(index)}`)}`,\n `--temp=${join(scratch, `temp-${String(index)}`)}`,\n \"--logger=json\",\n `--logfile=${logfile}`,\n \"--verbose\",\n ];\n\n let result: DitaOtProcessResult;\n try {\n result = await spawn(launcher, args, { cwd: opts.cwd });\n } catch (err) {\n if (isEnoent(err)) {\n throw new LintError(\n opts.home === undefined ? NOT_ON_PATH : noLauncher(opts.home),\n );\n }\n throw err;\n }\n\n const messages = await readLog(logfile);\n\n /**\n * A non-zero exit has to be accounted for by something we are going to\n * report. Otherwise the run failed for a reason this log does not hold,\n * and saying \"no findings\" would be inventing a clean bill of health.\n *\n * The asymmetry is DITA-OT's. It exits **0** while logging `DOTX010E`\n * for a broken conref, so a zero exit proves nothing and the log is the\n * verdict. It also exits **1** on a map that is not well-formed, having\n * already written a perfectly readable log whose only coded entries are\n * `DOTJ030I` at `INFO`, with the fatal parse error in neither the log\n * nor stdout nor stderr. Trusting the parse of that log reported the\n * file as passing, which is the false green this guard exists for.\n */\n // Only an `error` accounts for it. A warning or a notice is something\n // DITA-OT says while finishing the job, and it exits 0 having said it,\n // so a non-zero exit alongside nothing worse than `DOTJ047I` is still a\n // failure this log does not explain.\n const explained =\n messages !== null && messages.some((m) => m.severity === \"error\");\n\n if (result.code !== 0 && !explained) {\n if (opts.home === undefined && !(await isOnPath(launcher))) {\n throw new LintError(NOT_ON_PATH);\n }\n throw new LintError(\n `DITA Open Toolkit failed on ${target}: ${failureText(result)}. ` +\n `Run it directly on that file to see why.`,\n );\n }\n\n results.push({ target, messages: messages ?? [] });\n }\n return results;\n } finally {\n await rm(scratch, { recursive: true, force: true });\n }\n}\n\n/**\n * Whether DITA-OT can run here, and at what version.\n *\n * Never throws: reporting what cannot run is `manni lint tools`' job, and a\n * probe that threw would take the whole table down with it. So a launcher\n * that fails to start for any reason (`ENOENT`, but also `EACCES` or `EPERM`)\n * is unavailable. A non-zero exit counts as unavailable too, because on\n * Windows a missing `dita.bat` is cmd.exe's exit code rather than an `ENOENT`.\n */\nexport async function probeDitaOt(\n ctx: ToolProbeContext,\n spawn: DitaOtSpawn = spawnDitaOt,\n): Promise<ToolProbe> {\n const home =\n ctx.tools === undefined\n ? undefined\n : ditaOtHome(ctx.tools, ctx.configDir ?? ctx.cwd);\n const launcher = ditaOtLauncher(home);\n if (home !== undefined && !(await isFile(launcher))) {\n return { available: false, version: null };\n }\n\n let result: DitaOtProcessResult;\n try {\n result = await spawn(launcher, [\"--version\"], { cwd: ctx.cwd });\n } catch {\n return { available: false, version: null };\n }\n if (result.code !== 0) return { available: false, version: null };\n return {\n available: true,\n // Both channels, because which one carries the banner is not fixed: the\n // shell launcher prints it on stdout, and the Windows `.bat` run through\n // `cmd.exe` has been seen to put it on stderr. Reading one would report a\n // toolkit that is plainly there as versionless on the other platform.\n version: parseDitaOtVersion(`${result.stdout}\\n${result.stderr}`),\n };\n}\n\n/**\n * DITA's one format row.\n *\n * `kinds` is empty and that is correct: DITA-OT does not parse into manni's\n * content model, and the column exists to say what a template rule can ask\n * about. `.xml` is listed because a DITA topic is often named that way; the\n * walk set is narrower on purpose - see `walkExtensions`.\n */\nconst DITA_FORMAT: FormatInfo = {\n name: \"dita\",\n label: \"DITA\",\n extensions: [\".ditamap\", \".dita\", \".xml\"],\n kinds: [],\n};\n\nexport const ditaOt: StructureToolDescriptor = {\n name: \"dita-ot\",\n label: \"DITA Open Toolkit\",\n formats: () => [{ ...DITA_FORMAT }],\n // Maps only. DITA-OT takes one `--input` per invocation, so walking\n // every topic in a docset would pay a JVM start per topic. Naming a `.dita`\n // explicitly still works: a named file bypasses the extension filter.\n walkExtensions: () => [\".ditamap\"],\n probe: (ctx) => probeDitaOt(ctx),\n // None. Every option manni owns is refused under this tool, stdin included.\n ownedOptions: [],\n};\n","/**\n * The tools that can perform a lint job, one descriptor each.\n *\n * Deliberately **not** a unified `run()` interface. The tools take completely\n * different inputs: manni's engine needs the templates, the doctype index and\n * the parser registry, and a tool outside the package needs none of them. A\n * lowest-common-denominator `run(files, options)` would either lie about what\n * it accepts or grow a passthrough blob nobody can type. `src/shared/tools.ts`\n * records the same decision for `tools:`, and for the same reason.\n *\n * What the tools genuinely share is what the *command* has to know before it\n * dispatches: the formats to list, the extensions a directory walk collects,\n * whether the tool can run here, and which options belong to the tool rather\n * than to the job. That is this table, and nothing more. Running a tool stays\n * with the branch that runs it.\n */\nimport pkg from \"../../../package.json\" with { type: \"json\" };\nimport { listFormats, supportedExtensions } from \"../parsers/index.js\";\nimport type { FormatInfo } from \"../commands/tools.js\";\nimport { TOOLS_BY_JOB, type LintTool } from \"../core/config.js\";\nimport type { ToolsConfig } from \"../../shared/tools.js\";\nimport { LintError } from \"../types.js\";\nimport { ditaOt } from \"./dita-ot.js\";\n\n/** What a probe is allowed to look at. A tool outside the package shells out. */\nexport interface ToolProbeContext {\n /** The directory the run resolves from. */\n cwd: string;\n /**\n * The family's top-level `tools:`, when a config declared one.\n *\n * A tool outside the package is found where its own `tools.<tool>` says it\n * is, so a probe that could not read it would report \"not available\" over an\n * installation the run itself goes on to use. manni's own probe ignores it.\n */\n tools?: ToolsConfig;\n /** The directory the config that declared `tools:` sits in. */\n configDir?: string;\n}\n\n/** Whether a tool can run here, and at what version. */\nexport interface ToolProbe {\n available: boolean;\n /** The version the tool reported, or null when it reported none. */\n version: string | null;\n}\n\nexport interface StructureToolDescriptor {\n name: LintTool;\n label: string;\n /** Formats for `manni lint tools`' formats column. */\n formats: () => FormatInfo[];\n /** Extensions a directory walk collects when this tool runs. */\n walkExtensions: () => string[];\n /** Whether this tool can run here, and at what version. */\n probe: (ctx: ToolProbeContext) => Promise<ToolProbe>;\n /**\n * Options that belong to this tool alone, spelled as a user types them.\n *\n * `-` is in the list because stdin is one of them: a tool that reads a\n * document off a pipe is answering the same question as one that reads\n * `--template`, and deriving both from one list is what keeps the next tool\n * from having to remember to refuse either.\n */\n ownedOptions: readonly string[];\n}\n\n/**\n * manni's own engine, which ships in this package.\n *\n * `formats` and `walkExtensions` forward to the parser registry rather than\n * restating it: a format added there is listed and walked here the moment it\n * is registered, which is the property `parsers/index.ts` exists to have.\n */\nconst manni: StructureToolDescriptor = {\n name: \"manni\",\n label: \"manni's own structure engine\",\n formats: () => listFormats(),\n walkExtensions: () => supportedExtensions(),\n // Nothing to look for: the engine is this package, so it is available\n // wherever the CLI is. A tool that shells out answers this by looking.\n probe: () => Promise.resolve({ available: true, version: pkg.version }),\n // `-` is stdin, and it is in this list so the derivation in `lint.ts` has\n // one place to learn that manni owns it. The refusal itself is not shaped\n // like the others, because \"option\" is the wrong word for a positional:\n // `assertOptionsOwned` special-cases it to say \"cannot read stdin\" instead.\n // A tool that copies this list without reading that guard gets the right\n // ownership and the wrong sentence.\n ownedOptions: [\"--template\", \"--templates\", \"--explain\", \"--as\", \"-\"],\n};\n\n/**\n * Registered statically rather than through `registerStructureTool` at import\n * time, so the table and `LINT_TOOLS` agree whatever order the modules load\n * in: a name in `LINT_TOOLS` with no descriptor is a `--tool` value the config\n * validates and the run cannot dispatch.\n */\nconst descriptors = new Map<string, StructureToolDescriptor>([\n [manni.name, manni],\n [ditaOt.name, ditaOt],\n]);\n\n/**\n * Add a descriptor, and hand back the undo.\n *\n * The registry is mutable so a second tool can be stood up without a name\n * being added to `LINT_TOOLS` first. A name there is a `--tool` value the CLI\n * accepts and the config validates, so adding one for a tool that cannot run\n * is the silent green this seam exists to prevent.\n */\nexport function registerStructureTool(\n descriptor: StructureToolDescriptor,\n): () => void {\n const previous = descriptors.get(descriptor.name);\n descriptors.set(descriptor.name, descriptor);\n return () => {\n if (previous === undefined) descriptors.delete(descriptor.name);\n else descriptors.set(descriptor.name, previous);\n };\n}\n\n/** The descriptor registered under `name`, if there is one. */\nexport function structureTool(\n name: string,\n): StructureToolDescriptor | undefined {\n return descriptors.get(name);\n}\n\n/** Every registered descriptor, in registration order. */\nexport function structureTools(): StructureToolDescriptor[] {\n return [...descriptors.values()];\n}\n\n/**\n * The descriptor for a named tool, or the refusal a user reads.\n *\n * The message is the one the CLI's own `--tool` guard produced before the\n * registry existed, because `runLint` is exported from `src/index.ts` and\n * called in process: a caller that never touches commander owes the same\n * sentence.\n */\nexport function resolveStructureTool(name: string): StructureToolDescriptor {\n const descriptor = descriptors.get(name);\n if (descriptor) return descriptor;\n throw new LintError(\n `Unknown --tool \"${name}\" for structure. Use ${TOOLS_BY_JOB.structure.join(\", \")}.`,\n );\n}\n"],"mappings":"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","names":["readFile","resolve","rebased","dirname","isAbsolute","parseYaml","AjvNs","Ajv","isRecord","ajv","Ajv","compiled","describeError","parseYaml","dirname","isAbsolute","readFile","join","join","readFile","origin","resolve","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/lint/commands/lint.ts","../src/lint/core/template-registry.ts","../schemas/lint/template.json","../templates/lint/tgdp/manifest.json","../src/lint/core/resolve-template.ts","../src/lint/core/validator.ts","../src/lint/core/config.ts","../schemas/lint/config.json","../src/lint/tools/dita-ot.ts","../src/lint/tools/index.ts"],"sourcesContent":["/**\n * `lint` command core. Resolves targets, picks a parser per file, resolves a\n * template per file, and returns structured results. Free of CLI/IO plumbing so\n * it can be driven directly from tests and from the programmatic API.\n *\n * Template selection is per file, not per run: a page declares what it is\n * (`type: how-to`) and the template follows. `--template` still exists and\n * still overrides everything, but it is no longer required, which is what lets\n * one invocation lint a whole tree of mixed doctypes.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { extname, relative, resolve } from \"node:path\";\nimport { errorMessage, ToolError } from \"../../shared/errors.js\";\nimport {\n LintError,\n type DocumentParser,\n type DocumentTree,\n type FileResult,\n type Finding,\n type Position,\n} from \"../types.js\";\nimport {\n parserByName,\n parserForExtension,\n unsupportedFormatMessage,\n} from \"../parsers/index.js\";\nimport type { Template, TemplateFile } from \"../core/template.js\";\nimport {\n classifyRef,\n refRelativeTo,\n listBuiltins,\n loadResolvedTemplate,\n loadTemplateFile,\n} from \"../core/template-registry.js\";\nimport {\n buildTypeIndex,\n knownTypes,\n resolveTemplateRef,\n FILE_TEMPLATE_KEY,\n type Resolution,\n type TemplateOverride,\n type TypeIndexEntry,\n} from \"../core/resolve-template.js\";\nimport { validateDocument } from \"../core/validator.js\";\nimport {\n resolveStructureTool,\n structureTools,\n type StructureToolDescriptor,\n} from \"../tools/index.js\";\nimport {\n ditaOt,\n runDitaOtValidate,\n type DitaOtMessage,\n} from \"../tools/dita-ot.js\";\nimport { ditaOtHome } from \"../../shared/tools.js\";\nimport {\n assertNonEmpty,\n gitignoreOptions,\n resolveTargetSet,\n STDIN_TOKEN,\n} from \"../../meta/internal.js\";\nimport { isErrorSeverity } from \"../../meta/index.js\";\nimport { resolveLintRun, type LintConfig } from \"../core/config.js\";\n\n/** File label used for a document read from stdin. */\nexport const STDIN_LABEL = \"<stdin>\";\n\nexport interface LintOptions {\n /**\n * Positional inputs: files, directories, or globs. `-` reads stdin. Empty\n * falls back to the `collections:` the config declares (proposal 0041).\n */\n inputs: string[];\n /** `--collection <name>`, repeatable: the collections this run covers. */\n collection?: string[];\n /**\n * `--tool`: which tool performs the structure job. Overrides the config's\n * own `lint.structure.tool`, and defaults to manni's engine.\n */\n tool?: string;\n /** `--template`: a built-in id, a path, or a name inside `templates`. */\n template?: string;\n /**\n * `--templates`: template files whose `types:` join the routing table. A bare\n * `--template <name>` is resolved as a name inside the *last* of them, which\n * is the pairing the manni docevals adapter invokes. Last, not first, to\n * agree with `buildTypeIndex`: a later file overrides an earlier one there,\n * so resolving a bare name against the first would make one name mean two\n * different templates depending on how it was written.\n */\n templates?: string | string[];\n /** `--as`: force an input format, by parser name. */\n as?: string;\n /** `--ext`: extensions kept during directory and glob expansion. */\n exts?: string[];\n /** `--exclude`: globs removed from directory/glob expansion. */\n exclude?: string[];\n cwd?: string;\n /** Content for the `-` input, injected by the CLI and by tests. */\n stdinContent?: string;\n /** `--explain`: record how each file's template was chosen. */\n explain?: boolean;\n /** Repo policy: first matching glob wins. Overrides the config's own. */\n overrides?: TemplateOverride[];\n /** Applied when a page declares no doctype. Overrides the config's own. */\n defaultTemplate?: string;\n /** Explicit doctype -> template ref map. Overrides the config's own. */\n types?: Record<string, string>;\n /** `--allow-empty`: zero matched files is a success, not an error. */\n allowEmpty?: boolean;\n /** `--no-gitignore`: only an explicit `false` travels, so config can decide. */\n respectGitignore?: boolean;\n /** `-c/--config`. */\n configPath?: string;\n /** `--no-config`: skip discovery and run on the built-in defaults. */\n noConfig?: boolean;\n /** Told which config governed the run, and where it came from. */\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n /** Told what the run had to say beside its findings. */\n onNotice?: (message: string) => void;\n /**\n * The DITA-OT seam, injected by tests, as `term lint` injects Vale's.\n *\n * There is no JVM and no DITA-OT on a CI runner of ours, so the only way to\n * exercise this branch end to end is to hand it the process boundary. The\n * shipped default is the real one.\n */\n runDitaOt?: typeof runDitaOtValidate;\n}\n\n/** Extensions that make a bare `--template` value a filename, not a name. */\nconst TEMPLATE_FILE_EXTENSIONS = [\".yaml\", \".yml\", \".json\"];\n\n/**\n * Run a family helper, rethrowing the error it raises as this tool's.\n *\n * The walker and its guards are meta's, and they throw `DocmetaError` - a\n * sibling of `LintError`, not a parent of it. `runLint` is exported from\n * `src/index.ts` and called in process by manni docevals, so a caller doing\n * the documented thing, `catch (err) { if (err instanceof LintError) … }`,\n * missed the two most ordinary operational failures there are: a mistyped path\n * and a pattern matching nothing. The message is the walker's own, which is\n * the part the user reads; only the class changes.\n */\nasync function asLintError<T>(run: () => T | Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (err) {\n if (err instanceof LintError) throw err;\n // A `ToolError` from any sibling is operational and already worded for a\n // user. Anything else is a bug and keeps its class and its stack.\n if (err instanceof ToolError) throw new LintError(err.message);\n throw err;\n }\n}\n\n/** `--templates` accepts one path or several; normalize to a list. */\nfunction templateFiles(value: string | string[] | undefined): string[] {\n if (value == null) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * The options a tool can own, in the order the guard reports them, each paired\n * with how to tell that the caller passed it.\n *\n * `--templates` reads `opts` alone. A `templates:` key in the config describes\n * the repo rather than the invocation, so a repo that has written one must not\n * be refused the moment it points a different tool at the same tree.\n */\nconst TOOL_OPTIONS: {\n spelling: string;\n given: (opts: LintOptions) => boolean;\n}[] = [\n { spelling: \"--template\", given: (o) => o.template !== undefined },\n {\n spelling: \"--templates\",\n given: (o) => templateFiles(o.templates).length > 0,\n },\n { spelling: \"--explain\", given: (o) => o.explain === true },\n { spelling: \"--as\", given: (o) => o.as !== undefined },\n];\n\n/**\n * The options some other registered tool owns and this one does not, each with\n * the tool that does own it.\n *\n * Derived rather than written out per tool, because a hand-written chain is a\n * list the next tool has to remember to join - and the failure mode of\n * forgetting is a silently ignored option, which is a run that reports success\n * having done something other than what was asked.\n */\nfunction foreignOptions(\n tool: StructureToolDescriptor,\n): Map<string, StructureToolDescriptor> {\n const foreign = new Map<string, StructureToolDescriptor>();\n for (const other of structureTools()) {\n // By identity, not by name: the registry holds one descriptor per name, so\n // the tool this run resolved *is* its entry, and comparing the names is a\n // tautology to the compiler while `LintTool` carries one of them.\n if (other === tool) continue;\n for (const spelling of other.ownedOptions) {\n if (tool.ownedOptions.includes(spelling)) continue;\n if (!foreign.has(spelling)) foreign.set(spelling, other);\n }\n }\n return foreign;\n}\n\n/**\n * Refuse an option that belongs to a tool this run is not performing the job\n * with. One message, naming the first offending option.\n *\n * Ignoring it instead would be the quietest kind of wrong answer: the flag\n * reads as applied, the run exits 0, and nothing says the template, the format\n * override or the explanation was dropped on the floor.\n */\nfunction assertOptionsOwned(\n opts: LintOptions,\n tool: StructureToolDescriptor,\n usingStdin: boolean,\n): void {\n const foreign = foreignOptions(tool);\n if (foreign.size === 0) return;\n\n for (const { spelling, given } of TOOL_OPTIONS) {\n const owner = foreign.get(spelling);\n if (owner === undefined || !given(opts)) continue;\n throw new LintError(\n `${spelling} is an option of the ${owner.name} tool; ` +\n `the structure job's tool is ${tool.name}.`,\n );\n }\n\n // Stdin last, and in its own words: \"option\" is the wrong noun for a pipe,\n // and the advice a reader needs is what to pass instead.\n if (usingStdin && foreign.has(STDIN_TOKEN)) {\n throw new LintError(`${tool.name} cannot read stdin. Name files or a map.`);\n }\n}\n\nexport interface LintSummary {\n /** Files actually linted. Always `passed + failed`; skips are counted apart. */\n checked: number;\n passed: number;\n failed: number;\n skipped: number;\n}\n\n/**\n * A `FileResult` plus the human-readable reason a file was skipped.\n *\n * `types.ts` pins the fields manni docevals reads off the JSON reporter, so\n * this diagnostic lives here instead: it exists for pretty output, and the\n * JSON reporter never emits it.\n */\nexport interface LintFileResult extends FileResult {\n /** Why the file was skipped, in words. Set only alongside `skipped`. */\n reason?: string;\n /** How the template was chosen. Present when `--explain` asked for it. */\n resolution?: Resolution;\n /**\n * What the alignment block is rendered from. Present when `--explain` asked\n * for it, and only for a file that routed to a template.\n *\n * Nested rather than flat, because `FileResult.template` is already taken by\n * the template's *ref* - the string a reader sees on the routing line. A\n * loaded `Template` beside it under the same name would be two things called\n * one thing, and the reporter would read whichever it got.\n */\n alignment?: { tree: DocumentTree; template: Template };\n}\n\nexport interface LintRun {\n results: LintFileResult[];\n summary: LintSummary;\n}\n\n/** Zero-width span at the top of a file, for findings with nowhere better. */\nfunction origin(): Position {\n return {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n };\n}\n\nfunction skip(\n file: string,\n reason: string,\n cause: FileResult[\"skipped\"] = \"unsupported-format\",\n): LintFileResult {\n return {\n file,\n // `success` means \"linted and produced no findings\". A file that was never\n // linted is neither passing nor failing, and calling it a pass would hide\n // the gap from anyone reading CI output.\n success: false,\n findings: [],\n template: null,\n skipped: cause,\n reason,\n };\n}\n\nfunction parseFinding(message: string): Finding {\n return {\n type: \"parse_error\",\n heading: null,\n message,\n position: origin(),\n severity: \"error\",\n };\n}\n\n/**\n * A template ref, loaded once per run however many pages route to it.\n *\n * The registry deliberately leaves `extends` unresolved, so the merge happens\n * here. Skipping it would make inheritance silently inert: a template that\n * inherits every one of its sections would check nothing and report a clean\n * pass. `resolveExtends` is a no-op on a template without `extends`.\n */\nfunction templateLoader(): (ref: string) => Promise<Template> {\n const cache = new Map<string, Promise<Template>>();\n return (ref) => {\n let pending = cache.get(ref);\n if (!pending) {\n pending = loadResolvedTemplate(ref);\n cache.set(ref, pending);\n }\n return pending;\n };\n}\n\n/**\n * Normalize `--template` into a ref the registry understands.\n *\n * `--templates t.yaml --template how-to` is the pairing the manni docevals\n * adapter invokes, and it means \"the template named how-to inside t.yaml\".\n * A ref that already names a file, a URL, or a built-in is passed through.\n */\nfunction cliTemplateRef(\n template: string | undefined,\n templatePaths: string[],\n): string | undefined {\n if (!template) return undefined;\n // A ref that names itself is passed through. Sniffing for `#`, `/` and `\\`\n // missed built-in ids, which contain none of them, so `-t tgdp:how-to:1.6`\n // silently became a lookup for a template of that name inside the first\n // `--templates` file the moment one was given.\n if (template.includes(\"#\")) return template;\n if (classifyRef(template).kind !== \"file\") return template;\n if (template.includes(\"/\") || template.includes(\"\\\\\")) return template;\n // A bare filename names a file even without a separator: `--template\n // custom.yaml` means that file, not a template called \"custom.yaml\" inside\n // some other one.\n if (TEMPLATE_FILE_EXTENSIONS.some((e) => template.toLowerCase().endsWith(e))) {\n return template;\n }\n if (templatePaths.length === 0) return template;\n\n // A bare name is a name inside a `--templates` file. Search them last-first,\n // matching `buildTypeIndex`, so the file that wins routing also wins here.\n return `${templatePaths[templatePaths.length - 1] ?? \"\"}#${template}`;\n}\n\n/**\n * Which file a template came from and what it is called there.\n *\n * The validator and the matcher both name the template in messages - the\n * state-cap failure, and the warning about a rule a format cannot answer - and\n * neither can work either out from a `Template` object, which carries no\n * record of where it was loaded from. The ref does: `./templates.yaml#how-to`\n * is a file and a name, and a built-in id is both at once.\n */\nfunction templateIdentity(ref: string): { source: string; template: string } {\n const hash = ref.lastIndexOf(\"#\");\n if (hash === -1) return { source: ref, template: ref };\n return { source: ref.slice(0, hash), template: ref.slice(hash + 1) };\n}\n\n/** The finding a page gets when it declares a doctype nothing serves. */\nfunction unknownTypeFinding(\n resolution: Resolution,\n typeIndex: Map<string, TypeIndexEntry>,\n position: Position,\n): Finding {\n const suggestions = resolution.suggestions ?? [];\n const known = resolution.knownTypes ?? knownTypes(typeIndex);\n const hint = suggestions.length\n ? ` Did you mean ${suggestions.map((s) => `\"${s}\"`).join(\", \")}?`\n : ` Known doctypes: ${known.join(\", \") || \"(none)\"}.`;\n // `unknownType` is set whenever the cause is `unknown-type`, but the type\n // says `string | undefined` and only the resolver enforces the pairing. The\n // fallback keeps a future break from printing `type \"undefined\"`, which\n // would send the reader looking for a doctype they never wrote.\n const declared = resolution.unknownType ?? \"(none recorded)\";\n return {\n type: \"unknown_type\",\n heading: null,\n message:\n `No template serves type \"${declared}\".${hint} ` +\n `Declare it on a template with \"types:\", then pass that file with --templates.`,\n position,\n severity: \"error\",\n };\n}\n\ninterface LintContext {\n getTemplate: (ref: string) => Promise<Template>;\n /** Absolute, forward-slashed form of a display path, for override globs. */\n absoluteOf?: (file: string) => string;\n typeIndex: Map<string, TypeIndexEntry>;\n cliTemplate?: string;\n overrides?: TemplateOverride[];\n defaultTemplate?: string;\n explain: boolean;\n}\n\nasync function lintOne(\n label: string,\n content: string,\n parser: DocumentParser,\n ctx: LintContext,\n): Promise<LintFileResult> {\n let tree: DocumentTree;\n try {\n tree = parser.parse(content, label);\n } catch (err) {\n // A file that will not parse is that file's problem, not the run's: it is\n // reported and the run continues, so one broken page cannot hide the\n // findings in the other 199.\n return {\n file: label,\n success: false,\n findings: [parseFinding(errorMessage(err))],\n template: null,\n };\n }\n\n // Routing needs the frontmatter, so it happens after the parse rather than\n // before the read - which is also what makes `--explain` able to say what the\n // page actually declared.\n const resolution = resolveTemplateRef({\n filePath: label,\n absolutePath: ctx.absoluteOf?.(label),\n frontmatter: tree.frontmatter,\n cliTemplate: ctx.cliTemplate,\n overrides: ctx.overrides,\n typeIndex: ctx.typeIndex,\n defaultTemplate: ctx.defaultTemplate,\n });\n const withResolution = <T extends LintFileResult>(result: T): T =>\n ctx.explain ? { ...result, resolution } : result;\n\n // Anchor anything said about the frontmatter at the frontmatter.\n const metaPosition = tree.frontmatterPosition ?? origin();\n\n if (resolution.ref === null) {\n if (resolution.cause === \"unknown-type\") {\n // A declared type that resolves to nothing is a typo or a gap. Skipping\n // would mean a page silently stops being checked, which is the one\n // outcome worse than a false positive.\n return withResolution({\n file: label,\n success: false,\n findings: [unknownTypeFinding(resolution, ctx.typeIndex, metaPosition)],\n template: null,\n });\n }\n // An untyped page is manni meta's complaint, not this tool's - its OKF\n // schema already requires `type`. Skipping is what lets manni lint be\n // pointed at a whole tree on day one.\n return withResolution(\n skip(\n label,\n \"no type in frontmatter and no template resolved\",\n \"no-template\",\n ),\n );\n }\n\n /** A broken template, reported against this page rather than thrown. */\n const brokenTemplate = (err: unknown): LintFileResult =>\n withResolution({\n file: label,\n success: false,\n findings: [\n {\n type: \"template_error\",\n heading: null,\n message: errorMessage(err),\n position: metaPosition,\n severity: \"error\",\n },\n ],\n template: resolution.ref,\n });\n\n // A page may not send the linter to a URL of its own choosing.\n //\n // `type` comes out of the document too, but it only *selects* from the\n // routing table the operator assembled - a page can ask for a template on\n // offer, not introduce one. `$template` names a reference directly, which\n // makes it the one stage where a document says where a template comes from,\n // and documents are exactly what this tool is pointed at untrusted: a docset\n // from a fork, a contributor's branch, a vendored copy.\n // Left open, one line of frontmatter makes the linting host issue an\n // arbitrary request, which on CI means from inside the build network. The\n // `$ref` hole this mirrors was closed in the template loader; this is the\n // same hole one level up, where the ref itself is attacker-chosen.\n //\n // Refused rather than ignored, because silently falling through to `type`\n // would lint the page against something other than what it asked for and say\n // nothing. An operator who does want a remote template still has every other\n // stage: name it in `templates:`, map it under `types:`, or pass --template.\n if (\n resolution.stage === \"frontmatter-template\" &&\n classifyRef(resolution.ref).kind === \"url\"\n ) {\n return brokenTemplate(\n new LintError(\n `${FILE_TEMPLATE_KEY} may not name a URL (\"${resolution.ref}\"): a document ` +\n `must not choose what the linter fetches. Declare it in manni.config.yaml ` +\n `under \"templates:\" or \"types:\", or pass it with --template.`,\n ),\n );\n }\n\n // A relative `$template` means \"beside this page\", not \"beside wherever the\n // tool happened to be invoked from\". `extends` already follows that rule, and\n // for the same reason: resolved against the process cwd, `$template:\n // ./house.yaml` in `docs/guide.md` found the file only when the run started\n // in `docs/` - so the page routed correctly from one directory and reported\n // \"Template file not found\" from every other, including from CI.\n const ref =\n resolution.stage === \"frontmatter-template\"\n ? refRelativeTo(ctx.absoluteOf?.(label) ?? label, resolution.ref)\n : resolution.ref;\n\n let template: Template;\n try {\n template = await ctx.getTemplate(ref);\n } catch (err) {\n // One unloadable template must not abort a run over a whole tree; it is\n // reported against the pages that route to it.\n return brokenTemplate(err);\n }\n\n // `--explain` stops here, having promised to \"lint nothing\".\n //\n // The line is drawn after loading rather than before it, because loading is\n // part of the answer the flag exists to give: \"which template, and why\" is\n // not answered by naming a ref that does not resolve, and a broken\n // `--templates` path is exactly the surprise someone runs `--explain` to\n // find. Loading is also cheap in the way validation is not - templates are\n // cached, so a tree of one doctype loads once, while `validateDocument` runs\n // per file and its findings are then discarded unrendered.\n if (ctx.explain) {\n return withResolution({\n file: label,\n success: true,\n findings: [],\n template: resolution.ref,\n // The tree and the loaded template, for the alignment block. Both are\n // already in hand here, and neither survives this function otherwise.\n alignment: { tree, template },\n });\n }\n\n let findings: Finding[];\n try {\n findings = validateDocument(tree, template, {\n ...templateIdentity(ref),\n kinds: parser.kinds,\n });\n } catch (err) {\n // Validation can raise for the same reason loading can: a template the\n // author got wrong - an uncompilable `heading.pattern` is the live case.\n // Without this the raise escapes `runLint` and kills the run, so one bad\n // pattern hides the findings in every other page, which is the outcome the\n // load path above exists to prevent.\n if (err instanceof LintError) return brokenTemplate(err);\n throw err;\n }\n\n return withResolution({\n file: label,\n // \"Linted and found nothing wrong\", which is not the same as \"produced no\n // findings\". A `warning` - today, a rule this file's format cannot answer -\n // says something about the run rather than about the document, and the\n // exit code follows `success`, so counting it as a failure would turn a\n // docset of mixed formats permanently red. The family's rule, and the one\n // meta and cite already apply: `ok` is \"no error-severity finding\".\n success: !findings.some(isErrorSeverity),\n findings,\n template: resolution.ref,\n });\n}\n\n/** Everything manni's branch needs, settled once by the shared prefix. */\ninterface ManniRun {\n opts: LintOptions;\n config: LintConfig;\n /** The directory the resolved files are relative to. */\n cwd: string;\n /** The files the shared target resolution produced. */\n files: string[];\n /** The parser `--as` forced, when it did. */\n forcedParser?: DocumentParser;\n /** Whether `-` rode among the inputs. */\n usingStdin: boolean;\n}\n\n/**\n * Lint with manni's own engine.\n *\n * Everything below this line is manni's and nobody else's: the template files,\n * the doctype index, the parser registry, and the guard that fires when a run\n * checks nothing. The loading in particular has to sit here rather than in the\n * shared prefix, because it is file I/O a tool that reads no templates must\n * not be made to pay - and a missing `templates:` entry is then the loader's\n * error under manni and nothing at all under anybody else.\n */\nasync function lintWithManni(run: ManniRun): Promise<LintFileResult[]> {\n const { opts, config, cwd, files, forcedParser, usingStdin } = run;\n\n // The doctype -> template map. Built-ins go in first and user templates\n // overwrite them, so overriding `how-to` for a repo is one file with\n // `types: [how-to]` in it - no config entry, no flag.\n const templatePaths = templateFiles(opts.templates ?? config.templates);\n const getTemplate = templateLoader();\n\n const userFiles: { ref: string; file: TemplateFile }[] = [];\n for (const ref of templatePaths) {\n const file = await loadTemplateFile(ref);\n // `extends` is resolved before `types` is read. A template that inherits\n // its doctypes declares none of its own, so reading the raw file routes\n // nothing to it - the override loads, merges, and is silently inert while\n // the page goes to the built-in it meant to replace. The loader is cached,\n // so this costs nothing the lint would not already pay.\n const templates: Record<string, Template> = {};\n for (const name of Object.keys(file.templates ?? {})) {\n templates[name] = await getTemplate(`${ref}#${name}`);\n }\n userFiles.push({ ref, file: { ...file, templates } });\n }\n\n const typeIndex = buildTypeIndex({\n builtins: listBuiltins(),\n userFiles,\n explicitTypes: opts.types ?? config.types,\n });\n\n const ctx: LintContext = {\n getTemplate,\n absoluteOf: (file) => resolve(cwd, file).replace(/\\\\/g, \"/\"),\n typeIndex,\n cliTemplate: cliTemplateRef(opts.template, templatePaths),\n overrides: opts.overrides ?? config.overrides,\n // `template:` in config is the default for a page that declares no type -\n // the bottom of the chain, not the top. `--template` is the top.\n defaultTemplate: opts.defaultTemplate ?? config.template,\n explain: opts.explain === true,\n };\n\n // `--template` applies to every file, so a ref that will not load is bad\n // usage, not a property of any page. Resolved before a single page is linted\n // so it exits 2 with one message, rather than becoming an identical\n // `template_error` finding on all 200 pages and exiting 1 - which reads to\n // CI as \"the docs are wrong\".\n if (ctx.cliTemplate != null) await getTemplate(ctx.cliTemplate);\n\n const results: LintFileResult[] = [];\n\n // `forcedParser` is set whenever `usingStdin` is - the shared prefix refused\n // the run otherwise - but only that guard knows it, so the pair is tested\n // rather than asserted.\n if (usingStdin && forcedParser) {\n results.push(\n await lintOne(STDIN_LABEL, opts.stdinContent ?? \"\", forcedParser, ctx),\n );\n }\n\n for (const file of files) {\n const ext = extname(file);\n const parser = forcedParser ?? parserForExtension(ext);\n // The parser is chosen before the read, not after, because a file nothing\n // can parse must not depend on being readable to be reported. Reading\n // first pulls every skipped file into memory for nothing, and an\n // unreadable one - a denied permission, a file a doc build moved mid-run -\n // throws out of this loop, so a `.xyz` nobody asked us to parse takes the\n // whole run down with it: exit 2 and no verdict for any of the pages after\n // it, instead of one skip line and a report.\n if (!parser) {\n results.push(skip(file, unsupportedFormatMessage(ext || file)));\n continue;\n }\n let content: string;\n try {\n content = await readFile(resolve(cwd, file), \"utf8\");\n } catch (err) {\n // The read is the other thing that can fail per file, and it failed out\n // of the loop: a denied permission, a symlink loop, or a page a doc\n // build moved between `resolveTargetSet` and here threw straight through\n // `runLint` to `fail()`. Exit 2, no report, and every result already\n // gathered discarded - which is exactly what moving the parser lookup\n // before the read was meant to prevent, one step further along.\n //\n // The OS message travels verbatim: \"permission denied\" and \"no such\n // file\" send the reader to different places, and this code cannot tell\n // which it was.\n results.push(\n skip(file, `could not be read: ${errorMessage(err)}`, \"unreadable\"),\n );\n continue;\n }\n results.push(await lintOne(file, content, parser, ctx));\n }\n\n return results;\n}\n\n/** Everything the DITA-OT branch needs, and nothing else. */\ninterface DitaOtRun {\n /** The directory the resolved files are relative to. */\n cwd: string;\n /** The files the shared target resolution produced, as it spelled them. */\n files: string[];\n /** `tools.dita-ot.home`, resolved; undefined means the `dita` on PATH. */\n home?: string;\n onNotice?: (message: string) => void;\n /** The seam. The shipped default starts the real launcher. */\n validate: typeof runDitaOtValidate;\n}\n\n/** One DITA-OT message as a finding. */\nfunction ditaOtFinding(message: DitaOtMessage): Finding {\n // The code is the finding's `type`, so `ruleId` yields\n // `manni:lint/structure/DOTX010E` and the JSON reporter publishes the code\n // DITA-OT's own message reference is indexed by.\n const point =\n message.line === undefined\n ? null\n : {\n line: message.line,\n // The log calls the column `row`; see `readMessage` in the seam.\n column: message.column ?? 1,\n // DITA-OT reports no byte offset, and nothing downstream needs one.\n offset: 0,\n };\n return {\n type: message.code,\n heading: null,\n message: message.message,\n position: point === null ? origin() : { start: point, end: point },\n severity: message.severity,\n };\n}\n\n/**\n * Lint with DITA Open Toolkit.\n *\n * One invocation per target, and the log is the verdict. A map's log speaks\n * about the topics it reached, so the findings are grouped by the file each\n * message named rather than by the file the run was pointed at - which is how\n * one map target produces a result per topic.\n */\nasync function lintWithDitaOt(run: DitaOtRun): Promise<LintFileResult[]> {\n const { cwd, files, onNotice } = run;\n\n /**\n * Only the formats this tool says it reads are handed to it.\n *\n * `manni lint tools` prints that list, and the rule the registry documents\n * is that a listed format is one that is read. Passing anything else on and\n * reporting whatever came back meant a `notes.md` named on the command line\n * came out as a clean pass, because DITA-OT shrugged and logged nothing to\n * disagree with. The manni branch skips a format no parser claims; this is\n * the same answer for the same reason.\n */\n const readable = ditaOt.formats().flatMap((format) => format.extensions);\n const reads = new Set(readable.map((ext) => ext.toLowerCase()));\n const targets: string[] = [];\n const unsupported: LintFileResult[] = [];\n for (const file of files) {\n if (reads.has(extname(file).toLowerCase())) targets.push(file);\n else {\n unsupported.push(\n skip(\n file,\n `dita-ot does not read \"${extname(file) || file}\". It reads ${readable.join(\", \")}.`,\n ),\n );\n }\n }\n\n // The label the run resolved is what the report says, whatever spelling the\n // absolute path comes back with.\n /**\n * One entry per absolute path, first spelling wins.\n *\n * **Defensive, not a fix for anything reachable today.** `resolveTargetSet`\n * normalizes every input to a cwd-relative posix path and holds them in a\n * `Set`, so two spellings of one file cannot arrive here. Review raised the\n * collision as a hazard and it is a real one in isolation: built naively the\n * later label replaces the earlier, `byFile` keeps both, and findings reach\n * only the survivor while the other reports as a clean pass.\n *\n * Kept because this function starts a JVM per target, so being correct on\n * its own terms rather than on its caller's is worth eight lines. Do not\n * write a test that claims to prove the collision: it cannot be reached\n * from `runLint`, and a test that passes either way proves nothing.\n */\n const labels = new Map<string, string>();\n for (const file of targets) {\n const absolute = resolve(cwd, file);\n if (!labels.has(absolute)) labels.set(absolute, file);\n }\n const unique = [...labels.values()];\n const labelOf = (absolute: string): string => {\n const known = labels.get(absolute);\n if (known !== undefined) return known;\n const near = relative(cwd, absolute).replace(/\\\\/g, \"/\");\n return near === \"\" ? absolute : near;\n };\n\n for (const file of unique) {\n // Checked, not skipped: a topic on its own is validated, it just resolves\n // fewer references than the same topic reached through its map. Calling it\n // a skip would say nothing was checked, which is false.\n if (extname(file).toLowerCase() !== \".ditamap\") {\n onNotice?.(\n `checked ${file} without a map; references outside it are not resolved.`,\n );\n }\n }\n\n // Nothing this tool can read is not a run: starting a JVM per target for an\n // empty list would be work with no answer at the end of it.\n if (unique.length === 0) return unsupported;\n\n const results = await run.validate({\n targets: [...labels.keys()],\n cwd,\n ...(run.home === undefined ? {} : { home: run.home }),\n });\n\n // Seeded with the targets, so a target DITA-OT had nothing to say about is a\n // result that passed rather than a file missing from the report.\n const byFile = new Map<string, Finding[]>(unique.map((file) => [file, []]));\n const findingsFor = (file: string): Finding[] => {\n let list = byFile.get(file);\n if (list === undefined) {\n list = [];\n byFile.set(file, list);\n }\n return list;\n };\n\n // A topic DITA-OT names that nobody targeted becomes a result of its own, so\n // `checked` can exceed the number of paths the user typed. That is the\n // answer, not an inflation of it: pointing at a map is how you ask about the\n // topics it gathers, and a finding against `topic.dita` has to be reported\n // against `topic.dita` for anyone to fix it. One map in, two files checked.\n for (const { target, messages } of results) {\n const invoked = labelOf(target);\n for (const message of messages) {\n // A message with no location belongs to the target the run was invoked\n // on, at a zero-width origin: DITA-OT's semantic messages routinely\n // carry no file, and discarding them would drop real errors.\n const file =\n message.file === undefined\n ? invoked\n : labelOf(resolve(cwd, message.file));\n findingsFor(file).push(ditaOtFinding(message));\n }\n }\n\n return [\n ...unsupported,\n ...[...byFile].map(([file, findings]) => ({\n file,\n // The family's rule, as manni's branch applies it: `ok` is \"no\n // error-severity finding\", so a warning-only file stays green.\n success: !findings.some(isErrorSeverity),\n findings,\n template: null,\n })),\n ];\n}\n\n/**\n * A run that found files and checked none of them is the worst thing this tool\n * can do quietly: it exits 0 and reads as a clean bill of health for a docset\n * nothing looked at. A repo that adopts manni lint in CI before backfilling\n * `type:` keys would get a permanently green job.\n *\n * This replaces a guard that tested `typeIndex.size === 0` before any file was\n * read. That could never fire - the index is always seeded with the built-ins\n * - so it protected nothing. Counting the outcome does.\n *\n * `--explain` is exempt: showing why nothing routed is exactly its job.\n */\nfunction assertSomethingWasChecked(\n results: LintFileResult[],\n explain: boolean,\n tool: StructureToolDescriptor,\n): void {\n const skipped = results.filter((r) => r.skipped != null).length;\n const checked = results.length - skipped;\n if (explain || checked > 0 || skipped === 0) return;\n\n // The advice has to follow the cause. The guard fires on any skip, but it\n // used to describe routing only - so a run over files no parser claims was\n // told to add a `type:` key, which changes nothing about an extension the\n // tool cannot read. Advice the reader cannot act on is only half a guard.\n const unrouted = results.filter((r) => r.skipped === \"no-template\").length;\n const unsupported = results.filter(\n (r) => r.skipped === \"unsupported-format\",\n ).length;\n const unreadable = results.filter((r) => r.skipped === \"unreadable\").length;\n const advice = [\n unrouted > 0\n ? `${unrouted} had no template: give a page a \"type:\" that a template ` +\n `serves, pass -t/--template <ref>, or set \"lint.template\" as a default.`\n : null,\n unsupported > 0\n ? // `--as` belongs to manni's engine, so offering it to someone running\n // another tool is advice that answers with a usage error. The formats\n // line is true for every tool, because every tool declares one.\n `${unsupported} ${unsupported === 1 ? \"is\" : \"are\"} in a format ` +\n `${tool.name} does not read: ` +\n (tool.ownedOptions.includes(\"--as\")\n ? `pass --as <format> to force one, or target files in a format `\n : `target files in a format `) +\n `\"manni lint tools\" lists.`\n : null,\n unreadable > 0\n ? `${unreadable} could not be read: check the permissions on those ` +\n `paths, or drop them from the run with --exclude <glob>.`\n : null,\n ]\n .filter((line): line is string => line !== null)\n .join(\" \");\n\n throw new LintError(\n `Nothing was checked: all ${skipped} file(s) were skipped. ${advice} ` +\n `Run \"manni lint structure <paths> --explain\" to see how each file resolved.`,\n );\n}\n\nexport async function runLint(opts: LintOptions): Promise<LintRun> {\n // Which config governs the run, and what it covers: positional paths, or\n // the `collections:` the family file declares. One base per run, so a\n // collection's globs resolve beside the config that wrote them.\n const run = await resolveLintRun({\n cwd: opts.cwd ?? process.cwd(),\n configPath: opts.configPath,\n noConfig: opts.noConfig,\n inputs: opts.inputs,\n collection: opts.collection,\n onConfigLoaded: opts.onConfigLoaded,\n });\n const config: LintConfig = run.config;\n const cwd = run.base;\n\n if (run.inputs.length === 0) {\n throw new LintError(\n \"No files to check. Pass paths/globs, or declare a collection under `collections:` in manni.config.yaml.\",\n );\n }\n\n // Which tool performs the structure job: `--tool` first, then the config's\n // `lint.structure.tool`, then manni's own engine. Settled before anything is\n // read, because it decides what \"anything\" even is - the extensions a walk\n // collects, and which of the options below the run may carry at all.\n const tool = resolveStructureTool(\n opts.tool ?? config.structure?.tool ?? \"manni\",\n );\n\n const usingStdin = run.inputs.includes(STDIN_TOKEN);\n assertOptionsOwned(opts, tool, usingStdin);\n\n // Resolve `--as` before anything is read: a typo should fail immediately,\n // not after walking a tree of files it was going to mis-parse anyway.\n //\n // A name no parser answers to is unknown, whatever it is meant to name: the\n // formats `manni lint tools` lists are exactly the ones `--as` accepts.\n const forcedParser = opts.as != null ? parserByName(opts.as) : undefined;\n if (opts.as != null && !forcedParser) {\n throw new LintError(\n `Unknown format \"${opts.as}\". Run \"manni lint tools\" to see the formats manni lint reads.`,\n );\n }\n\n // Before the targets are resolved, as cite does: a mistyped path beside `-`\n // was reported as \"File not found\", which names the wrong mistake when the\n // run could not have read stdin either way.\n if (usingStdin && !forcedParser) {\n throw new LintError(\n \"Reading from stdin (-) requires --as <format> to choose a parser.\",\n );\n }\n\n const fileInputs = run.inputs.filter((input) => input !== STDIN_TOKEN);\n // The resolved tool's own walk set is the default, not the walker's. Left\n // undefined, the family walker falls back to the *metadata* tool's extractor\n // extensions, which sweep in every format that tool reads - `.xml` among\n // them, where manni lint's registry honours `walkExtensions` and\n // deliberately walks `.dita` and not `.xml`, so an ordinary `pom.xml` cannot\n // fail a clean tree. Read off the descriptor rather than off that registry\n // directly, so a tool with input formats of its own walks for those.\n // Resolved here rather than at the walk, so `assertNonEmpty` names the same\n // set it filtered with.\n const exts = opts.exts ?? forcedParser?.extensions ?? tool.walkExtensions();\n const allowEmpty = opts.allowEmpty ?? config.allowEmpty;\n // A collection's `exclude:` shapes the collection, so it applies when the\n // inputs came from the collections and never to a path the operator typed.\n // `--exclude` filters either way; the union is deduplicated.\n const exclude = [\n ...new Set([\n ...(opts.exclude ?? []),\n ...(run.fromCollections ? run.collections.flatMap((c) => c.exclude) : []),\n ]),\n ];\n const { files, gitignoreSkipped } = await asLintError(() =>\n resolveTargetSet({\n inputs: fileInputs,\n exts,\n exclude,\n cwd,\n allowEmpty,\n ...gitignoreOptions({\n flag: opts.respectGitignore,\n onNotice: opts.onNotice,\n }),\n }),\n );\n // Resolving zero files is an operational error, not a pass: with no files\n // there is no verdict, and exit 0 would read as a clean bill of health.\n await asLintError(() => {\n assertNonEmpty({\n files,\n inputs: fileInputs,\n usingStdin,\n allowEmpty,\n exclude,\n exts,\n gitignoreSkipped,\n action: \"linted\",\n });\n });\n\n // The branch per tool. Reaching the refusal is an internal fault rather than\n // user error - every name the registry does not carry was refused by\n // `resolveStructureTool` above - and it is a `LintError` for that reason: a\n // bare `Error` escapes the bin runner and exits 1, the code that means \"the\n // documents have findings\".\n const name: string = tool.name;\n let results: LintFileResult[];\n if (name === \"manni\") {\n results = await lintWithManni({\n opts,\n config,\n cwd,\n files,\n forcedParser,\n usingStdin,\n });\n } else if (name === \"dita-ot\") {\n // `tools:` is the family's key, not the `lint:` section's, so the home is\n // resolved against the directory of the config that declared it.\n const home = ditaOtHome(run.tools, run.configDir ?? cwd);\n results = await lintWithDitaOt({\n cwd,\n files,\n ...(home === undefined ? {} : { home }),\n ...(opts.onNotice === undefined ? {} : { onNotice: opts.onNotice }),\n validate: opts.runDitaOt ?? runDitaOtValidate,\n });\n } else {\n throw new LintError(`No implementation is registered for the ${name} tool.`);\n }\n\n // Every tool, not just manni's. A run that resolved files and checked none\n // of them exits 0 and reads as a clean bill of health, which is the worst\n // thing this command can do quietly. It sat inside the manni branch until\n // `dita-ot` started skipping the formats it does not read, at which point\n // `manni lint structure notes.md` reported \"0 files checked, 1 skipped\" and\n // exited 0. One call here is one the next tool cannot forget to make.\n assertSomethingWasChecked(results, opts.explain === true, tool);\n\n const skipped = results.filter((r) => r.skipped != null).length;\n const failed = results.filter((r) => r.skipped == null && !r.success).length;\n const checked = results.length - skipped;\n\n return {\n results,\n summary: { checked, passed: checked - failed, failed, skipped },\n };\n}\n","/**\n * Source of truth for templates. Holds the built-in doctype templates\n * (addressed by `vendor:name:version`-style ids) and knows how to load any\n * template reference - a built-in id, a local `.yaml`/`.yml`/`.json` path, or\n * an `http(s)` URL, optionally naming one template inside a file with a `#`\n * fragment (`./templates.yaml#how-to`).\n *\n * Two ordering decisions are load-bearing:\n *\n * - A file is dereferenced *before* it is validated. `$ref` is how a template\n * file shares one section rule between templates, and a section rule is\n * `additionalProperties: false`, so any `$ref` still standing at validation\n * time would be reported as an unexpected key.\n * - `instructions` is looked for by hand *before* Ajv runs. The schema rejects\n * it too (`not: {required: [\"instructions\"]}`), but a raw schema error says\n * nothing useful about where the feature went; scanning the parsed data\n * directly is both more reliable than reverse-engineering which Ajv error\n * came from the `not` keyword and the only way to quote the author's own\n * instruction back at them in the migration snippet.\n *\n * `extends` is deliberately *not* resolved by `loadTemplate`. Inheritance needs\n * a resolver that knows what a relative ref is relative to, which only the\n * caller knows, so `resolveExtends` takes that resolver as an argument.\n */\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, isAbsolute, join, resolve as resolvePath } from \"node:path\";\nimport { packageRoot } from \"../../shared/package-root.js\";\nimport { parse as parseYaml } from \"yaml\";\nimport { dereference } from \"@apidevtools/json-schema-ref-parser\";\nimport * as AjvNs from \"ajv\";\nimport type { ErrorObject, SchemaObject, ValidateFunction } from \"ajv\";\nimport { LintError } from \"../types.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\nimport { warn } from \"../../shared/warn.js\";\nimport { BLOCK_KINDS, isWildcard, occurrenceRange } from \"./template.js\";\nimport type {\n BlockRule,\n ListItemsRule,\n Occurrences,\n Rule,\n Template,\n TemplateFile,\n} from \"./template.js\";\nimport templateFileSchema from \"../../../schemas/lint/template.json\" with { type: \"json\" };\nimport tgdpManifest from \"../../../templates/lint/tgdp/manifest.json\" with { type: \"json\" };\n\n// ajv is CommonJS with a default export; under NodeNext the constructable\n// value lives on `.default`. Cast through the named default type so tsc sees a\n// constructor.\ntype AjvCtor = typeof import(\"ajv\").default;\nconst Ajv = AjvNs.default as unknown as AjvCtor;\n\n/**\n * Options for both `dereference` calls, named so the two cannot drift apart.\n *\n * `resolve.external: false` confines `$ref` to the file it is written in. A\n * template file is untrusted input - `loadTemplateFile` will fetch one over\n * `http(s)`, and a local one is only as trustworthy as whoever wrote it, who is\n * not necessarily whoever runs the lint. The dereferencer resolves a `$ref` by\n * reading the file or making the request it names, from the linting host and\n * with its privileges. Left on, `$ref: /etc/passwd` or\n * `$ref: http://169.254.169.254/latest/meta-data/` in a template turned a lint\n * run into an arbitrary read, and a remote template into one that reports back\n * what it found.\n *\n * Nothing in the template DSL wants it. `$ref` is documented as how one file\n * shares a section rule between its own templates, and reuse *across* files is\n * what `extends` is for - which goes through `loadTemplate`, is re-based against\n * the declaring file, and is cycle-checked. Same-document (`#/...`) pointers are\n * untouched; an external one is left standing instead, and the schema then\n * rejects it as an unexpected `$ref` key rather than following it.\n */\nconst DEREFERENCE_OPTIONS = { resolve: { external: false } };\n\nexport interface BuiltinInfo {\n id: string;\n title: string;\n types: string[];\n}\n\n/**\n * Built-in doctype templates.\n *\n * The manifest is a JSON import, so it is bundled and `listBuiltins()` stays\n * synchronous. The templates themselves are YAML files beside it, read on\n * demand: they are meant to be opened, read, and copied by anyone writing their\n * own template, and YAML is the format that survives that. They ship at the\n * package root as `templates/lint/**`, listed in `files`, and are found by\n * walking up from this module to `package.json`, which works identically from\n * source and from the built package.\n */\ninterface ManifestEntry {\n id: string;\n file: string;\n title: string;\n types: string[];\n /** Upstream file this was derived from, for provenance. */\n source?: string;\n}\n\ninterface Manifest {\n vendor: string;\n upstream: string;\n pin: string;\n templates: ManifestEntry[];\n}\n\nconst MANIFESTS: Manifest[] = [tgdpManifest];\n\nconst BUILTINS = new Map<string, ManifestEntry>();\nfor (const manifest of MANIFESTS) {\n for (const entry of manifest.templates) BUILTINS.set(entry.id, entry);\n}\n\n/** Parsed built-ins, keyed by id. Populated on first load. */\nconst builtinCache = new Map<string, Template>();\n\nexport function listBuiltins(): BuiltinInfo[] {\n return [...BUILTINS.values()].map((entry) => ({\n id: entry.id,\n title: entry.title,\n types: entry.types,\n }));\n}\n\n/**\n * Locate a built-in's YAML at the package root.\n *\n * The templates ship as `templates/lint/**` beside `package.json`. Resolving\n * from the package root rather than from `import.meta.url` is what keeps one\n * path correct both in the repo, where this module sits under\n * `src/lint/core/`, and in the bundle, where it sits in `dist/`.\n */\nasync function readBuiltinFile(id: string, file: string): Promise<string> {\n const path = join(packageRoot(import.meta.url), \"templates\", \"lint\", file);\n try {\n return await readFile(path, \"utf8\");\n } catch (err) {\n throw new LintError(\n `Built-in template \"${id}\" is registered but its file could not be read (${path}): ${errorMessage(err)}`,\n );\n }\n}\n\n/**\n * Read, validate, and cache one built-in.\n *\n * Built-ins go through exactly the same schema validation as a user's file. A\n * template we ship is not more trustworthy than one you write - it is only\n * better tested - and a shipped template that violates its own schema should\n * fail loudly here rather than behave strangely during matching.\n */\nasync function loadBuiltin(id: string): Promise<Template> {\n const cached = builtinCache.get(id);\n if (cached) return cached;\n\n // Every caller checks `BUILTINS.has` first, so this is an internal\n // invariant rather than a user error - but it fails loudly all the same,\n // because the alternative is a `TypeError` several lines later on a field\n // of `undefined`.\n const entry = BUILTINS.get(id);\n if (!entry) {\n throw new LintError(`No built-in template is registered under \"${id}\".`);\n }\n const raw = await readBuiltinFile(id, entry.file);\n\n // The same guard `loadTemplateFile` applies to an outside file. A built-in\n // is ours and should never fail it, which is the point: if one ever does,\n // this says which file and what it held, rather than letting `dereference`\n // report something opaque about a value it did not expect.\n const parsed: unknown = parseYaml(raw);\n if (!isRecord(parsed)) {\n throw new LintError(\n `Built-in template \"${id}\" (${entry.file}) is not a template file: ` +\n `expected an object at the top level, got ${\n parsed === null ? \"null\" : Array.isArray(parsed) ? \"an array\" : typeof parsed\n }.`,\n );\n }\n\n const file = validateTemplateFile(\n await dereference<Record<string, unknown>>(parsed, DEREFERENCE_OPTIONS),\n entry.file,\n );\n const names = Object.keys(file.templates ?? {});\n const template = file.templates?.[names[0] ?? \"\"];\n if (!template || names.length !== 1) {\n throw new LintError(\n `Built-in template \"${id}\" must define exactly one template, found ${names.length}.`,\n );\n }\n\n // `types` lives in the manifest so the type map can be built without reading\n // every YAML file; mirror it onto the template so both agree.\n const resolved: Template = { types: entry.types, ...template };\n builtinCache.set(id, resolved);\n return resolved;\n}\n\nexport type RefKind = \"builtin\" | \"file\" | \"url\";\n\n/**\n * A built-in id looks like `seg(:seg)+` using only [a-z0-9._-] segments, with\n * no path separators and no template-file extension. This deliberately\n * excludes Windows paths (`C:\\...`), URLs, and `.yaml`/`.yml`/`.json` files so\n * a typo'd built-in is reported as an unknown id rather than silently treated\n * as a missing file.\n */\nconst BUILTIN_ID = /^[a-z0-9][a-z0-9._-]*(?::[a-z0-9][a-z0-9._-]*)+$/i;\n\nconst FILE_EXTENSIONS = [\".yaml\", \".yml\", \".json\"];\n\nexport function classifyRef(ref: string): { kind: RefKind; ref: string } {\n if (/^https?:\\/\\//i.test(ref)) return { kind: \"url\", ref };\n const lower = ref.toLowerCase();\n if (\n !ref.includes(\"/\") &&\n !ref.includes(\"\\\\\") &&\n !FILE_EXTENSIONS.some((ext) => lower.endsWith(ext)) &&\n BUILTIN_ID.test(ref)\n ) {\n return { kind: \"builtin\", ref };\n }\n return { kind: \"file\", ref };\n}\n\n/** Default network timeout for fetching a remote (`http(s)`) template file. */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport interface LoadTemplateOptions {\n /** Abort a remote fetch after this many ms (default 10_000). */\n timeoutMs?: number;\n}\n\n/* -------------------------------------------------------------------------- *\n * Validation\n * -------------------------------------------------------------------------- */\n\n// `useDefaults` is deliberately off, and the schema declares no `default`\n// anywhere. A default Ajv writes into the data is indistinguishable from a\n// value the author typed, so it beats the parent's real value in an `extends`\n// merge. Every default in this format lives in the code that reads it\n// (`occurrenceRange`), where an absent key stays absent.\nconst ajv = new Ajv();\n\nlet compiled: ValidateFunction | null = null;\n\nfunction fileValidator(): ValidateFunction {\n compiled ??= ajv.compile(templateFileSchema as SchemaObject);\n return compiled;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** One `instructions` key found in a template file, and where it lives. */\ninterface InstructionsHit {\n /** Dotted path to the rule carrying it, e.g. `templates.how-to.sections.title`. */\n path: string;\n /** Suggested eval name, kebab-cased from the path. */\n name: string;\n /** The instruction strings, verbatim. */\n values: string[];\n}\n\n/** The strings in an `instructions:` value, whatever shape it was written in. */\nfunction stringsOf(value: unknown): string[] {\n if (typeof value === \"string\") return [value];\n if (Array.isArray(value)) return value.filter((v): v is string => typeof v === \"string\");\n return [];\n}\n\n/**\n * Name for the suggested eval: the path with the structural keys (`templates`,\n * `sections`) dropped, kebab-cased. `templates.how-to.sections.title` becomes\n * `how-to-title`.\n */\nfunction suggestEvalName(segments: string[]): string {\n const name = segments\n .filter((segment) => segment !== \"templates\" && segment !== \"sections\")\n .join(\"-\")\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return name || \"section-instructions\";\n}\n\n/**\n * First `instructions` key anywhere in a parsed template file.\n *\n * Dereferencing can make two rules share one object, and a self-referential\n * `$ref` can make the tree cyclic, hence the `seen` set.\n */\nfunction findInstructions(\n node: unknown,\n segments: string[] = [],\n seen = new WeakSet(),\n): InstructionsHit | null {\n if (typeof node !== \"object\" || node === null) return null;\n if (seen.has(node)) return null;\n seen.add(node);\n\n if (Array.isArray(node)) {\n for (const [index, item] of node.entries()) {\n const hit = findInstructions(item, [...segments, String(index)], seen);\n if (hit) return hit;\n }\n return null;\n }\n\n const record = node as Record<string, unknown>;\n\n // Only a section's own `instructions` *property* is the legacy key. Inside a\n // `sections:` map the keys are section names the author chose, and a section\n // legitimately called \"instructions\" - TGDP's README doctype wants one - must\n // not be mistaken for it. So descend into a `sections` map without testing\n // its keys, and test the property everywhere else.\n const insideSectionsMap = segments.at(-1) === \"sections\";\n if (!insideSectionsMap && \"instructions\" in record) {\n return {\n path: segments.join(\".\"),\n name: suggestEvalName(segments),\n values: stringsOf(record.instructions),\n };\n }\n for (const [key, value] of Object.entries(record)) {\n const hit = findInstructions(value, [...segments, key], seen);\n if (hit) return hit;\n }\n return null;\n}\n\n/**\n * The message a template still carrying `instructions` gets: what replaced the\n * feature, and the config to paste, with the author's own instruction in it.\n */\nfunction instructionsMessage(source: string, hit: InstructionsHit): string {\n const assertion = hit.values[0] ?? \"\";\n return [\n `${source}: \"${hit.path}\" uses \\`instructions\\`, which manni lint no longer evaluates — structure checking is deterministic. Move it to a manni docevals assertion eval in manni.config.yaml:`,\n \"\",\n \" docevals:\",\n \" evals:\",\n ` ${hit.name}:`,\n ` assertion: ${assertion}`,\n \" grader: ai\",\n ].join(\"\\n\");\n}\n\n/* -------------------------------------------------------------------------- *\n * Refusing a v1 file\n *\n * v1 never shipped, so there is no migration mode and no alias. What there is\n * instead is a sentence per key naming the v2 spelling, because a v1 file is\n * what anyone who tried the format early still has on disk. Left to the schema\n * these all read as \"must NOT have additional properties\", which says nothing\n * about where the key went.\n *\n * The scan runs before Ajv, for the same reason the `instructions` scan does.\n * -------------------------------------------------------------------------- */\n\n/** v1 keys that are gone outright, and what to write instead. */\nconst V1_KEYS: Record<string, string> = {\n required: '\"required\" is not a template key. A rule is optional with \"min: 0\".',\n additionalSections:\n '\"additionalSections\" is not a template key. v2 has no equivalent, so drop it.',\n code_blocks: '\"code_blocks\" is not a template key. The v2 spelling is \"codeBlocks\".',\n};\n\nconst V1_SECTIONS_MAP =\n '\"sections\" is a list of rules, not a map. Name each rule with \"id\" and list them in order.';\n\nconst V1_REPEAT_BOOLEAN =\n '\"repeat\" is a list of rules, not a boolean. A rule repeats through \"min\" and \"max\".';\n\nconst V1_PARAGRAPH_PATTERNS =\n '\"patterns\" is not a paragraphs key. The v2 spelling is \"pattern\", one regular expression.';\n\n/**\n * The first v1 key anywhere under `templates` or `components`.\n *\n * `info` is skipped: it is free-form by contract, so a key of one of these\n * names in it is the author's own metadata rather than a template rule.\n *\n * A node carrying a `sections` *map* returns before its children are walked.\n * Inside such a map the keys are section names the author chose, and a v1\n * template with a section named `required` must not be reported as using the\n * `required` key.\n */\nfunction findV1Key(node: unknown, seen = new WeakSet()): string | null {\n if (typeof node !== \"object\" || node === null) return null;\n if (seen.has(node)) return null;\n seen.add(node);\n\n if (Array.isArray(node)) {\n for (const item of node) {\n const hit = findV1Key(item, seen);\n if (hit) return hit;\n }\n return null;\n }\n\n const record = node as Record<string, unknown>;\n for (const [key, message] of Object.entries(V1_KEYS)) {\n if (key in record) return message;\n }\n if (isRecord(record.sections)) return V1_SECTIONS_MAP;\n if (typeof record.repeat === \"boolean\") return V1_REPEAT_BOOLEAN;\n if (isRecord(record.paragraphs) && \"patterns\" in record.paragraphs) {\n return V1_PARAGRAPH_PATTERNS;\n }\n\n for (const value of Object.values(record)) {\n const hit = findV1Key(value, seen);\n if (hit) return hit;\n }\n return null;\n}\n\nfunction findV1File(data: unknown): string | null {\n if (!isRecord(data)) return null;\n return findV1Key(data.templates) ?? findV1Key(data.components);\n}\n\n/* -------------------------------------------------------------------------- *\n * Checks JSON Schema cannot express\n *\n * Three of these compare two siblings, which draft-07 has no keyword for, and\n * one compiles a regular expression, which it has no opinion about. The fifth\n * is a warning rather than an error: the template is legal, and only its author\n * can say whether the ambiguity is intended.\n * -------------------------------------------------------------------------- */\n\n/** How a rule is named in a message: its id where it has one, else its path. */\nfunction ruleLabel(rule: Rule, path: string): string {\n return rule.id === undefined ? path : `\"${rule.id}\"`;\n}\n\n/**\n * `max` below `min`, where the author wrote both.\n *\n * Only where both are written. `min` defaults to one, so comparing against the\n * default would refuse `max: 0`, which is how the format spells \"none of\n * these\".\n */\nfunction checkRange(range: Occurrences, source: string, label: string): void {\n const { min, max } = range;\n if (min !== undefined && max !== undefined && max < min) {\n throw new LintError(`${source}: rule ${label} sets max ${max} below min ${min}.`);\n }\n}\n\n/**\n * Whether two rules claim the same occurrence range, the one comparison the\n * ambiguity warning below needs. Compares what `occurrenceRange` resolves\n * `min`/`max` to, not the raw (possibly absent) fields, so a written `min: 1`\n * and an absent `min` - both one occurrence by default - count as the same.\n */\nfunction sameOccurrenceRange(a: Occurrences, b: Occurrences): boolean {\n const rangeA = occurrenceRange(a);\n const rangeB = occurrenceRange(b);\n return rangeA.min === rangeB.min && rangeA.max === rangeB.max;\n}\n\n/**\n * Compile an author's pattern here, so a broken one names the file it is in.\n *\n * \"Broken\" means it does not compile. A pattern that compiles can still hang\n * the run by backtracking, and this does not catch that; `compilePattern` in\n * `../rules/index.ts` carries the full note, including why a length cap is not\n * the mitigation it looks like.\n */\nfunction checkPattern(pattern: string | undefined, source: string): void {\n if (pattern === undefined) return;\n try {\n new RegExp(pattern);\n } catch (err) {\n throw new LintError(\n `Invalid pattern \"${pattern}\" in ${source}: ${errorMessage(err)}.`,\n );\n }\n}\n\nfunction checkListItems(items: ListItemsRule, source: string, path: string): void {\n checkRange(items, source, path);\n if (items.contains) checkBlockRule(items.contains, source, `${path}.contains`);\n items.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.sequence[${index}]`);\n });\n}\n\nfunction checkBlockRule(block: BlockRule, source: string, path: string): void {\n for (const kind of BLOCK_KINDS) {\n const counted: Occurrences | undefined = block[kind];\n if (counted) checkRange(counted, source, `${path}.${kind}`);\n }\n checkPattern(block.paragraphs?.pattern, source);\n if (block.lists?.items) {\n checkListItems(block.lists.items, source, `${path}.lists.items`);\n }\n const element = block.elements;\n if (element?.contains) {\n checkBlockRule(element.contains, source, `${path}.elements.contains`);\n }\n element?.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.elements.sequence[${index}]`);\n });\n}\n\n/** Everything a rule and a template share. A template is a rule. */\nfunction checkRuleBody(rule: Rule, source: string, path: string): void {\n const heading = rule.heading;\n if (typeof heading === \"object\" && !Array.isArray(heading)) {\n checkPattern(heading.pattern, source);\n }\n if (rule.contains) checkBlockRule(rule.contains, source, `${path}.contains`);\n rule.sequence?.forEach((entry, index) => {\n checkBlockRule(entry, source, `${path}.sequence[${index}]`);\n });\n checkRuleList(rule.repeat, source, `${path}.repeat`);\n checkRuleList(rule.sections, source, `${path}.sections`);\n}\n\n/**\n * One list of sibling rules: their ids, their ranges, and whether two\n * neighbours are told apart by nothing but their order.\n */\nfunction checkRuleList(rules: Rule[] | undefined, source: string, path: string): void {\n if (!rules) return;\n\n const ids = new Set<string>();\n for (const [index, rule] of rules.entries()) {\n const here = `${path}[${index}]`;\n if (rule.id !== undefined) {\n if (ids.has(rule.id)) {\n throw new LintError(`${source}: two sibling rules share the id \"${rule.id}\".`);\n }\n ids.add(rule.id);\n }\n checkRange(rule, source, ruleLabel(rule, here));\n checkRuleBody(rule, source, here);\n }\n\n // A warning, not an error. Two adjacent wildcards are how TGDP's \"{Task\n // name}\" then \"{Next task}\" is written, and that is legal. It is also how a\n // template accidentally describes one section twice, and nothing in the file\n // distinguishes the two cases - unless their occurrence ranges differ, in\n // which case the matcher can still tell them apart: one claims a fixed count\n // of sections and the other claims however many are left. That is\n // `reference-description` (`max: 1`) next to `structured-entry` (`min: 0`,\n // no `max`) in tgdp:reference - legal and unambiguous, so only a shared range\n // is flagged.\n for (const [index, rule] of rules.entries()) {\n const next = rules[index + 1];\n if (!next) break;\n if (\n isWildcard(rule) &&\n rule.repeat === undefined &&\n isWildcard(next) &&\n next.repeat === undefined &&\n sameOccurrenceRange(rule, next)\n ) {\n warn(\n `${source}: two adjacent rules have no heading and no repeat; only rule order tells them apart.`,\n );\n break;\n }\n }\n}\n\n/** The checks that run over a validated file, template by template. */\nfunction checkTemplateFile(file: TemplateFile, source: string): void {\n for (const [name, template] of Object.entries(file.templates ?? {})) {\n if (template.min !== undefined || template.max !== undefined) {\n throw new LintError(\n `${source}: a template may not set min or max; a page is one page.`,\n );\n }\n checkRuleBody(template, source, `templates.${name}`);\n }\n}\n\n/**\n * What each `not` constraint means, in the words its author needs.\n *\n * Ajv renders every `not` as \"must NOT be valid\", which names the path and\n * then says nothing whatsoever about what is wrong with it. The schema carries\n * a `description` on each of these; the sentence lives here instead because it\n * is user-facing text, and every other message in this file does too.\n */\nconst NOT_CONSTRAINTS: Record<string, string> = {\n oneOfSequenceOrContains:\n \"sets both sequence and contains; a rule says what it holds in order, or in any order, never both\",\n noHeadingOnRepeat:\n \"sets both heading and repeat; a repeat group has no heading of its own, so put the heading on the first rule inside it\",\n noInstructions: \"carries an instructions key, which this tool does not read\",\n};\n\n/** `/templates/how-to/sections/title must NOT have additional properties (foo)`. */\nfunction describeError(error: ErrorObject): string {\n const where = error.instancePath || \"/\";\n\n if (error.keyword === \"not\") {\n for (const [name, why] of Object.entries(NOT_CONSTRAINTS)) {\n if (error.schemaPath.includes(name)) return `${where} ${why}`;\n }\n }\n\n const params = error.params as { additionalProperty?: unknown };\n const extra =\n typeof params.additionalProperty === \"string\" ? ` (\"${params.additionalProperty}\")` : \"\";\n return `${where} ${error.message ?? \"is invalid\"}${extra}`;\n}\n\n/**\n * Validate a parsed, dereferenced template file, filling in rule defaults.\n *\n * Throws `LintError` naming `source` and the offending instance path. The\n * pre-rewrite loader threw a bare \"Template is invalid\" and passed the real\n * detail as `new Error(msg, {message: ...})` - an options bag that only\n * understands `cause` - so every schema error reached the user as four words.\n */\nexport function validateTemplateFile(data: unknown, source: string): TemplateFile {\n const instructions = findInstructions(data);\n if (instructions) throw new LintError(instructionsMessage(source, instructions));\n\n // Before Ajv, so a v1 file is told where its keys went rather than that it\n // has additional properties.\n const v1 = findV1File(data);\n if (v1) throw new LintError(`${source}: ${v1}`);\n\n const validate = fileValidator();\n if (!validate(data)) {\n const errors = validate.errors ?? [];\n const detail = errors.map(describeError).join(\"; \") || \"does not match the template schema\";\n throw new LintError(`${source} is not a valid template file: ${detail}.`);\n }\n const file = data as TemplateFile;\n checkTemplateFile(file, source);\n return file;\n}\n\n/* -------------------------------------------------------------------------- *\n * Loading\n * -------------------------------------------------------------------------- */\n\n/** Split `./templates.yaml#how-to` into its file part and template name. */\nfunction splitFragment(ref: string): { base: string; fragment: string | null } {\n const hash = ref.lastIndexOf(\"#\");\n if (hash === -1) return { base: ref, fragment: null };\n return { base: ref.slice(0, hash), fragment: ref.slice(hash + 1) || null };\n}\n\n/** The part of a ref an extension should be read off - a URL's path, not its query. */\nfunction extensionSource(ref: string, kind: RefKind): string {\n if (kind !== \"url\") return ref;\n try {\n return new URL(ref).pathname;\n } catch {\n return ref;\n }\n}\n\n/**\n * Parse a template file by extension. Anything unrecognized is parsed as YAML,\n * which is a superset of JSON, so an extensionless URL still loads.\n */\nfunction parseTemplateFile(raw: string, source: string, kind: RefKind): unknown {\n const lower = extensionSource(source, kind).toLowerCase();\n if (lower.endsWith(\".json\")) {\n try {\n return JSON.parse(raw) as unknown;\n } catch (err) {\n throw new LintError(`${source} is not valid JSON: ${errorMessage(err)}`);\n }\n }\n try {\n return parseYaml(raw) as unknown;\n } catch (err) {\n throw new LintError(`${source} is not valid YAML: ${errorMessage(err)}`);\n }\n}\n\n/**\n * Read a local template file, saying which way the read failed.\n *\n * Every failure used to be reported as \"not found\", which is the one wording\n * that contradicts `ls -la`: a directory named `templates.yaml` (EISDIR), a\n * file the user cannot open (EACCES), and a process out of descriptors\n * (EMFILE) are three different problems with three different fixes, and the\n * reader was sent after a path that is plainly there. Only ENOENT is a missing\n * file; anything else keeps the OS's own message.\n */\nasync function readText(ref: string): Promise<string> {\n try {\n return await readFile(ref, \"utf8\");\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | null)?.code;\n if (code === \"ENOENT\") {\n throw new LintError(`Template file not found: \"${ref}\".`);\n }\n throw new LintError(\n `Template file \"${ref}\" could not be read: ${errorMessage(err)}`,\n );\n }\n}\n\nconst urlCache = new Map<string, TemplateFile>();\n\n/**\n * Drop the parsed-template caches.\n *\n * Nothing in the CLI needs this: a process lints one docset and exits, and the\n * caches are what keep a tree of one doctype from re-reading and re-validating\n * the same template per page. It exists for the other caller - a long-lived\n * process using this as a library - where the two behave differently: the\n * memory grows with the number of distinct refs seen, and a `urlCache` entry\n * for a remote template is served for the life of the process, so a template\n * republished upstream is never picked up.\n */\nexport function clearTemplateCaches(): void {\n builtinCache.clear();\n urlCache.clear();\n}\n\nasync function fetchText(ref: string, timeoutMs: number): Promise<string> {\n let res: Response;\n try {\n res = await fetch(ref, { signal: AbortSignal.timeout(timeoutMs) });\n } catch (err) {\n const e = err as Error;\n if (e.name === \"TimeoutError\" || e.name === \"AbortError\") {\n throw new LintError(\n `Failed to fetch template file \"${ref}\": timed out after ${timeoutMs}ms.`,\n );\n }\n throw new LintError(`Failed to fetch template file \"${ref}\": ${e.message}`);\n }\n if (!res.ok) {\n throw new LintError(`Failed to fetch template file \"${ref}\": HTTP ${res.status}.`);\n }\n return await res.text();\n}\n\n/** Resolve every `$ref` in place, so validation never sees one. */\nasync function dereferenceTemplates(\n data: Record<string, unknown>,\n source: string,\n): Promise<Record<string, unknown>> {\n try {\n return await dereference<Record<string, unknown>>(data, DEREFERENCE_OPTIONS);\n } catch (err) {\n throw new LintError(`${source}: could not resolve a \"$ref\": ${errorMessage(err)}`);\n }\n}\n\n/** Load, dereference, and validate a whole template file. */\nexport async function loadTemplateFile(\n ref: string,\n options: LoadTemplateOptions = {},\n): Promise<TemplateFile> {\n const { kind } = classifyRef(ref);\n if (kind === \"builtin\") {\n throw new LintError(\n `\"${ref}\" is a built-in template id, not a template file. Load it with loadTemplate().`,\n );\n }\n\n const cached = kind === \"url\" ? urlCache.get(ref) : undefined;\n if (cached) return cached;\n\n const raw =\n kind === \"url\"\n ? await fetchText(ref, options.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n : await readText(ref);\n\n const parsed = parseTemplateFile(raw, ref, kind);\n if (!isRecord(parsed)) {\n throw new LintError(\n `${ref} is not a template file: expected an object at the top level, got ${\n parsed === null ? \"null\" : Array.isArray(parsed) ? \"an array\" : typeof parsed\n }.`,\n );\n }\n\n const file = validateTemplateFile(await dereferenceTemplates(parsed, ref), ref);\n if (kind === \"url\") urlCache.set(ref, file);\n return file;\n}\n\n/**\n * Resolve a single template.\n *\n * A built-in id names one directly. A file or URL may name one inside itself\n * with a `#` fragment; without a fragment a single-template file resolves\n * unambiguously and anything else is an error listing what is on offer.\n *\n * `extends` is left unresolved - see `resolveExtends`.\n */\nexport async function loadTemplate(\n ref: string,\n options: LoadTemplateOptions = {},\n): Promise<Template> {\n const { base, fragment } = splitFragment(ref);\n const { kind } = classifyRef(base);\n\n if (kind === \"builtin\") {\n // A fragment here was silently dropped, so `tgdp:how-to:1.6#typo` loaded\n // `tgdp:how-to:1.6` and succeeded - the same quiet-wrong-answer as the\n // prototype lookup below, where a configuration error selects a different\n // template than the author wrote and nothing says so. A built-in id names\n // exactly one template, so there is nothing a fragment could select.\n if (fragment !== null) {\n throw new LintError(\n `\"${ref}\" adds a \"#${fragment}\" fragment to a built-in template id. ` +\n `A built-in id names one template on its own - drop the fragment and ` +\n `use \"${base}\". Fragments name a template inside a file, as in ` +\n `\"./templates.yaml#how-to\".`,\n );\n }\n if (!BUILTINS.has(base)) {\n const available = [...BUILTINS.keys()].join(\", \");\n throw new LintError(\n `Unknown built-in template \"${base}\". Available: ${available || \"(none)\"}.`,\n );\n }\n return loadBuiltin(base);\n }\n\n const file = await loadTemplateFile(base, options);\n const templates = file.templates ?? {};\n const names = Object.keys(templates);\n\n if (fragment !== null) {\n // An own-property check, not a truthiness test on the lookup. `templates`\n // is a plain object parsed from YAML, so `#constructor` and `#toString`\n // name members it inherits from `Object.prototype`. Those are truthy, so\n // such a fragment sailed past this guard and a function came back as a\n // template - one with no `sections`, which checks the document against\n // nothing. The bad fragment therefore produced silence rather than an\n // error: the page was reported as passing, not as naming a template that\n // does not exist.\n const named = Object.hasOwn(templates, fragment)\n ? templates[fragment]\n : undefined;\n if (named === undefined) {\n throw new LintError(\n `${base} has no template named \"${fragment}\". Available: ${names.join(\", \") || \"(none)\"}.`,\n );\n }\n return named;\n }\n\n const [only] = Object.values(templates);\n if (names.length === 1 && only !== undefined) return only;\n if (names.length === 0) throw new LintError(`${base} defines no templates.`);\n // Past both branches above, `names.length` is at least two, so `names[0]` is\n // there and the example fragment is never empty. The `?? \"\"` is\n // `noUncheckedIndexedAccess` paperwork rather than a case to look for.\n throw new LintError(\n `${base} defines ${names.length} templates; name one with a \"#\" fragment ` +\n `(e.g. \"${base}#${names[0] ?? \"\"}\"). Available: ${names.join(\", \")}.`,\n );\n}\n\n/* -------------------------------------------------------------------------- *\n * Inheritance\n * -------------------------------------------------------------------------- */\n\n/** How `resolveExtends` turns an `extends` ref into a template. */\nexport type TemplateResolver = (ref: string) => Promise<Template>;\n\n/**\n * Merge a child template onto its parent.\n *\n * Recursion follows `sections` and stops there. A rule's own keys are units:\n * overriding `contains` means replacing it, not merging a child's `min` into\n * the parent's `max` and hoping the pair still means something. But `sections`\n * is a container, not a rule. Replacing it wholesale would mean that tightening\n * one nested rule silently discards every sibling the parent declared, which is\n * the opposite of what `extends` is for.\n *\n * `sections` is a list in v2, so \"merges by key\" now means \"merges by `id`\". A\n * child rule carrying an id the parent also uses replaces that rule where the\n * parent had it, keeping the parent's order. A child rule with no id, or with\n * an id the parent does not use, is appended. Every other key the child sets\n * replaces the parent's outright, `repeat` included: a repeated run is one\n * unit, and merging two of them by position would be guesswork.\n */\nfunction mergeRuleLists(\n parent: Rule[] | undefined,\n child: Rule[] | undefined,\n): Rule[] | undefined {\n if (!parent) return child;\n if (!child) return parent;\n\n // Parent order first, so inherited rules keep the document order the parent\n // declared and child-only additions land at the end.\n const merged: Rule[] = [...parent];\n for (const childRule of child) {\n const id = childRule.id;\n const index =\n id === undefined ? -1 : merged.findIndex((rule) => rule.id === id);\n const target = index === -1 ? undefined : merged[index];\n if (target === undefined) merged.push(childRule);\n else merged[index] = mergeRules(target, childRule);\n }\n return merged;\n}\n\nfunction mergeRules(parent: Rule, child: Rule): Rule {\n // `sections` is dropped from both spreads and merged on its own. Every other\n // key replaces outright, which is what a spread does; sections merge by `id`,\n // which it cannot. Leaving the child's raw array in the spread and then\n // overwriting it worked, but it read as though the spread were the whole\n // answer.\n const sections = mergeRuleLists(parent.sections, child.sections);\n const { sections: _parentSections, ...parentRest } = parent;\n const { sections: _childSections, ...childRest } = child;\n const merged: Rule = { ...parentRest, ...childRest };\n // `!== undefined` rather than truthiness. The two are equivalent here, since\n // `mergeRuleLists` returns `Rule[] | undefined` and an empty array is truthy\n // in any case - but a rule may legitimately carry `sections: []`, meaning it\n // allows none, and a reader should not have to know that `[]` survives a\n // truthiness test to be sure that case is kept.\n if (sections !== undefined) merged.sections = sections;\n return merged;\n}\n\nfunction mergeTemplates(parent: Template, child: Template): Template {\n // Shaped like `mergeRules` above, and for the same reasons: merge first,\n // drop `sections` from both spreads, and test for `undefined` rather than\n // truthiness.\n const sections = mergeRuleLists(parent.sections, child.sections);\n const { sections: _parentSections, ...parentRest } = parent;\n const { sections: _childSections, ...childRest } = child;\n const merged: Template = { ...parentRest, ...childRest };\n if (sections !== undefined) merged.sections = sections;\n // The chain is resolved; leaving `extends` on would invite resolving twice.\n delete merged.extends;\n return merged;\n}\n\n/**\n * Resolve a template's `extends` chain, innermost first.\n *\n * `load` is injected because a relative `extends` is relative to the file the\n * child came from, which this function has no way to know. `chain` is internal\n * bookkeeping for cycle detection.\n */\nexport async function resolveExtends(\n template: Template,\n load: TemplateResolver = loadTemplate,\n chain: string[] = [],\n): Promise<Template> {\n const parentRef = template.extends;\n if (parentRef === undefined) return template;\n\n if (chain.includes(parentRef)) {\n throw new LintError(\n `Template \"extends\" cycle: ${[...chain, parentRef].join(\" -> \")}.`,\n );\n }\n\n const parent = await resolveExtends(await load(parentRef), load, [...chain, parentRef]);\n return mergeTemplates(parent, template);\n}\n\n/* -------------------------------------------------------------------------- *\n * Resolution relative to the declaring file\n * -------------------------------------------------------------------------- */\n\n/**\n * Re-base a ref against the file that declared it.\n *\n * A built-in id, a URL, and an absolute path all name themselves and are\n * returned unchanged. A relative path means \"beside the file I am written in\",\n * which is the one reading `loadTemplate` cannot produce on its own - it reads\n * against the process working directory, so `extends: ./base.yaml` in\n * `tpl/house.yaml` looked for `./base.yaml` at the cwd and found either nothing\n * or, worse, an unrelated file of that name.\n */\nexport function refRelativeTo(baseRef: string, ref: string): string {\n const { base, fragment } = splitFragment(ref);\n if (classifyRef(base).kind !== \"file\" || isAbsolute(base)) return ref;\n\n const { base: fromBase } = splitFragment(baseRef);\n const from = classifyRef(fromBase).kind;\n\n // A template fetched over HTTP names its neighbours the same way a local one\n // does, and `./base.yaml` beside it is a URL, not a path. Resolved as a path\n // it became a read of the process working directory - a local file quietly\n // standing in for the remote one, or a \"file not found\" naming a path that\n // appears nowhere in the template.\n if (from === \"url\") {\n const rebased = new URL(base, fromBase).href;\n return fragment === null ? rebased : `${rebased}#${fragment}`;\n }\n\n if (from !== \"file\") return ref;\n\n const rebased = resolvePath(dirname(fromBase), base);\n return fragment === null ? rebased : `${rebased}#${fragment}`;\n}\n\n/**\n * Load a template with its `extends` chain resolved, re-basing each relative\n * ref against the file that declared it.\n *\n * This is what a caller should use. `resolveExtends` stays exported for callers\n * that supply their own resolver, but its default is the cwd-relative\n * `loadTemplate`, and reaching that default silently is the bug this exists to\n * avoid - `.then(resolveExtends)` passes one argument, so the default is\n * exactly what you get.\n */\nexport async function loadResolvedTemplate(\n ref: string,\n options: LoadTemplateOptions = {},\n chain: string[] = [],\n): Promise<Template> {\n // Seeded with this ref when the caller started here, so the reported cycle\n // starts where the reader did. Chained from the parent only, `a -> b -> a`\n // was reported as `b -> a -> b`: the file the run was pointed at never\n // appeared in the cycle it was said to be part of, which is the one name in\n // the message the reader can act on.\n const seen = chain.length === 0 ? [ref] : chain;\n const template = await loadTemplate(ref, options);\n const parentRef = template.extends;\n if (parentRef === undefined) return template;\n\n const absolute = refRelativeTo(ref, parentRef);\n if (seen.includes(absolute)) {\n throw new LintError(\n `Template \"extends\" cycle: ${[...seen, absolute].join(\" -> \")}.`,\n );\n }\n\n const parent = await loadResolvedTemplate(absolute, options, [\n ...seen,\n absolute,\n ]);\n return mergeTemplates(parent, template);\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"manni-lint:template:2\",\n \"title\": \"manni lint template file\",\n \"description\": \"A template file: named doctype templates, plus the reusable fragments they point at with `$ref`. Validated after dereferencing, so no `$ref` survives to be seen here. A template is itself a rule: it describes the page, and it takes every rule key plus `title`, `types` and `extends`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"templates\"],\n \"properties\": {\n \"$schema\": {\n \"description\": \"URL of this schema, so an editor can offer completion. Read by editors, never by the linter.\",\n \"type\": \"string\"\n },\n \"info\": {\n \"description\": \"Free-form metadata about the file (title, version, author). Never read by the linter; declared so a file that carries it is not rejected.\",\n \"type\": \"object\"\n },\n \"templates\": {\n \"description\": \"The doctype templates this file defines, keyed by name.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"patternProperties\": {\n \"^[A-Za-z][A-Za-z0-9-_]*$\": { \"$ref\": \"#/definitions/template\" }\n }\n },\n \"components\": {\n \"description\": \"Reusable fragments, targeted by `$ref` from elsewhere in the file. Left unconstrained: a fragment is validated where it is inlined, not where it is declared.\",\n \"type\": \"object\"\n }\n },\n \"definitions\": {\n \"min\": {\n \"description\": \"Fewest occurrences. Absent means one. Deliberately carries no JSON Schema `default`, and neither does any other key in this file: Ajv's `useDefaults` would write one in at load time, and a written-in value is indistinguishable from a value the author typed, so it wins the `extends` merge against the parent's real value. The code reads `undefined` instead.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"max\": {\n \"description\": \"Most occurrences. Absent is unbounded, so write `max: 1` for exactly once and `max: 0` to forbid. Must be at least `min` where both are written; the loader checks that, because JSON Schema cannot compare two siblings.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"id\": {\n \"description\": \"Handle for this rule, unique among its siblings. It appears in messages, and `extends` targets it to replace one inherited rule in place.\",\n \"type\": \"string\",\n \"pattern\": \"^[A-Za-z][A-Za-z0-9-_]*$\"\n },\n \"description\": {\n \"description\": \"Prose description of what the rule is for. Never checked.\",\n \"type\": \"string\"\n },\n \"heading\": {\n \"description\": \"The heading of the section this rule describes. Absent means any heading, or none. A string is exact text, a list is one-of, `{ pattern }` is an unanchored regular expression, and `false` means the section has no heading of its own.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n },\n {\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"pattern\"],\n \"properties\": {\n \"pattern\": { \"type\": \"string\", \"minLength\": 1 }\n }\n },\n { \"const\": false }\n ]\n },\n \"attributes\": {\n \"description\": \"Literal attributes the node must carry. A string is the required value; `true` requires the attribute to be present, and `false` requires it to be absent.\",\n \"type\": \"object\",\n \"additionalProperties\": {\n \"oneOf\": [{ \"type\": \"string\" }, { \"type\": \"boolean\" }]\n }\n },\n \"oneOfSequenceOrContains\": {\n \"description\": \"A rule says what it holds in order (`sequence`) or in any order (`contains`), never both.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"sequence\", \"contains\"] }\n },\n \"noHeadingOnRepeat\": {\n \"description\": \"`repeat` groups sibling rules into one repeated unit, so the group has no heading of its own. Put the heading on the first rule inside it.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"heading\", \"repeat\"] }\n },\n \"noInstructions\": {\n \"description\": \"`instructions` was the language-model hook of doc-structure-lint. manni lint is deterministic and has no model, so a template carrying it is rejected outright rather than silently ignored; the loader turns this into a migration message pointing at manni docevals.\",\n \"type\": \"object\",\n \"not\": { \"required\": [\"instructions\"] }\n },\n \"template\": {\n \"description\": \"One doctype template. A template is a rule describing the page itself, so it takes every rule key, plus `title`, `types` and `extends`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [\n { \"$ref\": \"#/definitions/noInstructions\" },\n { \"$ref\": \"#/definitions/oneOfSequenceOrContains\" },\n { \"$ref\": \"#/definitions/noHeadingOnRepeat\" }\n ],\n \"properties\": {\n \"title\": {\n \"description\": \"Human-readable name for the template, shown by `manni lint templates`.\",\n \"type\": \"string\"\n },\n \"types\": {\n \"description\": \"Doctypes this template serves, matched against a page's `type` frontmatter.\",\n \"type\": \"array\",\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n },\n \"extends\": {\n \"description\": \"Template reference (built-in id, path, or URL, optionally with a `#name` fragment) whose rules this template inherits.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"description\": { \"$ref\": \"#/definitions/description\" },\n \"heading\": { \"$ref\": \"#/definitions/heading\" },\n \"repeat\": { \"$ref\": \"#/definitions/repeat\" },\n \"min\": {\n \"description\": \"Refused on a template: a page is one page. Declared here only so the loader can say that in a sentence, rather than leaving Ajv to report an unexpected key.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"max\": {\n \"description\": \"Refused on a template, for the same reason as `min`.\",\n \"type\": \"integer\",\n \"minimum\": 0\n },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" },\n \"sections\": { \"$ref\": \"#/definitions/rules\" }\n }\n },\n \"rule\": {\n \"description\": \"One rule. It describes a section of the page, or, with `repeat`, a run of sections matched as one unit.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [\n { \"$ref\": \"#/definitions/noInstructions\" },\n { \"$ref\": \"#/definitions/oneOfSequenceOrContains\" },\n { \"$ref\": \"#/definitions/noHeadingOnRepeat\" }\n ],\n \"properties\": {\n \"id\": { \"$ref\": \"#/definitions/id\" },\n \"description\": { \"$ref\": \"#/definitions/description\" },\n \"heading\": { \"$ref\": \"#/definitions/heading\" },\n \"repeat\": { \"$ref\": \"#/definitions/repeat\" },\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" },\n \"sections\": { \"$ref\": \"#/definitions/rules\" }\n }\n },\n \"rules\": {\n \"description\": \"Rules for the subsections, in document order.\",\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/definitions/rule\" }\n },\n \"repeat\": {\n \"description\": \"Rules matched in order as one unit, repeated by this rule's `min` and `max`.\",\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"$ref\": \"#/definitions/rule\" }\n },\n \"sequence\": {\n \"description\": \"What the section holds, in order. Each entry is one run of one content kind.\",\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": {\n \"allOf\": [\n { \"$ref\": \"#/definitions/blockRules\" },\n { \"type\": \"object\", \"maxProperties\": 1 }\n ]\n }\n },\n \"blockRules\": {\n \"description\": \"Block rules, keyed by content kind. The key is plural because it carries a count: the node model says `codeBlock`, a template says `codeBlocks: { min: 1 }`.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"minProperties\": 1,\n \"properties\": {\n \"paragraphs\": { \"$ref\": \"#/definitions/paragraphs\" },\n \"codeBlocks\": { \"$ref\": \"#/definitions/codeBlocks\" },\n \"lists\": { \"$ref\": \"#/definitions/lists\" },\n \"tables\": { \"$ref\": \"#/definitions/tables\" },\n \"admonitions\": { \"$ref\": \"#/definitions/admonitions\" },\n \"images\": { \"$ref\": \"#/definitions/images\" },\n \"blockquotes\": { \"$ref\": \"#/definitions/counted\" },\n \"definitionLists\": { \"$ref\": \"#/definitions/counted\" },\n \"elements\": { \"$ref\": \"#/definitions/elements\" }\n }\n },\n \"counted\": {\n \"description\": \"A block rule that only counts.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" }\n }\n },\n \"paragraphs\": {\n \"description\": \"Paragraph requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"pattern\": {\n \"description\": \"Unanchored regular expression every paragraph in the run must match.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n },\n \"codeBlocks\": {\n \"description\": \"Code block requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"language\": {\n \"description\": \"Language every code block must be tagged with, as the node model spells it. A list is one-of: the block must declare one of them.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n }\n ]\n },\n \"fenceInfo\": {\n \"description\": \"Info-string tail after the language, where the format carries one.\",\n \"type\": \"string\"\n }\n }\n },\n \"lists\": {\n \"description\": \"List requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"ordered\": {\n \"description\": \"Whether the list must be ordered. Absent accepts either.\",\n \"type\": \"boolean\"\n },\n \"items\": { \"$ref\": \"#/definitions/listItems\" }\n }\n },\n \"listItems\": {\n \"description\": \"Requirements applied to every item of every list in the run. Recurses arbitrarily deep.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [{ \"$ref\": \"#/definitions/oneOfSequenceOrContains\" }],\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" }\n }\n },\n \"tables\": {\n \"description\": \"Table requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"columns\": {\n \"description\": \"The header cells, in order.\",\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" }\n }\n }\n },\n \"admonitions\": {\n \"description\": \"Admonition requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"variant\": {\n \"description\": \"Which flavor of admonition. The node model's `variant`, not any one format's spelling.\",\n \"enum\": [\n \"note\",\n \"tip\",\n \"important\",\n \"caution\",\n \"warning\",\n \"danger\"\n ]\n }\n }\n },\n \"images\": {\n \"description\": \"Image requirements.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"url\": {\n \"description\": \"Where the image must point.\",\n \"type\": \"string\"\n },\n \"alt\": {\n \"description\": \"Alternative text the image must carry.\",\n \"type\": \"string\"\n },\n \"attributes\": { \"$ref\": \"#/definitions/attributes\" }\n }\n },\n \"elements\": {\n \"description\": \"Named-wrapper requirements. `sequence` and `contains` here describe what the element holds, which is never the enclosing section's content.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"allOf\": [{ \"$ref\": \"#/definitions/oneOfSequenceOrContains\" }],\n \"properties\": {\n \"min\": { \"$ref\": \"#/definitions/min\" },\n \"max\": { \"$ref\": \"#/definitions/max\" },\n \"tag\": {\n \"description\": \"The element's name, as an author of the format would write it. A list is one-of: an element counts when it carries any of them.\",\n \"oneOf\": [\n { \"type\": \"string\", \"minLength\": 1 },\n {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": { \"type\": \"string\", \"minLength\": 1 }\n }\n ]\n },\n \"attributes\": { \"$ref\": \"#/definitions/attributes\" },\n \"sequence\": { \"$ref\": \"#/definitions/sequence\" },\n \"contains\": { \"$ref\": \"#/definitions/blockRules\" }\n }\n }\n }\n}\n","{\n \"$comment\": \"Built-in doctype templates derived from The Good Docs Project templates, pinned to release v1.6.0 (\\\"Iron\\\"): https://gitlab.com/tgdp/templates/-/tree/v1.6.0. Each entry's `file` is a YAML template beside this manifest; `source` is the upstream Markdown it was derived from, vendored under test/lint/fixtures/tgdp/ and linted against the derived template so the two cannot drift. Run `npm run check:tgdp-pin` to see whether upstream has moved past the pin.\",\n \"vendor\": \"tgdp\",\n \"upstream\": \"https://gitlab.com/tgdp/templates\",\n \"pin\": \"v1.6.0\",\n \"templates\": [\n {\n \"id\": \"tgdp:how-to:1.6\",\n \"file\": \"tgdp/how-to.yaml\",\n \"title\": \"How-to guide (The Good Docs Project)\",\n \"types\": [\"how-to\"],\n \"source\": \"how-to/template_how-to.md\"\n },\n {\n \"id\": \"tgdp:tutorial:1.6\",\n \"file\": \"tgdp/tutorial.yaml\",\n \"title\": \"Tutorial (The Good Docs Project)\",\n \"types\": [\"tutorial\"],\n \"source\": \"tutorial/template_tutorial.md\"\n },\n {\n \"id\": \"tgdp:reference:1.6\",\n \"file\": \"tgdp/reference.yaml\",\n \"title\": \"Reference (The Good Docs Project)\",\n \"types\": [\"reference\"],\n \"source\": \"reference/template_reference.md\"\n },\n {\n \"id\": \"tgdp:concept:1.6\",\n \"file\": \"tgdp/concept.yaml\",\n \"title\": \"Concept (The Good Docs Project)\",\n \"types\": [\"concept\", \"explanation\"],\n \"source\": \"concept/template_concept.md\"\n },\n {\n \"id\": \"tgdp:troubleshooting:1.6\",\n \"file\": \"tgdp/troubleshooting.yaml\",\n \"title\": \"Troubleshooting (The Good Docs Project)\",\n \"types\": [\"troubleshooting\"],\n \"source\": \"troubleshooting/template_troubleshooting.md\"\n },\n {\n \"id\": \"tgdp:release-notes:1.6\",\n \"file\": \"tgdp/release-notes.yaml\",\n \"title\": \"Release notes (The Good Docs Project)\",\n \"types\": [\"release-notes\"],\n \"source\": \"release-notes/template_release-notes.md\"\n },\n {\n \"id\": \"tgdp:readme:1.6\",\n \"file\": \"tgdp/readme.yaml\",\n \"title\": \"README (The Good Docs Project)\",\n \"types\": [\"readme\"],\n \"source\": \"readme/template_readme.md\"\n }\n ]\n}\n","/**\n * Decide which template describes a given page.\n *\n * The headline case is the last one: a page declares what it is in its\n * frontmatter (`type: how-to`), and the template follows from that. It is the\n * same key `manni meta`'s OKF, Diataxis, and TGDP schemas validate, so a repo\n * that already types its pages gets routing for free, and one run can lint a\n * whole tree with each page checked against the doctype it claims.\n *\n * The precedence chain mirrors docmeta's `resolveSchemaSet`: an explicit\n * instruction beats a per-page declaration, which beats repo policy, which\n * beats the page's own vocabulary, which beats a blanket default.\n *\n * 1. `--template` an operator overriding everything, for one run\n * 2. `$template` this page, specifically, is an exception\n * 3. config override repo policy: \"everything under docs/api is a reference\"\n * 4. `type` what the page says it is\n * 5. config default what to assume when a page says nothing\n *\n * Every step is recorded, because \"why was this page linted with that\n * template?\" is otherwise a question only the source can answer. `--explain`\n * prints the record.\n */\nimport picomatch from \"picomatch\";\nimport type { Template, TemplateFile } from \"./template.js\";\n\n/** Where a resolution came from, in precedence order. */\nexport type ResolutionStage =\n | \"cli\"\n | \"frontmatter-template\"\n | \"config-override\"\n | \"type\"\n | \"config-default\";\n\nexport type SkipCause = \"no-type\" | \"unknown-type\";\n\nexport interface ResolutionStep {\n stage: ResolutionStage;\n /** What the stage found, or null when it had nothing to say. */\n ref: string | null;\n /** Human-readable account of what the stage saw. */\n detail: string;\n}\n\nexport interface Resolution {\n /** The template ref to lint with, or null when the page is not linted. */\n ref: string | null;\n /** Which stage decided, when one did. */\n stage: ResolutionStage | null;\n /** Why no template was found. Only set when `ref` is null. */\n cause?: SkipCause;\n /**\n * The `type` the page declared but that resolved to nothing. Set only for\n * `unknown-type`, so the caller can name it and suggest near misses.\n */\n unknownType?: string;\n /** Near-miss doctypes, for an unknown-type message. */\n suggestions?: string[];\n /**\n * Every doctype something serves, for an unknown-type message with no near\n * miss to offer. Carried on the resolution rather than looked up by each\n * reporter, because the type index does not reach them - which is how the\n * pretty reporter came to print the list and `--explain` came to print\n * nothing, for the same page.\n */\n knownTypes?: string[];\n /** The full chain, for `--explain`. */\n steps: ResolutionStep[];\n}\n\n/** Per-glob repo policy, from `manni.config.yaml`'s `lint.overrides`. */\nexport interface TemplateOverride {\n files: string;\n template: string;\n}\n\nexport interface TypeIndexEntry {\n /** Ref that `loadTemplate` understands. */\n ref: string;\n /** Where the mapping came from; later sources outrank earlier ones. */\n source: \"builtin\" | \"user\" | \"config\";\n}\n\n/** The frontmatter key a page uses to override its doctype's template. */\nexport const FILE_TEMPLATE_KEY = \"$template\";\n/** The frontmatter key a page uses to declare its doctype. */\nexport const TYPE_KEY = \"type\";\n\n/**\n * Build the doctype -> template map.\n *\n * Built-ins go in first and user templates overwrite them, so overriding\n * `how-to` for a repo is one file with `types: [how-to]` in it - no config, no\n * flag. Within each source, a later entry wins, which makes the last\n * `--templates` file the one that decides.\n */\nexport function buildTypeIndex(params: {\n builtins?: { id: string; types: string[] }[];\n /** User template files, in the order they were supplied. */\n userFiles?: { ref: string; file: TemplateFile }[];\n /**\n * The config's explicit `types:` map. Highest precedence of the three, because\n * it is the only one where someone wrote down this exact pairing on purpose -\n * `types:` on a template says what that template is for, while this says what\n * this repo does about a doctype.\n */\n explicitTypes?: Record<string, string>;\n}): Map<string, TypeIndexEntry> {\n const index = new Map<string, TypeIndexEntry>();\n\n for (const builtin of params.builtins ?? []) {\n for (const type of builtin.types) {\n index.set(type, { ref: builtin.id, source: \"builtin\" });\n }\n }\n\n for (const { ref, file } of params.userFiles ?? []) {\n for (const [name, template] of Object.entries(file.templates ?? {})) {\n for (const type of template.types ?? []) {\n index.set(type, { ref: `${ref}#${name}`, source: \"user\" });\n }\n }\n }\n\n for (const [type, ref] of Object.entries(params.explicitTypes ?? {})) {\n index.set(type, { ref, source: \"config\" });\n }\n\n return index;\n}\n\nconst matcherCache = new Map<string, (p: string) => boolean>();\n\n/** Drop the compiled override-glob matchers. See `clearCaches`. */\nexport function clearMatcherCache(): void {\n matcherCache.clear();\n}\n\nfunction matchesGlob(glob: string, filePath: string): boolean {\n let m = matcherCache.get(glob);\n if (!m) {\n m = picomatch(glob, { dot: true });\n matcherCache.set(glob, m);\n }\n // Globs are written with forward slashes whatever the platform uses.\n return m(filePath.replace(/\\\\/g, \"/\"));\n}\n\n/**\n * Doctypes close enough to a typo to be worth naming. Edit distance of one or\n * two on short strings, or a shared prefix - enough to catch `how-two` and\n * `refernce` without listing the whole vocabulary at someone.\n */\nfunction suggestTypes(unknown: string, known: Iterable<string>): string[] {\n const target = unknown.toLowerCase();\n const scored: { type: string; distance: number }[] = [];\n\n for (const type of known) {\n const distance = editDistance(target, type.toLowerCase());\n const threshold = target.length <= 4 ? 1 : 3;\n if (distance <= threshold) scored.push({ type, distance });\n }\n\n return scored\n .sort((a, b) => a.distance - b.distance || a.type.localeCompare(b.type))\n .slice(0, 3)\n .map((s) => s.type);\n}\n\n/**\n * Levenshtein, two rows. Inputs here are doctype slugs, so size is not a\n * concern.\n *\n * Each cell needs three neighbours: the one to its left, the one above, and\n * the one above-left. Only the one above is read out of the previous row; the\n * other two are carried in locals from the cell just written and the previous\n * `above`. So the loop reads no array slot back by a computed index, which is\n * what `noUncheckedIndexedAccess` cannot prove is filled.\n *\n * The characters are compared as code units for the same reason: `a[i]` is\n * typed `string | undefined`, so comparing the two would either need an\n * assertion or a guard for a case the loop bounds already rule out.\n * `charCodeAt` returns `NaN` past the end and `NaN !== NaN`, so an index that\n * did escape its bounds costs an edit rather than matching by accident - the\n * invariant expressed rather than asserted.\n */\nfunction editDistance(a: string, b: string): number {\n if (a === b) return 0;\n let prev = Array.from({ length: b.length + 1 }, (_, i) => i);\n // The answer when `a` is empty: every character of `b` is an insertion.\n let last = b.length;\n for (let i = 0; i < a.length; i++) {\n const row = [i + 1];\n let left = i + 1;\n let diagonal = i;\n for (const [j, above] of prev.slice(1).entries()) {\n const cost = a.charCodeAt(i) === b.charCodeAt(j) ? 0 : 1;\n const cell = Math.min(left + 1, above + 1, diagonal + cost);\n row.push(cell);\n diagonal = above;\n left = cell;\n }\n prev = row;\n last = left;\n }\n return last;\n}\n\n/** A frontmatter value that should be a template ref. Non-strings are ignored. */\nfunction refFromFrontmatter(\n frontmatter: Record<string, unknown> | null,\n key: string,\n): string | null {\n const value = frontmatter?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nexport interface ResolveParams {\n /** Path used for override glob matching. */\n filePath: string;\n /**\n * Absolute path of the same file, matched as a fallback.\n *\n * `overrides[].files` globs are written relative to the config file, while\n * `filePath` is relative to the working directory. Those agree only when the\n * tool is run from the config's own directory; anywhere else the glob matched\n * nothing, the page fell through to its own `type`, and the run exited 0 with\n * the repo's policy silently unapplied. The CLI now rebases the globs to\n * absolute, so matching the absolute path too is what makes them land.\n */\n absolutePath?: string;\n /** The page's frontmatter, or null when it carries none. */\n frontmatter: Record<string, unknown> | null;\n /** `--template`: applies to every file in the run. */\n cliTemplate?: string;\n /** Repo policy, first match wins. */\n overrides?: TemplateOverride[];\n /** Doctype -> template, from `buildTypeIndex`. */\n typeIndex?: Map<string, TypeIndexEntry>;\n /** Applied when a page declares no doctype and nothing else matches. */\n defaultTemplate?: string;\n}\n\nexport function resolveTemplateRef(params: ResolveParams): Resolution {\n const steps: ResolutionStep[] = [];\n const typeIndex = params.typeIndex ?? new Map<string, TypeIndexEntry>();\n\n const decide = (stage: ResolutionStage, ref: string): Resolution => ({\n ref,\n stage,\n steps,\n });\n\n if (params.cliTemplate) {\n steps.push({ stage: \"cli\", ref: params.cliTemplate, detail: \"--template\" });\n return decide(\"cli\", params.cliTemplate);\n }\n steps.push({ stage: \"cli\", ref: null, detail: \"--template not given\" });\n\n const fileTemplate = refFromFrontmatter(params.frontmatter, FILE_TEMPLATE_KEY);\n if (fileTemplate) {\n steps.push({\n stage: \"frontmatter-template\",\n ref: fileTemplate,\n detail: `${FILE_TEMPLATE_KEY} in frontmatter`,\n });\n return decide(\"frontmatter-template\", fileTemplate);\n }\n steps.push({\n stage: \"frontmatter-template\",\n ref: null,\n detail: `no ${FILE_TEMPLATE_KEY} in frontmatter`,\n });\n\n for (const override of params.overrides ?? []) {\n if (\n matchesGlob(override.files, params.filePath) ||\n (params.absolutePath !== undefined &&\n matchesGlob(override.files, params.absolutePath))\n ) {\n steps.push({\n stage: \"config-override\",\n ref: override.template,\n detail: `matched override \"${override.files}\"`,\n });\n return decide(\"config-override\", override.template);\n }\n }\n steps.push({\n stage: \"config-override\",\n ref: null,\n detail: (params.overrides ?? []).length\n ? \"no override glob matched\"\n : \"no overrides configured\",\n });\n\n const type = refFromFrontmatter(params.frontmatter, TYPE_KEY);\n if (type) {\n const entry = typeIndex.get(type);\n if (entry) {\n steps.push({\n stage: \"type\",\n ref: entry.ref,\n detail: `type: ${type} -> ${entry.source} template`,\n });\n return decide(\"type\", entry.ref);\n }\n\n // A declared type that resolves to nothing is a typo or a gap, and either\n // way silently not checking the page is the wrong answer.\n steps.push({\n stage: \"type\",\n ref: null,\n detail: `type: ${type} matches no template`,\n });\n return {\n ref: null,\n stage: null,\n cause: \"unknown-type\",\n unknownType: type,\n suggestions: suggestTypes(type, typeIndex.keys()),\n knownTypes: knownTypes(typeIndex),\n steps,\n };\n }\n steps.push({ stage: \"type\", ref: null, detail: \"no type in frontmatter\" });\n\n if (params.defaultTemplate) {\n steps.push({\n stage: \"config-default\",\n ref: params.defaultTemplate,\n detail: \"config default\",\n });\n return decide(\"config-default\", params.defaultTemplate);\n }\n steps.push({ stage: \"config-default\", ref: null, detail: \"no default configured\" });\n\n // An untyped page is manni meta's complaint, not this tool's: its OKF schema\n // already requires `type`. Skipping rather than failing is what lets you\n // point manni lint at a whole tree on day one.\n return { ref: null, stage: null, cause: \"no-type\", steps };\n}\n\n/** The doctypes a resolved index can serve, sorted, for error messages. */\nexport function knownTypes(index: Map<string, TypeIndexEntry>): string[] {\n return [...index.keys()].sort();\n}\n\nexport type { Template };\n","/**\n * Run a template over a parsed document.\n *\n * Matching and checking are deliberately separate: `match.ts` decides which\n * section each rule is talking about, and this module asks the content rules\n * about the pairs it produced. The old code fused the two - it inferred which\n * section a rule meant by running the rules and seeing whether they passed -\n * which made \"does this section match?\" and \"is this section valid?\" the same\n * question, so a section could never be both matched and wrong.\n *\n * Everything here is synchronous. It was async only to await a language model,\n * which this tool no longer has.\n *\n * Two things beyond that wiring live here, and both are about the document as a\n * whole rather than about any one section.\n *\n * **A template is a rule, and its subject is the page.** Its `heading`\n * constrains the page's own title, its `sequence`/`contains` describe what sits\n * before the first heading, and its `sections` describe what follows. See\n * `pageSection` for which section that makes the subject.\n *\n * **A rule the format cannot answer is said out loud.** A parser declares the\n * content kinds it emits (proposal 0065), and a rule about a kind absent from\n * that list is dropped and reported as a `warning` rather than silently passing\n * - or, worse, failing a document for holding none of something its format\n * cannot express.\n */\nimport type {\n ContentKind,\n DocumentTree,\n Finding,\n Position,\n SectionNode,\n} from \"../types.js\";\nimport {\n checkContains,\n checkContainsIn,\n checkHeading,\n checkSequence,\n checkSequenceIn,\n type RuleContext,\n} from \"../rules/index.js\";\nimport { parserByName } from \"../parsers/index.js\";\nimport { matchSections, type MatchOptions } from \"./match.js\";\nimport { BLOCK_KINDS, BLOCK_KIND_NODE } from \"./template.js\";\nimport type {\n BlockKind,\n BlockRule,\n ElementsRule,\n ListItemsRule,\n ListsRule,\n Rule,\n TablesRule,\n Template,\n} from \"./template.js\";\n\n/** What a caller can tell the validator beyond the tree and the template. */\nexport interface ValidateOptions {\n /** Template file the rules came from. Names the state-cap failure. */\n source?: string;\n /** Template within that file. Named in messages about the template. */\n template?: string;\n /**\n * Content kinds the parser that produced this tree emits. Defaults to what\n * the registry declares for `tree.format`, which is what the CLI wants; a\n * caller holding a tree from somewhere else passes its own.\n */\n kinds?: ContentKind[];\n}\n\n/* -------------------------------------------------------------------------- *\n * The page as the template's subject\n * -------------------------------------------------------------------------- */\n\n/** A zero-width span at the top of a file, for a document with no sections. */\nfunction origin(): Position {\n return {\n start: { line: 1, column: 1, offset: 0 },\n end: { line: 1, column: 1, offset: 0 },\n };\n}\n\n/** First section's start through the last one's end. */\nfunction span(sections: SectionNode[]): Position {\n const first = sections[0];\n const last = sections[sections.length - 1];\n if (!first || !last) return origin();\n return { start: { ...first.position.start }, end: { ...last.position.end } };\n}\n\n/**\n * The section the template describes: the page itself.\n *\n * A page whose body is one `# Title` with everything nested under it *is* that\n * section, and a page whose content starts before any heading is the implicit\n * lead section `sectionize` opens for it - which has no heading of its own, so\n * `heading: false` matches it and `heading: \"Overview\"` does not.\n *\n * Anything else - several top-level headings, or a body that starts at `##`,\n * which is what a page whose title lives in frontmatter looks like - has no one\n * section standing for the page, so one is synthesised. It has no heading and\n * no content of its own: content before the first heading always opens a lead\n * section, so a document that reaches this branch has none.\n */\nfunction pageSection(tree: DocumentTree): SectionNode {\n const [only] = tree.sections;\n if (tree.sections.length === 1 && only && only.level <= 1) return only;\n return {\n slug: \"\",\n title: \"\",\n level: 0,\n order: 0,\n parentSlug: null,\n titlePosition: null,\n position: span(tree.sections),\n children: [],\n sections: tree.sections,\n };\n}\n\n/* -------------------------------------------------------------------------- *\n * Rules the format cannot answer\n * -------------------------------------------------------------------------- */\n\n/** One rule that will not run here, and what the parser does not report. */\ninterface UncheckedRule {\n /** The template key, as the author wrote it. */\n rule: string;\n /** What is missing, in words: `tables`, `table columns`, `list items`. */\n noun: string;\n}\n\n/** What each block-rule key asks about, as a message says it. */\nconst BLOCK_NOUNS: Record<BlockKind, string> = {\n paragraphs: \"paragraphs\",\n codeBlocks: \"code blocks\",\n lists: \"lists\",\n tables: \"tables\",\n admonitions: \"admonitions\",\n images: \"images\",\n blockquotes: \"blockquotes\",\n definitionLists: \"definition lists\",\n elements: \"elements\",\n};\n\n/**\n * A template with the rules this format cannot answer removed, and the list of\n * what was removed.\n *\n * Removing them is the point, not a side effect. A `tables: { min: 1 }` left\n * standing against a parser that never emits a table counts zero tables and\n * fails the page, which is a verdict on the format rather than on the document\n * - and it would contradict, in the same report, the warning saying the rule\n * was not checked.\n */\nclass Pruner {\n private readonly emitted: Set<ContentKind>;\n /** Keyed by message, so one rule asked twice is reported once. */\n private readonly missing = new Map<string, UncheckedRule>();\n\n constructor(kinds: ContentKind[]) {\n this.emitted = new Set(kinds);\n }\n\n get gaps(): UncheckedRule[] {\n return [...this.missing.values()];\n }\n\n private emits(kind: ContentKind): boolean {\n return this.emitted.has(kind);\n }\n\n private drop(rule: string, noun: string): void {\n this.missing.set(`${rule}: ${noun}`, { rule, noun });\n }\n\n /** A table rule without its `columns`, when the parser reports no cells. */\n private tables(rule: TablesRule): TablesRule {\n if (rule.columns === undefined || this.emits(\"tableCell\")) return rule;\n this.drop(\"columns\", \"table columns\");\n const { columns: _columns, ...kept } = rule;\n return kept;\n }\n\n /**\n * A list rule with its `items` pruned.\n *\n * `items` has no capability of its own to check. A parser that reports a\n * list reports its items, since `ListNode.items` is not optional, and\n * `listItem` is not one of the `BLOCK_KINDS` a block rule can target. So\n * the `lists` gap covers the whole rule, and only what the items *hold* can\n * go unchecked.\n *\n * `listItem` is a `ContentKind` and parsers do declare it. That is a fact\n * about the node model rather than about this pruner: a template names\n * `lists`, and reaches an item only through `items`, so there is no rule\n * whose capability would be `listItem` on its own.\n */\n private lists(rule: ListsRule): ListsRule {\n const items = rule.items;\n if (items === undefined) return rule;\n const pruned = this.listItems(items);\n return pruned === items ? rule : { ...rule, items: pruned };\n }\n\n private listItems(rule: ListItemsRule): ListItemsRule {\n return this.holder(rule);\n }\n\n private elements(rule: ElementsRule): ElementsRule {\n return this.holder(rule);\n }\n\n /** The `sequence`/`contains` pair every content holder carries. */\n private holder<T extends { sequence?: BlockRule[]; contains?: BlockRule }>(\n rule: T,\n ): T {\n const kept = { ...rule };\n let changed = false;\n\n if (rule.sequence) {\n const sequence = this.sequence(rule.sequence);\n if (sequence === undefined) {\n delete kept.sequence;\n changed = true;\n } else if (sequence !== rule.sequence) {\n kept.sequence = sequence;\n changed = true;\n }\n }\n\n if (rule.contains) {\n const contains = this.block(rule.contains);\n if (contains === undefined) {\n delete kept.contains;\n changed = true;\n } else if (contains !== rule.contains) {\n kept.contains = contains;\n changed = true;\n }\n }\n\n return changed ? kept : rule;\n }\n\n /**\n * One `sequence`, with the runs this format cannot see taken out.\n *\n * Taking the entry out rather than leaving it empty is what keeps the rest of\n * the sequence meaningful: a run of a kind the parser never emits can never\n * appear among the actual runs, so an entry left standing would report an\n * order error on every file of that format. `undefined` means the key goes.\n */\n private sequence(entries: BlockRule[]): BlockRule[] | undefined {\n const kept: BlockRule[] = [];\n let changed = false;\n for (const entry of entries) {\n const pruned = this.block(entry);\n if (pruned === undefined) {\n changed = true;\n continue;\n }\n if (pruned !== entry) changed = true;\n kept.push(pruned);\n }\n if (!changed) return entries;\n return kept.length > 0 ? kept : undefined;\n }\n\n /** One block rule, or `undefined` when nothing in it can be checked. */\n private block(block: BlockRule): BlockRule | undefined {\n const kept: BlockRule = { ...block };\n let changed = false;\n\n for (const key of BLOCK_KINDS) {\n if (kept[key] === undefined) continue;\n if (this.emits(BLOCK_KIND_NODE[key])) continue;\n this.drop(key, BLOCK_NOUNS[key]);\n // Cleared rather than deleted. A computed `delete` is banned here, and\n // nothing that reads a block rule asks whether the key is present - every\n // one of them tests the value against `undefined`.\n Object.assign(kept, { [key]: undefined });\n changed = true;\n }\n\n const tables = kept.tables;\n if (tables) {\n const pruned = this.tables(tables);\n if (pruned !== tables) {\n kept.tables = pruned;\n changed = true;\n }\n }\n\n const lists = kept.lists;\n if (lists) {\n const pruned = this.lists(lists);\n if (pruned !== lists) {\n kept.lists = pruned;\n changed = true;\n }\n }\n\n const elements = kept.elements;\n if (elements) {\n const pruned = this.elements(elements);\n if (pruned !== elements) {\n kept.elements = pruned;\n changed = true;\n }\n }\n\n if (!changed) return block;\n return BLOCK_KINDS.some((key) => kept[key] !== undefined) ? kept : undefined;\n }\n\n private rules(rules: Rule[]): Rule[] {\n const kept: Rule[] = [];\n let changed = false;\n for (const rule of rules) {\n const pruned = this.rule(rule);\n if (pruned !== rule) changed = true;\n kept.push(pruned);\n }\n return changed ? kept : rules;\n }\n\n rule<T extends Rule>(rule: T): T {\n const kept = this.holder(rule);\n let changed = kept !== rule;\n const out = changed ? kept : { ...rule };\n\n if (rule.repeat) {\n const repeat = this.rules(rule.repeat);\n if (repeat !== rule.repeat) {\n out.repeat = repeat;\n changed = true;\n }\n }\n\n if (rule.sections) {\n const sections = this.rules(rule.sections);\n if (sections !== rule.sections) {\n out.sections = sections;\n changed = true;\n }\n }\n\n return changed ? out : rule;\n }\n}\n\n/** A pruned template and what pruning it took out. */\ninterface Pruned {\n template: Template;\n gaps: UncheckedRule[];\n}\n\n/**\n * Pruned templates, keyed by the template and the format's kinds.\n *\n * The identity matters as much as the saving. `match.ts` caches one compiled\n * automaton per rule *array*, so a fresh copy of `sections` per file would\n * recompile the template for every page of a docset. A template that needs no\n * pruning is therefore returned as-is, and one that does is pruned once per\n * format.\n */\nconst pruneCache = new WeakMap<Template, Map<string, Pruned>>();\n\nfunction prune(template: Template, kinds: ContentKind[]): Pruned {\n const key = [...kinds].sort().join(\",\");\n let byKinds = pruneCache.get(template);\n if (!byKinds) {\n byKinds = new Map<string, Pruned>();\n pruneCache.set(template, byKinds);\n }\n const hit = byKinds.get(key);\n if (hit) return hit;\n\n const pruner = new Pruner(kinds);\n const pruned: Pruned = { template: pruner.rule(template), gaps: pruner.gaps };\n byKinds.set(key, pruned);\n return pruned;\n}\n\n/** The warning one unchecked rule gets, anchored at the top of the file. */\nfunction uncheckedFinding(\n format: string,\n name: string,\n gap: UncheckedRule,\n): Finding {\n return {\n type: \"unsupported_content_kind\",\n heading: null,\n message:\n `The ${format} parser does not report ${gap.noun}, so the ` +\n `\"${gap.rule}\" rule in template \"${name}\" is not checked for this file.`,\n position: origin(),\n // A warning, because nothing about the document is wrong: the rule asks\n // about something this format cannot express. It never changes the exit\n // code - `runLint` counts a file as failed only on an error-severity\n // finding - so a docset of mixed formats stays green while still saying\n // which rules went unchecked, and where.\n severity: \"warning\",\n };\n}\n\n/* -------------------------------------------------------------------------- *\n * The walk\n * -------------------------------------------------------------------------- */\n\n/** Content and heading rules for one matched pair, without recursion. */\nfunction checkSection(section: SectionNode, rule: Rule): Finding[] {\n return [\n ...checkHeading(section, rule.heading),\n ...checkSequence(section, rule.sequence),\n ...checkContains(section, rule.contains),\n ];\n}\n\n/** Match one level of sections, check each pair, then recurse. */\nexport function validateSections(\n sections: SectionNode[],\n rules: Rule[] | undefined,\n options: MatchOptions = {},\n): Finding[] {\n const { matches, findings } = matchSections(sections, rules, options);\n const all = [...findings];\n\n for (const match of matches) {\n all.push(...checkSection(match.section, match.rule));\n all.push(\n ...validateSections(match.section.sections, match.rule.sections, {\n ...options,\n parent: match.section,\n }),\n );\n }\n\n return all;\n}\n\n/** Findings for one document against one template, in document order. */\nexport function validateDocument(\n tree: DocumentTree,\n template: Template,\n options: ValidateOptions = {},\n): Finding[] {\n // An unrecognised format falls back to *no* kinds rather than to no pruning.\n // Every tree the CLI builds carries a registered parser's own name, so this\n // is only reachable through the exported API, with a tree somebody else\n // parsed. Such a format has promised nothing, so no content rule can be said\n // to have run against it, and the alternative is the one 0065 rules out:\n // applying every rule to a format that may answer none of them, which makes\n // a green run mean two different things.\n const kinds = options.kinds ?? parserByName(tree.format)?.kinds ?? [];\n const pruned = prune(template, kinds);\n const rules = pruned.template;\n // The template's own name, for the messages that have to say which template\n // asked. The caller knows it; `title` is the next best thing a file carries.\n const name = options.template ?? template.title ?? \"unnamed\";\n\n const findings: Finding[] = pruned.gaps.map((gap) =>\n uncheckedFinding(tree.format, name, gap),\n );\n\n // The page is the template's subject. Its content is checked through the\n // `*In` rules rather than the section-shaped ones so that a finding about a\n // page with no heading of its own carries no heading either, instead of the\n // empty string a headless section's title is.\n const page = pageSection(tree);\n const ctx: RuleContext = {\n heading: page.title || null,\n position: page.position,\n };\n findings.push(...checkHeading(page, rules.heading));\n findings.push(...checkSequenceIn(page.children, rules.sequence, ctx));\n findings.push(...checkContainsIn(page.children, rules.contains, ctx));\n findings.push(\n ...validateSections(page.sections, rules.sections, {\n parent: page,\n source: options.source,\n template: options.template,\n }),\n );\n\n // Rules fire in rule order, which is not necessarily document order once a\n // template mixes optional sections and subsections. Readers scan a report top\n // to bottom against the file, so sort by where the finding actually is.\n return findings.sort(\n (a, b) => a.position.start.offset - b.position.start.offset,\n );\n}\n","/**\n * The `lint:` section of `manni.config.yaml`.\n *\n * The file is shared across the manni family - manni docevals, manni meta and\n * this tool all read one file per repo, each taking its own top-level key. So\n * the root is a mapping of tool name to that tool's settings, and manni lint\n * reads `lint:` and nothing else: sibling keys are neither read nor validated,\n * which is what keeps the family decoupled - no registry of known tools, no\n * coordination, no version coupling. See agentevals ADR 01009.\n *\n * Finding the file is the family's job (`src/shared/config-file.ts`): it walks\n * up from the working directory to the repository root, reads the `lint:` key\n * of the first family file that has one, and hands this module the slice. What\n * the slice may contain is decided here. The pre-family\n * `doc-structure-lint.config.yaml` is no longer discovered: its whole document\n * was a `lint:` section written in keys this one no longer takes, so reading it\n * could only produce the moved-key refusal from a file the author is not\n * looking at.\n *\n * The document set is not a `lint:` key. Proposal 0041 declares it once for\n * every tool, under the family-level `collections:`, which the shared loader\n * parses and `resolveLintRun` selects from; `paths:` and `exclude:` under\n * `lint:` are refused by name.\n *\n * That leaves the root unvalidatable by any single tool, so the two ways an\n * author can lose a whole config to this convention are caught by shape\n * instead: keys left at the top level, and a wrapper differing only in case. A\n * wrapper misspelled any other way (`lnt:`) is indistinguishable from another\n * tool's section and still yields defaults - the accepted cost of having no\n * registry.\n *\n * Inside the section, `config.json` is the authority and `additionalProperties`\n * is false at every level, so a typo'd key is a loud failure rather than a\n * silent default. Defaults themselves live with the consumers, not here: this\n * returns exactly what the author wrote.\n */\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport * as AjvNs from \"ajv\";\nimport type { ErrorObject, SchemaObject, ValidateFunction } from \"ajv\";\nimport configSchema from \"../../../schemas/lint/config.json\" with { type: \"json\" };\nimport { selectCollections, type CollectionConfig } from \"../../shared/collections.js\";\nimport {\n FAMILY_CONFIG_NAMES,\n findConfigFile,\n readConfigFile,\n type ConfigFile,\n type ConfigFileOptions,\n} from \"../../shared/config-file.js\";\nimport { LintError } from \"../types.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\nimport type { ToolsConfig } from \"../../shared/tools.js\";\nimport type { TemplateOverride } from \"./resolve-template.js\";\nimport { refRelativeTo } from \"./template-registry.js\";\n\n// ajv is CommonJS with a default export; under NodeNext the constructable\n// value is on `.default`, which the namespace import reaches without the\n// esModuleInterop-only call signature TypeScript otherwise complains about.\ntype AjvCtor = typeof import(\"ajv\").default;\nconst Ajv = AjvNs.default as unknown as AjvCtor;\n\n/**\n * Everything the `lint:` section can carry. Every key is optional, and an\n * absent one means \"the consumer's default\", never a value written in here.\n *\n * `overrides[].template` and the `types` values are template refs, left as the\n * strings they were written as: `resolve-template.ts` owns what a ref means.\n */\nexport interface LintConfig {\n /** Zero matched files is a success, not an operational error. */\n allowEmpty?: boolean;\n /** The structure job: what `lint structure` runs and `lint check` includes. */\n structure?: LintJobConfig;\n /** Template files to load; their `types:` join the routing table. */\n templates?: string[];\n /** Template for a page that declares no doctype. */\n template?: string;\n /** Explicit doctype -> template ref map. */\n types?: Record<string, string>;\n /** Repo policy, first matching glob wins. */\n overrides?: TemplateOverride[];\n}\n\nexport type { TemplateOverride };\n\n/** One job's settings. The tool that performs it is all a job configures. */\nexport interface LintJobConfig {\n tool?: LintTool;\n}\n\n/**\n * The tools that can perform a lint job: manni's own engine, and DITA Open\n * Toolkit for a DITA docset.\n */\nexport const LINT_TOOLS = [\"manni\", \"dita-ot\"] as const;\nexport type LintTool = (typeof LINT_TOOLS)[number];\n\n/** The jobs `lint check` runs: `structure`. */\nexport const LINT_JOBS = [\"structure\"] as const;\nexport type LintJob = (typeof LINT_JOBS)[number];\n\n/** The tools each job accepts, in the order its message lists them. */\nexport const TOOLS_BY_JOB: Readonly<Record<LintJob, readonly LintTool[]>> = {\n structure: [\"manni\", \"dita-ot\"],\n};\n\nexport function isLintTool(value: string): value is LintTool {\n return (LINT_TOOLS as readonly string[]).includes(value);\n}\n\n/** The top-level key manni lint owns inside the shared manni config. */\nexport const SECTION_KEY = \"lint\";\n\n/** The family filenames discovery accepts, in the order it tries them. */\nexport const CONFIG_FILENAMES: readonly string[] = FAMILY_CONFIG_NAMES;\n\n/**\n * How the family loader finds and slices this tool's config.\n *\n * `legacyNames` is empty on purpose. `doc-structure-lint.config.yaml` was this\n * tool's file before the family file, and its whole document was the `lint:`\n * section - written in `paths:` and `exclude:`, which the section no longer\n * takes. Reading one now could only refuse it, from a file the author is not\n * looking at, so discovery no longer knows the name at all.\n */\nconst CONFIG_FILE: ConfigFileOptions = {\n section: SECTION_KEY,\n legacyNames: [],\n toError: (message) => new LintError(message),\n};\n\n/**\n * The keys this tool owns, read off the schema rather than restated here.\n *\n * Finding any of them at the root of a manni config means the file was never\n * nested under `lint:`. Deriving the list from `config.json` is what stops it\n * drifting from the real key set the next time a key is added - a restated\n * list that fell behind would let exactly the un-nested config it exists to\n * catch fall through to defaults instead.\n */\nconst SECTION_KEYS: readonly string[] = Object.keys(configSchema.properties);\n\n/** The keys one job takes. Read off the schema for the same reason. */\nconst STRUCTURE_KEYS: readonly string[] = Object.keys(\n configSchema.properties.structure.properties,\n);\n\n/**\n * Where the metadata tool's configuration reference documents `collections:`.\n * The literal cite's `config.ts` carries, repeated for the same reason: that\n * module is meta's own, and sibling tools import only its public and family\n * barrels.\n */\nconst CONFIG_REF = \"https://hawkeyexl.github.io/manni/meta/reference/configuration/\";\n\n/**\n * The two keys proposal 0041 moved out of `lint:` and up to the family level.\n * Refused rather than aliased, as meta and cite refuse them: an alias would be\n * a second place to declare a document set that is meant to be declared once.\n */\nconst MOVED_KEYS: readonly string[] = [\"paths\", \"exclude\"];\n\n/**\n * Refuse a moved key before the unknown-key check, which would otherwise call\n * `paths` a typo. Same sentence as meta's and cite's, with the section renamed.\n */\nfunction assertNoMovedKeys(raw: Record<string, unknown>, source: string): void {\n for (const key of MOVED_KEYS) {\n if (Object.hasOwn(raw, key)) {\n throw new LintError(\n `${source}: \"${key}\" is no longer a ${SECTION_KEY} key. Document sets are declared once for every tool, under a top-level collections: list. See ${CONFIG_REF}#collections`,\n );\n }\n }\n}\n\n/**\n * A key the parser does not know is a typo, not a no-op. Naming every key the\n * mapping takes is what turns `allowEmtpy: true` from a line that reads as\n * configured and does nothing into a failure the author can act on. `where` is\n * the mapping as the user spells it: `lint` or `lint.structure`.\n */\nfunction rejectUnknownKeys(\n obj: Record<string, unknown>,\n allowed: readonly string[],\n where: string,\n source: string,\n): void {\n for (const key of Object.keys(obj)) {\n if (!allowed.includes(key)) {\n throw new LintError(\n `Unknown key \"${key}\" under ${where}: in ${source}. Supported keys: ${allowed.join(\", \")}.`,\n );\n }\n }\n}\n\n/**\n * The job mappings, checked before Ajv so the messages name the key set and\n * the tool list rather than restating a schema keyword. A tool nobody\n * implements is refused by name: silently falling back to manni's engine would\n * make `tool: vale` read as configured and lint with something else.\n */\nfunction checkJobs(section: Record<string, unknown>, source: string): void {\n for (const job of LINT_JOBS) {\n const value = section[job];\n if (value == null || !isRecord(value)) continue;\n rejectUnknownKeys(value, STRUCTURE_KEYS, `${SECTION_KEY}.${job}`, source);\n const tool = value.tool;\n if (tool === undefined) continue;\n if (typeof tool !== \"string\" || !TOOLS_BY_JOB[job].includes(tool as LintTool)) {\n throw new LintError(\n `${source}: ${SECTION_KEY}.${job}.tool must be one of: ${TOOLS_BY_JOB[job].join(\", \")}.`,\n );\n }\n }\n}\n\n// No `useDefaults`: the schema declares no defaults, deliberately. A default\n// written into the parsed object is indistinguishable from a value the author\n// wrote, which is the trap `template.json` documents on its `required` key.\nconst ajv = new Ajv({ allErrors: true });\n\nlet compiled: ValidateFunction<LintConfig> | null = null;\n\nfunction sectionValidator(): ValidateFunction<LintConfig> {\n compiled ??= ajv.compile<LintConfig>(configSchema as SchemaObject);\n return compiled;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * One Ajv error, as a path an author can find in their file.\n *\n * Ajv's `instancePath` is relative to the section, so it is prefixed back to\n * `lint`: a bare `/overrides/0/files` names a path that is not in the file.\n */\nfunction describeError(error: ErrorObject): string {\n const params = error.params as { additionalProperty?: unknown };\n const extra =\n typeof params.additionalProperty === \"string\"\n ? ` (\"${params.additionalProperty}\")`\n : \"\";\n return `${SECTION_KEY}${error.instancePath} ${error.message ?? \"is invalid\"}${extra}`;\n}\n\n/** The un-nested config: our own keys at the root, with no `lint:` wrapper. */\nfunction rejectUnNested(root: Record<string, unknown>, source: string): void {\n const stray = SECTION_KEYS.filter((key) => key in root);\n if (stray.length === 0) return;\n // The convention is named after the shared file, but the message must\n // describe the file the user actually passed: telling someone who ran\n // `-c my-custom.yaml` about `manni.config.yaml` reads as a complaint about a\n // file they never mentioned, and sends them looking for the wrong one.\n throw new LintError(\n `${source}: found ${stray.map((key) => `\"${key}:\"`).join(\", \")} at the top level, ` +\n `but no \"${SECTION_KEY}:\" key. This file is shared across the manni family, ` +\n `so every manni lint setting belongs under a top-level \"${SECTION_KEY}:\" key. ` +\n `Indent the file's contents one level and add that key.`,\n );\n}\n\n/**\n * The miscased wrapper: `Lint:`, which the stray-key check cannot see, because\n * the keys under it are nested rather than at the top level.\n */\nfunction rejectMiscasedWrapper(\n root: Record<string, unknown>,\n source: string,\n): void {\n const miscased = Object.keys(root).find(\n (key) => key !== SECTION_KEY && key.toLowerCase() === SECTION_KEY,\n );\n if (miscased === undefined) return;\n throw new LintError(\n `${source}: found \"${miscased}:\" at the top level, but the key is case-sensitive ` +\n `and manni lint reads \"${SECTION_KEY}:\" exactly. Rename \"${miscased}:\" to \"${SECTION_KEY}:\".`,\n );\n}\n\n/**\n * Validate the `lint:` section itself, whatever file it came from.\n *\n * `source` names the file in every message. `null` is the section written\n * with nothing under it, which an author means as \"defaults\"; it is not a\n * broken section.\n */\nexport function parseConfigSection(section: unknown, source: string): LintConfig {\n if (section == null) return {};\n\n // Before Ajv, which would call a moved key an additional property and an\n // unknown one a schema keyword. Both messages have to name what to do.\n if (isRecord(section)) {\n assertNoMovedKeys(section, source);\n rejectUnknownKeys(section, SECTION_KEYS, SECTION_KEY, source);\n checkJobs(section, source);\n }\n\n const validate = sectionValidator();\n if (!validate(section)) {\n const detail =\n (validate.errors ?? []).map(describeError).join(\"; \") ||\n `does not match the ${SECTION_KEY} config schema`;\n throw new LintError(\n `${source}: invalid \"${SECTION_KEY}\" section: ${detail}.`,\n );\n }\n return section;\n}\n\n/**\n * Parse a whole `manni.config.yaml` and return its `lint:` section.\n *\n * `source` names the file in every message. A file that is empty, or that\n * carries only other tools' sections, returns `{}` rather than throwing - that\n * is the case that keeps one shared file usable by a project which has not\n * adopted this tool yet.\n */\nexport function parseConfig(text: string, source: string): LintConfig {\n let raw: unknown;\n try {\n raw = parseYaml(text);\n } catch (err) {\n throw new LintError(\n `${source}: invalid YAML: ${errorMessage(err)}`,\n );\n }\n\n // An empty file configures nothing; it is not a broken one.\n if (raw == null) return {};\n if (!isRecord(raw)) {\n throw new LintError(\n `${source}: top level must be a mapping of tool name to that tool's settings, ` +\n `with manni lint's under \"${SECTION_KEY}:\".`,\n );\n }\n\n if (!(SECTION_KEY in raw)) {\n rejectUnNested(raw, source);\n rejectMiscasedWrapper(raw, source);\n // Every remaining root key belongs to a sibling tool. Reading none of them\n // is the whole point of the convention.\n return {};\n }\n\n return parseConfigSection(raw[SECTION_KEY], source);\n}\n\n/**\n * Load the config from an explicit path, or by walking up from `cwd`.\n *\n * Discovery is the family's: the nearest `manni.config.yaml` with a `lint:`\n * key or a family key wins, and a family file with neither belongs to a\n * sibling and is passed over. An explicit path skips discovery and errors if\n * it is missing; its `lint:` key is unwrapped when present and the whole\n * document is the section otherwise, so a file passed by hand may be called\n * anything.\n *\n * A wrapper differing from `lint:` only in case is still caught on an\n * un-wrapped document: nothing else in the section's schema would name it.\n *\n * Returns null when discovery finds no config, which is not an error - a repo\n * whose pages all declare their `type` needs no config at all.\n */\nexport async function loadConfig(\n explicitPath?: string,\n cwd: string = process.cwd(),\n): Promise<{ config: LintConfig; path: string } | null> {\n const file = await readLintConfigFile(explicitPath, cwd);\n if (file === null) return null;\n return { config: sectionOf(file), path: file.path };\n}\n\n/** The family file itself, found or named, before its `lint:` slice is read. */\nfunction readLintConfigFile(\n explicitPath: string | undefined,\n cwd: string,\n): Promise<ConfigFile | null> {\n return explicitPath\n ? readConfigFile(explicitPath, cwd, CONFIG_FILE)\n : findConfigFile(cwd, CONFIG_FILE);\n}\n\n/** One found file's `lint:` section, parsed, with the shape checks a file gets. */\nfunction sectionOf(file: ConfigFile): LintConfig {\n if (!file.wrapped && isRecord(file.value)) {\n rejectMiscasedWrapper(file.value, file.source);\n }\n return parseConfigSection(file.value, file.source);\n}\n\n/**\n * Re-base everything a config declares against the config file's own\n * directory.\n *\n * `loadConfig` returns the file's `path` precisely so this can happen, and\n * discarding it made the same config mean different things depending on where\n * the tool was invoked from. Running from a subdirectory turned `templates:`\n * into \"file not found\" - and turned `overrides:` into nothing at all,\n * silently: the glob stopped matching, every page fell through to its own\n * `type`, and the run exited 0 having applied none of the repo's policy.\n *\n * Refs go through `refRelativeTo`, which leaves built-in ids, URLs, and\n * absolute paths alone. Globs become absolute, which is what the run matches\n * them against. Every non-absolute glob is relative to the config, `**\\/*.md`\n * included: exempting a leading `**` left the original bug half-open, because\n * from a subdirectory that pattern expanded against the working directory.\n */\nexport function rebaseConfig(\n found: { config: LintConfig; path: string } | null,\n): LintConfig {\n if (!found) return {};\n const dir = dirname(resolve(found.path));\n const config = found.config;\n\n const ref = (value: string): string => refRelativeTo(found.path, value);\n const glob = (value: string): string =>\n isAbsolute(value) ? value : resolve(dir, value).replace(/\\\\/g, \"/\");\n\n return {\n ...config,\n ...(config.templates ? { templates: config.templates.map(ref) } : {}),\n ...(config.template ? { template: ref(config.template) } : {}),\n ...(config.types\n ? {\n types: Object.fromEntries(\n Object.entries(config.types).map(([type, value]) => [type, ref(value)]),\n ),\n }\n : {}),\n ...(config.overrides\n ? {\n overrides: config.overrides.map((o) => ({\n files: glob(o.files),\n template: ref(o.template),\n })),\n }\n : {}),\n };\n}\n\n/** The stdin token, repeated so this module does not depend on the file loader. */\nconst STDIN = \"-\";\n\nexport interface LintRunOptions {\n cwd: string;\n /** `-c/--config`. */\n configPath?: string;\n /** `--no-config`: skip discovery and run on the built-in defaults. */\n noConfig?: boolean;\n /** Positional inputs; empty means fall back to the selected collections' `paths:`. */\n inputs: string[];\n /**\n * `--collection <name>`, repeatable: the collections this run covers. Empty\n * or absent means every declared one, the same thing, because commander's\n * collector hands `[]` over when the flag was never typed.\n */\n collection?: string[];\n onConfigLoaded?: (info: { path: string; dir: string }) => void;\n}\n\n/** What every lint command settles before it touches a file. */\nexport interface ResolvedLintRun {\n /** The `lint:` section, rebased against the config's directory. */\n config: LintConfig;\n /** What to resolve: the positional inputs, or the collections' `paths:`. */\n inputs: string[];\n /** The directory `inputs` resolve from. */\n base: string;\n /** The collections this run covers; `[]` with no config. */\n collections: CollectionConfig[];\n /** Whether `inputs` came from the collections rather than the command line. */\n fromCollections: boolean;\n /**\n * The document's top-level `tools:`, an outside tool's settings (proposal\n * 0052). `{}` with no config. It is not part of the `lint:` section and is\n * not rebased here: a path in it is resolved against `configDir` by the run\n * that needs it, through the helpers in `src/shared/tools.ts`.\n */\n tools: ToolsConfig;\n configDir?: string;\n configPath?: string;\n configSource?: string;\n}\n\n/**\n * Settle which config governs the run and what it covers. The same shape cite\n * uses, for the same reason: positional inputs are typed from a shell and stay\n * cwd-relative, while a collection's `paths:` was written next to the config\n * and resolves there, so one run has exactly one base.\n */\nexport async function resolveLintRun(\n opts: LintRunOptions,\n): Promise<ResolvedLintRun> {\n const cwd = resolve(opts.cwd);\n\n // `--collection` names something only a config can define, and selects a set\n // the operator did not type, so it composes with neither positional paths\n // nor `--no-config`. The same two sentences meta, a11y and cite use. Stdin\n // is one more input rather than a path, so it rides beside the flag.\n const wanted = opts.collection ?? [];\n if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN)) {\n throw new LintError(\n \"--collection selects a configured collection; it cannot be combined with paths.\",\n );\n }\n\n const file = opts.noConfig ? null : await readLintConfigFile(opts.configPath, cwd);\n if (wanted.length > 0 && file === null) {\n throw new LintError(\"--collection needs a config file to select from.\");\n }\n if (file !== null) opts.onConfigLoaded?.({ path: file.path, dir: file.dir });\n\n const config = rebaseConfig(\n file === null ? null : { config: sectionOf(file), path: file.path },\n );\n // Selected whatever the inputs are: a typed file is still a member of the\n // collections that contain it. An unknown name is the shared helper's error.\n const collections =\n file === null\n ? []\n : selectCollections(\n file.collections,\n wanted,\n file.source,\n (message) => new LintError(message),\n );\n\n // `--collection` is a request the run has to honour, as in cite and meta:\n // the named collection's pages are resolved even when stdin rides beside the\n // flag. Counting `-` as a path made `lint check - --as markdown --collection\n // guides` lint the piped document, open not one file of `guides`, and exit 0\n // over a docset nothing read. The implicit fallback is not a request, and a\n // lone `-` cancels it: a piped page is a run of its own.\n const fromCollections = wanted.length > 0 || opts.inputs.length === 0;\n const collectionPaths = fromCollections ? collections.flatMap((c) => c.paths) : [];\n // Stdin keeps its typed position; the globs follow it in declaration order.\n const inputs = fromCollections ? [...opts.inputs, ...collectionPaths] : opts.inputs;\n // Only a path needs a base, so it follows the globs when they contributed\n // and stays at the working directory otherwise, stdin included.\n const base = collectionPaths.length > 0 && file ? file.dir : cwd;\n\n return {\n config,\n inputs,\n base,\n collections,\n fromCollections,\n tools: file?.tools ?? {},\n ...(file === null\n ? {}\n : { configDir: file.dir, configPath: file.path, configSource: file.source }),\n };\n}\n","{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": \"manni-lint:config:1\",\n \"title\": \"manni lint config section\",\n \"description\": \"The `lint:` section of a shared `manni.config.yaml`. The file's root is a mapping of tool name to that tool's settings, so this schema describes the section only - never the file. The document set is not here: proposal 0041 declares it once for every tool, under the family-level `collections:` key, and `paths`/`exclude` under `lint:` are refused by name. Every key is optional: a repo that configures no manni lint at all is a legitimate config, and so is one that carries only a sibling tool's section. `additionalProperties: false` applies at every level below `lint:`, which is what turns a typo'd key into a loud failure instead of a silent default.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"allowEmpty\": {\n \"description\": \"Treat a run that matched zero files as a success rather than an operational error. The same key, with the same meaning, as `cite.allowEmpty`: for a shared CI template or a pre-commit hook whose file list may legitimately be empty. `--allow-empty` on the command line sets it for one run.\",\n \"type\": \"boolean\"\n },\n \"overrides\": {\n \"description\": \"Repo policy: everything under a glob is linted with one template, whatever the pages say they are. First matching entry wins, and an override outranks a page's own `type`.\",\n \"type\": \"array\",\n \"items\": {\n \"description\": \"One policy entry. Both keys are required: an entry naming no template, or no files, is a line that silently does nothing.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"required\": [\"files\", \"template\"],\n \"properties\": {\n \"files\": {\n \"description\": \"Glob matched against each file's path, written with forward slashes whatever the platform uses.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"template\": {\n \"description\": \"Template ref applied to the files this entry matches.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n }\n },\n \"structure\": {\n \"description\": \"The structure job, which `manni lint structure` and `manni lint check` both run. Declaring it with no keys is how a repo says the job applies. Its one setting is the tool that performs it.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"properties\": {\n \"tool\": {\n \"description\": \"Which tool performs the job. `manni` is manni's own template-structure engine; `dita-ot` is DITA Open Toolkit, which validates a DITA map and the topics it reaches and is located through the top-level `tools.dita-ot.home`. `--tool <name>` on the command line names the same thing.\",\n \"type\": \"string\",\n \"enum\": [\"manni\", \"dita-ot\"]\n }\n }\n },\n \"template\": {\n \"description\": \"Template ref applied to a page that declares no doctype and matches no override. The last stage of the resolution chain, not an override: `--template`, a page's `$template`, an `overrides` entry, and the page's own `type` all outrank it.\",\n \"type\": \"string\",\n \"minLength\": 1\n },\n \"templates\": {\n \"description\": \"Template files to load. Their templates' `types:` join the doctype routing table, outranking the built-ins, so adopting a repo's own how-to shape is one entry here and no flag.\",\n \"type\": \"array\",\n \"items\": {\n \"type\": \"string\",\n \"minLength\": 1\n }\n },\n \"types\": {\n \"description\": \"Explicit doctype -> template ref map, for a doctype no template file claims with its own `types:`. Keys are the vocabulary a page's `type` frontmatter is written in, so they are the author's names rather than this schema's - constrained only to the shape a doctype slug takes, not to a fixed set.\",\n \"type\": \"object\",\n \"additionalProperties\": false,\n \"patternProperties\": {\n \"^[A-Za-z0-9._:-]+$\": {\n \"description\": \"A template ref: a built-in id, a path, or a URL, optionally with a `#name` fragment.\",\n \"type\": \"string\",\n \"minLength\": 1\n }\n }\n }\n }\n}\n","/**\n * The DITA Open Toolkit process seam: the only module that starts `dita`.\n *\n * Written in the shape `src/term/core/vale.ts` set, for the same reasons. The\n * launcher is started with an argv and never a shell string, the buffer is\n * raised because a build log is not small, and what comes back is validated\n * here, because it crosses a process boundary.\n *\n * **The test suite starts no DITA-OT, and needs no JVM.** Every unit test\n * injects a stub spawn. What the parser is fed is real output, though: the\n * logs under `test/lint/fixtures/dita-ot/logs/` are captures of DITA-OT 4.4.1\n * runs over the fixtures beside them, with only absolute paths rewritten.\n * Their README records what each one holds and where each field name comes\n * from.\n *\n * The verdict is the log, not the exit code: DITA-OT publishes no exit-code\n * contract, so a non-zero exit with a parseable log is findings, and only a\n * log that is missing or unreadable *and* a non-zero exit is a failure to run.\n */\nimport { execFile } from \"node:child_process\";\nimport { mkdtemp, readFile, rm, stat } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { delimiter, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Severity } from \"../../shared/severity.js\";\nimport { ditaOtHome } from \"../../shared/tools.js\";\nimport { LintError } from \"../types.js\";\n// Type-only, and erased at runtime: `./index.js` imports this module to put the\n// descriptor in its initial map, so a value import here would be a cycle.\nimport type { FormatInfo } from \"../commands/tools.js\";\nimport type { StructureToolDescriptor, ToolProbe, ToolProbeContext } from \"./index.js\";\n\n/** What the launcher did. `code` is null when a signal ended it. */\nexport interface DitaOtProcessResult {\n code: number | null;\n /**\n * The signal that ended it, when one did, and `code` is then null. Optional\n * because absent means what it says: the run was not killed.\n */\n signal?: string | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Start `launcher` with `args` and wait for it. Resolves for any exit; rejects\n * when the process could not start, with an error whose `code` is `ENOENT`\n * when there is no such launcher. Tests inject a stub.\n */\nexport type DitaOtSpawn = (\n launcher: string,\n args: string[],\n opts: { cwd?: string },\n) => Promise<DitaOtProcessResult>;\n\nconst MAX_BUFFER = 64 * 1024 * 1024;\n\n/** The two refusals a user reads, worded once. */\nconst NOT_ON_PATH =\n \"dita is not on PATH. Install DITA Open Toolkit to lint structure, or set tools.dita-ot.home.\";\n\nconst noLauncher = (home: string): string =>\n `no DITA Open Toolkit at \"${home}\". tools.dita-ot.home must be a DITA-OT installation directory.`;\n\n/**\n * `<home>/bin/dita`, or the bare name for the one on PATH. Windows has its own\n * launcher, `dita.bat`, which is what makes `ditaOtCommandLine` necessary.\n *\n * `platform` is a parameter so the mapping can be tested on either host.\n */\nexport function ditaOtLauncher(\n home: string | undefined,\n platform: NodeJS.Platform = process.platform,\n): string {\n const name = platform === \"win32\" ? \"dita.bat\" : \"dita\";\n return home === undefined ? name : join(home, \"bin\", name);\n}\n\n/** How a launcher is actually started. */\nexport interface DitaOtCommandLine {\n command: string;\n argv: string[];\n /** Windows: the argv is already one quoted string and must not be re-quoted. */\n verbatim: boolean;\n}\n\nfunction isBatch(launcher: string): boolean {\n const lower = launcher.toLowerCase();\n return lower.endsWith(\".bat\") || lower.endsWith(\".cmd\");\n}\n\n/**\n * Quote one token for `cmd.exe`. A `\"` inside a Windows path is impossible -\n * the character is not legal in a filename - so a value carrying one is a\n * shape this cannot quote safely, and it is refused rather than passed on.\n */\nfunction quoteForCmd(value: string): string {\n if (value.includes('\"')) {\n throw new LintError(\n `DITA Open Toolkit cannot be given an argument containing a quote: ${value}`,\n );\n }\n return `\"${value}\"`;\n}\n\n/**\n * How to start `launcher`, given what it is.\n *\n * Node refuses to `execFile` a `.bat` or `.cmd` without a shell, and\n * `shell: true` joins the argv into one string **with no quoting at all**. Our\n * argv carries user-supplied paths, so that is a command-injection hole, not a\n * convenience. The documented alternative is this one: run `cmd.exe` directly,\n * do the quoting here, and pass the whole command as one verbatim argument.\n * `/s` then strips exactly the outer pair of quotes, which is why the string\n * is wrapped a second time. The next reader will find `shell: true` simpler;\n * it is also the version with the hole in it.\n */\nexport function ditaOtCommandLine(\n launcher: string,\n args: string[],\n): DitaOtCommandLine {\n if (!isBatch(launcher)) {\n return { command: launcher, argv: args, verbatim: false };\n }\n const line = [launcher, ...args].map(quoteForCmd).join(\" \");\n return {\n command: process.env[\"COMSPEC\"] ?? \"cmd.exe\",\n argv: [\"/d\", \"/s\", \"/c\", `\"${line}\"`],\n verbatim: true,\n };\n}\n\nexport const spawnDitaOt: DitaOtSpawn = (launcher, args, opts) =>\n new Promise((settle, reject) => {\n const line = ditaOtCommandLine(launcher, args);\n execFile(\n line.command,\n line.argv,\n {\n cwd: opts.cwd,\n windowsHide: true,\n windowsVerbatimArguments: line.verbatim,\n maxBuffer: MAX_BUFFER,\n encoding: \"utf8\",\n },\n (error, stdout, stderr) => {\n if (!error) {\n settle({ code: 0, stdout, stderr });\n return;\n }\n if (typeof error.code === \"number\") {\n settle({ code: error.code, stdout, stderr });\n return;\n }\n // Killed by a signal. Node reports the exit code as **null** here, not\n // as undefined, so testing for undefined never matched and a killed\n // run rejected as though the launcher had failed to start. The log is\n // still read: a run cut short may have written findings before it died.\n if (error.code == null && typeof error.signal === \"string\") {\n settle({ code: null, signal: error.signal, stdout, stderr });\n return;\n }\n // A start failure (ENOENT) or an output over the buffer.\n const failure: Error = error;\n reject(failure);\n },\n );\n });\n\nfunction isEnoent(err: unknown): boolean {\n return (\n typeof err === \"object\" &&\n err !== null &&\n \"code\" in err &&\n err.code === \"ENOENT\"\n );\n}\n\nfunction isMapping(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a bare launcher name resolves to a file on `PATH`.\n *\n * Asked only after a failure, and only to choose the message. On Windows the\n * launcher is a `.bat` started through `cmd.exe`, so a missing one comes back\n * as cmd's own non-zero exit rather than as an `ENOENT` - and the advice a\n * reader needs is \"install DITA-OT\", not cmd's sentence about a command it did\n * not recognize.\n */\nasync function isOnPath(name: string): Promise<boolean> {\n for (const dir of (process.env[\"PATH\"] ?? \"\").split(delimiter)) {\n if (dir === \"\") continue;\n if (await isFile(join(dir, name))) return true;\n }\n return false;\n}\n\n/**\n * DITA-OT's `MessageBean.Type` onto the family's three-value scale, as\n * `foldValeSeverity` does for Vale's.\n *\n * `null` is \"not a finding\": `DEBUG` and `TRACE` are build chatter even when\n * they carry a code, and a level this does not know is dropped rather than\n * promoted to something it might not be.\n */\nexport function foldDitaOtSeverity(level: string): Severity | null {\n switch (level.toUpperCase()) {\n case \"FATAL\":\n case \"ERROR\":\n return \"error\";\n case \"WARN\":\n return \"warning\";\n case \"INFO\":\n return \"notice\";\n default:\n return null;\n }\n}\n\n/** One diagnostic DITA-OT reported, in the vocabulary the lint branch reads. */\nexport interface DitaOtMessage {\n /** DITA-OT's own message id, e.g. `DOTX010E`. Becomes the finding's `type`. */\n code: string;\n severity: Severity;\n message: string;\n /** The path its `location` URI named, absent when it named none. */\n file?: string;\n line?: number;\n /** The **column**. See `readMessage` for why the log calls it `row`. */\n column?: number;\n}\n\n/**\n * The path a `location` names.\n *\n * DITA-OT writes `file:/x`, one slash and no authority. `new URL` normalizes\n * that to the three-slash form, so the quirk costs nothing. What does cost\n * something is a POSIX-rooted URI read on Windows: `fileURLToPath` refuses\n * `file:///repo/a.dita` outright there, with `ERR_INVALID_FILE_URL_PATH`, and\n * a log written on a Linux CI runner is full of exactly those. The decoded\n * pathname is the answer in that case.\n */\nfunction pathFromFileUri(location: string): string {\n let url: URL;\n try {\n url = new URL(location);\n } catch {\n // Not a URI at all: DITA-OT named a bare path, so it is the path.\n return location;\n }\n if (url.protocol !== \"file:\") return location;\n try {\n return fileURLToPath(url);\n } catch {\n return decodeURIComponent(url.pathname);\n }\n}\n\n/**\n * One log object as a finding, or `null` when it is not one.\n *\n * `code` is the marker. DITA-OT writes hundreds of `INFO` lines that are\n * progress rather than diagnostics, and it is its own account of which is\n * which, so an object without a code is chatter whatever its level.\n */\nfunction readMessage(raw: unknown): DitaOtMessage | null {\n if (!isMapping(raw)) return null;\n const code = raw[\"code\"];\n if (typeof code !== \"string\" || code === \"\") return null;\n const level = raw[\"level\"];\n const severity =\n typeof level === \"string\" ? foldDitaOtSeverity(level) : null;\n if (severity === null) return null;\n\n const msg = raw[\"msg\"];\n const location = raw[\"location\"];\n const line = raw[\"line\"];\n // `row` is the column. The name is the logger's: its regex takes group 2 as\n // the line and group 3 as the column, then writes the column out under this\n // name. Read as a row it puts every finding on the wrong line, so do not\n // \"fix\" it to `line`.\n const row = raw[\"row\"];\n\n return {\n code,\n severity,\n // A coded message with no text is still a finding; the code says what it is.\n message: typeof msg === \"string\" && msg !== \"\" ? msg : code,\n ...(typeof location === \"string\" && location !== \"\"\n ? { file: pathFromFileUri(location) }\n : {}),\n ...(typeof line === \"number\" ? { line } : {}),\n ...(typeof row === \"number\" ? { column: row } : {}),\n };\n}\n\n/**\n * The findings in a `--logger=json` log, or `null` when the text is not one.\n *\n * Null rather than an empty list, because the caller treats the two\n * differently: no findings is a clean target, while no log at all is only a\n * failure when the launcher also exited non-zero.\n */\nexport function parseDitaOtLog(text: string): DitaOtMessage[] | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n if (!Array.isArray(parsed)) return null;\n const messages: DitaOtMessage[] = [];\n for (const raw of parsed as unknown[]) {\n const message = readMessage(raw);\n if (message !== null) messages.push(message);\n }\n return messages;\n}\n\n/** The first version-shaped number in what the launcher printed. */\nexport function parseDitaOtVersion(output: string): string | null {\n const match = /\\b(\\d+(?:\\.\\d+)+(?:[-.][0-9A-Za-z]+)*)/.exec(output);\n return match?.[1] ?? null;\n}\n\n/** What one invocation of DITA-OT had to say about one target. */\nexport interface DitaOtTargetResult {\n /** The absolute path this invocation was given as `--input`. */\n target: string;\n messages: DitaOtMessage[];\n}\n\nexport interface DitaOtRunOptions {\n /** Absolute paths. One invocation each: DITA-OT takes one `--input`. */\n targets: string[];\n /** The directory the run resolves from, and where the launcher is started. */\n cwd: string;\n /** `tools.dita-ot.home`, already resolved to an absolute directory. */\n home?: string;\n spawn?: DitaOtSpawn;\n}\n\n/** The message a failure to run reports, which is the launcher's own. */\nfunction failureText(result: DitaOtProcessResult): string {\n const stderr = result.stderr.trim();\n if (stderr !== \"\") return stderr;\n // \"exit code null\" says nothing a reader can act on, and a signal is the one\n // ending that reliably leaves no message behind.\n if (result.signal != null) return `killed by ${result.signal}`;\n return `exit code ${String(result.code)}`;\n}\n\nasync function readLog(path: string): Promise<DitaOtMessage[] | null> {\n let text: string;\n try {\n text = await readFile(path, \"utf8\");\n } catch {\n return null;\n }\n return parseDitaOtLog(text);\n}\n\n/**\n * DITA-OT over each target, and what its log said.\n *\n * ## Why this is not `dita validate`\n *\n * Because `dita validate` does not answer the question this tool exists for.\n * Run against a map whose topic conrefs an id that is not there, it exits 0 and\n * prints nothing. It parses and checks grammar, and it resolves no references.\n * Its own `--help` lists neither `--logger` nor `--logfile` nor `--temp`, so\n * the obvious invocation is silently accepted and silently useless.\n *\n * `--format=dita` is the DITA-to-DITA transformation. It runs the whole\n * preprocessing chain, which is where conref, keyref and link resolution\n * happen, and it reports what those stages find. Measured against the fixtures\n * beside this file, it catches everything `validate` catches and the references\n * as well: a grammar error is `DOTJ088E` with a line and a column, a missing\n * conref target is `DOTX010E`, an unreadable link target is `DOTX008E`.\n *\n * The cost is that a transformation writes output. It goes to the scratch\n * directory with the logs and the temp trees, and the whole scratch is removed\n * however the run ends, so nothing of ours lands in the user's tree.\n *\n * ## Why verbose\n *\n * Because DITA-OT reports an undefined key reference (`DOTJ047I`) at `INFO`,\n * and the default verbosity drops it. Verbose turns one run of a four-file map\n * into roughly 135 log entries, of which one carries a `code`. That is what\n * makes the `code` filter in `parseDitaOtLog` load-bearing rather than tidy.\n */\nexport async function runDitaOtValidate(\n opts: DitaOtRunOptions,\n): Promise<DitaOtTargetResult[]> {\n const spawn = opts.spawn ?? spawnDitaOt;\n const launcher = ditaOtLauncher(opts.home);\n // A home that holds no launcher is a configuration mistake, and saying so\n // before anything is started names the thing the user can fix.\n if (opts.home !== undefined && !(await isFile(launcher))) {\n throw new LintError(noLauncher(opts.home));\n }\n\n const scratch = await mkdtemp(join(tmpdir(), \"manni-dita-ot-\"));\n try {\n const results: DitaOtTargetResult[] = [];\n for (const [index, target] of opts.targets.entries()) {\n const logfile = join(scratch, `${String(index)}.json`);\n const args = [\n `--input=${target}`,\n \"--format=dita\",\n `--output=${join(scratch, `out-${String(index)}`)}`,\n `--temp=${join(scratch, `temp-${String(index)}`)}`,\n \"--logger=json\",\n `--logfile=${logfile}`,\n \"--verbose\",\n ];\n\n let result: DitaOtProcessResult;\n try {\n result = await spawn(launcher, args, { cwd: opts.cwd });\n } catch (err) {\n if (isEnoent(err)) {\n throw new LintError(\n opts.home === undefined ? NOT_ON_PATH : noLauncher(opts.home),\n );\n }\n throw err;\n }\n\n const messages = await readLog(logfile);\n\n /**\n * A non-zero exit has to be accounted for by something we are going to\n * report. Otherwise the run failed for a reason this log does not hold,\n * and saying \"no findings\" would be inventing a clean bill of health.\n *\n * The asymmetry is DITA-OT's. It exits **0** while logging `DOTX010E`\n * for a broken conref, so a zero exit proves nothing and the log is the\n * verdict. It also exits **1** on a map that is not well-formed, having\n * already written a perfectly readable log whose only coded entries are\n * `DOTJ030I` at `INFO`, with the fatal parse error in neither the log\n * nor stdout nor stderr. Trusting the parse of that log reported the\n * file as passing, which is the false green this guard exists for.\n */\n // Only an `error` accounts for it. A warning or a notice is something\n // DITA-OT says while finishing the job, and it exits 0 having said it,\n // so a non-zero exit alongside nothing worse than `DOTJ047I` is still a\n // failure this log does not explain.\n const explained =\n messages !== null && messages.some((m) => m.severity === \"error\");\n\n if (result.code !== 0 && !explained) {\n if (opts.home === undefined && !(await isOnPath(launcher))) {\n throw new LintError(NOT_ON_PATH);\n }\n throw new LintError(\n `DITA Open Toolkit failed on ${target}: ${failureText(result)}. ` +\n `Run it directly on that file to see why.`,\n );\n }\n\n results.push({ target, messages: messages ?? [] });\n }\n return results;\n } finally {\n await rm(scratch, { recursive: true, force: true });\n }\n}\n\n/**\n * Whether DITA-OT can run here, and at what version.\n *\n * Never throws: reporting what cannot run is `manni lint tools`' job, and a\n * probe that threw would take the whole table down with it. So a launcher\n * that fails to start for any reason (`ENOENT`, but also `EACCES` or `EPERM`)\n * is unavailable. A non-zero exit counts as unavailable too, because on\n * Windows a missing `dita.bat` is cmd.exe's exit code rather than an `ENOENT`.\n */\nexport async function probeDitaOt(\n ctx: ToolProbeContext,\n spawn: DitaOtSpawn = spawnDitaOt,\n): Promise<ToolProbe> {\n const home =\n ctx.tools === undefined\n ? undefined\n : ditaOtHome(ctx.tools, ctx.configDir ?? ctx.cwd);\n const launcher = ditaOtLauncher(home);\n if (home !== undefined && !(await isFile(launcher))) {\n return { available: false, version: null };\n }\n\n let result: DitaOtProcessResult;\n try {\n result = await spawn(launcher, [\"--version\"], { cwd: ctx.cwd });\n } catch {\n return { available: false, version: null };\n }\n if (result.code !== 0) return { available: false, version: null };\n return {\n available: true,\n // Both channels, because which one carries the banner is not fixed: the\n // shell launcher prints it on stdout, and the Windows `.bat` run through\n // `cmd.exe` has been seen to put it on stderr. Reading one would report a\n // toolkit that is plainly there as versionless on the other platform.\n version: parseDitaOtVersion(`${result.stdout}\\n${result.stderr}`),\n };\n}\n\n/**\n * DITA's one format row.\n *\n * `kinds` is empty and that is correct: DITA-OT does not parse into manni's\n * content model, and the column exists to say what a template rule can ask\n * about. `.xml` is listed because a DITA topic is often named that way; the\n * walk set is narrower on purpose - see `walkExtensions`.\n */\nconst DITA_FORMAT: FormatInfo = {\n name: \"dita\",\n label: \"DITA\",\n extensions: [\".ditamap\", \".dita\", \".xml\"],\n kinds: [],\n};\n\nexport const ditaOt: StructureToolDescriptor = {\n name: \"dita-ot\",\n label: \"DITA Open Toolkit\",\n formats: () => [{ ...DITA_FORMAT }],\n // Maps only. DITA-OT takes one `--input` per invocation, so walking\n // every topic in a docset would pay a JVM start per topic. Naming a `.dita`\n // explicitly still works: a named file bypasses the extension filter.\n walkExtensions: () => [\".ditamap\"],\n probe: (ctx) => probeDitaOt(ctx),\n // None. Every option manni owns is refused under this tool, stdin included.\n ownedOptions: [],\n};\n","/**\n * The tools that can perform a lint job, one descriptor each.\n *\n * Deliberately **not** a unified `run()` interface. The tools take completely\n * different inputs: manni's engine needs the templates, the doctype index and\n * the parser registry, and a tool outside the package needs none of them. A\n * lowest-common-denominator `run(files, options)` would either lie about what\n * it accepts or grow a passthrough blob nobody can type. `src/shared/tools.ts`\n * records the same decision for `tools:`, and for the same reason.\n *\n * What the tools genuinely share is what the *command* has to know before it\n * dispatches: the formats to list, the extensions a directory walk collects,\n * whether the tool can run here, and which options belong to the tool rather\n * than to the job. That is this table, and nothing more. Running a tool stays\n * with the branch that runs it.\n */\nimport pkg from \"../../../package.json\" with { type: \"json\" };\nimport { listFormats, supportedExtensions } from \"../parsers/index.js\";\nimport type { FormatInfo } from \"../commands/tools.js\";\nimport { TOOLS_BY_JOB, type LintTool } from \"../core/config.js\";\nimport type { ToolsConfig } from \"../../shared/tools.js\";\nimport { LintError } from \"../types.js\";\nimport { ditaOt } from \"./dita-ot.js\";\n\n/** What a probe is allowed to look at. A tool outside the package shells out. */\nexport interface ToolProbeContext {\n /** The directory the run resolves from. */\n cwd: string;\n /**\n * The family's top-level `tools:`, when a config declared one.\n *\n * A tool outside the package is found where its own `tools.<tool>` says it\n * is, so a probe that could not read it would report \"not available\" over an\n * installation the run itself goes on to use. manni's own probe ignores it.\n */\n tools?: ToolsConfig;\n /** The directory the config that declared `tools:` sits in. */\n configDir?: string;\n}\n\n/** Whether a tool can run here, and at what version. */\nexport interface ToolProbe {\n available: boolean;\n /** The version the tool reported, or null when it reported none. */\n version: string | null;\n}\n\nexport interface StructureToolDescriptor {\n name: LintTool;\n label: string;\n /** Formats for `manni lint tools`' formats column. */\n formats: () => FormatInfo[];\n /** Extensions a directory walk collects when this tool runs. */\n walkExtensions: () => string[];\n /** Whether this tool can run here, and at what version. */\n probe: (ctx: ToolProbeContext) => Promise<ToolProbe>;\n /**\n * Options that belong to this tool alone, spelled as a user types them.\n *\n * `-` is in the list because stdin is one of them: a tool that reads a\n * document off a pipe is answering the same question as one that reads\n * `--template`, and deriving both from one list is what keeps the next tool\n * from having to remember to refuse either.\n */\n ownedOptions: readonly string[];\n}\n\n/**\n * manni's own engine, which ships in this package.\n *\n * `formats` and `walkExtensions` forward to the parser registry rather than\n * restating it: a format added there is listed and walked here the moment it\n * is registered, which is the property `parsers/index.ts` exists to have.\n */\nconst manni: StructureToolDescriptor = {\n name: \"manni\",\n label: \"manni's own structure engine\",\n formats: () => listFormats(),\n walkExtensions: () => supportedExtensions(),\n // Nothing to look for: the engine is this package, so it is available\n // wherever the CLI is. A tool that shells out answers this by looking.\n probe: () => Promise.resolve({ available: true, version: pkg.version }),\n // `-` is stdin, and it is in this list so the derivation in `lint.ts` has\n // one place to learn that manni owns it. The refusal itself is not shaped\n // like the others, because \"option\" is the wrong word for a positional:\n // `assertOptionsOwned` special-cases it to say \"cannot read stdin\" instead.\n // A tool that copies this list without reading that guard gets the right\n // ownership and the wrong sentence.\n ownedOptions: [\"--template\", \"--templates\", \"--explain\", \"--as\", \"-\"],\n};\n\n/**\n * Registered statically rather than through `registerStructureTool` at import\n * time, so the table and `LINT_TOOLS` agree whatever order the modules load\n * in: a name in `LINT_TOOLS` with no descriptor is a `--tool` value the config\n * validates and the run cannot dispatch.\n */\nconst descriptors = new Map<string, StructureToolDescriptor>([\n [manni.name, manni],\n [ditaOt.name, ditaOt],\n]);\n\n/**\n * Add a descriptor, and hand back the undo.\n *\n * The registry is mutable so a second tool can be stood up without a name\n * being added to `LINT_TOOLS` first. A name there is a `--tool` value the CLI\n * accepts and the config validates, so adding one for a tool that cannot run\n * is the silent green this seam exists to prevent.\n */\nexport function registerStructureTool(\n descriptor: StructureToolDescriptor,\n): () => void {\n const previous = descriptors.get(descriptor.name);\n descriptors.set(descriptor.name, descriptor);\n return () => {\n if (previous === undefined) descriptors.delete(descriptor.name);\n else descriptors.set(descriptor.name, previous);\n };\n}\n\n/** The descriptor registered under `name`, if there is one. */\nexport function structureTool(\n name: string,\n): StructureToolDescriptor | undefined {\n return descriptors.get(name);\n}\n\n/** Every registered descriptor, in registration order. */\nexport function structureTools(): StructureToolDescriptor[] {\n return [...descriptors.values()];\n}\n\n/**\n * The descriptor for a named tool, or the refusal a user reads.\n *\n * The message is the one the CLI's own `--tool` guard produced before the\n * registry existed, because `runLint` is exported from `src/index.ts` and\n * called in process: a caller that never touches commander owes the same\n * sentence.\n */\nexport function resolveStructureTool(name: string): StructureToolDescriptor {\n const descriptor = descriptors.get(name);\n if (descriptor) return descriptor;\n throw new LintError(\n `Unknown --tool \"${name}\" for structure. Use ${TOOLS_BY_JOB.structure.join(\", \")}.`,\n );\n}\n"],"mappings":"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","names":["readFile","resolve","rebased","dirname","isAbsolute","parseYaml","AjvNs","Ajv","isRecord","ajv","Ajv","compiled","describeError","parseYaml","dirname","isAbsolute","readFile","join","join","readFile","origin","resolve","readFile"]}
|