@archwall/core 0.1.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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/index.cjs +1400 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +721 -0
- package/dist/index.d.mts +721 -0
- package/dist/index.mjs +1349 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internal.cjs +12 -0
- package/dist/internal.d.cts +106 -0
- package/dist/internal.d.mts +106 -0
- package/dist/internal.mjs +2 -0
- package/dist/prepare-BJHgDEui.mjs +748 -0
- package/dist/prepare-BJHgDEui.mjs.map +1 -0
- package/dist/prepare-C1FfL8Qd.cjs +921 -0
- package/dist/prepare-C1FfL8Qd.cjs.map +1 -0
- package/dist/transform-CnUPOO0E.d.cts +638 -0
- package/dist/transform-CnUPOO0E.d.mts +638 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/contracts/analysis.ts","../src/analysis/scc.ts","../src/contracts/classifier.ts","../src/match.ts","../src/classifiers/path.ts","../src/violations.ts","../src/reporters/console.ts","../src/reporters/json.ts","../src/reporters/sarif.ts","../src/reporters/resolve.ts","../src/config.ts","../src/contracts/preset.ts","../src/contracts/reporter.ts","../src/contracts/rule.ts","../src/contracts/transform.ts","../src/engine/analyze.ts","../src/transforms/drop-self-edges.ts"],"sourcesContent":["import type { GraphQuery } from \"../graph/query.js\";\n\n/**\n * A memoized derived value over the graph — an SCC decomposition, a reachability table —\n * computed at most once per run and shared by every rule that asks for it.\n *\n * Named `GraphComputation` rather than `Analysis` because \"analysis\" already meant four\n * other things: `AnalysisResult` (the run's output), `AnalysisStats`, `AnalysisCache`, and\n * `analyze()`, plus a directory called `analysis/`. Four meanings for one word in one\n * codebase is a thing contributors conflate weekly; the run-output family keeps the name\n * and this — the odd one out — gives it up.\n */\nexport interface GraphComputation<T> {\n name: string;\n compute(graph: GraphQuery): T;\n}\n\nexport function defineGraphComputation<T>(computation: GraphComputation<T>): GraphComputation<T> {\n return computation;\n}\n","import { defineGraphComputation } from \"../contracts/analysis.js\";\nimport type { ModuleId } from \"../graph/ir.js\";\n\n/**\n * Strongly connected components over static+reexport edges (a dynamic import is a\n * legal cycle-breaker). Iterative Tarjan — recursion would overflow at 10k+ modules.\n * Every module appears in exactly one component.\n */\nexport const stronglyConnectedComponents = defineGraphComputation<readonly (readonly ModuleId[])[]>(\n {\n name: \"scc\",\n compute(q) {\n const index = new Map<ModuleId, number>();\n const low = new Map<ModuleId, number>();\n const onStack = new Set<ModuleId>();\n const stack: ModuleId[] = [];\n const out: (readonly ModuleId[])[] = [];\n let counter = 0;\n const neighbors = (v: ModuleId): ModuleId[] =>\n q\n .edgesOutOf(v)\n .filter((e) => e.kind !== \"dynamic\" && q.has(e.to))\n .map((e) => e.to);\n for (const root of q.moduleIds()) {\n if (index.has(root)) continue;\n const work: [ModuleId, number, ModuleId[]][] = [[root, 0, neighbors(root)]];\n while (work.length > 0) {\n const frame = work[work.length - 1]!;\n const [v, i, ns] = frame;\n if (i === 0) {\n index.set(v, counter);\n low.set(v, counter);\n counter++;\n stack.push(v);\n onStack.add(v);\n }\n if (i < ns.length) {\n frame[1] = i + 1;\n const w = ns[i]!;\n if (!index.has(w)) work.push([w, 0, neighbors(w)]);\n else if (onStack.has(w)) low.set(v, Math.min(low.get(v)!, index.get(w)!));\n } else {\n if (low.get(v) === index.get(v)) {\n const comp: ModuleId[] = [];\n for (;;) {\n const w = stack.pop()!;\n onStack.delete(w);\n comp.push(w);\n if (w === v) break;\n }\n out.push(comp);\n }\n work.pop();\n const parent = work[work.length - 1];\n if (parent) low.set(parent[0], Math.min(low.get(parent[0])!, low.get(v)!));\n }\n }\n }\n return out;\n },\n },\n);\n","import type { ModuleNode } from \"../graph/ir.js\";\n\nexport interface ClassifierContext {\n /**\n * Absolute source root from resolved config. Classifier patterns describe the shape of\n * the source tree, so they are relative to this and never to the repository root.\n */\n sourceRoot: string;\n /**\n * A file's path relative to {@link sourceRoot}, forward-slashed, or null when it lies\n * outside. Every path-based classifier needs exactly this, and none of them should be\n * re-deriving it — guards, slash normalisation and all — in user code.\n */\n relative(file: string): string | null;\n}\n\nexport type TagPatch = Record<string, string> | null | undefined | void;\n\nexport interface Classifier {\n name: string;\n classify(module: ModuleNode, ctx: ClassifierContext): TagPatch;\n}\n\nexport function defineClassifier(classifier: Classifier): Classifier {\n return classifier;\n}\n","import picomatch from \"picomatch\";\n\n/**\n * Pattern matching, on ONE grammar.\n *\n * Patterns appear in `include`/`exclude`, `overrides` keys, `pathClassifier` patterns,\n * specifier patterns, and the CLI scanner. This is the grammar all of them share, anchored\n * full-match:\n *\n * * matches within one segment (no \"/\")\n * ** matches across segments, and ZERO of them: `src/**` matches `src` itself and\n * `src/**\\/*.ts` matches `src/index.ts`\n * {a,b} alternation, nestable\n * :name captures exactly one segment as `name` — {@link matchCaptures} only\n *\n * Two implementations, deliberately. {@link matchesPattern} delegates to picomatch;\n * {@link matchCaptures} compiles its own regex, because `:name` SEGMENT CAPTURES are the one\n * thing picomatch cannot do and they are how `pathClassifier` turns a path into tags\n * (`:layer/:slice/**` → `{ layer, slice }`). Extracting a capture is a different job from\n * deciding a match; reimplementing the decision is the price of doing it.\n *\n * What keeps that price honest is that the grammar above is the CONTRACT and both\n * implementations owe it: `test/match-dialect.test.ts` asserts they agree on match/no-match\n * across a shared corpus. `{app,pages}/**` used to mean alternation in one place and a\n * literal brace in the other; that is what the differential test exists to prevent recurring.\n *\n * BEYOND the grammar above, picomatch accepts more than the capture compiler does — extglobs\n * (`+(a|b)`), negation (`!`), `?`, numeric ranges (`{1..3}`), POSIX classes. Those are not\n * part of the contract, are not exercised by the differential test, and must not be used in a\n * classifier pattern, where they match literally. Widening the shared grammar means teaching\n * {@link translate} the same syntax and extending the corpus, in that order.\n *\n * ONE divergence is known and deliberate: a trailing `**` preceded by a wildcard segment.\n * We read it consistently (`X` alone always matches); picomatch does so for literal and brace\n * prefixes but not for wildcard ones, and inconsistently even there. The test file states the\n * case and pins picomatch's behaviour so we find out if it ever changes.\n */\n\n/**\n * Bounded compile cache.\n *\n * Unbounded module-level caches leak in long-lived watch processes whenever patterns are\n * dynamic, and both caches here are keyed by user-supplied strings. Patterns come from\n * configuration and are few, so a small cap costs nothing and removes the failure mode.\n */\nconst MAX_CACHED = 500;\n\nfunction cached<V>(store: Map<string, V>, key: string, make: () => V): V {\n const hit = store.get(key);\n if (hit !== undefined) return hit;\n const value = make();\n // Evict the oldest rather than clearing: `Map` iterates in insertion order, so this is a\n // one-line LRU-ish bound. Clearing threw away 499 live entries to make room for one, which\n // turned a full cache into a recompile of every pattern on the next pass.\n if (store.size >= MAX_CACHED) {\n const oldest = store.keys().next();\n if (!oldest.done) store.delete(oldest.value);\n }\n store.set(key, value);\n return value;\n}\n\nconst matchers = new Map<string, (value: string) => boolean>();\n\n/**\n * Anchored full-match test.\n *\n * `dot: true` so a pattern matches dotfiles without every caller remembering to say so —\n * a rule that silently skips `.storybook/` is the kind of quiet gap this tool exists to\n * prevent.\n */\nexport function matchesPattern(value: string, pattern: string): boolean {\n return cached(matchers, pattern, () => picomatch(pattern, { dot: true }))(value);\n}\n\n/** Segments of the capture grammar, in the order the regex builder must handle them. */\nconst CAPTURE = /^:([A-Za-z_][A-Za-z0-9_]*)/;\n\n/** Regex metacharacters that must survive as literals. `*` and `{` never reach the escaper. */\nconst META = /[.+?^$}()|[\\]\\\\]/;\n\ninterface Compiled {\n regex: RegExp;\n /**\n * Capture name per regex group, positionally: `names[k]` names group `k + 1`. Dense,\n * because `:name` is the only construct that emits a capturing group — alternation uses\n * `(?:…)`.\n */\n names: string[];\n}\n\nconst compiled = new Map<string, Compiled>();\n\n/** Index of the `}` closing the `{` at `start`, or -1 if it is never closed. */\nfunction closingBrace(pattern: string, start: number): number {\n let depth = 0;\n for (let i = start; i < pattern.length; i++) {\n if (pattern[i] === \"{\") depth++;\n else if (pattern[i] === \"}\" && --depth === 0) return i;\n }\n return -1;\n}\n\n/** Splits a brace body on its top-level commas, leaving nested groups intact. */\nfunction splitAlternatives(body: string): string[] {\n const out: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < body.length; i++) {\n const c = body[i];\n if (c === \"{\") depth++;\n else if (c === \"}\") depth--;\n else if (c === \",\" && depth === 0) {\n out.push(body.slice(start, i));\n start = i + 1;\n }\n }\n out.push(body.slice(start));\n return out;\n}\n\n/** Translates one pattern into regex source, appending any capture names it emits. */\nfunction translate(pattern: string, names: string[]): string {\n let source = \"\";\n let i = 0;\n while (i < pattern.length) {\n const rest = pattern.slice(i);\n const capture = CAPTURE.exec(rest);\n if (capture) {\n names.push(capture[1]!);\n source += \"([^/]+)\";\n i += capture[0].length;\n continue;\n }\n // Globstar swallows the slash next to it, which is the whole of what makes `**` mean\n // \"zero or more directories\" rather than \"one or more\". Without these three cases\n // `src/**/*.ts` misses `src/index.ts` and `src/**` misses `src` itself — silently, and\n // in exactly the patterns every preset is built from.\n if (rest.startsWith(\"/**/\")) {\n source += \"\\\\/(?:.*\\\\/)?\";\n i += 4;\n continue;\n }\n if (rest === \"/**\") {\n source += \"(?:\\\\/.*)?\";\n i += 3;\n continue;\n }\n if (i === 0 && rest.startsWith(\"**/\")) {\n source += \"(?:.*\\\\/)?\";\n i += 3;\n continue;\n }\n if (rest.startsWith(\"**\")) {\n source += \".*\";\n i += 2;\n continue;\n }\n if (rest.startsWith(\"*\")) {\n source += \"[^/]*\";\n i += 1;\n continue;\n }\n if (rest.startsWith(\"{\")) {\n const end = closingBrace(pattern, i);\n // An unclosed `{` falls through to the escaper and matches literally, which is what\n // picomatch does with it too.\n if (end !== -1) {\n // Alternatives share ONE `names` array, appended in source order, so group numbering\n // stays aligned with it. Only one alternative can match, so a `:name` inside any of\n // the others comes back undefined and is dropped when captures are extracted.\n const alternatives = splitAlternatives(pattern.slice(i + 1, end));\n source += `(?:${alternatives.map((a) => translate(a, names)).join(\"|\")})`;\n i = end + 1;\n continue;\n }\n }\n const ch = pattern[i]!;\n source += META.test(ch) ? `\\\\${ch}` : ch;\n i += 1;\n }\n return source;\n}\n\nfunction compile(pattern: string): Compiled {\n return cached(compiled, pattern, () => {\n const names: string[] = [];\n const source = translate(pattern, names);\n // The lookahead requires at least one character, so an all-wildcard pattern does not match\n // the empty string — picomatch does not either, and an empty path is not a path.\n return { regex: new RegExp(`^(?=[\\\\s\\\\S])${source}$`), names };\n });\n}\n\n/**\n * Anchored full-match returning the `:name` captures, or null when the pattern does not\n * match. A pattern with no captures yields an empty object on match — callers must check\n * for null rather than for emptiness.\n */\nexport function matchCaptures(value: string, pattern: string): Record<string, string> | null {\n const { regex, names } = compile(pattern);\n const m = regex.exec(value);\n if (!m) return null;\n const out: Record<string, string> = {};\n names.forEach((name, idx) => {\n const captured = m[idx + 1];\n // Undefined when the group sits in a brace alternative that did not match — that part of\n // the pattern never participated, so the tag is absent rather than empty.\n if (captured !== undefined) out[name] = captured;\n });\n return out;\n}\n","import * as path from \"node:path\";\nimport type { Classifier } from \"../contracts/classifier.js\";\nimport { defineClassifier } from \"../contracts/classifier.js\";\nimport { matchCaptures } from \"../match.js\";\nimport { sourceRelative } from \"../paths.js\";\n\nexport interface PathPattern {\n /**\n * Glob-lite, relative to the classifier `root` (itself under the config `sourceRoot`),\n * anchored full-match: `:name` captures one segment as a tag, `*` matches within a\n * segment, `**` across.\n */\n pattern: string;\n /** Literal tags, merged over the captures. */\n tags?: Record<string, string>;\n /**\n * Constrains captured values. A capture outside its allow-list makes the pattern NOT\n * match, so the next pattern is tried — this is how unknown top-level folders stay\n * untagged (and therefore ignored by every rule) instead of inventing layers.\n */\n only?: Record<string, readonly string[]>;\n}\n\nexport interface PathClassifierOptions {\n name?: string;\n /** Directory the patterns are relative to, itself relative to the config `sourceRoot`. Default \".\". */\n root?: string;\n /** First match wins. */\n patterns: PathPattern[];\n}\n\n/**\n * Declarative path→tag mapping. Every built-in preset is built on this, and it is the\n * supported way to describe a custom architecture without writing a classify function.\n */\nexport function pathClassifier(opts: PathClassifierOptions): Classifier {\n const { name = \"path\", root = \".\", patterns } = opts;\n return defineClassifier({\n name,\n classify(module, ctx) {\n if (module.kind !== \"source\" || !module.file) return null;\n const base = path.resolve(ctx.sourceRoot, root);\n const rel = sourceRelative(base, module.file);\n // Outside the classifier's root: not ours to tag.\n if (rel === null) return null;\n\n for (const entry of patterns) {\n const captures = matchCaptures(rel, entry.pattern);\n if (!captures) continue;\n if (entry.only && !allowed(captures, entry.only)) continue;\n return { ...captures, ...entry.tags };\n }\n return null;\n },\n });\n}\n\nfunction allowed(\n captures: Record<string, string>,\n only: Record<string, readonly string[]>,\n): boolean {\n return Object.entries(only).every(([key, values]) => {\n const captured = captures[key];\n return captured === undefined || values.includes(captured);\n });\n}\n","import type { Edge, ModuleId, SourceLocation } from \"./graph/ir.js\";\nimport { hashParts, toRelative } from \"./paths.js\";\n\n/**\n * The ONE severity vocabulary, shared by violations and diagnostics.\n *\n * `info` is available to violations too: a rule may report something worth surfacing that\n * should never gate a build.\n */\nexport type Severity = \"error\" | \"warn\" | \"info\";\n\n/**\n * Where a violation is.\n *\n * A tagged union rather than a pair of optional fields, and an ARRAY on the violation\n * rather than one value, because findings are not all edge-shaped. A cycle has no single\n * offending location — it has N of them, and the old model could only name one and had to\n * serialise the rest into the message string. A finding about a package, a directory, or\n * the configuration has no module at all.\n *\n * See docs/adr/0004-violation-locations.md.\n */\nexport type ViolationLocation =\n | { type: \"edge\"; edge: Edge }\n | { type: \"module\"; module: ModuleId }\n | { type: \"path\"; path: string; loc?: SourceLocation };\n\nexport interface Violation {\n ruleName: string;\n /**\n * Rule *instance* id — what you put in `overrides`, and what every reporter prints.\n * Differs from `ruleName` when the rule came from a preset (`fsd/public-api`) or was\n * given an explicit id; equal to it otherwise.\n */\n ruleId: string;\n severity: Severity;\n /** Rendered human summary. Derived from `messageId` + `data` unless set literally. */\n message: string;\n /**\n * Stable identifier for WHICH of the rule's messages this is, independent of wording.\n * Machine consumers group on this; translators key on it; `ConfiguredRule.message`\n * retargets it.\n */\n messageId?: string;\n /**\n * The values interpolated into the message — and the structured payload a reporter needs\n * in order to do anything other than print English. Without it a consumer wanting the\n * layer names out of a `layer-dependencies` finding has to parse the sentence.\n */\n data?: Readonly<Record<string, string | number>>;\n /**\n * Every place this finding is about, most significant first. Always at least one entry\n * for a rule that reported a location; may be empty for a finding about the run itself.\n */\n locations: readonly ViolationLocation[];\n /** \"Why\": resolution chain, which constraint, how to fix. */\n explanation?: string;\n /**\n * Stable, machine-independent identity. The same architecture problem on two developers'\n * machines, or under two bundlers, yields the same fingerprint.\n *\n * This is what makes a baseline file possible, and a graph-based linter has no other\n * suppression mechanism available: with no source text there can be no `// archwall-ignore`.\n */\n fingerprint: string;\n}\n\nexport interface ViolationInput {\n /** Literal message. Mutually exclusive with `messageId`; one of the two is required. */\n message?: string;\n /** Key into the rule's `meta.messages`. Preferred — it is what `data` interpolates into. */\n messageId?: string;\n data?: Record<string, string | number>;\n /** Convenience for the overwhelmingly common single-edge finding. */\n edge?: Edge;\n /** Convenience for the single-module finding. */\n module?: ModuleId;\n /** Full control, for findings with several locations or a non-module subject. */\n locations?: readonly ViolationLocation[];\n explanation?: string;\n /**\n * Overrides the rule instance's configured severity for this one finding — e.g. a\n * two-module cycle as a warning and a forty-module cycle as an error.\n */\n severity?: Severity;\n /**\n * Explicit identity, for findings whose sameness is not captured by their locations.\n * Order-insensitive: parts are sorted before hashing.\n */\n identity?: readonly string[];\n}\n\n/** Normalizes the three input spellings into the canonical location list. */\nexport function locationsOf(input: ViolationInput): readonly ViolationLocation[] {\n if (input.locations !== undefined) return input.locations;\n if (input.edge !== undefined) return [{ type: \"edge\", edge: input.edge }];\n if (input.module !== undefined) return [{ type: \"module\", module: input.module }];\n return [];\n}\n\n/** The edge a finding is primarily about, when it is about one. */\nexport function primaryEdge(v: Pick<Violation, \"locations\">): Edge | undefined {\n for (const l of v.locations) if (l.type === \"edge\") return l.edge;\n return undefined;\n}\n\n/** The module a finding is primarily about: an explicit module, else an edge's source. */\nexport function primaryModule(v: Pick<Violation, \"locations\">): ModuleId | undefined {\n for (const l of v.locations) {\n if (l.type === \"module\") return l.module;\n if (l.type === \"edge\") return l.edge.from;\n }\n return undefined;\n}\n\n/** Where a finding should be anchored in an editor or in SARIF, when that is knowable. */\nexport function primarySourceLocation(v: Pick<Violation, \"locations\">): SourceLocation | undefined {\n for (const l of v.locations) {\n if (l.type === \"edge\" && l.edge.loc !== undefined) return l.edge.loc;\n if (l.type === \"path\" && l.loc !== undefined) return l.loc;\n }\n return undefined;\n}\n\n/**\n * Renders `{placeholder}` templates. Unknown placeholders are left verbatim, so a\n * mis-keyed template is visible in the output rather than silently blank.\n */\nexport function renderMessage(\n template: string,\n data: Readonly<Record<string, string | number>> | undefined,\n): string {\n if (data === undefined) return template;\n return template.replace(/\\{(\\w+)\\}/g, (whole, key: string) =>\n key in data ? String(data[key]) : whole,\n );\n}\n\n/**\n * Fingerprint scheme version. Bump when the algorithm changes so that a stale baseline\n * ERRORS instead of silently mismatching every entry.\n *\n * `aw3` is the first scheme over canonical module ids\n * (docs/adr/0012-canonical-module-identity.md). Before it, a violation about `react` hashed the\n * host's own id — a resolved `node_modules` path under the CLI, the bare specifier under esbuild\n * — so the same finding fingerprinted differently under two bundlers.\n */\nexport const FINGERPRINT_SCHEME = \"aw3\";\n\n/**\n * `toRelative` is a no-op on a canonical id, which is never absolute — it is here for the ids\n * that are not canonical: in-memory graphs built by hand (`@archwall/test-utils`, a playground)\n * use bare absolute paths, and those must still fingerprint identically across machines.\n */\nfunction locationParts(repoRoot: string, l: ViolationLocation): string[] {\n switch (l.type) {\n case \"edge\":\n // Endpoints ONLY. `rawSpecifier` and `kind` are host-variable — Vite expands an alias\n // before any plugin sees it, and `reexport` versus `static` is capability-gated — so\n // including them would make the fingerprint differ by bundler for one architectural\n // fact, which is exactly what canonical ids exist to prevent. The conformance suite\n // coarsens edge kinds for the same reason.\n //\n // The cost is that two imports of one target from one module (`react` and\n // `react/jsx-runtime`, which share the node `pkg:react`) share a fingerprint. That is\n // the right granularity for a baseline: \"domain must not import react\" is one finding.\n return [\"e\", toRelative(repoRoot, l.edge.from), toRelative(repoRoot, l.edge.to)];\n case \"module\":\n return [\"m\", toRelative(repoRoot, l.module)];\n case \"path\":\n return [\"p\", toRelative(repoRoot, l.path)];\n }\n}\n\n/**\n * Identity is (rule instance, offending locations) — deliberately NOT the message, so\n * improving the wording of a rule's output does not invalidate every baseline entry that\n * rule ever produced. `identity` overrides the locations when a rule knows better.\n */\nexport function fingerprintOf(\n repoRoot: string,\n ruleId: string,\n input: Pick<ViolationInput, \"edge\" | \"module\" | \"locations\" | \"identity\">,\n): string {\n let parts: string[];\n if (input.identity !== undefined) {\n parts = input.identity.map((p) => toRelative(repoRoot, p)).sort();\n } else {\n const locations = locationsOf(input);\n parts = locations.length === 0 ? [\"\"] : locations.flatMap((l) => locationParts(repoRoot, l));\n }\n return `${FINGERPRINT_SCHEME}:${hashParts([ruleId, ...parts])}`;\n}\n\nexport type SeverityCounts = Record<Severity, number>;\n\n/** One definition of \"how many of each\", shared by every consumer that needs counts. */\nexport function countBySeverity(violations: readonly { severity: Severity }[]): SeverityCounts {\n const counts: SeverityCounts = { error: 0, warn: 0, info: 0 };\n for (const v of violations) counts[v.severity]++;\n return counts;\n}\n\n/** Sortable string for a location, so ordering is a property of the finding. */\nfunction locationKey(l: ViolationLocation | undefined): string {\n if (l === undefined) return \"\";\n switch (l.type) {\n case \"edge\":\n return `${l.edge.from}\u0000${l.edge.to}\u0000${l.edge.rawSpecifier}`;\n case \"module\":\n return l.module;\n case \"path\":\n return l.path;\n }\n}\n\n/**\n * Total order over violations, so two runs of the same analysis produce byte-identical\n * output. Required by baselines, CI diffing, and snapshot tests; without it, ordering\n * follows rule registration order and each rule's internal scan order, which differs\n * between hosts because module insertion order does.\n */\nexport function compareViolations(a: Violation, b: Violation): number {\n return (\n a.ruleId.localeCompare(b.ruleId) ||\n locationKey(a.locations[0]).localeCompare(locationKey(b.locations[0])) ||\n a.message.localeCompare(b.message)\n );\n}\n","import type { OutputDestination, OutputSink, Reporter, ReporterIO } from \"../contracts/reporter.js\";\nimport { ArchWallError } from \"../errors.js\";\nimport { displayModuleId } from \"../graph/ir.js\";\nimport { toRelative } from \"../paths.js\";\nimport type { Violation } from \"../violations.js\";\nimport { countBySeverity, primarySourceLocation } from \"../violations.js\";\n\n/**\n * Console-only IO: the portable default.\n *\n * Core stays runnable wherever a graph can be built — browser playground, worker, edge\n * runtime — so it cannot open files. A host with a filesystem supplies an IO that can\n * (`@archwall/integration-kit` exports `nodeIO`); asking this one for a file is an error\n * rather than a silent fallback to stdout, because a run that was told to write\n * `archwall.sarif` and printed to the terminal instead has failed at its actual job.\n */\nexport const defaultIO: ReporterIO = {\n open(destination: OutputDestination): OutputSink {\n if (destination === \"stdout\") return { write: (text) => console.log(text) };\n if (destination === \"stderr\") return { write: (text) => console.error(text) };\n throw new ArchWallError(\n `Cannot write reporter output to \"${destination}\": this environment has no filesystem. ` +\n `Use \"stdout\"/\"stderr\", or run through a host that supplies a filesystem-capable ReporterIO.`,\n );\n },\n};\n\n/**\n * Shared violation block format — also used by adapters when mapping violations into host\n * diagnostics (error locality: anchored on the importer edge, resolution shown as\n * explanation, never as the location).\n *\n * `repoRoot` makes every path repository-relative. Absolute paths are the right module\n * identity inside a run and the wrong thing in every output.\n */\nexport function formatViolation(v: Violation, repoRoot?: string): string {\n const at = (p: string): string => (repoRoot === undefined ? p : toRelative(repoRoot, p));\n // Module ids get BOTH treatments, and the order matters. A canonical id is never absolute, so\n // `at` passes it through and the scheme is then stripped; a bare id from an in-memory graph is\n // absolute, so `at` relativizes it and there is no scheme to strip. One expression, both\n // worlds, and neither ever prints a path from the machine that produced the graph.\n const idOf = (id: string): string => displayModuleId(at(id));\n // The printed id is exactly the string to paste into `overrides`.\n const lines = [`[${v.severity}] ${v.ruleId}: ${v.message}`];\n const loc = primarySourceLocation(v);\n if (loc) lines.push(` at ${at(loc.file)}:${loc.line}:${loc.column}`);\n for (const l of v.locations) {\n if (l.type === \"edge\") {\n lines.push(\n l.edge.rawSpecifier !== l.edge.resolvedPath\n ? ` import \"${l.edge.rawSpecifier}\" → resolves to ${idOf(l.edge.resolvedPath)}`\n : ` import \"${l.edge.rawSpecifier}\"`,\n );\n }\n }\n // A finding with several module locations — a cycle — lists them, rather than naming one\n // and burying the rest in prose.\n const modules = v.locations.filter((l) => l.type === \"module\");\n if (modules.length > 1) {\n for (const m of modules) lines.push(` · ${idOf(m.module)}`);\n }\n if (v.explanation) lines.push(` ${v.explanation}`);\n return lines.join(\"\\n\");\n}\n\n/**\n * Stateless: one pass over the finished result, in `onRunEnd`.\n *\n * There is no `onRunStart` and no per-run state to reset, which is what makes it safe for\n * the run object to be memoized across watch rebuilds in the bundler adapters — a reporter\n * that accumulated anything would grow for the life of the process.\n */\nexport function consoleReporter(sink: OutputSink): Reporter {\n return {\n name: \"console\",\n onRunEnd(result) {\n for (const v of result.violations) sink.write(formatViolation(v, result.repoRoot));\n // A violation's most useful next step is the rule's documentation; print it once per\n // rule that actually fired rather than on every line.\n const docs = new Map(\n result.rules\n .filter((r) => r.docsUrl !== undefined && r.violations > 0)\n .map((r) => [r.id, r.docsUrl!]),\n );\n for (const [id, url] of docs) sink.write(` ${id}: ${url}`);\n for (const d of result.diagnostics) sink.write(`${d.severity}: ${d.message}`);\n const { error, warn, info } = countBySeverity(result.violations);\n sink.write(\n `${error} error(s), ${warn} warning(s)${info > 0 ? `, ${info} info` : \"\"} — ${result.stats.moduleCount} modules, ${result.stats.edgeCount} edges in ${Math.round(result.stats.durationMs)}ms`,\n );\n },\n };\n}\n","import type { OutputSink, Reporter } from \"../contracts/reporter.js\";\nimport { toRelative } from \"../paths.js\";\nimport type { Violation, ViolationLocation } from \"../violations.js\";\n\n/**\n * Ids keep their scheme — a machine consumer wants the identity it can correlate against a\n * fingerprint or a baseline, not a prettified path\n *\n * `toRelative` is still applied, and is a no-op on a canonical id, which is never absolute. It\n * is here for the ids that are not canonical: an in-memory graph built by hand uses bare\n * absolute paths, and this document has to be identical on every machine either way.\n */\nfunction serializeLocation(repoRoot: string, l: ViolationLocation): Record<string, unknown> {\n switch (l.type) {\n case \"edge\":\n return {\n type: \"edge\",\n edge: {\n ...l.edge,\n from: toRelative(repoRoot, l.edge.from),\n to: toRelative(repoRoot, l.edge.to),\n resolvedPath: toRelative(repoRoot, l.edge.resolvedPath),\n ...(l.edge.loc !== undefined\n ? { loc: { ...l.edge.loc, file: toRelative(repoRoot, l.edge.loc.file) } }\n : {}),\n },\n };\n case \"module\":\n return { type: \"module\", module: toRelative(repoRoot, l.module) };\n case \"path\":\n return {\n type: \"path\",\n path: toRelative(repoRoot, l.path),\n ...(l.loc !== undefined\n ? { loc: { ...l.loc, file: toRelative(repoRoot, l.loc.file) } }\n : {}),\n };\n }\n}\n\n/** Paths are repository-relative so the document is identical on every machine. */\nfunction serialize(repoRoot: string, v: Violation): Record<string, unknown> {\n return {\n ruleName: v.ruleName,\n ruleId: v.ruleId,\n severity: v.severity,\n message: v.message,\n ...(v.messageId !== undefined ? { messageId: v.messageId } : {}),\n ...(v.data !== undefined ? { data: v.data } : {}),\n locations: v.locations.map((l) => serializeLocation(repoRoot, l)),\n ...(v.explanation !== undefined ? { explanation: v.explanation } : {}),\n fingerprint: v.fingerprint,\n };\n}\n\nexport function jsonReporter(sink: OutputSink): Reporter {\n return {\n name: \"json\",\n onRunEnd(result) {\n sink.write(\n JSON.stringify(\n {\n violations: result.violations.map((v) => serialize(result.repoRoot, v)),\n diagnostics: result.diagnostics,\n rules: result.rules,\n stats: result.stats,\n host: {\n name: result.host.name,\n version: result.host.version,\n capabilities: [...result.host.capabilities],\n },\n delivery: result.delivery,\n },\n null,\n 2,\n ),\n );\n },\n };\n}\n","import type { OutputSink, Reporter } from \"../contracts/reporter.js\";\nimport { toRelative } from \"../paths.js\";\nimport type { Severity, Violation } from \"../violations.js\";\nimport { primarySourceLocation } from \"../violations.js\";\n\n/** ArchWall's vocabulary is not SARIF's; `info` is SARIF's \"note\". */\nconst SARIF_LEVEL: Record<Severity, string> = {\n error: \"error\",\n warn: \"warning\",\n info: \"note\",\n};\n\n/**\n * SARIF locations for a violation.\n *\n * All of them, not just the first: a cycle is one result about N files, and SARIF's\n * `locations` array is exactly the right shape for that. Locations without a source\n * position are omitted — SARIF needs a `physicalLocation`, and inventing line 1 for a\n * module the host gave us no position for would point reviewers at the wrong line.\n */\nfunction sarifLocations(repoRoot: string, v: Violation): unknown[] {\n const out: unknown[] = [];\n for (const l of v.locations) {\n const loc = l.type === \"edge\" ? l.edge.loc : l.type === \"path\" ? l.loc : undefined;\n if (loc === undefined) continue;\n out.push({\n physicalLocation: {\n // MUST be root-relative: GitHub code scanning silently fails to associate a result\n // with a repository file when given an absolute path from the producing machine.\n artifactLocation: { uri: toRelative(repoRoot, loc.file) },\n region: { startLine: loc.line, startColumn: loc.column + 1 },\n },\n });\n }\n if (out.length > 0) return out;\n // No positions anywhere: fall back to naming the file, without a region.\n const fallback = primarySourceLocation(v);\n if (fallback !== undefined) {\n return [\n {\n physicalLocation: {\n artifactLocation: { uri: toRelative(repoRoot, fallback.file) },\n },\n },\n ];\n }\n return [];\n}\n\nexport function sarifReporter(sink: OutputSink): Reporter {\n return {\n name: \"sarif\",\n onRunEnd(result) {\n // Built from the rule INVENTORY, not from the violations: a rule that found nothing\n // still belongs in `tool.driver.rules`, and only the inventory carries the metadata\n // that makes the entry worth anything to a consumer.\n const described = new Map(result.rules.map((r) => [r.id, r]));\n for (const v of result.violations) {\n if (!described.has(v.ruleId)) {\n described.set(v.ruleId, {\n id: v.ruleId,\n name: v.ruleName,\n description: \"\",\n severity: v.severity,\n status: \"ran\",\n violations: 0,\n durationMs: 0,\n });\n }\n }\n const doc = {\n $schema:\n \"https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json\",\n version: \"2.1.0\",\n runs: [\n {\n tool: {\n driver: {\n name: \"archwall\",\n rules: [...described.values()].map((r) => ({\n id: r.id,\n name: r.name,\n ...(r.description !== \"\" ? { shortDescription: { text: r.description } } : {}),\n // SARIF requires an absolute URI here; anything else is silently useless\n // to a consumer, so an unset or relative `docsUrl` is simply omitted.\n ...(r.docsUrl !== undefined && /^https?:\\/\\//.test(r.docsUrl)\n ? { helpUri: r.docsUrl }\n : {}),\n })),\n },\n },\n results: result.violations.map((v) => ({\n ruleId: v.ruleId,\n level: SARIF_LEVEL[v.severity],\n message: {\n text: v.explanation ? `${v.message} — ${v.explanation}` : v.message,\n },\n // Lets consuming tools track a finding across commits even as it moves.\n partialFingerprints: { archwall: v.fingerprint },\n ...(v.data !== undefined ? { properties: v.data } : {}),\n locations: sarifLocations(result.repoRoot, v),\n })),\n // SARIF has a channel for tool-level notifications; diagnostics belong in it.\n // Dropping them made `no-modules-classified` — \"ArchWall never looked at your\n // code\" — invisible in exactly the CI path where it matters most.\n invocations: [\n {\n executionSuccessful: !result.diagnostics.some((d) => d.severity === \"error\"),\n toolExecutionNotifications: result.diagnostics.map((d) => ({\n level: SARIF_LEVEL[d.severity],\n message: { text: d.message },\n descriptor: { id: d.code },\n ...(d.ruleId !== undefined ? { associatedRule: { id: d.ruleId } } : {}),\n })),\n },\n ],\n },\n ],\n };\n sink.write(JSON.stringify(doc, null, 2));\n },\n };\n}\n","import type { OutputDestination, OutputSink, Reporter, ReporterIO } from \"../contracts/reporter.js\";\nimport { ArchWallError } from \"../errors.js\";\nimport { consoleReporter, defaultIO } from \"./console.js\";\nimport { jsonReporter } from \"./json.js\";\nimport { sarifReporter } from \"./sarif.js\";\n\nexport type BuiltinReporterName = \"console\" | \"json\" | \"sarif\";\n\nexport const BUILTIN_REPORTER_NAMES: readonly BuiltinReporterName[] = [\"console\", \"json\", \"sarif\"];\n\nconst BUILTINS: Record<BuiltinReporterName, (sink: OutputSink) => Reporter> = {\n console: consoleReporter,\n json: jsonReporter,\n sarif: sarifReporter,\n};\n\nexport function isBuiltinReporterName(name: string): name is BuiltinReporterName {\n return name in BUILTINS;\n}\n\n/**\n * A reporter plus where its output goes.\n *\n * The object form exists so that `sarif` can write `archwall.sarif` while `console` keeps\n * the terminal, in one run. Without it every reporter shares one stream and machine-readable\n * output is contaminated by human-readable output.\n */\nexport interface ReporterOutputSpec {\n reporter: BuiltinReporterName | Reporter;\n /** Default `\"stdout\"`. Also accepts `\"stderr\"` or a file path. */\n output?: OutputDestination;\n}\n\n/**\n * `string` is accepted so a config file can name a third-party reporter\n * (`\"archwall-reporter-teamcity\"`). Those are resolved to objects before reaching the\n * engine; a string that survives to here is one nothing could load.\n */\nexport type ReporterSpec = BuiltinReporterName | Reporter | ReporterOutputSpec | (string & {});\n\nexport interface ResolvedReporters {\n reporters: readonly Reporter[];\n /** Closes every sink this opened. Awaited after `onRunEnd`, before acting on the result. */\n close(): Promise<void>;\n}\n\nfunction normalize(spec: ReporterSpec): ReporterOutputSpec {\n if (typeof spec === \"string\") return { reporter: spec as BuiltinReporterName };\n if (\"reporter\" in spec) return spec;\n return { reporter: spec };\n}\n\nexport function resolveReporters(\n specs: readonly ReporterSpec[],\n io: ReporterIO = defaultIO,\n): ResolvedReporters {\n const opened: OutputSink[] = [];\n const reporters = specs.map((raw) => {\n const spec = normalize(raw);\n if (typeof spec.reporter !== \"string\") return spec.reporter;\n const factory = BUILTINS[spec.reporter as BuiltinReporterName];\n if (!factory) {\n throw new ArchWallError(\n `Unknown reporter \"${spec.reporter}\". Built-ins: ${BUILTIN_REPORTER_NAMES.join(\", \")}. ` +\n `A third-party reporter must be installed and resolvable, or passed as an object.`,\n );\n }\n const sink = io.open(spec.output ?? \"stdout\");\n opened.push(sink);\n return factory(sink);\n });\n\n return {\n reporters,\n async close() {\n for (const sink of opened) await sink.close?.();\n },\n };\n}\n","import * as path from \"node:path\";\nimport type { Classifier } from \"./contracts/classifier.js\";\nimport type { Diagnostic, DiagnosticCode } from \"./contracts/diagnostic.js\";\nimport type { Preset } from \"./contracts/preset.js\";\nimport type { AnyConfiguredRule, Rule, RuleScope, RuleSettings } from \"./contracts/rule.js\";\nimport type { GraphTransform } from \"./contracts/transform.js\";\nimport { matchesPattern } from \"./match.js\";\nimport type { ReporterSpec } from \"./reporters/resolve.js\";\nimport { BUILTIN_REPORTER_NAMES, isBuiltinReporterName } from \"./reporters/resolve.js\";\nimport type { Severity } from \"./violations.js\";\n\nexport type FailOn = \"error\" | \"warn\" | \"never\";\nexport type { BuiltinReporterName, ReporterOutputSpec, ReporterSpec } from \"./reporters/resolve.js\";\n\n/**\n * Which diagnostics are severe enough to fail the run, independently of violations.\n *\n * `ruleFailed` and `invalidConfig` default to true and should stay that way: a rule that\n * throws, and a rule dropped because its configuration was invalid, both produce no results\n * — so a run in which either happened is a run that did not check what you asked it to. An\n * enforcement tool that passes green when a rule crashes is not an enforcement tool.\n */\nexport interface FailOnDiagnostics {\n /** A rule threw. Default true. */\n ruleFailed?: boolean;\n /** A rule was skipped for missing host capabilities. Default false. */\n ruleSkipped?: boolean;\n /** Classification tagged nothing, or the boundary matched nothing. Default false. */\n emptyAnalysis?: boolean;\n /**\n * A rule's `scope` resolved to zero modules. Default false.\n *\n * Its own switch rather than part of `emptyAnalysis`: \"the run looked at nothing\" and \"this\n * one rule looked at nothing\" are different failures, and a monorepo where some packages\n * legitimately have no modules yet wants to tolerate the second while still gating the first.\n */\n emptyScope?: boolean;\n /** A rule's options failed its schema, so the rule did not run. Default true. */\n invalidOptions?: boolean;\n /** The configuration itself is wrong and something was dropped. Default true. */\n invalidConfig?: boolean;\n /** A configured rule is deprecated. Default false. */\n deprecated?: boolean;\n}\n\nexport interface ResolvedFailOnDiagnostics {\n ruleFailed: boolean;\n ruleSkipped: boolean;\n emptyAnalysis: boolean;\n emptyScope: boolean;\n invalidOptions: boolean;\n invalidConfig: boolean;\n deprecated: boolean;\n}\n\n/**\n * Which diagnostic codes each `failOnDiagnostics` switch governs, and whether it is on by\n * default. The single source of truth for both.\n *\n * One table because there used to be three: the code list lived in `@archwall/integration-kit`,\n * the defaults lived in `resolveConfig` below, and a second copy of the defaults lived beside\n * the code list. Nothing linked them, so adding a gate meant remembering all three, and\n * forgetting the third produced a switch that resolved correctly and then gated nothing.\n *\n * The `satisfies` is what keeps it honest: a key added to {@link ResolvedFailOnDiagnostics}\n * and not here is a compile error, and vice versa.\n */\nexport const DIAGNOSTIC_GATES = {\n ruleFailed: { codes: [\"rule-failed\"], default: true },\n ruleSkipped: { codes: [\"rule-skipped\"], default: false },\n emptyAnalysis: { codes: [\"no-modules-classified\", \"empty-project\"], default: false },\n emptyScope: { codes: [\"empty-scope\"], default: false },\n invalidOptions: { codes: [\"invalid-rule-options\"], default: true },\n invalidConfig: { codes: [\"invalid-config\"], default: true },\n deprecated: { codes: [\"rule-deprecated\"], default: false },\n} as const satisfies Record<\n keyof ResolvedFailOnDiagnostics,\n { codes: readonly DiagnosticCode[]; default: boolean }\n>;\n\nconst GATE_KEYS = Object.keys(DIAGNOSTIC_GATES) as (keyof ResolvedFailOnDiagnostics)[];\n\n/**\n * Applies {@link DIAGNOSTIC_GATES}' defaults to whatever the user left unset.\n *\n * Spelled out key by key rather than mapped over `GATE_KEYS`, so that adding a gate is a\n * compile error here until it is handled. The values still come from the one table; only the\n * exhaustiveness is restated, and restating it is the thing being bought.\n */\nexport function resolveFailOnDiagnostics(\n user: FailOnDiagnostics | undefined,\n): ResolvedFailOnDiagnostics {\n const gate = (key: keyof ResolvedFailOnDiagnostics): boolean =>\n user?.[key] ?? DIAGNOSTIC_GATES[key].default;\n return {\n ruleFailed: gate(\"ruleFailed\"),\n ruleSkipped: gate(\"ruleSkipped\"),\n emptyAnalysis: gate(\"emptyAnalysis\"),\n emptyScope: gate(\"emptyScope\"),\n invalidOptions: gate(\"invalidOptions\"),\n invalidConfig: gate(\"invalidConfig\"),\n deprecated: gate(\"deprecated\"),\n };\n}\n\n/** The diagnostic codes that should fail a run, given the resolved gates. */\nexport function failingDiagnosticCodes(gates: ResolvedFailOnDiagnostics): Set<DiagnosticCode> {\n return new Set(\n GATE_KEYS.filter((key) => gates[key]).flatMap((key) => DIAGNOSTIC_GATES[key].codes),\n );\n}\n\n/**\n * Retune one rule instance. The shorthand form sets severity only; the object form can also\n * patch options, scope, and wording.\n *\n * Options merge by ONE policy, the same one used everywhere: **top-level keys replace, and\n * arrays are replaced wholesale, never concatenated.**\n */\nexport type RuleOverride =\n | Severity\n | \"off\"\n | {\n severity?: Severity | \"off\";\n options?: Record<string, unknown>;\n scope?: RuleScope;\n message?: string | Record<string, string>;\n };\n\n/**\n * A preset, or the name of a package exporting one.\n *\n * The string form is what makes a plugin ecosystem possible: without it a preset can only\n * be `import`ed, which forecloses JSON/YAML configuration and any `--preset` flag forever.\n * Strings are resolved by the config loader (`@archwall/integration-kit`), which is the\n * layer that has a module resolver; one that reaches {@link resolveConfig} unresolved is\n * reported as a configuration error rather than silently ignored.\n *\n * `[\"@acme/preset\", { … }]` calls the package's default export with those options.\n */\nexport type PresetSpec = Preset | string | readonly [string, Record<string, unknown>?];\n\n/** A configured rule, or the name of a package/built-in exporting one. See {@link PresetSpec}. */\nexport type RuleSpec =\n | AnyConfiguredRule\n | string\n | readonly [string, Record<string, unknown>?, RuleSettings?];\n\nexport interface UserConfig {\n /**\n * Configurations to inherit from, nearest-last: a later entry wins over an earlier one,\n * and this config wins over all of them.\n *\n * Arrays (`presets`, `rules`, `classifiers`, `transforms`, `reporters`, `exclude`)\n * CONCATENATE base-first, because rules already merge by instance id downstream and\n * `overrides` already exists for retuning. Scalars replace. `overrides` merges key-wise.\n *\n * This is the only way to ship an organisation-wide configuration: a `Preset` cannot set\n * `failOn`, `include`, `exclude`, `repoRoot`, or `reporters`, so without `extends` a\n * shared config is a preset plus a README telling every repository to copy twenty lines.\n */\n extends?: string | string[];\n /**\n * Where the *repository* starts, relative to the config file / cwd. Default \".\".\n *\n * The base for everything that leaves the process — reporter output, SARIF\n * `artifactLocation.uri`, violation fingerprints — so it must be the path a checkout is\n * rooted at, not the path your sources happen to live under.\n */\n repoRoot?: string;\n /**\n * Where the *sources* start, relative to {@link repoRoot}. Default \".\".\n *\n * The base for `include`/`exclude` matching and for classifier patterns — the tree whose\n * shape your architecture is described in. This is the one that is usually `\"src\"`.\n */\n sourceRoot?: string;\n include?: string[];\n /**\n * Patterns ADDED to the defaults (`node_modules`, `*.test.*`, `*.spec.*`), not a\n * replacement for them. Use {@link excludeDefaults} to opt out deliberately.\n */\n exclude?: string[];\n /** Set false to drop the built-in `exclude` defaults entirely. */\n excludeDefaults?: boolean;\n presets?: PresetSpec[];\n /** Appended after preset classifiers. */\n classifiers?: Classifier[];\n /** Appended after preset transforms; run between the project boundary and classification. */\n transforms?: GraphTransform[];\n /** Merged after preset rules (last-writer-wins, keyed by rule instance id). */\n rules?: RuleSpec[];\n /**\n * Retunes rule instances; ALWAYS wins over presets and rules. Keys are an exact instance\n * id (\"fsd/public-api\"), a bare rule name (every instance of it), or a glob (\"fsd/*\").\n * A key that matches no rule is an error, not a silent no-op.\n */\n overrides?: Record<string, RuleOverride>;\n /**\n * Built-ins by name, customs by object, third-party by package name, and\n * `{ reporter, output }` to send one somewhere other than stdout. Default [\"console\"].\n */\n reporters?: ReporterSpec[];\n /** Which VIOLATION severity gates the run. `info` findings never fail it. */\n failOn?: FailOn;\n /** Which DIAGNOSTICS gate the run, regardless of `failOn`. */\n failOnDiagnostics?: FailOnDiagnostics;\n}\n\nexport function defineConfig(config: UserConfig): UserConfig {\n return config;\n}\n\nexport interface ResolvedRule {\n rule: Rule<any>;\n /** Instance id; what violations report and what `overrides` keys match. */\n id: string;\n options: unknown;\n severity: Severity;\n /** Narrows the graph this instance sees; applied by the engine, never by the rule. */\n scope?: RuleScope;\n /** Per-instance message templates. */\n message?: string | Record<string, string>;\n}\n\nexport interface ResolvedConfig {\n /** Absolute. Base for reported paths and fingerprints. */\n repoRoot: string;\n /** Absolute, at or below {@link repoRoot}. Base for the boundary and classifiers. */\n sourceRoot: string;\n include: string[];\n exclude: string[];\n classifiers: readonly Classifier[];\n transforms: readonly GraphTransform[];\n rules: readonly ResolvedRule[];\n /** Reporter instantiation is deferred to the run edge (resolveReporters). */\n reporterSpecs: readonly ReporterSpec[];\n failOn: FailOn;\n failOnDiagnostics: ResolvedFailOnDiagnostics;\n /**\n * Everything wrong with the configuration itself, found before any graph work.\n *\n * Reported rather than thrown: a throw inside a bundler's `buildEnd` produces a stack\n * trace and destroys every other finding in the run. One mistyped `overrides` key costs\n * you that key, not the analysis — and `failOnDiagnostics.invalidConfig` still fails the\n * run.\n */\n diagnostics: readonly Diagnostic[];\n}\n\n/**\n * Everything under `sourceRoot`, deliberately — NOT an extension allow-list.\n *\n * `include`/`exclude` are applied to the graph, where the compiler has already decided what\n * counts as a module. Re-filtering by extension there would silently drop every `.vue`,\n * `.svelte`, `.astro`, and `.mts` module the host legitimately compiled. Deciding *which\n * files to open and parse* belongs to the one surface that enumerates a directory tree: the\n * CLI's scanner keeps its own list of extensions it can lex.\n */\nconst DEFAULT_INCLUDE = [\"**\"];\nconst DEFAULT_EXCLUDE = [\"**/node_modules/**\", \"**/*.test.*\", \"**/*.spec.*\"];\n\nfunction configError(message: string, ruleId?: string): Diagnostic {\n return {\n code: \"invalid-config\",\n severity: \"error\",\n ...(ruleId !== undefined ? { ruleId } : {}),\n message,\n };\n}\n\ninterface IdentifiedRule {\n configured: AnyConfiguredRule;\n id: string;\n}\n\n/**\n * Namespacing preset rules is what makes `presets: [a(), b()]` safe: without it two presets\n * configuring the same rule collide on one key and shallow-merge their options.\n *\n * …which only works if the names are actually distinct. Two instances of one preset — the\n * natural way to describe a monorepo — would produce identical ids, the same collision\n * arrived at from the other direction, so a duplicate name is reported and the later preset\n * is namespaced apart rather than silently merged.\n */\nfunction withIds(\n presets: readonly Preset[],\n userRules: readonly AnyConfiguredRule[],\n diagnostics: Diagnostic[],\n): IdentifiedRule[] {\n const namespaces = new Map<string, number>();\n const preset: IdentifiedRule[] = [];\n for (const p of presets) {\n const seen = namespaces.get(p.name) ?? 0;\n namespaces.set(p.name, seen + 1);\n let namespace = p.name;\n if (seen > 0) {\n namespace = `${p.name}#${seen + 1}`;\n diagnostics.push(\n configError(\n `Two presets are both named \"${p.name}\", so their rules would collide on the same ids and ` +\n `silently merge their options. The later one's rules were namespaced \"${namespace}/…\" instead. ` +\n `Give it a distinct name, or configure its rules explicitly with their own \\`id\\`s.`,\n ),\n );\n }\n for (const r of p.rules) {\n preset.push({ configured: r, id: r.id ?? `${namespace}/${r.rule.meta.name}` });\n }\n }\n\n const own: IdentifiedRule[] = [];\n for (const r of userRules) {\n if (r.id !== undefined) {\n own.push({ configured: r, id: r.id });\n continue;\n }\n const name = r.rule.meta.name;\n // A bare user rule tunes the preset's instance rather than adding a second one — two\n // instances of the same rule would report every violation twice.\n const fromPresets = preset.filter((p) => p.configured.rule.meta.name === name);\n if (fromPresets.length === 1) {\n own.push({ configured: r, id: fromPresets[0]!.id });\n } else if (fromPresets.length > 1) {\n diagnostics.push(\n configError(\n `Rule \"${name}\" is configured by more than one preset (${fromPresets.map((p) => p.id).join(\", \")}), ` +\n `so a bare rules[] entry is ambiguous and was dropped. Give it an explicit \\`id\\`, or use \\`overrides\\` to target one.`,\n ),\n );\n } else {\n own.push({ configured: r, id: name });\n }\n }\n\n return [...preset, ...own];\n}\n\n/**\n * THE options-merge policy. One rule, applied identically everywhere two option bags meet:\n * preset over preset, `rules[]` over preset, and `overrides.options` over both.\n *\n * **Top-level keys replace. Nothing is deep-merged, and arrays are never concatenated.**\n * Arrays here are values, not collections: `layers: [\"ui\", \"domain\"]` describes a total\n * order and `forbid: [...]` a complete policy. Replacement is the only rule that lets an\n * override *remove* something.\n */\nfunction mergeRuleOptions(\n base: Record<string, unknown> | undefined,\n patch: Record<string, unknown> | undefined,\n): Record<string, unknown> {\n return { ...base, ...patch };\n}\n\n/**\n * Validates one rule's options against its `optionsSchema`, at CONFIG time.\n *\n * A bad options bag is a configuration mistake: it is known before any graph work happens,\n * it cannot be fixed by re-running, and it should be reported once, as a diagnostic,\n * alongside everything else wrong with the config.\n *\n * Schemas must validate synchronously. An async schema is not rejected as invalid — it is\n * reported as unusable, which is a different and more accurate complaint.\n */\nfunction validateOptions(\n rule: Rule<any>,\n id: string,\n options: Record<string, unknown>,\n): { value: unknown; diagnostic?: undefined } | { value?: undefined; diagnostic: Diagnostic } {\n const schema = rule.meta.optionsSchema;\n if (!schema) return { value: options };\n\n const result = schema[\"~standard\"].validate(options);\n if (result instanceof Promise) {\n return {\n diagnostic: {\n code: \"invalid-rule-options\",\n severity: \"error\",\n ruleId: id,\n message:\n `Rule \"${id}\" has an asynchronous \\`optionsSchema\\`, which cannot be evaluated while resolving ` +\n `configuration. Use a synchronous schema.`,\n },\n };\n }\n if (result.issues) {\n const detail = result.issues.map((i) => i.message).join(\"; \");\n return {\n diagnostic: {\n code: \"invalid-rule-options\",\n severity: \"error\",\n ruleId: id,\n message: `Invalid options for rule \"${id}\": ${detail}`,\n details: { issues: result.issues.map((i) => i.message) },\n },\n };\n }\n return { value: result.value };\n}\n\n/** Splits materialized entries from string specs the loader was supposed to resolve. */\nfunction materialized<T extends object, S>(\n specs: readonly (T | S)[],\n what: string,\n diagnostics: Diagnostic[],\n): T[] {\n const out: T[] = [];\n for (const spec of specs) {\n if (typeof spec === \"string\" || Array.isArray(spec)) {\n const name = typeof spec === \"string\" ? spec : String((spec as unknown[])[0]);\n diagnostics.push(\n configError(\n `${what} \"${name}\" was given as a name, but nothing resolved it to a module. ` +\n `Named ${what.toLowerCase()}s are resolved when the config is loaded from a file; ` +\n `if you are calling resolveConfig() directly, pass the imported object instead.`,\n ),\n );\n continue;\n }\n out.push(spec as T);\n }\n return out;\n}\n\nexport function resolveConfig(user: UserConfig, opts?: { cwd?: string }): ResolvedConfig {\n const cwd = opts?.cwd ?? process.cwd();\n const diagnostics: Diagnostic[] = [];\n\n if (\"root\" in user) {\n diagnostics.push(\n configError(\n \"`root` has been split into `repoRoot` (base for reported paths, SARIF, and fingerprints) \" +\n \"and `sourceRoot` (base for include/exclude and classifier patterns). \" +\n 'A config that used `root: \"src\"` almost certainly wants `sourceRoot: \"src\"` with `repoRoot` left at its default.',\n ),\n );\n }\n if (user.extends !== undefined) {\n diagnostics.push(\n configError(\n \"`extends` was not resolved. It is followed when the config is loaded from a file; \" +\n \"resolveConfig() receives an already-flattened config.\",\n ),\n );\n }\n\n const presets = materialized<Preset, string | readonly unknown[]>(\n user.presets ?? [],\n \"Preset\",\n diagnostics,\n );\n const userRules = materialized<AnyConfiguredRule, string | readonly unknown[]>(\n user.rules ?? [],\n \"Rule\",\n diagnostics,\n );\n\n interface Entry {\n rule: Rule<any>;\n scope?: RuleScope;\n options: Record<string, unknown>;\n severity?: Severity | \"off\";\n message?: string | Record<string, string>;\n }\n const merged = new Map<string, Entry>();\n for (const { configured, id } of withIds(presets, userRules, diagnostics)) {\n const prev = merged.get(id);\n const severity = configured.severity ?? prev?.severity;\n // Scope replaces rather than merges, for the same reason array options do: a scope is\n // one complete description of where a rule applies, and a merged one is a place neither\n // author asked for.\n const scope = configured.scope ?? prev?.scope;\n const message = configured.message ?? prev?.message;\n merged.set(id, {\n rule: configured.rule,\n options: mergeRuleOptions(\n prev?.options,\n configured.options as Record<string, unknown> | undefined,\n ),\n ...(severity !== undefined ? { severity } : {}),\n ...(scope !== undefined ? { scope } : {}),\n ...(message !== undefined ? { message } : {}),\n });\n }\n\n for (const [key, override] of Object.entries(user.overrides ?? {})) {\n const targets = [...merged.entries()].filter(\n ([id, entry]) => id === key || entry.rule.meta.name === key || matchesPattern(id, key),\n );\n if (targets.length === 0) {\n const known = [...merged.keys()].sort().join(\", \");\n diagnostics.push(\n configError(\n `Override key \"${key}\" matches no configured rule and was ignored. Configured rules: ${known || \"(none)\"}.`,\n ),\n );\n continue;\n }\n const patch = typeof override === \"string\" ? { severity: override } : override;\n for (const [, entry] of targets) {\n if (patch.severity !== undefined) entry.severity = patch.severity;\n if (patch.options !== undefined)\n entry.options = mergeRuleOptions(entry.options, patch.options);\n if (patch.scope !== undefined) entry.scope = patch.scope;\n if (patch.message !== undefined) entry.message = patch.message;\n }\n }\n\n const rules: ResolvedRule[] = [];\n for (const [id, entry] of merged) {\n const severity = entry.severity ?? entry.rule.meta.defaultSeverity;\n if (severity === \"off\") continue;\n const validated = validateOptions(entry.rule, id, entry.options);\n if (validated.diagnostic !== undefined) {\n // A rule whose options are invalid cannot run correctly, so it does not run at all.\n // Dropping it loudly beats running it on a bad options bag and reporting nonsense.\n diagnostics.push(validated.diagnostic);\n continue;\n }\n rules.push({\n rule: entry.rule,\n id,\n options: validated.value,\n severity,\n ...(entry.scope !== undefined ? { scope: entry.scope } : {}),\n ...(entry.message !== undefined ? { message: entry.message } : {}),\n });\n }\n\n const reporterSpecs: ReporterSpec[] = [];\n for (const spec of [\n ...(user.reporters ?? [\"console\"]),\n ...presets.flatMap((p) => p.reporters ?? []),\n ]) {\n const name =\n typeof spec === \"string\"\n ? spec\n : typeof (spec as { reporter?: unknown }).reporter === \"string\"\n ? (spec as { reporter: string }).reporter\n : undefined;\n if (name !== undefined && !isBuiltinReporterName(name)) {\n diagnostics.push(\n configError(\n `Reporter \"${name}\" is not a built-in (${BUILTIN_REPORTER_NAMES.join(\", \")}) and nothing resolved it ` +\n `to a module, so it was dropped. Named reporters are resolved when the config is loaded from a file.`,\n ),\n );\n continue;\n }\n reporterSpecs.push(spec);\n }\n\n const repoRoot = path.resolve(cwd, user.repoRoot ?? \".\");\n\n return {\n repoRoot,\n // Relative to the repo root, not to cwd: the two roots describe one nested tree, and\n // resolving them independently would let them drift apart under a different cwd.\n sourceRoot: path.resolve(repoRoot, user.sourceRoot ?? \".\"),\n include: user.include ?? [...DEFAULT_INCLUDE],\n // MERGED, not replaced. Adding one pattern must not silently re-admit node_modules and\n // every test file in the project.\n exclude: [...(user.excludeDefaults === false ? [] : DEFAULT_EXCLUDE), ...(user.exclude ?? [])],\n classifiers: [...presets.flatMap((p) => p.classifiers), ...(user.classifiers ?? [])],\n transforms: [...presets.flatMap((p) => p.transforms ?? []), ...(user.transforms ?? [])],\n rules,\n reporterSpecs,\n failOn: user.failOn ?? \"error\",\n failOnDiagnostics: resolveFailOnDiagnostics(user.failOnDiagnostics),\n diagnostics,\n };\n}\n","import type { Classifier } from \"./classifier.js\";\nimport type { Reporter } from \"./reporter.js\";\nimport type { AnyConfiguredRule } from \"./rule.js\";\nimport type { GraphTransform } from \"./transform.js\";\n\n/**\n * Everything a third party can ship as one installable unit.\n *\n * There is deliberately no separate `Plugin` type above this one. A second, near-identical\n * bundle would mean everyone has to learn which of the two they need and every downstream\n * API has to accept both; widening the one bundle that already exists costs a few optional\n * fields and no new vocabulary.\n *\n * The optional fields are declared up front on purpose: `Preset` is promised as stable, and\n * adding a field to a stable type is a breaking change for anyone who wrote\n * `satisfies Preset`.\n */\n/**\n * Descriptive facts about a preset. Nothing in core reads these yet — they exist now because\n * `Preset` is promised as stable, so this is the last moment at which adding them is free.\n *\n * The index signature is the load-bearing part: with it, every future named field is additive\n * and a third party can carry its own facts without waiting for core. Without it, this type\n * would have exactly the problem it exists to solve.\n */\nexport interface PresetMeta {\n /** The preset package's version, for reporters and bug reports. */\n version?: string;\n description?: string;\n docsUrl?: string;\n [key: string]: unknown;\n}\n\nexport interface Preset {\n name: string;\n classifiers: Classifier[];\n rules: AnyConfiguredRule[];\n /** See {@link PresetMeta}. Purely descriptive; it never affects analysis. */\n meta?: PresetMeta;\n /**\n * Passes that enrich the graph before classification — the slot a TypeScript type-edge\n * enricher, or any other \"add facts the bundler didn't give us\" pass, lives in.\n */\n transforms?: GraphTransform[];\n /**\n * Reporters the preset contributes. Appended to whatever the user configured rather\n * than replacing it: a preset that ships an uploader should not silently remove the\n * console output the user is reading.\n */\n reporters?: Reporter[];\n}\n\nexport function definePreset<A extends unknown[]>(\n fn: (...args: A) => Preset,\n): (...args: A) => Preset {\n return fn;\n}\n","import type { Capability, GraphDelivery, HostInfo } from \"../graph/ir.js\";\nimport type { Severity, Violation } from \"../violations.js\";\nimport type { Diagnostic } from \"./diagnostic.js\";\n\n/**\n * Where a reporter's output goes.\n *\n * `\"stdout\"` and `\"stderr\"` are the two every environment has; anything else is a file\n * path, which only a host with a filesystem can honour. A reporter never decides this —\n * it writes to the sink it is handed.\n */\nexport type OutputDestination = \"stdout\" | \"stderr\" | (string & {});\n\nexport interface OutputSink {\n write(text: string): void;\n /** Flushed and awaited before the run's result is acted on. */\n close?(): void | Promise<void>;\n}\n\n/**\n * Opens destinations. The seam that lets the same built-in reporter write to a terminal,\n * to stderr, or to `archwall.sarif` without knowing which.\n *\n * Per-reporter, so that a machine-readable document and a human summary in the same run\n * never share a stream.\n */\nexport interface ReporterIO {\n open(destination: OutputDestination): OutputSink;\n}\n\nexport interface RunInfo {\n /**\n * Unique per analysis. In watch mode one reporter instance may see many runs, and\n * without a way to tell them apart any per-run state it keeps grows forever. A custom\n * reporter that accumulates anything should key it on this and drop the previous run's.\n */\n runId: string;\n host: HostInfo;\n startedAt: number;\n /** Absolute repository root, so a reporter can relativize before its first output. */\n repoRoot: string;\n}\n\nexport interface AnalysisStats {\n moduleCount: number;\n edgeCount: number;\n durationMs: number;\n}\n\n/**\n * What happened to one configured rule instance in this run.\n *\n * Without it there is no way to answer \"did my rule actually run?\" — the question behind\n * every report of the tool being silently wrong. It is also what lets a reporter emit rule\n * metadata for rules that produced no violations, which SARIF's `tool.driver.rules` wants.\n */\nexport interface RuleRunInfo {\n /** Instance id — what `overrides` matches and what violations report. */\n id: string;\n name: string;\n description: string;\n docsUrl?: string;\n severity: Severity;\n /**\n * `ran` — checked, whether or not it found anything.\n * `skipped` — the host could not provide capabilities it requires.\n * `failed` — it threw; see the matching `rule-failed` diagnostic.\n *\n * Rules dropped for invalid options or invalid configuration never reach the engine and\n * so are absent here; they appear as diagnostics.\n */\n status: \"ran\" | \"skipped\" | \"failed\";\n /** Violations this instance produced. */\n violations: number;\n durationMs: number;\n /** Present when `status: \"skipped\"`. */\n missingCapabilities?: readonly Capability[];\n /** Present when the rule is deprecated; mirrors the `rule-deprecated` diagnostic. */\n deprecated?: boolean;\n}\n\nexport interface AnalysisResult {\n /** Deterministically ordered; see `compareViolations`. */\n violations: readonly Violation[];\n /** Everything that is not a violation: skipped rules, crashed rules, config problems. */\n diagnostics: readonly Diagnostic[];\n stats: AnalysisStats;\n /** Every rule instance the engine saw, in configuration order. */\n rules: readonly RuleRunInfo[];\n host: HostInfo;\n delivery: GraphDelivery;\n /**\n * Absolute repository root. Reporters need it to emit repo-relative paths, and without\n * it correct SARIF is impossible: `artifactLocation.uri` must be repo-relative or GitHub\n * code scanning cannot associate a result with a file.\n */\n repoRoot: string;\n}\n\n/**\n * Two hooks, both batch. There is deliberately no per-violation streaming hook\n */\nexport interface Reporter {\n name: string;\n /**\n * Called before the engine runs. The place to reset per-run state, which matters because\n * one reporter instance can outlive many runs in watch mode.\n */\n onRunStart?(info: RunInfo): void | Promise<void>;\n /** Awaited, so a reporter may write a file or flush a socket. */\n onRunEnd(result: AnalysisResult): void | Promise<void>;\n}\n\nexport function defineReporter(reporter: Reporter): Reporter {\n return reporter;\n}\n","import type { Capability, Edge, ModuleId, ModuleNode } from \"../graph/ir.js\";\nimport type { EdgeFilter, GraphQuery, ModuleFilter } from \"../graph/query.js\";\nimport type { Severity, ViolationInput } from \"../violations.js\";\nimport type { GraphComputation } from \"./analysis.js\";\nimport type { StandardSchemaV1 } from \"./standard-schema.js\";\n\n/**\n * Marks a rule, or one of its options, as on the way out.\n *\n * One optional field, and the only thing standing between the project and a choice\n * between \"never rename anything\" and \"break everyone\". The engine turns it into a\n * `rule-deprecated` diagnostic when a deprecated rule is configured.\n */\nexport interface RuleDeprecation {\n /** Version in which the deprecation was announced. */\n since: string;\n /** Instance id or rule name to migrate to, when there is a direct replacement. */\n replacedBy?: string;\n /** Why, and what to do instead, when `replacedBy` alone does not say it. */\n reason?: string;\n /** Option names that are deprecated, when the rule itself is not. */\n options?: Record<string, string>;\n}\n\nexport interface RuleMeta<Options> {\n name: string;\n description: string;\n docsUrl?: string;\n optionsSchema?: StandardSchemaV1<unknown, Options>;\n defaultSeverity: Severity;\n requiredCapabilities?: Capability[];\n /**\n * `messageId` → template, with `{placeholder}` interpolation from the reported `data`.\n *\n * Rules report an id and a data bag rather than a finished sentence, so the wording stays\n * a property of the rule's *metadata*: retargetable per instance via\n * `ConfiguredRule.message`, translatable, and machine-groupable.\n */\n messages?: Record<string, string>;\n /** Part of the curated set a \"recommended\" preset would enable. */\n recommended?: boolean;\n deprecated?: RuleDeprecation;\n}\n\nexport interface RuleContext<Options> {\n options: Options;\n graph: GraphQuery;\n /**\n * Absolute source root. The base for any path *pattern* a rule matches against, so that\n * rule options read the same way as classifier patterns and `include`/`exclude`.\n */\n sourceRoot: string;\n /**\n * Absolute repository root. For paths a rule puts in front of a human or another tool;\n * reporters relativize against this.\n */\n repoRoot: string;\n /**\n * A file's path relative to {@link sourceRoot}, forward-slashed, or null when it lies\n * outside. Every rule that matches paths needs exactly this, and hand-rolling it is how\n * six copies with three different edge-case behaviours came to exist.\n */\n relative(file: string): string | null;\n /**\n * A module id as a human should read it — `src/domain/rules.ts`, `react`, `node:fs`.\n *\n * Use it for anything that goes into a message's `data`. A canonical {@link ModuleId} is\n * scheme-prefixed, and a rule that interpolates one raw puts `file:src/a.ts` in front of a\n * user.\n */\n display(id: ModuleId): string;\n /**\n * Shared memoized graph computations, one evaluation per run.\n *\n * Scoped like `graph`: a computation requested by a scoped rule is evaluated over that rule's\n * slice.\n */\n compute<T>(computation: GraphComputation<T>): T;\n report(violation: ViolationInput): void;\n}\n\n/**\n * What a rule wants to look at, declared rather than fetched.\n *\n * The engine owns the traversal, so one slice of the graph is evaluated once for every rule\n * that wants it, and the engine knows which rules a given edge can affect — the\n * prerequisite for incremental validation.\n *\n * `check` remains for rules that genuinely need the whole graph at once (cycle detection,\n * reachability). It is the exception, not the interface.\n */\nexport interface RuleVisitors<Options> {\n edges?: {\n /**\n * Narrows the edges `visit` receives. A function of the rule's options, because the\n * interesting filters depend on them (`crossing: options.tagKey`).\n */\n filter?: (options: Options) => EdgeFilter | undefined;\n visit(edge: Edge, ctx: RuleContext<Options>): void;\n };\n modules?: {\n filter?: (options: Options) => ModuleFilter | undefined;\n visit(module: ModuleNode, ctx: RuleContext<Options>): void;\n };\n}\n\nexport interface Rule<Options = unknown> {\n meta: RuleMeta<Options>;\n /** Declared interest; the engine drives the traversal. Preferred. */\n visits?: RuleVisitors<Options>;\n /** Whole-graph escape hatch, for rules that cannot be expressed as a traversal. */\n check?(ctx: RuleContext<Options>): void | Promise<void>;\n}\n\n/**\n * Instance settings, deliberately a SEPARATE bag from the rule's options, so that no\n * option name is reserved across every rule that will ever exist.\n */\nexport interface RuleSettings {\n id?: string;\n severity?: Severity | \"off\";\n scope?: RuleScope;\n /**\n * Retargets this instance's wording: one template when the rule has a single message, or\n * `messageId` → template.\n */\n message?: string | Record<string, string>;\n}\n\n/**\n * Restricts one rule instance to part of the graph.\n *\n * This is what makes a monorepo expressible: \"FSD under `apps/web`, layered under\n * `services/api`\" is two instances of two rules with two scopes, in ONE config and ONE\n * pass. Applied by the ENGINE, by narrowing the `GraphQuery` a rule receives, so every\n * rule that will ever be written inherits scoping for free and none of them has to know it\n * exists.\n */\nexport interface RuleScope {\n /**\n * Glob-lite paths relative to `sourceRoot`, matched against module files. Default: the\n * whole project.\n */\n include?: string[];\n /** Glob-lite paths to remove from `include`. */\n exclude?: string[];\n /** Only modules carrying ALL of these tags. */\n tag?: Record<string, string>;\n}\n\n/** A rule that is also a function returning a configured instance of itself. */\nexport interface CallableRule<Options = unknown> extends Rule<Options> {\n (options?: Partial<Options>, settings?: RuleSettings): ConfiguredRule<Options>;\n}\n\nexport function defineRule<O>(rule: Rule<O>): CallableRule<O> {\n const callable = (options?: Partial<O>, settings?: RuleSettings): ConfiguredRule<O> =>\n configureRule(callable as CallableRule<O>, options, settings);\n return Object.assign(callable, rule) as CallableRule<O>;\n}\n\n/**\n * A configured rule instance whose option type is not known at the use site — what a\n * `Preset`, a `UserConfig`, and the engine all hold.\n *\n * `any` rather than `unknown` deliberately: `RuleVisitors` puts `Options` in contravariant\n * position (`filter(options)`, `visit(item, ctx)`), which makes `ConfiguredRule` invariant\n * in `Options`, so `ConfiguredRule<unknown>` would reject every concrete rule there is.\n */\n// biome-ignore lint/suspicious/noExplicitAny: see above; invariance makes `unknown` unusable.\nexport type AnyConfiguredRule = ConfiguredRule<any>;\n\nexport interface ConfiguredRule<O = unknown> {\n rule: Rule<O>;\n /**\n * Merge key and `overrides` key. Inside a preset it defaults to\n * `<preset>/<rule.meta.name>`; elsewhere to `rule.meta.name`. Set it explicitly to carry\n * two instances of the same rule in one preset.\n */\n id?: string;\n options?: Partial<O>;\n severity?: Severity | \"off\";\n /** Restricts this instance to part of the graph; applied by the engine. */\n scope?: RuleScope;\n /** Per-instance message templates; see {@link RuleSettings.message}. */\n message?: string | Record<string, string>;\n}\n\nexport function configureRule<O>(\n rule: Rule<O>,\n options?: Partial<O>,\n settings?: RuleSettings,\n): ConfiguredRule<O> {\n return {\n rule,\n options: options ?? ({} as Partial<O>),\n ...(settings?.id !== undefined ? { id: settings.id } : {}),\n ...(settings?.severity !== undefined ? { severity: settings.severity } : {}),\n ...(settings?.scope !== undefined ? { scope: settings.scope } : {}),\n ...(settings?.message !== undefined ? { message: settings.message } : {}),\n };\n}\n","import type { Capability, GraphMutation } from \"../graph/ir.js\";\n\nexport interface TransformContext {\n /** Absolute source root from resolved config. */\n sourceRoot: string;\n /** Absolute repository root from resolved config. */\n repoRoot: string;\n /** A file's path relative to {@link sourceRoot}, or null when it lies outside. */\n relative(file: string): string | null;\n}\n\n/**\n * A pass that enriches the graph, between the project boundary and classification.\n *\n * The slot a TypeScript type-edge enricher — or any other \"add facts the bundler did not\n * give us\" pass — lives in. Ordered after the boundary so a transform sees which modules\n * are actually in the project, and before classification so anything it adds gets tagged\n * like everything else. Modules a transform adds are boundary-checked too: the pipeline\n * runs the boundary again over its contributions rather than trusting them.\n *\n * A transform may also declare capabilities it CONTRIBUTES. A rule requiring `type-edges`\n * should run when a transform supplies them, even though no host does — which is the whole\n * reason capabilities are a set rather than a property of the adapter.\n */\nexport interface GraphTransform {\n name: string;\n /**\n * Capabilities this transform adds to the graph. Declaring one is a promise that the\n * transform actually produced it; rules requiring it will now run.\n */\n provides?: Capability[];\n /**\n * Writes through {@link GraphMutation} rather than returning a new graph.\n *\n * Graph-in/graph-out meant every third-party transform in existence saw and\n * reconstructed the concrete representation, which froze it — and invited the whole\n * class of bug where a transform rebuilds a graph and silently drops a field it did not\n * know about.\n *\n * A transform that throws is isolated the same way a rule is: reported as a diagnostic,\n * its partial writes discarded, and the pipeline continues — one broken enricher must\n * not destroy the whole run.\n */\n transform(graph: GraphMutation, ctx: TransformContext): void;\n}\n\nexport function defineTransform(transform: GraphTransform): GraphTransform {\n return transform;\n}\n","import { GraphComputationCache } from \"../analysis/cache.js\";\nimport type { ResolvedConfig, ResolvedRule } from \"../config.js\";\nimport type { Diagnostic } from \"../contracts/diagnostic.js\";\nimport type { AnalysisResult, RuleRunInfo } from \"../contracts/reporter.js\";\nimport type { RuleContext, RuleScope } from \"../contracts/rule.js\";\nimport type { Capability, Edge, ModuleId, ModuleNode, ProjectGraph } from \"../graph/ir.js\";\nimport { assertIrCompatible, displayModuleId } from \"../graph/ir.js\";\nimport { filterKey, GraphQuery } from \"../graph/query.js\";\nimport { matchesPattern } from \"../match.js\";\nimport { sourceRelative } from \"../paths.js\";\nimport type { Severity, Violation } from \"../violations.js\";\nimport { compareViolations, fingerprintOf, locationsOf, renderMessage } from \"../violations.js\";\nimport { prepareGraph } from \"./prepare.js\";\n\n/** Per-rule state the dispatcher carries while a run is in flight. */\ninterface RuleRun {\n resolved: ResolvedRule;\n ctx: RuleContext<unknown>;\n info: RuleRunInfo;\n /**\n * This rule takes no further part in the run. Set for three different reasons — the host\n * lacks a capability it requires, its declaration is invalid, or it threw — which is why\n * it is not the flag that decides whether to keep its violations.\n */\n halted: boolean;\n /**\n * It threw. Separate from {@link halted} because only this one means \"whatever it already\n * reported is untrustworthy\": a rule that stopped halfway through the edge list reported\n * findings from a partial view of the graph, and the absence of a finding it never got to\n * is not evidence of anything.\n */\n crashed: boolean;\n}\n\n/**\n * The engine: prepare the graph (boundary → transforms → classify), then check it.\n *\n * Pure — no I/O, no reporter calls; reporters are driven by the run edge (integration-kit).\n */\nexport async function analyze(\n graph: ProjectGraph,\n config: ResolvedConfig,\n): Promise<AnalysisResult> {\n const started = performance.now();\n assertIrCompatible(graph.irVersion);\n\n const diagnostics: Diagnostic[] = [...config.diagnostics];\n\n const effective = new Set<Capability>(graph.host.capabilities);\n // In progressive delivery the absence of a module is not evidence; completeness-dependent\n // rules must not run even if the host is capable in principle.\n if (graph.delivery === \"progressive\") effective.delete(\"complete-graph\");\n\n const prepared = prepareGraph(graph, config, config.transforms, config.classifiers);\n const classified = prepared.graph;\n diagnostics.push(...prepared.diagnostics);\n for (const c of prepared.provided) effective.add(c);\n\n const query = new GraphQuery(classified);\n const cache = new GraphComputationCache();\n const relative = (file: string): string | null => sourceRelative(config.sourceRoot, file);\n\n // One scoped VIEW per distinct scope — sharing the base query's index, not rebuilding it.\n // \"FSD under apps/web\" is typically the scope of several rules at once.\n //\n // `size` rides along because resolving a scope is O(modules) and the `empty-scope` audit\n // needs the count for every rule, not once per distinct scope.\n const scopedQueries = new Map<string, { query: GraphQuery; size: number }>();\n const scopeKeyOf = (scope: RuleScope | undefined): string =>\n scope === undefined\n ? \"*\"\n : JSON.stringify([scope.include ?? null, scope.exclude ?? null, scope.tag ?? null]);\n const queryFor = (\n scope: RuleScope | undefined,\n key: string,\n ): { query: GraphQuery; size: number } => {\n if (scope === undefined) return { query, size: classified.moduleCount };\n let scoped = scopedQueries.get(key);\n if (!scoped) {\n const ids = modulesInScope(classified, scope, config.sourceRoot);\n scoped = { query: query.scoped(ids), size: ids.size };\n scopedQueries.set(key, scoped);\n }\n return scoped;\n };\n\n const violations: Violation[] = [];\n const runs: RuleRun[] = [];\n\n for (const resolved of config.rules) {\n const { rule, id, options, severity, scope, message } = resolved;\n const base: Omit<RuleRunInfo, \"status\" | \"violations\" | \"durationMs\"> = {\n id,\n name: rule.meta.name,\n description: rule.meta.description,\n ...(rule.meta.docsUrl !== undefined ? { docsUrl: rule.meta.docsUrl } : {}),\n severity,\n ...(rule.meta.deprecated !== undefined ? { deprecated: true } : {}),\n };\n\n if (rule.meta.deprecated !== undefined) {\n const d = rule.meta.deprecated;\n diagnostics.push({\n code: \"rule-deprecated\",\n severity: \"warn\",\n ruleId: id,\n message:\n `Rule \"${rule.meta.name}\" is deprecated since ${d.since}` +\n (d.replacedBy !== undefined ? `; use \"${d.replacedBy}\" instead` : \"\") +\n (d.reason !== undefined ? `. ${d.reason}` : \".\"),\n details: {\n since: d.since,\n ...(d.replacedBy !== undefined ? { replacedBy: d.replacedBy } : {}),\n },\n });\n }\n\n const missing = (rule.meta.requiredCapabilities ?? []).filter((c) => !effective.has(c));\n if (missing.length > 0) {\n diagnostics.push({\n code: \"rule-skipped\",\n severity: \"warn\",\n ruleId: id,\n message: `Rule \"${rule.meta.name}\" needs capabilities [${missing.join(\", \")}] that host \"${graph.host.name}\" cannot provide in this mode; the rule was skipped. Run via a host with these capabilities for full coverage.`,\n details: { missingCapabilities: missing, host: graph.host.name },\n });\n runs.push({\n resolved,\n ctx: null as never,\n info: {\n ...base,\n status: \"skipped\",\n violations: 0,\n durationMs: 0,\n missingCapabilities: missing,\n },\n halted: true,\n crashed: false,\n });\n continue;\n }\n\n if (rule.visits === undefined && rule.check === undefined) {\n diagnostics.push({\n code: \"invalid-config\",\n severity: \"error\",\n ruleId: id,\n message: `Rule \"${rule.meta.name}\" declares neither \\`visits\\` nor \\`check\\`, so it can never report anything. This is a bug in the rule.`,\n });\n runs.push({\n resolved,\n ctx: null as never,\n info: { ...base, status: \"skipped\", violations: 0, durationMs: 0 },\n halted: true,\n crashed: false,\n });\n continue;\n }\n\n const templates = messageTemplates(rule.meta.messages, message);\n const scopeKey = scopeKeyOf(scope);\n // Scoping happens HERE, once, for every rule that will ever exist — rather than as a\n // `within` option each rule has to remember to implement.\n const { query: scopedQuery, size: scopeSize } = queryFor(scope, scopeKey);\n\n // The silence doctrine, per rule. Global silence was already diagnosed; this closes the\n // hole one level down, where a typo in `scope.include` makes a rule survey nothing,\n // report nothing, and pass green — indistinguishable from a clean architecture. Emitted\n // per RULE rather than inside the memoized `queryFor`, so ten rules sharing one bad scope\n // produce ten diagnostics naming ten rules rather than one naming none of them.\n if (scope !== undefined && scopeSize === 0) {\n diagnostics.push({\n code: \"empty-scope\",\n severity: \"warn\",\n ruleId: id,\n message: `Rule \"${id}\" is scoped to 0 of ${classified.moduleCount} modules, so it cannot report anything. Check \\`scope\\` — path patterns are matched relative to \\`sourceRoot\\` (\"${config.sourceRoot}\"), and \\`tag\\` requires the module to already be classified.`,\n details: { scope, totalModules: classified.moduleCount },\n });\n }\n const ctx: RuleContext<unknown> = {\n // Already validated (and possibly transformed) by `resolveConfig`.\n options,\n graph: scopedQuery,\n sourceRoot: config.sourceRoot,\n repoRoot: config.repoRoot,\n relative,\n display: displayModuleId,\n // The rule's OWN view, not the root one: a computation enumerates the graph, and enumeration is scoped.\n compute: (c) => cache.get(c, scopedQuery),\n report: (v) => {\n const locations = locationsOf(v);\n const template = v.messageId !== undefined ? templates[v.messageId] : undefined;\n let text: string;\n if (v.message !== undefined) {\n text = v.message;\n } else if (template !== undefined) {\n text = renderMessage(template, v.data);\n } else {\n text = `${rule.meta.name}: ${v.messageId ?? \"(no message)\"}`;\n diagnostics.push({\n code: \"invalid-config\",\n severity: \"error\",\n ruleId: id,\n message: `Rule \"${rule.meta.name}\" reported messageId \"${v.messageId ?? \"\"}\" but no template is defined for it, in either \\`meta.messages\\` or the instance's \\`message\\`.`,\n });\n }\n violations.push({\n ruleName: rule.meta.name,\n ruleId: id,\n severity: v.severity ?? severity,\n message: text,\n ...(v.messageId !== undefined ? { messageId: v.messageId } : {}),\n ...(v.data !== undefined ? { data: v.data } : {}),\n locations,\n ...(v.explanation !== undefined ? { explanation: v.explanation } : {}),\n // Repo root, not source root: a fingerprint must survive someone reconfiguring\n // `sourceRoot`, and it is compared across machines and hosts.\n fingerprint: fingerprintOf(config.repoRoot, id, v),\n });\n },\n };\n\n runs.push({\n resolved,\n ctx,\n info: { ...base, status: \"ran\", violations: 0, durationMs: 0 },\n halted: false,\n crashed: false,\n });\n }\n\n const active = runs.filter((r) => !r.halted);\n dispatchVisitors(active, diagnostics, scopeKeyOf);\n\n // Whole-graph rules run after the traversal, and one at a time: they may be async, and\n // several of them share the memoized computation cache.\n for (const run of active) {\n if (run.halted || run.resolved.rule.check === undefined) continue;\n const startedRule = performance.now();\n try {\n await run.resolved.rule.check(run.ctx);\n } catch (err) {\n markFailed(run, err, diagnostics);\n }\n run.info.durationMs += performance.now() - startedRule;\n }\n\n // A crashed rule's partial findings are discarded, which is what makes the `rule-failed`\n // diagnostic (\"threw and produced no results\") true. Keeping them would be worse than\n // useless: they come from a rule that stopped partway through the graph, so the set is\n // neither complete nor known-incomplete to anyone reading it, and a baseline built over it\n // would encode findings that vanish the moment the crash is fixed. Rule instance ids are\n // unique, so matching on `ruleId` is exact.\n //\n // Diagnostics the rule caused on its way down are NOT discarded — an `invalid-config` for a\n // missing message template is a real defect regardless of what happened next.\n const crashed = new Set(runs.filter((r) => r.crashed).map((r) => r.info.id));\n const kept = crashed.size === 0 ? violations : violations.filter((v) => !crashed.has(v.ruleId));\n\n // Rules interleave inside a shared traversal, so a start/end offset per rule would not\n // attribute correctly; counts come from the violations themselves. Counted over what was\n // KEPT and set for every status, so a failed rule reports 0 because it produced nothing —\n // `result.rules` and `result.violations` cannot disagree.\n const perRule = new Map<string, number>();\n for (const v of kept) perRule.set(v.ruleId, (perRule.get(v.ruleId) ?? 0) + 1);\n for (const run of runs) run.info.violations = perRule.get(run.info.id) ?? 0;\n\n diagnostics.push(...auditClassification(classified));\n\n return {\n // Deterministic order: baselines, CI diffing, and snapshot tests all need two runs of\n // the same analysis to be byte-identical.\n violations: kept.sort(compareViolations),\n diagnostics,\n rules: runs.map((r) => r.info),\n repoRoot: config.repoRoot,\n host: graph.host,\n delivery: graph.delivery,\n stats: {\n moduleCount: classified.moduleCount,\n edgeCount: classified.edgeCount,\n durationMs: performance.now() - started,\n },\n };\n}\n\n/**\n * Runs every declared-interest rule, one traversal per distinct (scope, filter) pair.\n *\n * The filtered slice is materialized once and shared by every rule that asked for it, which\n * is what makes the cost O(distinct slices + total visits) rather than O(rules × graph).\n * Rules that want the whole edge list with no filter share the graph's own array and copy\n * nothing at all.\n *\n * Isolation is per rule, not per visit: the try/catch wraps a rule's entire pass over the\n * slice, so a rule that throws stops and is marked failed while the other thirty-nine keep\n * their results — without paying for exception handling on every edge.\n */\nfunction dispatchVisitors(\n runs: readonly RuleRun[],\n diagnostics: Diagnostic[],\n scopeKeyOf: (scope: RuleScope | undefined) => string,\n): void {\n interface Bucket<T> {\n /** Evaluated once, then shared by every member. */\n slice: () => readonly T[];\n members: { run: RuleRun; visit: (item: T, ctx: RuleContext<unknown>) => void }[];\n }\n\n const edgeBuckets = new Map<string, Bucket<Edge>>();\n const moduleBuckets = new Map<string, Bucket<ModuleNode>>();\n\n for (const run of runs) {\n const visits = run.resolved.rule.visits;\n if (visits === undefined) continue;\n const scopeKey = scopeKeyOf(run.resolved.scope);\n const query = run.ctx.graph;\n\n const edgeSpec = visits.edges;\n if (edgeSpec !== undefined) {\n try {\n const filter = edgeSpec.filter?.(run.ctx.options);\n const key = `${scopeKey}|e|${filterKey(filter)}`;\n let bucket = edgeBuckets.get(key);\n if (bucket === undefined) {\n bucket = { slice: () => query.edges(filter), members: [] };\n edgeBuckets.set(key, bucket);\n }\n bucket.members.push({\n run,\n visit: edgeSpec.visit as (e: Edge, c: RuleContext<unknown>) => void,\n });\n } catch (err) {\n markFailed(run, err, diagnostics);\n continue;\n }\n }\n\n const moduleSpec = visits.modules;\n if (moduleSpec !== undefined) {\n try {\n const filter = moduleSpec.filter?.(run.ctx.options);\n const key = `${scopeKey}|m|${filterKey(filter)}`;\n let bucket = moduleBuckets.get(key);\n if (bucket === undefined) {\n bucket = { slice: () => query.modules(filter).toArray(), members: [] };\n moduleBuckets.set(key, bucket);\n }\n bucket.members.push({\n run,\n visit: moduleSpec.visit as (m: ModuleNode, c: RuleContext<unknown>) => void,\n });\n } catch (err) {\n markFailed(run, err, diagnostics);\n }\n }\n }\n\n const drain = <T>(buckets: Map<string, Bucket<T>>): void => {\n for (const bucket of buckets.values()) {\n const items = bucket.slice();\n for (const { run, visit } of bucket.members) {\n if (run.halted) continue;\n const startedRule = performance.now();\n try {\n for (const item of items) visit(item, run.ctx);\n } catch (err) {\n markFailed(run, err, diagnostics);\n }\n run.info.durationMs += performance.now() - startedRule;\n }\n }\n };\n\n drain(edgeBuckets);\n drain(moduleBuckets);\n}\n\nfunction markFailed(run: RuleRun, err: unknown, diagnostics: Diagnostic[]): void {\n run.halted = true;\n run.crashed = true;\n run.info.status = \"failed\";\n diagnostics.push({\n code: \"rule-failed\",\n severity: \"error\",\n ruleId: run.resolved.id,\n message: `Rule \"${run.resolved.id}\" threw and produced no results: ${err instanceof Error ? err.message : String(err)}`,\n ...(err instanceof Error && err.stack !== undefined ? { details: { stack: err.stack } } : {}),\n });\n}\n\n/**\n * The instance's message templates over the rule's own.\n *\n * A bare string retargets a single-message rule; a record retargets by id. Anything the\n * instance does not mention keeps the rule's wording.\n */\nfunction messageTemplates(\n own: Record<string, string> | undefined,\n override: string | Record<string, string> | undefined,\n): Record<string, string> {\n const base = { ...(own ?? {}) };\n if (override === undefined) return base;\n if (typeof override === \"string\") {\n const ids = Object.keys(base);\n // One message: unambiguous. Several: retarget them all, since the user asked for one\n // sentence and getting it on only an arbitrary one of them would be worse.\n for (const id of ids.length > 0 ? ids : [\"default\"]) base[id] = override;\n return base;\n }\n return { ...base, ...override };\n}\n\n/**\n * Resolves a {@link RuleScope} to the concrete set of modules a scoped rule is about.\n *\n * Path patterns are matched source-root-relative — the same base `include`/`exclude` and\n * classifier patterns use. A module with no file (a builtin, a virtual module) can never be\n * *in* a path scope, but it remains reachable as an edge target, which is where a scoped\n * rule actually needs to see it.\n */\nfunction modulesInScope(graph: ProjectGraph, scope: RuleScope, sourceRoot: string): Set<ModuleId> {\n const ids = new Set<ModuleId>();\n for (const m of graph.modules()) {\n if (scope.tag !== undefined) {\n const ok = Object.entries(scope.tag).every(([k, v]) => m.tags.get(k) === v);\n if (!ok) continue;\n }\n if (scope.include !== undefined || scope.exclude !== undefined) {\n if (m.file === null) continue;\n const rel = sourceRelative(sourceRoot, m.file);\n if (rel === null) continue;\n if (scope.include !== undefined && !scope.include.some((p) => matchesPattern(rel, p)))\n continue;\n if (scope.exclude !== undefined && scope.exclude.some((p) => matchesPattern(rel, p)))\n continue;\n }\n ids.add(m.id);\n }\n return ids;\n}\n\n/**\n * The tool's most dangerous property is that its failure mode is *silence*: every rule\n * ignores modules it cannot classify, so a misconfigured `sourceRoot` tags nothing, matches\n * nothing, reports nothing, and passes. These diagnostics are the difference between \"your\n * architecture is clean\" and \"ArchWall never looked at your code\".\n */\nfunction auditClassification(graph: ProjectGraph): Diagnostic[] {\n let source = 0;\n let tagged = 0;\n for (const m of graph.modules()) {\n if (m.kind !== \"source\") continue;\n source++;\n if (m.tags.size > 0) tagged++;\n }\n\n if (source === 0) {\n return [\n {\n code: \"empty-project\",\n severity: \"warn\",\n message:\n \"No source modules were analysed — every module was external or filtered out by the project boundary. Check `sourceRoot`, `include`, and `exclude`.\",\n details: { sourceModules: 0 },\n },\n ];\n }\n if (tagged === 0) {\n return [\n {\n code: \"no-modules-classified\",\n severity: \"warn\",\n message: `0 of ${source} source modules were classified, so every tag-based rule matched nothing and this run cannot have found anything. This almost always means \\`sourceRoot\\` points somewhere other than your source tree, or that no classifier or preset is configured.`,\n details: { sourceModules: source, classifiedModules: 0 },\n },\n ];\n }\n return [];\n}\n","import type { GraphTransform } from \"../contracts/transform.js\";\nimport { defineTransform } from \"../contracts/transform.js\";\n\n/**\n * Removes edges from a module to itself.\n *\n * This is a *semantic policy*, and it belongs in shared code a host opts into rather than\n * inside one adapter: HMR instrumentation adds self-edges (React Fast Refresh makes every\n * transformed component module import itself), and that reasoning is not Vite-specific —\n * the moment another bundler's HMR does the same thing, an adapter-local fix has to be\n * written a second time, and the two can then disagree.\n *\n * Deliberately NOT on by default: a genuine self-import is a real finding, and build mode\n * sees the real graph. A host applies this only where it knows its own instrumentation\n * created the edges.\n */\nexport function dropSelfEdges(): GraphTransform {\n return defineTransform({\n name: \"drop-self-edges\",\n transform(graph) {\n graph.removeEdges((e) => e.from === e.to);\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_prepare = require("./prepare-C1FfL8Qd.cjs");
|
|
3
|
+
exports.GraphComputationCache = require_prepare.GraphComputationCache;
|
|
4
|
+
exports.GraphDraft = require_prepare.GraphDraft;
|
|
5
|
+
exports.GraphIndex = require_prepare.GraphIndex;
|
|
6
|
+
exports.applyProjectBoundary = require_prepare.applyProjectBoundary;
|
|
7
|
+
exports.filterKey = require_prepare.filterKey;
|
|
8
|
+
exports.hashParts = require_prepare.hashParts;
|
|
9
|
+
exports.prepareGraph = require_prepare.prepareGraph;
|
|
10
|
+
exports.sourceRelative = require_prepare.sourceRelative;
|
|
11
|
+
exports.stableHash = require_prepare.stableHash;
|
|
12
|
+
exports.toRelative = require_prepare.toRelative;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { I as Capability, M as Classifier, V as GraphDraft, X as ProjectGraph, c as GraphQuery, d as filterKey, f as Diagnostic, i as GraphComputation, s as GraphIndex, t as GraphTransform } from "./transform-CnUPOO0E.cjs";
|
|
2
|
+
//#region src/analysis/cache.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
|
|
5
|
+
* one traversal.
|
|
6
|
+
*
|
|
7
|
+
* The view is part of the key because a computation is an ENUMERATION of the graph, and
|
|
8
|
+
* enumeration is scoped. A cache bound to the root query
|
|
9
|
+
* would hand a rule scoped to `apps/web` the cycles of the whole repository — the rule's
|
|
10
|
+
* `ctx.graph` narrowed and its `ctx.compute` silently not.
|
|
11
|
+
*
|
|
12
|
+
* Rules sharing a scope share the base query object, so they share the entry; the common case
|
|
13
|
+
* (no scope at all) is still one evaluation for everyone.
|
|
14
|
+
*/
|
|
15
|
+
declare class GraphComputationCache {
|
|
16
|
+
#private;
|
|
17
|
+
get<T>(computation: GraphComputation<T>, graph: GraphQuery): T;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/engine/prepare.d.ts
|
|
21
|
+
/** What the project boundary needs: where sources start and which of them count. */
|
|
22
|
+
interface BoundaryConfig {
|
|
23
|
+
sourceRoot: string;
|
|
24
|
+
include: readonly string[];
|
|
25
|
+
exclude: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/** Adds what transforms need, which is the repository root they report paths against. */
|
|
28
|
+
interface PrepareConfig extends BoundaryConfig {
|
|
29
|
+
repoRoot: string;
|
|
30
|
+
}
|
|
31
|
+
interface PrepareResult {
|
|
32
|
+
graph: ProjectGraph;
|
|
33
|
+
diagnostics: Diagnostic[];
|
|
34
|
+
/** Capabilities contributed by transforms that actually ran. */
|
|
35
|
+
provided: Capability[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The one pipeline: project boundary → transforms → boundary again → classification.
|
|
39
|
+
*
|
|
40
|
+
* The boundary belongs to the ENGINE, not to producers: producers are the component that
|
|
41
|
+
* varies, so anything that must be identical across hosts cannot live in them. Producers
|
|
42
|
+
* over-collect; the engine trims.
|
|
43
|
+
*
|
|
44
|
+
* It runs twice because a transform may ADD modules, and those must be bounded exactly as
|
|
45
|
+
* if a producer had supplied them. Running it again is safe because it is idempotent — it
|
|
46
|
+
* only ever re-kinds `source` → `excluded`. With no transforms configured, boundary and
|
|
47
|
+
* classification are one fused pass over the modules.
|
|
48
|
+
*
|
|
49
|
+
* Excluded modules are re-kinded, never deleted. An edge *into* an excluded file still says
|
|
50
|
+
* something true about the architecture, and deleting the node would silently rewrite the
|
|
51
|
+
* graph's shape (a cycle through a test helper would vanish).
|
|
52
|
+
*/
|
|
53
|
+
declare function prepareGraph(graph: ProjectGraph, config: PrepareConfig, transforms: readonly GraphTransform[], classifiers: readonly Classifier[]): PrepareResult;
|
|
54
|
+
/**
|
|
55
|
+
* The project boundary on its own, for callers that want kinds settled without tagging.
|
|
56
|
+
* One implementation, shared with {@link prepareGraph}.
|
|
57
|
+
*/
|
|
58
|
+
declare function applyProjectBoundary(graph: ProjectGraph, config: BoundaryConfig): ProjectGraph;
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/paths.d.ts
|
|
61
|
+
/**
|
|
62
|
+
* A file's path relative to `root`, forward-slashed, or `null` when it does not lie
|
|
63
|
+
* strictly inside `root`.
|
|
64
|
+
*
|
|
65
|
+
* The one answer to "where is this file, in the terms my patterns are written in".
|
|
66
|
+
* `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
|
|
67
|
+
* `within` all describe positions in a tree, and they must all agree on what a position
|
|
68
|
+
* is — including on the edge cases: the root itself is not *inside* the root, and a file
|
|
69
|
+
* above it has no position at all.
|
|
70
|
+
*
|
|
71
|
+
* A path that is already relative is taken as relative *to the root* rather than resolved
|
|
72
|
+
* against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
|
|
73
|
+
* `@archwall/test-utils`) use bare ids, and resolving those against the working directory
|
|
74
|
+
* would silently place every module outside the project.
|
|
75
|
+
*/
|
|
76
|
+
declare function sourceRelative(root: string, file: string): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Repository-relative, for anything that leaves the process: violation fingerprints,
|
|
79
|
+
* reporter output, SARIF `artifactLocation.uri`.
|
|
80
|
+
*
|
|
81
|
+
* Absolute paths are the right module identity *inside* a run and wrong in every output,
|
|
82
|
+
* because they make results machine-specific. SARIF in particular is silently useless with
|
|
83
|
+
* absolute URIs: GitHub code scanning cannot associate the result with a repository file.
|
|
84
|
+
*
|
|
85
|
+
* Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
|
|
86
|
+
* root is returned as-is rather than as `null`, because output must always print something,
|
|
87
|
+
* whereas matching must be able to say "not here".
|
|
88
|
+
*/
|
|
89
|
+
declare function toRelative(root: string, id: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* FNV-1a, 64-bit, as 16 lowercase hex chars.
|
|
92
|
+
*
|
|
93
|
+
* Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
|
|
94
|
+
* worker, edge runtime), and this hash is used for identity, never for security.
|
|
95
|
+
*/
|
|
96
|
+
declare function stableHash(input: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Joins parts into one hashable string.
|
|
99
|
+
*
|
|
100
|
+
* `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
|
|
101
|
+
* a delimiter that can occur inside a part makes two different tuples hash identically.
|
|
102
|
+
*/
|
|
103
|
+
declare function hashParts(parts: readonly string[]): string;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { type BoundaryConfig, GraphComputationCache, GraphDraft, GraphIndex, type PrepareConfig, type PrepareResult, applyProjectBoundary, filterKey, hashParts, prepareGraph, sourceRelative, stableHash, toRelative };
|
|
106
|
+
//# sourceMappingURL=internal.d.cts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { I as Capability, M as Classifier, V as GraphDraft, X as ProjectGraph, c as GraphQuery, d as filterKey, f as Diagnostic, i as GraphComputation, s as GraphIndex, t as GraphTransform } from "./transform-CnUPOO0E.mjs";
|
|
2
|
+
//#region src/analysis/cache.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Memoizes graph computations per (computation, view): ten unscoped rules requesting SCCs cost
|
|
5
|
+
* one traversal.
|
|
6
|
+
*
|
|
7
|
+
* The view is part of the key because a computation is an ENUMERATION of the graph, and
|
|
8
|
+
* enumeration is scoped. A cache bound to the root query
|
|
9
|
+
* would hand a rule scoped to `apps/web` the cycles of the whole repository — the rule's
|
|
10
|
+
* `ctx.graph` narrowed and its `ctx.compute` silently not.
|
|
11
|
+
*
|
|
12
|
+
* Rules sharing a scope share the base query object, so they share the entry; the common case
|
|
13
|
+
* (no scope at all) is still one evaluation for everyone.
|
|
14
|
+
*/
|
|
15
|
+
declare class GraphComputationCache {
|
|
16
|
+
#private;
|
|
17
|
+
get<T>(computation: GraphComputation<T>, graph: GraphQuery): T;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/engine/prepare.d.ts
|
|
21
|
+
/** What the project boundary needs: where sources start and which of them count. */
|
|
22
|
+
interface BoundaryConfig {
|
|
23
|
+
sourceRoot: string;
|
|
24
|
+
include: readonly string[];
|
|
25
|
+
exclude: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/** Adds what transforms need, which is the repository root they report paths against. */
|
|
28
|
+
interface PrepareConfig extends BoundaryConfig {
|
|
29
|
+
repoRoot: string;
|
|
30
|
+
}
|
|
31
|
+
interface PrepareResult {
|
|
32
|
+
graph: ProjectGraph;
|
|
33
|
+
diagnostics: Diagnostic[];
|
|
34
|
+
/** Capabilities contributed by transforms that actually ran. */
|
|
35
|
+
provided: Capability[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The one pipeline: project boundary → transforms → boundary again → classification.
|
|
39
|
+
*
|
|
40
|
+
* The boundary belongs to the ENGINE, not to producers: producers are the component that
|
|
41
|
+
* varies, so anything that must be identical across hosts cannot live in them. Producers
|
|
42
|
+
* over-collect; the engine trims.
|
|
43
|
+
*
|
|
44
|
+
* It runs twice because a transform may ADD modules, and those must be bounded exactly as
|
|
45
|
+
* if a producer had supplied them. Running it again is safe because it is idempotent — it
|
|
46
|
+
* only ever re-kinds `source` → `excluded`. With no transforms configured, boundary and
|
|
47
|
+
* classification are one fused pass over the modules.
|
|
48
|
+
*
|
|
49
|
+
* Excluded modules are re-kinded, never deleted. An edge *into* an excluded file still says
|
|
50
|
+
* something true about the architecture, and deleting the node would silently rewrite the
|
|
51
|
+
* graph's shape (a cycle through a test helper would vanish).
|
|
52
|
+
*/
|
|
53
|
+
declare function prepareGraph(graph: ProjectGraph, config: PrepareConfig, transforms: readonly GraphTransform[], classifiers: readonly Classifier[]): PrepareResult;
|
|
54
|
+
/**
|
|
55
|
+
* The project boundary on its own, for callers that want kinds settled without tagging.
|
|
56
|
+
* One implementation, shared with {@link prepareGraph}.
|
|
57
|
+
*/
|
|
58
|
+
declare function applyProjectBoundary(graph: ProjectGraph, config: BoundaryConfig): ProjectGraph;
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/paths.d.ts
|
|
61
|
+
/**
|
|
62
|
+
* A file's path relative to `root`, forward-slashed, or `null` when it does not lie
|
|
63
|
+
* strictly inside `root`.
|
|
64
|
+
*
|
|
65
|
+
* The one answer to "where is this file, in the terms my patterns are written in".
|
|
66
|
+
* `include`/`exclude`, classifier patterns, `RuleScope.include`, and `require-tag`'s
|
|
67
|
+
* `within` all describe positions in a tree, and they must all agree on what a position
|
|
68
|
+
* is — including on the edge cases: the root itself is not *inside* the root, and a file
|
|
69
|
+
* above it has no position at all.
|
|
70
|
+
*
|
|
71
|
+
* A path that is already relative is taken as relative *to the root* rather than resolved
|
|
72
|
+
* against `process.cwd()`. Real producers emit absolute paths, but in-memory graphs (tests,
|
|
73
|
+
* `@archwall/test-utils`) use bare ids, and resolving those against the working directory
|
|
74
|
+
* would silently place every module outside the project.
|
|
75
|
+
*/
|
|
76
|
+
declare function sourceRelative(root: string, file: string): string | null;
|
|
77
|
+
/**
|
|
78
|
+
* Repository-relative, for anything that leaves the process: violation fingerprints,
|
|
79
|
+
* reporter output, SARIF `artifactLocation.uri`.
|
|
80
|
+
*
|
|
81
|
+
* Absolute paths are the right module identity *inside* a run and wrong in every output,
|
|
82
|
+
* because they make results machine-specific. SARIF in particular is silently useless with
|
|
83
|
+
* absolute URIs: GitHub code scanning cannot associate the result with a repository file.
|
|
84
|
+
*
|
|
85
|
+
* Distinct from {@link sourceRelative} in its failure mode, deliberately: an id outside the
|
|
86
|
+
* root is returned as-is rather than as `null`, because output must always print something,
|
|
87
|
+
* whereas matching must be able to say "not here".
|
|
88
|
+
*/
|
|
89
|
+
declare function toRelative(root: string, id: string): string;
|
|
90
|
+
/**
|
|
91
|
+
* FNV-1a, 64-bit, as 16 lowercase hex chars.
|
|
92
|
+
*
|
|
93
|
+
* Not `node:crypto`: core stays runnable wherever a graph can be built (browser playground,
|
|
94
|
+
* worker, edge runtime), and this hash is used for identity, never for security.
|
|
95
|
+
*/
|
|
96
|
+
declare function stableHash(input: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Joins parts into one hashable string.
|
|
99
|
+
*
|
|
100
|
+
* `\0` rather than a space: parts are paths and specifiers, which may contain spaces, and
|
|
101
|
+
* a delimiter that can occur inside a part makes two different tuples hash identically.
|
|
102
|
+
*/
|
|
103
|
+
declare function hashParts(parts: readonly string[]): string;
|
|
104
|
+
//#endregion
|
|
105
|
+
export { type BoundaryConfig, GraphComputationCache, GraphDraft, GraphIndex, type PrepareConfig, type PrepareResult, applyProjectBoundary, filterKey, hashParts, prepareGraph, sourceRelative, stableHash, toRelative };
|
|
106
|
+
//# sourceMappingURL=internal.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as stableHash, S as sourceRelative, a as filterKey, c as GraphDraft, n as prepareGraph, o as GraphComputationCache, r as GraphIndex, t as applyProjectBoundary, w as toRelative, x as hashParts } from "./prepare-BJHgDEui.mjs";
|
|
2
|
+
export { GraphComputationCache, GraphDraft, GraphIndex, applyProjectBoundary, filterKey, hashParts, prepareGraph, sourceRelative, stableHash, toRelative };
|