@hawkeyexl/manni 4.3.1 → 4.4.1
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 +9 -1
- package/dist/batch-GFNLTGOH.js +36 -0
- package/dist/build-COYIQ25L.js +31 -0
- package/dist/calibrate-MDBLJKEE.js +31 -0
- package/dist/capture-LRML2PKV.js +32 -0
- package/dist/check-ASPIHA6B.js +40 -0
- package/dist/check-ASPIHA6B.js.map +1 -0
- package/dist/check-EDLBYL5N.js +27 -0
- package/dist/check-EDLBYL5N.js.map +1 -0
- package/dist/check-HYNGXYJV.js +18 -0
- package/dist/check-HYNGXYJV.js.map +1 -0
- package/dist/chunk-2B7ZNTPV.js +230 -0
- package/dist/chunk-2B7ZNTPV.js.map +1 -0
- package/dist/chunk-2H7UOFLK.js +11 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-37LP5HMZ.js +1042 -0
- package/dist/chunk-37LP5HMZ.js.map +1 -0
- package/dist/chunk-3VK37OR2.js +2957 -0
- package/dist/chunk-3VK37OR2.js.map +1 -0
- package/dist/chunk-4IUC2MNL.js +2165 -0
- package/dist/chunk-4IUC2MNL.js.map +1 -0
- package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
- package/dist/chunk-4TSWU2SU.js.map +1 -0
- package/dist/chunk-57NEQRVO.js +10921 -0
- package/dist/chunk-57NEQRVO.js.map +1 -0
- package/dist/chunk-6JSSEWBG.js +6737 -0
- package/dist/chunk-6JSSEWBG.js.map +1 -0
- package/dist/chunk-6OYEI2VV.js +88 -0
- package/dist/chunk-6OYEI2VV.js.map +1 -0
- package/dist/chunk-77AINBYM.js +19 -0
- package/dist/chunk-77AINBYM.js.map +1 -0
- package/dist/chunk-7UKO3ASZ.js +54 -0
- package/dist/chunk-7UKO3ASZ.js.map +1 -0
- package/dist/chunk-A5ULMVHQ.js +164 -0
- package/dist/chunk-A5ULMVHQ.js.map +1 -0
- package/dist/chunk-C4SQM22E.js +299 -0
- package/dist/chunk-C4SQM22E.js.map +1 -0
- package/dist/chunk-CK7SKVHR.js +1042 -0
- package/dist/chunk-CK7SKVHR.js.map +1 -0
- package/dist/{chunk-G5CM2VGQ.js → chunk-DXNZS72W.js} +8 -94
- package/dist/chunk-DXNZS72W.js.map +1 -0
- package/dist/chunk-EPH54CAY.js +374 -0
- package/dist/chunk-EPH54CAY.js.map +1 -0
- package/dist/{chunk-KJR7OVDX.js → chunk-FBEQCK47.js} +56 -137
- package/dist/chunk-FBEQCK47.js.map +1 -0
- package/dist/{chunk-IOYXPBCL.js → chunk-G3T47CFV.js} +1 -7
- package/dist/{chunk-IOYXPBCL.js.map → chunk-G3T47CFV.js.map} +1 -1
- package/dist/chunk-GCQRPUSB.js +1287 -0
- package/dist/chunk-GCQRPUSB.js.map +1 -0
- package/dist/chunk-GLP5PSXB.js +72 -0
- package/dist/chunk-GLP5PSXB.js.map +1 -0
- package/dist/chunk-I2AH3CJJ.js +1192 -0
- package/dist/chunk-I2AH3CJJ.js.map +1 -0
- package/dist/chunk-IDV6HJCB.js +388 -0
- package/dist/chunk-IDV6HJCB.js.map +1 -0
- package/dist/chunk-IKEWBMCN.js +98 -0
- package/dist/chunk-IKEWBMCN.js.map +1 -0
- package/dist/chunk-JCDXE76J.js +52 -0
- package/dist/chunk-JCDXE76J.js.map +1 -0
- package/dist/chunk-JXLTXMJ7.js +455 -0
- package/dist/chunk-JXLTXMJ7.js.map +1 -0
- package/dist/chunk-LJ2RDH3F.js +49 -0
- package/dist/chunk-LJ2RDH3F.js.map +1 -0
- package/dist/chunk-M2JCK5F6.js +805 -0
- package/dist/chunk-M2JCK5F6.js.map +1 -0
- package/dist/chunk-M3W7J6MU.js +4398 -0
- package/dist/chunk-M3W7J6MU.js.map +1 -0
- package/dist/chunk-MMTSSTG7.js +23 -0
- package/dist/chunk-MMTSSTG7.js.map +1 -0
- package/dist/chunk-MNH7QO4Z.js +15 -0
- package/dist/chunk-MNH7QO4Z.js.map +1 -0
- package/dist/chunk-MWJLZMBL.js +934 -0
- package/dist/chunk-MWJLZMBL.js.map +1 -0
- package/dist/chunk-MYMBCTDE.js +387 -0
- package/dist/chunk-MYMBCTDE.js.map +1 -0
- package/dist/chunk-NCPWXHDO.js +683 -0
- package/dist/chunk-NCPWXHDO.js.map +1 -0
- package/dist/chunk-NLXTXE5Y.js +1661 -0
- package/dist/chunk-NLXTXE5Y.js.map +1 -0
- package/dist/{chunk-CFDUZRM2.js → chunk-NXV7WE2D.js} +32 -240
- package/dist/chunk-NXV7WE2D.js.map +1 -0
- package/dist/chunk-OWBRDNRZ.js +16 -0
- package/dist/chunk-OWBRDNRZ.js.map +1 -0
- package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
- package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
- package/dist/chunk-PCE7QHXJ.js +68 -0
- package/dist/chunk-PCE7QHXJ.js.map +1 -0
- package/dist/chunk-PIKOVN6W.js +1651 -0
- package/dist/chunk-PIKOVN6W.js.map +1 -0
- package/dist/chunk-PNPQTHBT.js +17 -0
- package/dist/chunk-PNPQTHBT.js.map +1 -0
- package/dist/chunk-PU6DFJMY.js +1975 -0
- package/dist/chunk-PU6DFJMY.js.map +1 -0
- package/dist/chunk-Q35YIDBV.js +1202 -0
- package/dist/chunk-Q35YIDBV.js.map +1 -0
- package/dist/{chunk-5HNHEUD5.js → chunk-QQUKGCAJ.js} +3 -3
- package/dist/chunk-QRWGPQKO.js +198 -0
- package/dist/chunk-QRWGPQKO.js.map +1 -0
- package/dist/{chunk-UBMMXS2Y.js → chunk-QZ6LRSNV.js} +69 -48
- package/dist/{chunk-UBMMXS2Y.js.map → chunk-QZ6LRSNV.js.map} +1 -1
- package/dist/chunk-RL5XWQOL.js +3872 -0
- package/dist/chunk-RL5XWQOL.js.map +1 -0
- package/dist/chunk-RNVYGWIC.js +30 -0
- package/dist/chunk-RNVYGWIC.js.map +1 -0
- package/dist/chunk-SE4JN5XN.js +19 -0
- package/dist/chunk-SE4JN5XN.js.map +1 -0
- package/dist/chunk-SKSDRUUK.js +622 -0
- package/dist/chunk-SKSDRUUK.js.map +1 -0
- package/dist/chunk-TIUTIOFB.js +25 -0
- package/dist/chunk-TIUTIOFB.js.map +1 -0
- package/dist/chunk-TK3FC4RF.js +3090 -0
- package/dist/chunk-TK3FC4RF.js.map +1 -0
- package/dist/chunk-TU2WUBVZ.js +2 -0
- package/dist/chunk-TU2WUBVZ.js.map +1 -0
- package/dist/chunk-TZOYZK2E.js +98 -0
- package/dist/chunk-TZOYZK2E.js.map +1 -0
- package/dist/chunk-UJCVAWP7.js +178 -0
- package/dist/chunk-UJCVAWP7.js.map +1 -0
- package/dist/chunk-UKZNLVJW.js +17 -0
- package/dist/chunk-UKZNLVJW.js.map +1 -0
- package/dist/{chunk-HAPTXWU2.js → chunk-UQISLDAV.js} +4 -4
- package/dist/chunk-WB3QM6PP.js +5440 -0
- package/dist/chunk-WB3QM6PP.js.map +1 -0
- package/dist/chunk-YB4IBW3M.js +96 -0
- package/dist/chunk-YB4IBW3M.js.map +1 -0
- package/dist/chunk-YEUYKR62.js +72 -0
- package/dist/chunk-YEUYKR62.js.map +1 -0
- package/dist/chunk-YJOGUXGP.js +634 -0
- package/dist/chunk-YJOGUXGP.js.map +1 -0
- package/dist/chunk-YNHZHSSM.js +41 -0
- package/dist/chunk-YNHZHSSM.js.map +1 -0
- package/dist/chunk-YTURO7SI.js +979 -0
- package/dist/chunk-YTURO7SI.js.map +1 -0
- package/dist/chunk-ZXRAE55H.js +31 -0
- package/dist/chunk-ZXRAE55H.js.map +1 -0
- package/dist/cli-3BIBYHFK.js +473 -0
- package/dist/cli-3BIBYHFK.js.map +1 -0
- package/dist/cli-673OTRGE.js +28 -0
- package/dist/cli-673OTRGE.js.map +1 -0
- package/dist/cli-6HIDIFVT.js +1203 -0
- package/dist/cli-6HIDIFVT.js.map +1 -0
- package/dist/cli-6UB5N6CU.js +232 -0
- package/dist/cli-6UB5N6CU.js.map +1 -0
- package/dist/cli-LFP5ZGYS.js +346 -0
- package/dist/cli-LFP5ZGYS.js.map +1 -0
- package/dist/cli-MN7PYMBI.js +552 -0
- package/dist/cli-MN7PYMBI.js.map +1 -0
- package/dist/cli-PYPX6O6E.js +739 -0
- package/dist/cli-PYPX6O6E.js.map +1 -0
- package/dist/cli-RPM5PELX.js +626 -0
- package/dist/cli-RPM5PELX.js.map +1 -0
- package/dist/cli-VI7VHWNZ.js +423 -0
- package/dist/cli-VI7VHWNZ.js.map +1 -0
- package/dist/cli.d.ts +12 -2
- package/dist/cli.js +109 -7661
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +15 -3
- package/dist/docmeta.js.map +1 -1
- package/dist/github-6QMYOHC3.js +27 -0
- package/dist/github-6QMYOHC3.js.map +1 -0
- package/dist/github-SXPGP5DB.js +32 -0
- package/dist/github-SXPGP5DB.js.map +1 -0
- package/dist/index.d.ts +65 -3
- package/dist/index.js +407 -337
- package/dist/index.js.map +1 -1
- package/dist/json-7WV7KL6H.js +17 -0
- package/dist/json-7WV7KL6H.js.map +1 -0
- package/dist/json-SY7KLNC7.js +35 -0
- package/dist/json-SY7KLNC7.js.map +1 -0
- package/dist/lint-ASMJLLPA.js +28 -0
- package/dist/lint-ASMJLLPA.js.map +1 -0
- package/dist/list-KZNVTNVC.js +31 -0
- package/dist/list-KZNVTNVC.js.map +1 -0
- package/dist/list-Q57YTFUQ.js +29 -0
- package/dist/list-Q57YTFUQ.js.map +1 -0
- package/dist/page-KSGV5RD4.js +41 -0
- package/dist/page-KSGV5RD4.js.map +1 -0
- package/dist/picker-3XF36MJF.js +14 -0
- package/dist/picker-3XF36MJF.js.map +1 -0
- package/dist/pretty-A5EGTN35.js +37 -0
- package/dist/pretty-A5EGTN35.js.map +1 -0
- package/dist/pretty-CXSDXNAS.js +43 -0
- package/dist/pretty-CXSDXNAS.js.map +1 -0
- package/dist/reporters-HUY7UXFS.js +42 -0
- package/dist/reporters-HUY7UXFS.js.map +1 -0
- package/dist/reporters-K2VUKSAK.js +71 -0
- package/dist/reporters-K2VUKSAK.js.map +1 -0
- package/dist/reporters-M6LJ2IYU.js +21 -0
- package/dist/reporters-M6LJ2IYU.js.map +1 -0
- package/dist/run-3DYJNRIG.js +33 -0
- package/dist/run-3DYJNRIG.js.map +1 -0
- package/dist/term-BIQUPMOR.js +1592 -0
- package/dist/term-BIQUPMOR.js.map +1 -0
- package/dist/types-Y6TXHM7M.js +10 -0
- package/dist/types-Y6TXHM7M.js.map +1 -0
- package/dist/validate-53RKM2XG.js +16 -0
- package/dist/validate-53RKM2XG.js.map +1 -0
- package/package.json +1 -1
- package/plugin/manni/.claude-plugin/plugin.json +9 -0
- package/plugin/manni/hooks/hooks.json +69 -0
- package/plugin/manni/hooks/manni.mjs +157 -0
- package/plugin/manni/skills/check/SKILL.md +20 -0
- package/plugin/manni/skills/evals/SKILL.md +27 -0
- package/plugin/manni/skills/fix/SKILL.md +56 -0
- package/plugin/manni/skills/setup/SKILL.md +41 -0
- package/dist/batch-A5HZKYIW.js +0 -18
- package/dist/calibrate-S5K6QHUY.js +0 -13
- package/dist/capture-OZEZDABQ.js +0 -15
- package/dist/chunk-3R23C5DT.js.map +0 -1
- package/dist/chunk-CFDUZRM2.js.map +0 -1
- package/dist/chunk-G5CM2VGQ.js.map +0 -1
- package/dist/chunk-KJR7OVDX.js.map +0 -1
- package/dist/chunk-V4KUVE47.js +0 -27170
- package/dist/chunk-V4KUVE47.js.map +0 -1
- package/dist/chunk-ZQBTK3DO.js +0 -26518
- package/dist/chunk-ZQBTK3DO.js.map +0 -1
- package/dist/picker-6HRMETE7.js +0 -12
- package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
- package/plugin/tracevals/hooks/hooks.json +0 -17
- /package/dist/{batch-A5HZKYIW.js.map → batch-GFNLTGOH.js.map} +0 -0
- /package/dist/{calibrate-S5K6QHUY.js.map → build-COYIQ25L.js.map} +0 -0
- /package/dist/{capture-OZEZDABQ.js.map → calibrate-MDBLJKEE.js.map} +0 -0
- /package/dist/{picker-6HRMETE7.js.map → capture-LRML2PKV.js.map} +0 -0
- /package/dist/{chunk-5HNHEUD5.js.map → chunk-QQUKGCAJ.js.map} +0 -0
- /package/dist/{chunk-HAPTXWU2.js.map → chunk-UQISLDAV.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/meta/reporters/rule-id.ts","../src/meta/reporters/sarif.ts","../src/shared/xml.ts","../src/meta/reporters/junit.ts","../src/meta/reporters/index.ts"],"sourcesContent":["/**\n * A violation's **rule identity**, shared by the SARIF `ruleId` and the JUnit\n * `<failure type>`.\n *\n * Built from `schema` + `keyword` and nothing else. `message` is Ajv-generated\n * prose, so a rule id derived from it would rename itself on any Ajv upgrade\n * and every historical alert in the consumer would close and reopen as new.\n * That is the same reasoning `src/core/baseline.ts` applies to fingerprints,\n * and for the same reason: these are the durable half of a violation.\n */\nimport type { FieldError } from \"../types.js\";\nimport { canonicalSchemaRef, type FingerprintContext } from \"../core/baseline.js\";\n\n/** The `schema` label the command layer stamps on its own failures. */\nconst SYNTHETIC_SCHEMA = \"(parse)\";\n\n/** A document whose metadata block could not be parsed. */\nexport const PARSE_ERROR_RULE = \"manni/parse-error\";\n\n/** A document no schema set could be resolved for. */\nexport const SCHEMA_ERROR_RULE = \"manni/schema-error\";\n\n/**\n * A document carrying a key a manifest owns (proposal 0037). Not a\n * synthetic-schema special case: `external:owned` is a builtin-shaped ref\n * like `check:<name>`, so the ordinary join produces this id.\n */\nexport const EXTERNAL_OWNED_RULE = \"external:owned/external\";\n\n/**\n * Two documents carry the same value of a manifest's join field (proposal\n * 0039), so one manifest entry matched both.\n */\nexport const EXTERNAL_DUPLICATE_RULE = \"external:duplicate/external\";\n\n/**\n * A property its schema marks `x-manni-encrypt` holds a plain value\n * (proposal 0045). Builtin-shaped like `external:owned`.\n */\nexport const ENCRYPTED_PLAIN_RULE = \"encrypted:plain/encrypted\";\n\n/**\n * A marked property holds a ciphertext that does not decrypt under the\n * current key (proposal 0045).\n */\nexport const ENCRYPTED_UNREADABLE_RULE = \"encrypted:unreadable/encrypted\";\n\n/**\n * A managed field whose asserted value differs from the derived one, or is\n * missing while a source has one. `derived:stale` is builtin-shaped like the\n * external refs, so the ordinary join produces this id too.\n */\nexport const DERIVED_STALE_RULE = \"derived:stale/derived\";\n\n/**\n * A key the page holds that its schemas mark `x-manni-location: external`\n * (proposal 0047). A warning, builtin-shaped like `external:owned`.\n */\nexport const LOCATION_EXTERNAL_RULE = \"location:external/location\";\n\n/**\n * A key a manifest supplies that its schemas mark `x-manni-location: page`\n * (proposal 0047). A warning, filed at the manifest line.\n */\nexport const LOCATION_PAGE_RULE = \"location:page/location\";\n\n/** What each reserved rule means, for the consumer's rule listing. */\nexport const RESERVED_RULES: Record<string, string> = {\n [LOCATION_EXTERNAL_RULE]:\n \"The page stores a key its schema prefers external metadata for; run `manni meta relocate` to move it to the collection's manifest.\",\n [LOCATION_PAGE_RULE]:\n \"A manifest stores a key its schema prefers the page for; run `manni meta relocate` to move it into the page.\",\n [ENCRYPTED_PLAIN_RULE]:\n \"A property the schema marks x-manni-encrypt holds a plain value; the page must hold it encrypted.\",\n [ENCRYPTED_UNREADABLE_RULE]:\n \"A property the schema marks x-manni-encrypt holds a value that does not decrypt under the current key.\",\n [PARSE_ERROR_RULE]: \"The document's metadata block could not be parsed.\",\n [SCHEMA_ERROR_RULE]: \"No schema set could be resolved for the document.\",\n [EXTERNAL_OWNED_RULE]:\n \"The document carries a key a manifest owns; the manifest is the only place that key may be set.\",\n [EXTERNAL_DUPLICATE_RULE]:\n \"Two documents carry the same value of a manifest's join field, so one manifest entry matched both.\",\n [DERIVED_STALE_RULE]:\n \"A managed field whose value differs from what git, CODEOWNERS or the GitHub or GitLab review record says; run `manni meta derive` to refresh it.\",\n};\n\n/**\n * The rule id for one violation.\n *\n * docmeta's own failures carry `schema: \"(parse)\"` and no real keyword, so the\n * naive join yields `(parse)/parse` — a rule id that names an implementation\n * detail and reads as garbage in a security tab. They get reserved ids instead.\n *\n * With a `frame`, the schema reference is canonicalized exactly as a\n * fingerprint's is, so a local-file schema does not produce one rule id from\n * the repository root and a different, machine-specific one from `docs/`.\n */\nexport function ruleIdFor(e: FieldError, frame?: FingerprintContext): string {\n if (e.schema === SYNTHETIC_SCHEMA) {\n if (e.keyword === \"parse\") return PARSE_ERROR_RULE;\n if (e.keyword === \"schema\") return SCHEMA_ERROR_RULE;\n }\n return `${canonicalSchemaRef(e.schema, frame)}/${e.keyword}`;\n}\n\n/** `\"\"` is the whole document; every reporter spells that the same way. */\nexport function fieldLabel(instancePath: string): string {\n return instancePath === \"\" ? \"(root)\" : instancePath;\n}\n","/**\n * SARIF 2.1.0 — the interchange format GitHub code scanning, GitLab, and Azure\n * DevOps ingest directly.\n *\n * What `--format github` cannot do is persist: `::error` workflow commands\n * render on the pull request and then vanish with the job log. SARIF findings\n * become tracked alerts with state across commits, which is what makes \"when\n * did this regress\" and \"is metadata debt trending down\" answerable at all.\n *\n * Three properties carry that tracking, and each has a trap:\n *\n * - **`artifactLocation.uri` must be repository-root-relative.** GitHub\n * resolves it against the repository root and *silently drops* results that\n * do not resolve — the upload succeeds, with zero alerts. `ValidationResult`\n * labels are relative to whatever the run resolved against, which is only\n * coincidentally the repository root, so they are rebased here.\n * - **`ruleId` must not be built from prose.** See `./rule-id.ts`.\n * - **`partialFingerprints` must match the baseline's fingerprint.** One\n * identity function, two consumers.\n */\nimport { isAbsolute } from \"node:path\";\nimport pkg from \"../../../package.json\" with { type: \"json\" };\nimport {\n isErrorSeverity,\n type FieldError,\n type ValidationResult,\n} from \"../types.js\";\nimport {\n canonicalFilePath,\n canonicalSchemaRef,\n fingerprint,\n type FingerprintContext,\n} from \"../core/baseline.js\";\nimport { findGitRoot } from \"../core/config.js\";\n// One definition, in core: commands set this label and reporters test for it,\n// so two copies would drift and a `<stdin>` result would acquire an\n// `artifactLocation.uri` naming a file that does not exist.\nimport { STDIN_LABEL } from \"../core/load-files.js\";\nimport { RESERVED_RULES, fieldLabel, ruleIdFor } from \"./rule-id.js\";\n\n/** Where a consumer is sent to read about docmeta itself. */\nconst INFORMATION_URI = \"https://hawkeyexl.github.io/manni/meta/\";\n\n/** Where a consumer is sent to read about one finding. */\nconst HELP_URI = \"https://hawkeyexl.github.io/manni/meta/fix/\";\n\n/**\n * The property-bag key GitHub keys alert identity on. Versioned deliberately,\n * and **not renamed with the tool**: the value is what lets an alert opened by\n * a docmeta run stay the same alert under manni. A new key would re-open every\n * finding once; that is what `/v2` is for, should the fingerprint ever change.\n */\nconst FINGERPRINT_KEY = \"docmetaViolation/v1\";\n\n/** Not a path anyone can resolve, so it can never be an artifact location. */\n// Imported rather than redeclared: two independent copies of this literal\n// would drift silently, and the failure mode is a `<stdin>` result acquiring an\n// `artifactLocation.uri` that names no file.\n\n/**\n * Said once, on stderr, when SARIF paths could not be made\n * repository-root-relative — the `GITIGNORE_UNAVAILABLE` precedent. Degrading\n * in silence is the wrong failure here: a uri that does not resolve produces a\n * *successful* upload with no alerts, so nothing downstream ever complains.\n */\nexport const SARIF_NO_GIT_ROOT =\n \"SARIF paths could not be made repository-root-relative: no git repository was found here. GitHub code scanning resolves artifactLocation.uri against the repository root and drops results that do not resolve, so an upload may report no findings at all.\";\n\nexport interface SarifOptions {\n /** The run's path frame. Without one, `file` labels are emitted as they are. */\n frame?: FingerprintContext;\n /** Diagnostics for the user; the CLI writes these to stderr. */\n onNotice?: (message: string) => void;\n}\n\ntype SarifLevel = \"error\" | \"warning\" | \"note\";\n\n/**\n * SARIF's word for a finding's weight. The family's `error` and `warning`\n * are SARIF's own; its `notice` is SARIF's `note`. An absent severity is\n * `error`, as everywhere (`isErrorSeverity`).\n */\nfunction sarifLevel(e: Parameters<typeof isErrorSeverity>[0]): SarifLevel {\n if (isErrorSeverity(e)) return \"error\";\n return e.severity === \"notice\" ? \"note\" : \"warning\";\n}\n\ninterface SarifRule {\n id: string;\n shortDescription: { text: string };\n helpUri: string;\n}\n\ninterface SarifResult {\n ruleId: string;\n level: SarifLevel;\n message: { text: string };\n locations: {\n physicalLocation: {\n artifactLocation: { uri: string };\n region?: { startLine: number };\n };\n }[];\n partialFingerprints: Record<string, string>;\n}\n\n/**\n * What a rule *is*, in one line.\n *\n * Only the reserved rules get bespoke prose; everything else is named by the\n * schema and keyword that produced it, which is all docmeta actually knows. A\n * per-keyword phrasebook would be prose docmeta would then have to keep in step\n * with Ajv's own vocabulary for no consumer benefit.\n */\nfunction describeRule(id: string, e: FieldError, schemaRef: string): string {\n return (\n RESERVED_RULES[id] ??\n `Schema \"${schemaRef}\" rejected the document on its \"${e.keyword}\" keyword.`\n );\n}\n\n/**\n * Said when a result had to be dropped because its path lies outside the\n * repository.\n *\n * Dropping is the only truthful option — GitHub would drop it anyway, having\n * failed to resolve it — but doing so in silence reproduces the exact failure\n * this reporter exists to prevent: an upload that succeeds while reporting\n * fewer findings than the run actually made.\n */\nexport const sarifDroppedNotice = (n: number): string =>\n `${n} SARIF ${n === 1 ? \"finding lies\" : \"findings lie\"} outside the repository and ${n === 1 ? \"was\" : \"were\"} omitted: an artifactLocation.uri that leaves the repository root cannot be resolved by a code-scanning consumer.`;\n\n/**\n * The frame `artifactLocation.uri` is measured against.\n *\n * Same rebase `canonicalFilePath` already performs for baseline keys, with\n * `base` swapped for the repository root. Returns null when there is nothing to\n * rebase against, which the caller reports rather than papers over.\n */\nfunction uriFrame(frame: FingerprintContext): FingerprintContext | null {\n const root = findGitRoot(frame.cwd);\n return root === null ? null : { ...frame, base: root };\n}\n\n/**\n * A uri SARIF accepts: relative, posix, and never climbing out of the\n * repository.\n *\n * A file above the repository root rebases to `../…`, which SARIF permits but\n * GitHub cannot resolve. There is nothing truthful to emit for it, so the\n * result is dropped rather than pointed at a path that does not exist — the\n * same reasoning that skips `<stdin>`.\n */\nfunction artifactUri(\n file: string,\n frame: FingerprintContext | undefined,\n): string | null {\n const uri = canonicalFilePath(file, frame);\n // An absolute path is not a repository-relative uri either. `relative()`\n // returns one when the two paths share no root, which on Windows is any\n // checkout and temp directory on different drives.\n if (uri === \"\" || uri.startsWith(\"../\") || isAbsolute(uri)) return null;\n return uri;\n}\n\nexport function renderSarif(\n results: ValidationResult[],\n opts: SarifOptions = {},\n): string {\n const { frame, onNotice } = opts;\n\n // Two frames, deliberately: fingerprints stay measured against the config's\n // directory (so a baseline recorded from anywhere matches), while uris are\n // measured against the repository root (so GitHub can resolve them).\n const rebased = frame ? uriFrame(frame) : undefined;\n if (frame && rebased === null) onNotice?.(SARIF_NO_GIT_ROOT);\n // With no repository there is no root to measure from, and falling back to\n // `frame` would measure against the *config's* directory — which for a config\n // outside the tree being validated yields `../…` for every file, and\n // `artifactUri` drops those. That turns \"no git repo\" into \"no findings at\n // all\". Measure from where the run actually resolved its inputs instead: the\n // paths are no longer repository-relative, which the notice above already\n // says, but they are at least the paths the user typed.\n const pathFrame =\n rebased ?? (frame ? { ...frame, base: frame.runBase ?? frame.cwd } : undefined);\n\n const rules = new Map<string, SarifRule>();\n const sarifResults: SarifResult[] = [];\n let dropped = 0;\n\n for (const r of results) {\n if (r.errors.length === 0) continue;\n // `<stdin>` is not a path; nothing is lost by leaving it out, so it is not\n // counted as a drop.\n if (r.file === STDIN_LABEL) continue;\n const docUri = artifactUri(r.file, pathFrame);\n\n for (const e of r.errors) {\n // A value a manifest supplied is located in the manifest (proposal\n // 0037), which may be the one file of the run that *is* inside the\n // repository when the documents are not, or the reverse — so each\n // finding resolves its own uri, and is dropped on its own.\n const uri = e.file != null ? artifactUri(e.file, pathFrame) : docUri;\n if (uri === null) {\n dropped += 1;\n continue;\n }\n const ruleId = ruleIdFor(e, frame);\n if (!rules.has(ruleId)) {\n rules.set(ruleId, {\n id: ruleId,\n shortDescription: {\n text: describeRule(ruleId, e, canonicalSchemaRef(e.schema, frame)),\n },\n helpUri: HELP_URI,\n });\n }\n sarifResults.push({\n ruleId,\n // `FieldError.severity` maps onto SARIF's triage axis directly. Meta's\n // own validation never sets it, so its findings are all `error`; the\n // `warning` and `note` levels are for a sibling tool's advisory\n // findings (#78).\n level: sarifLevel(e),\n message: { text: `${fieldLabel(e.instancePath)} ${e.message}` },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri },\n // SARIF requires `startLine >= 1`. `0` is schema-invalid and `1`\n // silently mislocates the finding at the top of the file, so an\n // unknown line omits the region and renders file-level, which is\n // true. `startColumn` is deliberately still absent: html and xml\n // now populate `col`, but the rest of the region work (endLine,\n // endColumn, snippet) belongs to 0003, and a lone startColumn\n // would be the half of it that misleads.\n ...(e.line != null ? { region: { startLine: e.line } } : {}),\n },\n },\n ],\n partialFingerprints: { [FINGERPRINT_KEY]: fingerprint(e, frame) },\n });\n }\n }\n\n if (dropped > 0) onNotice?.(sarifDroppedNotice(dropped));\n\n return `${JSON.stringify(\n {\n $schema: \"https://json.schemastore.org/sarif-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"manni\",\n version: pkg.version,\n informationUri: INFORMATION_URI,\n // Only rules this run actually hit. Enumerating every keyword of\n // every schema would mean compiling and walking each one, for no\n // consumer benefit.\n rules: [...rules.values()],\n },\n },\n results: sarifResults,\n },\n ],\n },\n null,\n 2,\n )}`;\n}\n","/**\n * XML text escaping, shared by every reporter that writes XML.\n *\n * Moved here from meta's JUnit writer when tracevals needed it too, so the\n * family keeps one escaping rule rather than one per tool.\n */\n\nconst XML_ENTITIES: Record<string, string> = {\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n};\n\n/**\n * Escape every XML metacharacter, and drop the control characters XML 1.0\n * forbids outright.\n *\n * `>` and `'` do not strictly need escaping in every position, but escaping all\n * five unconditionally means no caller has to know which position it is in —\n * which is precisely the reasoning a partial escaper gets wrong. The control\n * character strip is separate: those cannot be represented in XML 1.0 at all,\n * escaped or not, so a stray one makes the whole document unparseable rather\n * than merely mis-rendered. Tab, newline, and carriage return are legal, and\n * kept.\n */\n/**\n * XML 1.0's `Char` production, in full:\n *\n * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]\n *\n * C0 controls are the familiar exclusion, but they are not the only one: a lone\n * surrogate half and the noncharacters U+FFFE/U+FFFF are equally illegal, and\n * both sit in a JavaScript string quite happily — a schema-authored `pattern`\n * or a filename can carry either. Iterating with `for…of` yields whole code\n * points, so a *well-formed* surrogate pair arrives as one astral character\n * (>= U+10000) and is kept; only an unpaired half falls in the D800-DFFF gap\n * and is dropped.\n */\nfunction isXmlChar(code: number): boolean {\n return (\n code === 0x9 ||\n code === 0xa ||\n code === 0xd ||\n (code >= 0x20 && code <= 0xd7ff) ||\n (code >= 0xe000 && code <= 0xfffd) ||\n (code >= 0x10000 && code <= 0x10ffff)\n );\n}\n\nexport function xmlEscape(value: string): string {\n let out = \"\";\n for (const ch of value) {\n const code = ch.codePointAt(0) ?? 0;\n if (!isXmlChar(code)) continue;\n out += XML_ENTITIES[ch] ?? ch;\n }\n return out;\n}\n","/**\n * JUnit XML — what CI systems parse for the \"Tests\" tab.\n *\n * There is no authoritative JUnit schema; Jenkins, GitLab, CircleCI, and Azure\n * each accept a different superset. This writer sticks to the attributes all of\n * them honor (`name`, `tests`, `failures`, `errors`, `classname`, `type`,\n * `message`) and avoids the contested ones — `time`, which would be meaningless\n * here, `system-out`, and nested suites. That is a compatibility judgement, not\n * a specification.\n *\n * **One `<testcase>` per file, one `<failure>` per violation.** So the tab reads\n * \"2 tests, 1 failed\" and matches `2 files checked, 1 failed`.\n * Violation-as-testcase would make the test count rise and fall with document\n * quality, which reads as a suite someone broke.\n *\n * **A warning is not a `<failure>`.** JUnit has no level below failure, and a\n * `<failure>` that did not fail the run would make the tab disagree with the\n * exit code. A warning-only file is a passing testcase; the warning is still\n * in the JSON and SARIF output, which have somewhere to put it.\n *\n * Escaping is the one thing a hand-rolled writer gets wrong. Messages carry\n * schema-authored text — a `pattern` regex may hold `<`, `&`, and quotes — and\n * paths can hold `&`. Every attribute value goes through `xmlEscape`; nothing\n * is interpolated raw.\n */\nimport type { FingerprintContext } from \"../core/baseline.js\";\nimport { isErrorSeverity, type ValidationResult } from \"../types.js\";\nimport { fieldLabel, ruleIdFor } from \"./rule-id.js\";\nimport { xmlEscape } from \"../../shared/xml.js\";\n\nexport { xmlEscape };\n\n/** Suite and classname. One suite per run; nested suites are not portable. */\nconst SUITE_NAME = \"manni\";\nconst CLASS_NAME = \"manni.validate\";\n\nconst attr = (name: string, value: string): string =>\n ` ${name}=\"${xmlEscape(value)}\"`;\n\nexport interface JunitOptions {\n /**\n * The `classname` each `<testcase>` carries: which docmeta command produced\n * these findings. Defaults to `docmeta.validate`, the only producer before\n * proposal 0026 made `query --check` a second one — whose findings must not\n * ship under validate's name.\n */\n classname?: string;\n /**\n * The run's path frame, used only to canonicalize a local-file schema ref in\n * `<failure type>`.\n *\n * Without it the attribute carries the ref exactly as the run received it —\n * `./my.schema.json` from the repo root, `../my.schema.json` from a\n * subdirectory, or a machine-absolute path once config discovery has rebased\n * it. SARIF's `ruleId` is already canonical, so a consumer correlating the two\n * for one run would find them disagreeing on the same violation.\n */\n frame?: FingerprintContext;\n}\n\nexport function renderJunit(\n results: ValidationResult[],\n opts: JunitOptions = {},\n): string {\n const tests = results.length;\n const failures = results.filter((r) => r.errors.some(isErrorSeverity)).length;\n\n const counts =\n attr(\"name\", SUITE_NAME) +\n attr(\"tests\", String(tests)) +\n attr(\"failures\", String(failures)) +\n // Every violation is a `<failure>`; `errors` is reserved for a test that\n // could not run, which has no analogue here. Emitted as 0 rather than\n // omitted, because consumers read the attribute and show a blank column\n // without it.\n attr(\"errors\", \"0\");\n\n const lines: string[] = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n `<testsuites${counts}>`,\n ` <testsuite${counts}>`,\n ];\n\n const classname = opts.classname ?? CLASS_NAME;\n for (const r of results) {\n const open = ` <testcase${attr(\"name\", r.file)}${attr(\"classname\", classname)}`;\n const failing = r.errors.filter(isErrorSeverity);\n if (failing.length === 0) {\n // Self-closing: a passing test has nothing to carry.\n lines.push(`${open}/>`);\n continue;\n }\n lines.push(`${open}>`);\n for (const e of failing) {\n const where =\n e.file != null\n ? ` (${e.file}${e.line != null ? `:${e.line}` : \"\"})`\n : e.line != null\n ? ` (line ${e.line})`\n : \"\";\n lines.push(\n ` <failure${attr(\"type\", ruleIdFor(e, opts.frame))}${attr(\n \"message\",\n `${fieldLabel(e.instancePath)} ${e.message}${where}`,\n )}/>`,\n );\n }\n lines.push(\" </testcase>\");\n }\n\n lines.push(\" </testsuite>\", \"</testsuites>\");\n return lines.join(\"\\n\");\n}\n","/**\n * Reporters render validation results to a string. The command layer writes\n * the result to stdout; diagnostics go to stderr separately.\n */\nimport {\n DocmetaError,\n isErrorSeverity,\n type BaselineSummary,\n type RunSummary,\n type ValidationResult,\n} from \"../types.js\";\nimport type { FingerprintContext } from \"../core/baseline.js\";\nimport { palette } from \"../../shared/color.js\";\nimport { fieldLabel } from \"./rule-id.js\";\nimport { renderSarif, type SarifOptions } from \"./sarif.js\";\nimport { renderJunit, type JunitOptions } from \"./junit.js\";\nimport {\n escapeWorkflowCommandMessage,\n escapeWorkflowCommandProperty,\n} from \"./github.js\";\n\nexport { renderSarif, SARIF_NO_GIT_ROOT } from \"./sarif.js\";\nexport type { SarifOptions } from \"./sarif.js\";\nexport { renderJunit, xmlEscape } from \"./junit.js\";\nexport type { JunitOptions } from \"./junit.js\";\nexport {\n escapeWorkflowCommandMessage,\n escapeWorkflowCommandProperty,\n} from \"./github.js\";\nexport {\n PARSE_ERROR_RULE,\n SCHEMA_ERROR_RULE,\n ruleIdFor,\n} from \"./rule-id.js\";\n\n/**\n * Every value `--format` accepts, in the order help and error messages list\n * them.\n *\n * The list is stated **once**. It used to live in five unlinked places — this\n * union, a separate `Set<string>` in the CLI, the error message, the option\n * description, and the docs — and the `Set` was not tied to the union at all,\n * so widening the union alone type-checked and still rejected the new value at\n * runtime. Deriving the union from the array makes that drift impossible.\n */\nexport const REPORT_FORMATS = [\n \"pretty\",\n \"json\",\n \"github\",\n \"sarif\",\n \"junit\",\n] as const;\n\nexport type ReportFormat = (typeof REPORT_FORMATS)[number];\n\n/**\n * A format list as a sentence fragment: `\"pretty or json\"`,\n * `\"pretty, json, or github\"`.\n *\n * The two-value case is why this is a function rather than one `join`. A pair\n * takes no comma before \"or\", and `get` and `schemas` have said \"Use pretty or\n * json.\" since long before the list was shared — a mechanical\n * `map(...).join(\", \")` would have quietly reworded every one of those messages\n * to \"pretty, or json\".\n */\nexport function formatList(formats: readonly string[]): string {\n // Destructured and guarded rather than indexed. `noUncheckedIndexedAccess`\n // types `formats[0]` as `string | undefined` however the length was checked,\n // so the interpolation below would happily print \"undefined\" — these two\n // guards say what `length < 2` said, in a form the compiler can carry.\n const [first, second] = formats;\n if (first === undefined) return \"\";\n if (second === undefined) return first;\n // Still needed, and not redundant however it reads: a defined `second` proves\n // there are *at least* two, not exactly two. Three or more falls through to\n // the comma-joined form below.\n if (formats.length === 2) return `${first} or ${second}`;\n return formats\n .map((format, i) => (i === formats.length - 1 ? `or ${format}` : format))\n .join(\", \");\n}\n\n/** `\"pretty, json, github, sarif, or junit\"`, for messages and help text. */\nexport const REPORT_FORMAT_LIST = formatList(REPORT_FORMATS);\n\nexport function isReportFormat(value: string): value is ReportFormat {\n return (REPORT_FORMATS as readonly string[]).includes(value);\n}\n\n/**\n * The formats **every** command produces.\n *\n * `get` and `schemas` accept exactly these two, and each used to spell the pair\n * out inline in `src/cli.ts` as `format !== \"pretty\" && format !== \"json\"` with\n * a hand-written \"Use pretty or json.\" beside it — the same stringly-typed\n * drift this module's `REPORT_FORMATS` comment describes, reproduced three more\n * times. Stated once, the guard and the message cannot disagree.\n */\nexport const COMMON_FORMATS = [\"pretty\", \"json\"] as const;\n\nexport type CommonFormat = (typeof COMMON_FORMATS)[number];\n\n/** `\"pretty or json\"`, for messages and help text. */\nexport const COMMON_FORMAT_LIST = formatList(COMMON_FORMATS);\n\nexport function isCommonFormat(value: string): value is CommonFormat {\n return (COMMON_FORMATS as readonly string[]).includes(value);\n}\n\n/**\n * The formats `query` accepts, one six-value list with per-value gates:\n * `pretty | json | csv` render result rows unconditionally (csv is proposal\n * 0029), and the findings three — `github | sarif | junit` — are legal only\n * under `--check` (proposal 0026).\n *\n * A list of its own rather than `REPORT_FORMATS`: `validate`'s list and\n * `query`'s grow independently — `csv` lives here and not there — and one\n * shared const would make every addition to either a change to both.\n */\nexport const QUERY_FORMATS = [\n \"pretty\",\n \"json\",\n \"csv\",\n \"github\",\n \"sarif\",\n \"junit\",\n] as const;\n\nexport type QueryFormat = (typeof QUERY_FORMATS)[number];\n\n/** `\"pretty, json, csv, github, sarif, or junit\"`, for messages and help. */\nexport const QUERY_FORMAT_LIST = formatList(QUERY_FORMATS);\n\nexport function isQueryFormat(value: string): value is QueryFormat {\n return (QUERY_FORMATS as readonly string[]).includes(value);\n}\n\n/**\n * The `query` formats that render **findings** rather than rows — legal only\n * with `--check`, and only when the result carries the `path` column the\n * finding convention is built from. Typed as its own union (not just a set\n * membership test) so the CLI's dispatch can hand a narrowed format straight\n * to `render`, which takes `ReportFormat` and has no `csv` case.\n */\nexport const QUERY_FINDINGS_FORMATS = [\n \"github\",\n \"sarif\",\n \"junit\",\n] as const satisfies readonly QueryFormat[];\n\nexport type QueryFindingsFormat = (typeof QUERY_FINDINGS_FORMATS)[number];\n\n/**\n * A *narrowing* guard on purpose: the CLI's query switch handles the findings\n * formats in an early return, and this is what lets its remaining cases stay\n * compile-time exhaustive over `pretty | json | csv` — a bare `Set.has` would\n * leave the union unnarrowed and force the `never` guard out.\n */\nexport function isQueryFindingsFormat(\n value: QueryFormat,\n): value is QueryFindingsFormat {\n return (QUERY_FINDINGS_FORMATS as readonly string[]).includes(value);\n}\n\n/**\n * Formats something other than a person reads.\n *\n * These own stdout completely: a diagnostic printed alongside them (which\n * config governed the run, say) would corrupt the parse. Colored escape codes\n * would too, which is why the same predicate answers both questions.\n */\nexport const MACHINE_FORMATS: ReadonlySet<ReportFormat> = new Set<ReportFormat>(\n [\"json\", \"github\", \"sarif\", \"junit\"],\n);\n\nexport const isMachineFormat = (format: ReportFormat): boolean =>\n MACHINE_FORMATS.has(format);\n\n/**\n * Formats whose output may legitimately be empty.\n *\n * `github` prints one annotation per finding, so a clean run has nothing to\n * say. Every other format has an **envelope** that must arrive even when it\n * holds nothing: a zero-byte SARIF file makes `upload-sarif` fail outright, and\n * a zero-byte JUnit file reads as a lost report rather than a passing one. So\n * emptiness is a property of the format, not of the text — which is what the\n * CLI's old bare `if (text.length > 0)` guard got wrong.\n */\nexport const OMITTED_WHEN_CLEAN: ReadonlySet<ReportFormat> =\n new Set<ReportFormat>([\"github\"]);\n\nexport interface ReportOptions extends SarifOptions, JunitOptions {\n color?: boolean;\n /** In pretty output, omit passing files. */\n quiet?: boolean;\n}\n\nexport type { FingerprintContext };\n\nconst plural = (n: number, one: string, many: string): string =>\n `${n} ${n === 1 ? one : many}`;\n\n/**\n * The baseline's line in the summary.\n *\n * A baseline is amnesty, and amnesty that goes quiet becomes permanent — so the\n * count is printed on **every** run, including the one where nothing was\n * forgiven, rather than only when something changes. The stale clause is what\n * makes a rename diagnosable: without it the user sees a wall of \"new\" findings\n * with no hint that the fix is a re-record.\n */\nfunction baselineLines(b: BaselineSummary): string[] {\n if (b.written) {\n const added = b.added ?? 0;\n const removed = b.removed ?? 0;\n return [\n `Baseline written to ${b.path}`,\n ` ${plural(b.recorded, \"finding\", \"findings\")} recorded (+${added} new, -${removed} ${\n removed === 1 ? \"no longer occurs\" : \"no longer occur\"\n })`,\n ];\n }\n const head = plural(b.recorded, \"baselined finding\", \"baselined findings\");\n if (b.stale === 0) return [head];\n const stale = b.stale === 1 ? \"1 no longer occurs\" : `${b.stale} no longer occur`;\n return [`${head}, ${stale} — run --write-baseline to prune`];\n}\n\nexport function renderPretty(\n results: ValidationResult[],\n summary: RunSummary,\n opts: ReportOptions = {},\n): string {\n const c = palette(opts.color ?? false);\n const lines: string[] = [];\n\n for (const r of results) {\n // What the baseline forgave here, so a passing file still shows its debt.\n const forgiven =\n r.baselined != null && r.baselined > 0\n ? c.dim(` (${r.baselined} baselined)`)\n : \"\";\n if (r.ok && r.errors.length === 0) {\n if (!opts.quiet) lines.push(`${c.green(\"✓\")} ${r.file}${forgiven}`);\n continue;\n }\n // A file that is `ok` with findings holds only warnings: something to\n // say, so `--quiet` keeps it, but not a failure, so it is not a `✗`.\n const mark = r.ok ? c.yellow(\"⚠\") : c.red(\"✗\");\n lines.push(`${mark} ${r.file}${forgiven}`);\n for (const e of r.errors) {\n // A value a manifest supplied is located in the manifest, not the\n // document, and the location says so (proposal 0037).\n const loc =\n e.file != null\n ? c.dim(` (${e.file}${e.line != null ? `:${e.line}` : \"\"})`)\n : e.line != null\n ? c.dim(` (line ${e.line})`)\n : \"\";\n const level = isErrorSeverity(e)\n ? \"\"\n : `${e.severity === \"notice\" ? c.dim(\"notice\") : c.yellow(\"warning\")} `;\n lines.push(\n ` ${c.cyan(fieldLabel(e.instancePath))} ${level}${e.message}${loc} ${c.dim(\n `[${e.schema}]`,\n )}`,\n );\n }\n }\n\n // Files `.gitignore` took away are named on the summary line, not left to be\n // inferred from a count that quietly shrank. A gate getting quieter without\n // being asked is the dangerous direction of change; this is what makes it\n // auditable. Omitted at zero, which is every run in a clean repo.\n const skipped =\n summary.gitignoreSkipped != null && summary.gitignoreSkipped > 0\n ? `, ${summary.gitignoreSkipped} skipped by .gitignore`\n : \"\";\n // Warnings are named only when there are any, for the same reason as the\n // skip count: every run of meta's own validation has none.\n const warnings =\n summary.warnings != null && summary.warnings > 0\n ? `, ${plural(summary.warnings, \"warning\", \"warnings\")}`\n : \"\";\n const notices =\n summary.notices != null && summary.notices > 0\n ? `, ${plural(summary.notices, \"notice\", \"notices\")}`\n : \"\";\n const summaryText = `${summary.files} file${summary.files === 1 ? \"\" : \"s\"} checked, ${summary.passed} passed, ${summary.failed} failed, ${summary.errors} error${summary.errors === 1 ? \"\" : \"s\"}${warnings}${notices}${skipped}`;\n if (lines.length > 0) lines.push(\"\");\n lines.push(summary.failed > 0 ? c.red(summaryText) : c.green(summaryText));\n if (summary.baseline) {\n for (const l of baselineLines(summary.baseline)) lines.push(c.dim(l));\n }\n return lines.join(\"\\n\");\n}\n\nexport function renderJson(\n results: ValidationResult[],\n summary: RunSummary,\n): string {\n return JSON.stringify({ summary, results }, null, 2);\n}\n\nexport function renderGithub(results: ValidationResult[]): string {\n const lines: string[] = [];\n for (const r of results) {\n for (const e of r.errors) {\n const params = [`file=${escapeWorkflowCommandProperty(e.file ?? r.file)}`];\n if (e.line != null) params.push(`line=${e.line}`);\n if (e.col != null) params.push(`col=${e.col}`);\n // Escaped as one string, after assembly: the schema id and the field\n // label are as capable of carrying a `%` as the message is.\n const msg = escapeWorkflowCommandMessage(\n `[${e.schema}] ${fieldLabel(e.instancePath)} ${e.message}`,\n );\n // The family scale was chosen to be GitHub's, so the level is the\n // severity, and an absent one is `error` (`isErrorSeverity`).\n // `::warning` and `::notice` render inline like `::error` but do not\n // fail the check, which is the severity invariant in GitHub's terms.\n const level = e.severity ?? \"error\";\n lines.push(`::${level} ${params.join(\",\")}::${msg}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nexport function render(\n format: ReportFormat,\n results: ValidationResult[],\n summary: RunSummary,\n opts: ReportOptions = {},\n): string {\n switch (format) {\n case \"json\":\n return renderJson(results, summary);\n case \"github\":\n return renderGithub(results);\n case \"sarif\":\n return renderSarif(results, opts);\n case \"junit\":\n return renderJunit(results, opts);\n case \"pretty\":\n return renderPretty(results, summary, opts);\n default: {\n // Exhaustive: adding a format to `REPORT_FORMATS` without a case here is\n // a compile error. The throw is for the runtime half — `render` is public\n // API, and before this existed a caller passing \"sarif\" fell through to\n // `pretty` and got a human report where it asked for a machine one.\n const unreachable: never = format;\n throw new DocmetaError(\n `Unknown report format ${JSON.stringify(unreachable)}. Use ${REPORT_FORMAT_LIST}.`,\n );\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/shared/code-regions.ts
|
|
4
|
+
var SKIP_MARKDOWN_FENCE = /^[ \t]*(`{3,}|~{3,})/;
|
|
5
|
+
var SKIP_ASCIIDOC_FENCE = /^(-{4,})/;
|
|
6
|
+
var SPAN_FORMATS = /* @__PURE__ */ new Set(["markdown", "mdx", "asciidoc", "rst"]);
|
|
7
|
+
var BACKTICK = 96;
|
|
8
|
+
function fenceFor(format) {
|
|
9
|
+
switch (format) {
|
|
10
|
+
case "markdown":
|
|
11
|
+
case "mdx":
|
|
12
|
+
return SKIP_MARKDOWN_FENCE;
|
|
13
|
+
case "asciidoc":
|
|
14
|
+
return SKIP_ASCIIDOC_FENCE;
|
|
15
|
+
default:
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function backtickRun(text, i) {
|
|
20
|
+
let n = 0;
|
|
21
|
+
while (text.charCodeAt(i + n) === BACKTICK) n++;
|
|
22
|
+
return n;
|
|
23
|
+
}
|
|
24
|
+
function spanRegions(line, lineStart, out) {
|
|
25
|
+
let i = 0;
|
|
26
|
+
while (i < line.length) {
|
|
27
|
+
const n = backtickRun(line, i);
|
|
28
|
+
if (n === 0) {
|
|
29
|
+
i++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
let j = i + n;
|
|
33
|
+
let closer = -1;
|
|
34
|
+
while (j < line.length) {
|
|
35
|
+
const m = backtickRun(line, j);
|
|
36
|
+
if (m === 0) {
|
|
37
|
+
j++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (m === n) {
|
|
41
|
+
closer = j;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
j += m;
|
|
45
|
+
}
|
|
46
|
+
if (closer === -1) {
|
|
47
|
+
i += n;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
out.push({ start: lineStart + i, end: lineStart + closer + n });
|
|
51
|
+
i = closer + n;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function closesFence(line, open) {
|
|
55
|
+
const trimmed = line.trim();
|
|
56
|
+
let n = 0;
|
|
57
|
+
while (trimmed.charAt(n) === open.char) n++;
|
|
58
|
+
return n >= open.length && n === trimmed.length;
|
|
59
|
+
}
|
|
60
|
+
function codeRegions(text, format, options = {}) {
|
|
61
|
+
const fence = fenceFor(format);
|
|
62
|
+
const spans = options.spans !== false && SPAN_FORMATS.has(format);
|
|
63
|
+
const out = [];
|
|
64
|
+
if (fence === void 0 && !spans) return out;
|
|
65
|
+
let open;
|
|
66
|
+
let pos = 0;
|
|
67
|
+
while (pos < text.length) {
|
|
68
|
+
const nl = text.indexOf("\n", pos);
|
|
69
|
+
const end = nl === -1 ? text.length : nl;
|
|
70
|
+
const raw = text.slice(pos, end);
|
|
71
|
+
const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
|
|
72
|
+
if (open !== void 0) {
|
|
73
|
+
if (closesFence(line, open)) {
|
|
74
|
+
out.push({ start: open.start, end });
|
|
75
|
+
open = void 0;
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
const opener = fence?.exec(line)?.[1];
|
|
79
|
+
if (opener !== void 0) {
|
|
80
|
+
open = { char: opener.charAt(0), length: opener.length, start: pos };
|
|
81
|
+
} else if (spans) {
|
|
82
|
+
spanRegions(line, pos, out);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
pos = end + 1;
|
|
86
|
+
}
|
|
87
|
+
if (open !== void 0) out.push({ start: open.start, end: text.length });
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function codeEndAt(regions, offset) {
|
|
91
|
+
return regions.find((r) => offset >= r.start && offset < r.end)?.end;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export {
|
|
95
|
+
codeRegions,
|
|
96
|
+
codeEndAt
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=chunk-IKEWBMCN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/code-regions.ts"],"sourcesContent":["/**\n * The stretches of a text that are code, so a scanner can skip them: a page\n * that documents a syntax is not an instance of it. cite skips code when it\n * looks for markers, and term's body readers skip it when they look for\n * glossary entries.\n *\n * - A fenced block: ``` or ~~~ in markdown and mdx, indented or not, since a\n * fence keeps its meaning inside a list item; `----` at column 0 in\n * asciidoc. It is closed by the same character, at least as many of it, and\n * nothing else on the line. An unclosed fence runs to the end, as a renderer\n * reads it.\n * - A backtick span, in markdown, mdx, asciidoc and rst: a run of backticks\n * closed by a run of the same length on the same line. A run with no closer\n * is literal text.\n *\n * html and xml have neither; a `<pre>` is not code here.\n */\n\n/** A stretch of code, as `[start, end)` offsets of the text it was found in. */\nexport interface CodeRegion {\n start: number;\n end: number;\n}\n\nexport interface CodeRegionOptions {\n /** Include backtick spans. Default `true`; `false` returns fenced blocks only. */\n spans?: boolean;\n}\n\nconst SKIP_MARKDOWN_FENCE = /^[ \\t]*(`{3,}|~{3,})/;\nconst SKIP_ASCIIDOC_FENCE = /^(-{4,})/;\n/** Formats where a backtick span is inline code. */\nconst SPAN_FORMATS = new Set([\"markdown\", \"mdx\", \"asciidoc\", \"rst\"]);\nconst BACKTICK = 96;\n\n/** The fence-opener pattern for a format, or undefined when it has no fences. */\nfunction fenceFor(format: string): RegExp | undefined {\n switch (format) {\n case \"markdown\":\n case \"mdx\":\n return SKIP_MARKDOWN_FENCE;\n case \"asciidoc\":\n return SKIP_ASCIIDOC_FENCE;\n default:\n return undefined;\n }\n}\n\n/** Length of the run of backticks starting at `i` in `text` (0 when none). */\nfunction backtickRun(text: string, i: number): number {\n let n = 0;\n while (text.charCodeAt(i + n) === BACKTICK) n++;\n return n;\n}\n\n/** The backtick spans on one line, appended to `out` as offsets of the whole text. */\nfunction spanRegions(line: string, lineStart: number, out: CodeRegion[]): void {\n let i = 0;\n while (i < line.length) {\n const n = backtickRun(line, i);\n if (n === 0) {\n i++;\n continue;\n }\n let j = i + n;\n let closer = -1;\n while (j < line.length) {\n const m = backtickRun(line, j);\n if (m === 0) {\n j++;\n continue;\n }\n if (m === n) {\n closer = j;\n break;\n }\n j += m;\n }\n if (closer === -1) {\n i += n;\n continue;\n }\n out.push({ start: lineStart + i, end: lineStart + closer + n });\n i = closer + n;\n }\n}\n\n/**\n * Whether a line closes the fence `open`. A longer run does not close a\n * shorter one's block, which is how a ```` block carries a ``` block inside it.\n */\nfunction closesFence(line: string, open: { char: string; length: number }): boolean {\n const trimmed = line.trim();\n let n = 0;\n while (trimmed.charAt(n) === open.char) n++;\n return n >= open.length && n === trimmed.length;\n}\n\n/**\n * Every fenced block and backtick span in `text`, in order, for a format that\n * has them. A closed fence's region ends at its closing line's terminator; an\n * unclosed one ends at the end of the text. Spans are not looked for inside a\n * fence.\n */\nexport function codeRegions(\n text: string,\n format: string,\n options: CodeRegionOptions = {},\n): CodeRegion[] {\n const fence = fenceFor(format);\n const spans = options.spans !== false && SPAN_FORMATS.has(format);\n const out: CodeRegion[] = [];\n if (fence === undefined && !spans) return out;\n let open: { char: string; length: number; start: number } | undefined;\n let pos = 0;\n while (pos < text.length) {\n const nl = text.indexOf(\"\\n\", pos);\n const end = nl === -1 ? text.length : nl;\n const raw = text.slice(pos, end);\n const line = raw.endsWith(\"\\r\") ? raw.slice(0, -1) : raw;\n if (open !== undefined) {\n if (closesFence(line, open)) {\n out.push({ start: open.start, end });\n open = undefined;\n }\n } else {\n const opener = fence?.exec(line)?.[1];\n if (opener !== undefined) {\n open = { char: opener.charAt(0), length: opener.length, start: pos };\n } else if (spans) {\n spanRegions(line, pos, out);\n }\n }\n pos = end + 1;\n }\n if (open !== undefined) out.push({ start: open.start, end: text.length });\n return out;\n}\n\n/** The end of the region `offset` falls in, or undefined when it falls in none. */\nexport function codeEndAt(regions: readonly CodeRegion[], offset: number): number | undefined {\n return regions.find((r) => offset >= r.start && offset < r.end)?.end;\n}\n"],"mappings":";;;AA6BA,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAE5B,IAAM,eAAe,oBAAI,IAAI,CAAC,YAAY,OAAO,YAAY,KAAK,CAAC;AACnE,IAAM,WAAW;AAGjB,SAAS,SAAS,QAAoC;AACpD,UAAQ,QAAQ;AAAA,IACd,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGA,SAAS,YAAY,MAAc,GAAmB;AACpD,MAAI,IAAI;AACR,SAAO,KAAK,WAAW,IAAI,CAAC,MAAM,SAAU;AAC5C,SAAO;AACT;AAGA,SAAS,YAAY,MAAc,WAAmB,KAAyB;AAC7E,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACtB,UAAM,IAAI,YAAY,MAAM,CAAC;AAC7B,QAAI,MAAM,GAAG;AACX;AACA;AAAA,IACF;AACA,QAAI,IAAI,IAAI;AACZ,QAAI,SAAS;AACb,WAAO,IAAI,KAAK,QAAQ;AACtB,YAAM,IAAI,YAAY,MAAM,CAAC;AAC7B,UAAI,MAAM,GAAG;AACX;AACA;AAAA,MACF;AACA,UAAI,MAAM,GAAG;AACX,iBAAS;AACT;AAAA,MACF;AACA,WAAK;AAAA,IACP;AACA,QAAI,WAAW,IAAI;AACjB,WAAK;AACL;AAAA,IACF;AACA,QAAI,KAAK,EAAE,OAAO,YAAY,GAAG,KAAK,YAAY,SAAS,EAAE,CAAC;AAC9D,QAAI,SAAS;AAAA,EACf;AACF;AAMA,SAAS,YAAY,MAAc,MAAiD;AAClF,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,IAAI;AACR,SAAO,QAAQ,OAAO,CAAC,MAAM,KAAK,KAAM;AACxC,SAAO,KAAK,KAAK,UAAU,MAAM,QAAQ;AAC3C;AAQO,SAAS,YACd,MACA,QACA,UAA6B,CAAC,GAChB;AACd,QAAM,QAAQ,SAAS,MAAM;AAC7B,QAAM,QAAQ,QAAQ,UAAU,SAAS,aAAa,IAAI,MAAM;AAChE,QAAM,MAAoB,CAAC;AAC3B,MAAI,UAAU,UAAa,CAAC,MAAO,QAAO;AAC1C,MAAI;AACJ,MAAI,MAAM;AACV,SAAO,MAAM,KAAK,QAAQ;AACxB,UAAM,KAAK,KAAK,QAAQ,MAAM,GAAG;AACjC,UAAM,MAAM,OAAO,KAAK,KAAK,SAAS;AACtC,UAAM,MAAM,KAAK,MAAM,KAAK,GAAG;AAC/B,UAAM,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;AACrD,QAAI,SAAS,QAAW;AACtB,UAAI,YAAY,MAAM,IAAI,GAAG;AAC3B,YAAI,KAAK,EAAE,OAAO,KAAK,OAAO,IAAI,CAAC;AACnC,eAAO;AAAA,MACT;AAAA,IACF,OAAO;AACL,YAAM,SAAS,OAAO,KAAK,IAAI,IAAI,CAAC;AACpC,UAAI,WAAW,QAAW;AACxB,eAAO,EAAE,MAAM,OAAO,OAAO,CAAC,GAAG,QAAQ,OAAO,QAAQ,OAAO,IAAI;AAAA,MACrE,WAAW,OAAO;AAChB,oBAAY,MAAM,KAAK,GAAG;AAAA,MAC5B;AAAA,IACF;AACA,UAAM,MAAM;AAAA,EACd;AACA,MAAI,SAAS,OAAW,KAAI,KAAK,EAAE,OAAO,KAAK,OAAO,KAAK,KAAK,OAAO,CAAC;AACxE,SAAO;AACT;AAGO,SAAS,UAAU,SAAgC,QAAoC;AAC5F,SAAO,QAAQ,KAAK,CAAC,MAAM,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,GAAG;AACnE;","names":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
TERM_FIELDS,
|
|
4
|
+
displayPath
|
|
5
|
+
} from "./chunk-PU6DFJMY.js";
|
|
6
|
+
|
|
7
|
+
// src/term/reporters/json.ts
|
|
8
|
+
function termEntry(term) {
|
|
9
|
+
const entry = { id: term.id };
|
|
10
|
+
if (term.language !== void 0) entry["language"] = term.language;
|
|
11
|
+
for (const field of TERM_FIELDS) {
|
|
12
|
+
const value = term.record[field];
|
|
13
|
+
if (value !== void 0) entry[field] = value;
|
|
14
|
+
}
|
|
15
|
+
return entry;
|
|
16
|
+
}
|
|
17
|
+
function renderListJson(terms) {
|
|
18
|
+
return JSON.stringify({ terms: terms.map(termEntry) }, null, 2);
|
|
19
|
+
}
|
|
20
|
+
function renderGetJson(term) {
|
|
21
|
+
return JSON.stringify(termEntry(term), null, 2);
|
|
22
|
+
}
|
|
23
|
+
function renderFindingsJson(report) {
|
|
24
|
+
const count = (severity) => report.findings.filter((f) => f.severity === severity).length;
|
|
25
|
+
return JSON.stringify(
|
|
26
|
+
{
|
|
27
|
+
findings: report.findings.map((finding) => ({ ...finding, file: displayPath(finding.file, report.cwd) })),
|
|
28
|
+
summary: {
|
|
29
|
+
terms: report.terms,
|
|
30
|
+
references: report.references,
|
|
31
|
+
errors: count("error"),
|
|
32
|
+
warnings: count("warning"),
|
|
33
|
+
notices: count("notice"),
|
|
34
|
+
...report.baseline === void 0 ? {} : { baseline: report.baseline }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
null,
|
|
38
|
+
2
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
function renderFormatsJson(rows) {
|
|
42
|
+
return JSON.stringify({ formats: rows }, null, 2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
termEntry,
|
|
47
|
+
renderListJson,
|
|
48
|
+
renderGetJson,
|
|
49
|
+
renderFindingsJson,
|
|
50
|
+
renderFormatsJson
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=chunk-JCDXE76J.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/term/reporters/json.ts"],"sourcesContent":["/**\n * JSON output for every `manni term` command. A term is the object\n * `write -f json` writes: its id, its language when it has one, and every field\n * in the vocabulary's order, so what `list -f json` prints can be read back as\n * a render.\n */\nimport { displayPath } from \"../commands/run.js\";\nimport type { FormatRow } from \"../commands/formats.js\";\nimport type { TermReport } from \"../commands/findings.js\";\nimport { TERM_FIELDS, type Term } from \"../types.js\";\n\nexport function termEntry(term: Term): Record<string, unknown> {\n const entry: Record<string, unknown> = { id: term.id };\n if (term.language !== undefined) entry[\"language\"] = term.language;\n for (const field of TERM_FIELDS) {\n const value = term.record[field];\n if (value !== undefined) entry[field] = value;\n }\n return entry;\n}\n\nexport function renderListJson(terms: readonly Term[]): string {\n return JSON.stringify({ terms: terms.map(termEntry) }, null, 2);\n}\n\nexport function renderGetJson(term: Term): string {\n return JSON.stringify(termEntry(term), null, 2);\n}\n\nexport function renderFindingsJson(report: TermReport): string {\n const count = (severity: string): number => report.findings.filter((f) => f.severity === severity).length;\n return JSON.stringify(\n {\n findings: report.findings.map((finding) => ({ ...finding, file: displayPath(finding.file, report.cwd) })),\n summary: {\n terms: report.terms,\n references: report.references,\n errors: count(\"error\"),\n warnings: count(\"warning\"),\n notices: count(\"notice\"),\n ...(report.baseline === undefined ? {} : { baseline: report.baseline }),\n },\n },\n null,\n 2,\n );\n}\n\nexport function renderFormatsJson(rows: readonly FormatRow[]): string {\n return JSON.stringify({ formats: rows }, null, 2);\n}\n"],"mappings":";;;;;;;AAWO,SAAS,UAAU,MAAqC;AAC7D,QAAM,QAAiC,EAAE,IAAI,KAAK,GAAG;AACrD,MAAI,KAAK,aAAa,OAAW,OAAM,UAAU,IAAI,KAAK;AAC1D,aAAW,SAAS,aAAa;AAC/B,UAAM,QAAQ,KAAK,OAAO,KAAK;AAC/B,QAAI,UAAU,OAAW,OAAM,KAAK,IAAI;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,SAAS,eAAe,OAAgC;AAC7D,SAAO,KAAK,UAAU,EAAE,OAAO,MAAM,IAAI,SAAS,EAAE,GAAG,MAAM,CAAC;AAChE;AAEO,SAAS,cAAc,MAAoB;AAChD,SAAO,KAAK,UAAU,UAAU,IAAI,GAAG,MAAM,CAAC;AAChD;AAEO,SAAS,mBAAmB,QAA4B;AAC7D,QAAM,QAAQ,CAAC,aAA6B,OAAO,SAAS,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ,EAAE;AACnG,SAAO,KAAK;AAAA,IACV;AAAA,MACE,UAAU,OAAO,SAAS,IAAI,CAAC,aAAa,EAAE,GAAG,SAAS,MAAM,YAAY,QAAQ,MAAM,OAAO,GAAG,EAAE,EAAE;AAAA,MACxG,SAAS;AAAA,QACP,OAAO,OAAO;AAAA,QACd,YAAY,OAAO;AAAA,QACnB,QAAQ,MAAM,OAAO;AAAA,QACrB,UAAU,MAAM,SAAS;AAAA,QACzB,SAAS,MAAM,QAAQ;AAAA,QACvB,GAAI,OAAO,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,OAAO,SAAS;AAAA,MACvE;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEO,SAAS,kBAAkB,MAAoC;AACpE,SAAO,KAAK,UAAU,EAAE,SAAS,KAAK,GAAG,MAAM,CAAC;AAClD;","names":[]}
|