@aburi/diff 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@aburi/diff",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Aburi diff engine: 5-stage semantic matcher (id / git-rename / logic fingerprint / name+signature / dropped weak) followed by status classification and per-Symbol delta computation, emitting aburi.diff.v1-conformant output for PR-review workflows",
5
- "license": "MIT",
5
+ "license": "Apache-2.0",
6
6
  "author": "kage1020",
7
7
  "homepage": "https://github.com/kage1020/Aburi/tree/main/packages/diff#readme",
8
8
  "bugs": {
@@ -25,7 +25,6 @@
25
25
  },
26
26
  "files": [
27
27
  "dist",
28
- "src",
29
28
  "README.md"
30
29
  ],
31
30
  "sideEffects": false,
@@ -33,15 +32,15 @@
33
32
  "access": "public"
34
33
  },
35
34
  "dependencies": {
36
- "@aburi/core": "0.2.0",
37
- "@aburi/types": "0.2.0"
35
+ "@aburi/core": "0.4.0",
36
+ "@aburi/types": "0.4.0"
38
37
  },
39
38
  "devDependencies": {
40
39
  "@types/node": "^26.0.1",
41
40
  "ajv": "^8.20.0",
42
41
  "tsdown": "^0.22.3",
43
42
  "typescript": "^6.0.3",
44
- "vitest": "^4.1.9"
43
+ "vitest": "^4.1.11"
45
44
  },
46
45
  "scripts": {
47
46
  "build": "tsdown",
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/components.ts","../src/delta.ts","../src/match.ts","../src/diff.ts","../src/errors.ts","../src/signature.ts","../src/similarity.ts","../src/slice.ts","../src/status.ts"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/components.ts","../src/errors.ts","../src/delta.ts","../src/signature.ts","../src/similarity.ts","../src/match.ts","../src/slice.ts","../src/status.ts","../src/diff.ts"],"sourcesContent":["import type {\n Component,\n ComponentDiff,\n ComponentId,\n Dependency,\n DependencyDiff,\n} from \"@aburi/types\"\n\n/**\n * §6.1 — Component diff. Component identity is `id`; when a Component is present in both\n * base and head with the same id, the three delta booleans describe which axes actually\n * moved (roots reshuffle, public API globs, or framework hint list). `modified` deltas\n * are intentionally absent from this diff (§5.2.3): fields are reported as before/after\n * pairs via the `changed[]` entries so consumers can render them without stringifying\n * arrays.\n */\nexport function diffComponents(\n base: readonly Component[],\n head: readonly Component[],\n): ComponentDiff {\n const baseById = new Map<ComponentId, Component>()\n for (const c of base) baseById.set(c.id, c)\n const headById = new Map<ComponentId, Component>()\n for (const c of head) headById.set(c.id, c)\n\n const added: Component[] = []\n const removed: Component[] = []\n const changed: ComponentDiff[\"changed\"] = []\n\n for (const [id, headComp] of headById) {\n const baseComp = baseById.get(id)\n if (baseComp === undefined) {\n added.push(headComp)\n continue\n }\n const rootsChanged = !stringArraysEqual(baseComp.roots, headComp.roots)\n const publicApiChanged = !stringArraysEqual(baseComp.publicApi ?? [], headComp.publicApi ?? [])\n const frameworksChanged = !stringArraysEqual(\n baseComp.frameworks ?? [],\n headComp.frameworks ?? [],\n )\n if (rootsChanged || publicApiChanged || frameworksChanged) {\n changed.push({\n before: baseComp,\n after: headComp,\n delta: { rootsChanged, publicApiChanged, frameworksChanged },\n })\n }\n }\n for (const [id, baseComp] of baseById) {\n if (!headById.has(id)) removed.push(baseComp)\n }\n sortById(added)\n sortById(removed)\n changed.sort((a, b) => (a.after.id < b.after.id ? -1 : a.after.id > b.after.id ? 1 : 0))\n return { added, removed, changed }\n}\n\n/**\n * §6.2 — Dependency diff. Identity is the composite `(from, to, via)` triple; direction\n * and effect changes are surfaced as an added + removed pair so `modified` is not part of\n * the schema (§6.2 tail).\n */\nexport function diffDependencies(\n base: readonly Dependency[],\n head: readonly Dependency[],\n): DependencyDiff {\n const baseKeys = new Map<string, Dependency>()\n for (const d of base) baseKeys.set(dependencyKey(d), d)\n const headKeys = new Map<string, Dependency>()\n for (const d of head) headKeys.set(dependencyKey(d), d)\n const added: Dependency[] = []\n const removed: Dependency[] = []\n for (const [key, dep] of headKeys) {\n const b = baseKeys.get(key)\n if (b === undefined) {\n added.push(dep)\n continue\n }\n if (b.direction !== dep.direction || (b.effect ?? null) !== (dep.effect ?? null)) {\n removed.push(b)\n added.push(dep)\n }\n }\n for (const [key, dep] of baseKeys) {\n if (!headKeys.has(key)) removed.push(dep)\n }\n added.sort(compareDependencies)\n removed.sort(compareDependencies)\n return { added, removed }\n}\n\nfunction dependencyKey(d: Dependency): string {\n return `${d.from}::${d.to}::${d.via}`\n}\n\nfunction compareDependencies(a: Dependency, b: Dependency): number {\n const keyA = dependencyKey(a)\n const keyB = dependencyKey(b)\n return keyA < keyB ? -1 : keyA > keyB ? 1 : 0\n}\n\nfunction stringArraysEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false\n return true\n}\n\nfunction sortById<T extends { id: string }>(items: T[]): void {\n items.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))\n}\n","/**\n * Coded error class for every @aburi/diff failure. Consumers branch on `code` without\n * parsing message text. Codes are stable and additions are non-breaking; renames are not.\n */\n\nexport type DiffErrorCode =\n /** `base.$schema` and `head.$schema` disagree; a diff across schema versions cannot be trusted (§9.1). */\n | \"schema-mismatch\"\n /** `config.diff.lineFuzz` was outside the documented [0, 10] range (diff-algorithm.md §5.2.1). */\n | \"invalid-line-fuzz\"\n /**\n * `baseIR` or `headIR` failed the top-level shape check that `buildDiff` enforces\n * before touching any Symbol. Missing / non-array `symbols`, `components`,\n * `dependencies`, or an absent `$schema` — anything that would otherwise crash with a\n * cryptic `TypeError: undefined is not iterable` deep in a matching stage.\n */\n | \"ir-shape-invalid\"\n /**\n * A `SliceRecord` broke the derivation invariant of slice-view.md §7.1 / §8.2:\n * `members[]` empty, `members[]` not in strictly ascending order, or `id` not\n * equal to `\"slice:\" + members[0]`. The last two compare one property against\n * another and so have no JSON Schema equivalent; the pass checks them itself\n * (§7.4). Raised only by an Aburi bug, never by user input — see\n * `SliceRecordViolation.kind` for which clause broke.\n */\n | \"slice-invariant-violated\"\n\nexport interface DiffErrorDetail {\n code: DiffErrorCode\n /** Offending value (schema id, fuzz value, etc.) when applicable. */\n value?: string\n}\n\nexport class DiffError extends Error {\n readonly code: DiffErrorCode\n readonly value: string | undefined\n\n constructor(message: string, detail: DiffErrorDetail, options?: { cause?: unknown }) {\n super(message, options)\n this.name = \"DiffError\"\n this.code = detail.code\n this.value = detail.value\n }\n}\n","import type {\n ArrayDelta,\n Call,\n Decorator,\n Effect,\n Symbol as IRSymbol,\n Rule,\n Signature,\n SignatureDelta,\n SymbolDelta,\n} from \"@aburi/types\"\nimport { DiffError } from \"./errors\"\n\nexport const DEFAULT_LINE_FUZZ = 2\nexport const MAX_LINE_FUZZ = 10\nexport const MIN_LINE_FUZZ = 0\n\nexport interface DeltaOptions {\n /**\n * §5.2.1 line fuzz for rule/call identity. Must be an integer in\n * `[MIN_LINE_FUZZ, MAX_LINE_FUZZ]` (`0..10`); anything outside — or a non-finite value —\n * throws `DiffError({ code: \"invalid-line-fuzz\" })`. Setting `0` disables fuzz; omitting\n * the field falls back to `DEFAULT_LINE_FUZZ` (2).\n */\n lineFuzz?: number\n}\n\n/**\n * §5 — Compute the full per-Symbol delta between two paired Symbols. The three axis\n * booleans (`apiChanged` / `logicChanged` / `syntaxChanged`) come from fingerprint\n * comparison; the array deltas (`rules` / `effects` / `calls` / `decorators`) come from\n * identity-preserving diff with configurable line fuzz.\n */\nexport function computeSymbolDelta(\n base: IRSymbol,\n head: IRSymbol,\n options: DeltaOptions = {},\n): SymbolDelta {\n const fuzz = validateLineFuzz(options.lineFuzz ?? DEFAULT_LINE_FUZZ)\n const delta: SymbolDelta = {\n apiChanged: base.fingerprint.api !== head.fingerprint.api,\n logicChanged: base.fingerprint.logic !== head.fingerprint.logic,\n syntaxChanged: base.fingerprint.syntax !== head.fingerprint.syntax,\n componentChanged: (base.component ?? null) !== (head.component ?? null),\n visibilityChanged: base.visibility !== head.visibility,\n }\n delta.rules = diffRules(base.rules, head.rules, fuzz)\n delta.effects = diffEffects(base.effects, head.effects)\n delta.calls = diffCalls(base.calls, head.calls, fuzz)\n delta.decorators = diffDecorators(base.decorators, head.decorators, fuzz)\n const sigDelta = diffSignature(base.signature ?? null, head.signature ?? null)\n delta.signature = sigDelta\n return delta\n}\n\n/**\n * §5.2.1 range check. Loud on violation rather than silently clamping so a config typo\n * (`lineFuzz: 999`) or an upstream numeric bug (`NaN`, `Infinity`) surfaces at the diff\n * boundary instead of quietly rounding into a range that produces the wrong deltas.\n */\nfunction validateLineFuzz(value: number): number {\n if (!Number.isFinite(value) || !Number.isInteger(value)) {\n throw new DiffError(\n `config.diff.lineFuzz must be an integer in [${MIN_LINE_FUZZ}, ${MAX_LINE_FUZZ}]; got ${String(value)}.`,\n { code: \"invalid-line-fuzz\", value: String(value) },\n )\n }\n if (value < MIN_LINE_FUZZ || value > MAX_LINE_FUZZ) {\n throw new DiffError(\n `config.diff.lineFuzz must be within [${MIN_LINE_FUZZ}, ${MAX_LINE_FUZZ}]; got ${value}.`,\n { code: \"invalid-line-fuzz\", value: String(value) },\n )\n }\n return value\n}\n\ninterface Identified<T> {\n item: T\n key: string\n line: number\n}\n\n/**\n * Two-index diff: build key-indexed maps for both sides, then classify each key into\n * `added` / `removed` / `modified`. Line-fuzzed identities are matched within tolerance\n * so cosmetic line shifts do not produce added/removed noise. `modified` only fires when\n * the identity key stays the same but the content differs.\n */\nfunction differentiate<T>(\n base: readonly Identified<T>[],\n head: readonly Identified<T>[],\n isEqual: (a: T, b: T) => boolean,\n lineFuzz: number,\n): ArrayDelta {\n const added: T[] = []\n const removed: T[] = []\n const modified: T[] = []\n const consumedBase = new Set<number>()\n for (const h of head) {\n let matchIdx = -1\n for (let j = 0; j < base.length; j++) {\n if (consumedBase.has(j)) continue\n const b = base[j]\n if (b === undefined) continue\n if (b.key !== h.key) continue\n if (Math.abs(b.line - h.line) > lineFuzz) continue\n matchIdx = j\n break\n }\n if (matchIdx === -1) {\n added.push(h.item)\n continue\n }\n consumedBase.add(matchIdx)\n const bmatch = base[matchIdx]\n if (bmatch === undefined) continue\n if (!isEqual(bmatch.item, h.item)) modified.push(h.item)\n }\n for (let j = 0; j < base.length; j++) {\n if (consumedBase.has(j)) continue\n const b = base[j]\n if (b === undefined) continue\n removed.push(b.item)\n }\n return { added, removed, modified }\n}\n\nfunction diffRules(base: readonly Rule[], head: readonly Rule[], lineFuzz: number): ArrayDelta {\n const mapper = (r: Rule): Identified<Rule> => ({\n item: r,\n key: r.type,\n line: r.line,\n })\n return differentiate(base.map(mapper), head.map(mapper), rulesEqual, lineFuzz)\n}\n\nfunction rulesEqual(a: Rule, b: Rule): boolean {\n return (\n a.type === b.type &&\n (a.condition ?? null) === (b.condition ?? null) &&\n (a.what ?? null) === (b.what ?? null) &&\n (a.expr ?? null) === (b.expr ?? null) &&\n (a.loopKind ?? null) === (b.loopKind ?? null)\n )\n}\n\nfunction diffEffects(base: readonly Effect[], head: readonly Effect[]): ArrayDelta {\n const mapper = (e: Effect): Identified<Effect> => ({\n item: e,\n key: `${e.id}::${e.target}`,\n // Propagated entries (effect-propagation.md §5.1) omit `line`; the diff\n // matcher tolerates missing lines by treating them as position-0. Effect\n // pairing here uses lineFuzz=Infinity anyway, so any placeholder is safe.\n line: e.line ?? 0,\n })\n return differentiate(base.map(mapper), head.map(mapper), effectsEqual, Number.POSITIVE_INFINITY)\n}\n\nfunction effectsEqual(a: Effect, b: Effect): boolean {\n return (\n a.id === b.id && a.target === b.target && a.plugin === b.plugin && a.confidence === b.confidence\n )\n}\n\nfunction diffCalls(base: readonly Call[], head: readonly Call[], lineFuzz: number): ArrayDelta {\n const mapper = (c: Call): Identified<Call> => ({\n item: c,\n key: c.target,\n line: c.line,\n })\n return differentiate(base.map(mapper), head.map(mapper), callsEqual, lineFuzz)\n}\n\nfunction callsEqual(a: Call, b: Call): boolean {\n return a.target === b.target && (a.resolved ?? null) === (b.resolved ?? null)\n}\n\n/**\n * §5.2.2 — decorator diff. Identity is `name`; when the same-named decorator is present\n * on both sides within the line-fuzz window, the argument list determines whether the\n * entry is a modified pair or an implicit no-op.\n */\nfunction diffDecorators(\n base: readonly Decorator[],\n head: readonly Decorator[],\n lineFuzz: number,\n): ArrayDelta {\n const mapper = (d: Decorator): Identified<Decorator> => ({\n item: d,\n key: d.name,\n line: d.line,\n })\n return differentiate(base.map(mapper), head.map(mapper), decoratorsEqual, lineFuzz)\n}\n\nfunction decoratorsEqual(a: Decorator, b: Decorator): boolean {\n if (a.name !== b.name) return false\n if (a.arguments.length !== b.arguments.length) return false\n for (let i = 0; i < a.arguments.length; i++) {\n if (a.arguments[i] !== b.arguments[i]) return false\n }\n return true\n}\n\n/**\n * §5.3 — signature delta. Three cases:\n * - both sides `null` → `null` (nothing to compare)\n * - exactly one side `null` → the present side is emitted verbatim as\n * `added` (when only head has a signature) or `removed` (when only base has one);\n * the three axis booleans compare the missing side against `false` defaults\n * - both sides non-null → per-list sub-deltas:\n * - `inputs`: strict positional compare via `differentiate(..., lineFuzz: 0)` — index\n * used as the line key so ordered parameter lists match position-for-position\n * - `outputs`: positional compare (added/removed emitted when the arrays diverge at\n * an index), no `modified` category\n * - `throws`: unordered set diff — duplicates collapse per Set semantics\n * - `async` / `generator` / `typeParameters` change flags follow the raw fields\n */\nfunction diffSignature(base: Signature | null, head: Signature | null): SignatureDelta | null {\n if (base === null && head === null) return null\n if (base === null || head === null) {\n const empty: ArrayDelta = { added: [], removed: [], modified: [] }\n const present = base ?? head\n if (present === null) return null\n const isBaseNull = base === null\n const inputs: ArrayDelta = isBaseNull\n ? { added: [...present.inputs], removed: [], modified: [] }\n : { added: [], removed: [...present.inputs], modified: [] }\n const outputs: ArrayDelta = isBaseNull\n ? { added: [...present.outputs], removed: [], modified: [] }\n : { added: [], removed: [...present.outputs], modified: [] }\n const throws: ArrayDelta = isBaseNull\n ? { added: [...present.throws], removed: [], modified: [] }\n : { added: [], removed: [...present.throws], modified: [] }\n return {\n inputs,\n outputs,\n throws,\n asyncChanged: (base?.async ?? false) !== (head?.async ?? false),\n generatorChanged: (base?.generator ?? false) !== (head?.generator ?? false),\n typeParametersChanged: !arraysEqual(\n base?.typeParameters ?? empty.added.map(String),\n head?.typeParameters ?? empty.added.map(String),\n ),\n }\n }\n const inputMapper = (\n input: { name: string; type: string },\n index: number,\n ): Identified<{ name: string; type: string }> => ({\n item: input,\n key: `${index}:${input.name}`,\n line: index,\n })\n const inputs = differentiate(\n base.inputs.map(inputMapper),\n head.inputs.map(inputMapper),\n (a, b) => a.name === b.name && a.type === b.type,\n 0,\n )\n const outputs = diffStringList(base.outputs, head.outputs)\n const throws = diffStringSet(base.throws, head.throws)\n return {\n inputs,\n outputs,\n throws,\n asyncChanged: base.async !== head.async,\n generatorChanged: base.generator !== head.generator,\n typeParametersChanged: !arraysEqual(base.typeParameters, head.typeParameters),\n }\n}\n\nfunction diffStringList(base: readonly string[], head: readonly string[]): ArrayDelta {\n const added: string[] = []\n const removed: string[] = []\n const max = Math.max(base.length, head.length)\n for (let i = 0; i < max; i++) {\n const b = i < base.length ? base[i] : undefined\n const h = i < head.length ? head[i] : undefined\n if (b === undefined && h !== undefined) added.push(h)\n else if (h === undefined && b !== undefined) removed.push(b)\n else if (b !== h) {\n if (b !== undefined) removed.push(b)\n if (h !== undefined) added.push(h)\n }\n }\n return { added, removed, modified: [] }\n}\n\nfunction diffStringSet(base: readonly string[], head: readonly string[]): ArrayDelta {\n const setB = new Set(base)\n const setH = new Set(head)\n const added: string[] = []\n const removed: string[] = []\n for (const h of setH) if (!setB.has(h)) added.push(h)\n for (const b of setB) if (!setH.has(b)) removed.push(b)\n return { added, removed, modified: [] }\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false\n for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false\n return true\n}\n","import type { Signature } from \"@aburi/types\"\n\n/**\n * §3.4.2 signatureSimilarity — averaged type-similarity across `inputs`, `outputs`, and\n * `throws`. Follows diff-algorithm.md §3.4.2:\n * - null + null → 1.0 (both symbols are signature-less; nothing to compare)\n * - null + non-null (either side) → 0.0\n * - both non-null: mean of three subscores\n *\n * Subscores:\n * - inputs: ordered type-string equality rate (`i-th type equal` counts as 1, else 0)\n * - outputs: same shape, but outputs is an array of type strings\n * - throws: unordered set intersection over union\n *\n * Empty subarrays on both sides count as 1.0 (they are trivially equal); on one side only\n * they count as 0.0 (asymmetric information).\n */\nexport function signatureSimilarity(\n base: Signature | null | undefined,\n head: Signature | null | undefined,\n): number {\n const baseSig = base ?? null\n const headSig = head ?? null\n if (baseSig === null && headSig === null) return 1\n if (baseSig === null || headSig === null) return 0\n const inputsScore = compareOrderedTypes(\n baseSig.inputs.map((i) => i.type),\n headSig.inputs.map((i) => i.type),\n )\n const outputsScore = compareOrderedTypes(baseSig.outputs, headSig.outputs)\n const throwsScore = compareUnorderedSet(baseSig.throws, headSig.throws)\n return (inputsScore + outputsScore + throwsScore) / 3\n}\n\nfunction compareOrderedTypes(a: readonly string[], b: readonly string[]): number {\n if (a.length === 0 && b.length === 0) return 1\n if (a.length !== b.length) return 0\n let matches = 0\n for (let i = 0; i < a.length; i++) {\n if (a[i] === b[i]) matches++\n }\n return matches / a.length\n}\n\nfunction compareUnorderedSet(a: readonly string[], b: readonly string[]): number {\n if (a.length === 0 && b.length === 0) return 1\n const setA = new Set(a)\n const setB = new Set(b)\n let intersection = 0\n for (const t of setA) if (setB.has(t)) intersection++\n const union = setA.size + setB.size - intersection\n return union === 0 ? 1 : intersection / union\n}\n","/**\n * String similarity utilities dedicated to the diff engine. Kept in one file so the\n * name/signature/owner formulas are auditable side by side against diff-algorithm.md §3.4.\n */\n\n/**\n * Tokenise a qualified name into normalised lowercase word segments. Splits on:\n * - camelCase / PascalCase boundaries (`fooBar` → `foo`, `Bar`)\n * - snake_case underscores (`foo_bar` → `foo`, `bar`)\n * - dotted namespaces (`ns.Class` → `ns`, `Class`)\n * - `::` static-method separator (`Class::method` → `Class`, `method`)\n * - runs of digits (`get2Users` → `get`, `2`, `Users`)\n *\n * The result is de-duplicated so `InvoiceService.createInvoice` collapses to\n * `[\"invoice\", \"service\", \"create\"]` — Jaccard similarity relies on set semantics, so\n * repeated tokens must not double-count.\n */\nexport function tokenizeName(input: string): string[] {\n const seen = new Set<string>()\n const out: string[] = []\n for (const raw of input.split(/[.:_\\-\\s/]+/)) {\n if (raw.length === 0) continue\n for (const piece of splitCamel(raw)) {\n const norm = piece.toLowerCase()\n if (norm.length === 0 || seen.has(norm)) continue\n seen.add(norm)\n out.push(norm)\n }\n }\n return out\n}\n\nfunction splitCamel(word: string): string[] {\n const chunks: string[] = []\n let current = \"\"\n for (const ch of word) {\n if (current === \"\") {\n current = ch\n continue\n }\n const prev = current.slice(-1)\n const boundary = isCamelBoundary(prev, ch)\n if (boundary) {\n chunks.push(current)\n current = ch\n } else {\n current += ch\n }\n }\n if (current.length > 0) chunks.push(current)\n return chunks\n}\n\nfunction isCamelBoundary(prev: string, curr: string): boolean {\n const prevLower = prev >= \"a\" && prev <= \"z\"\n const prevUpper = prev >= \"A\" && prev <= \"Z\"\n const prevDigit = prev >= \"0\" && prev <= \"9\"\n const currLower = curr >= \"a\" && curr <= \"z\"\n const currUpper = curr >= \"A\" && curr <= \"Z\"\n const currDigit = curr >= \"0\" && curr <= \"9\"\n if (prevLower && currUpper) return true\n if ((prevLower || prevUpper) && currDigit) return true\n if (prevDigit && (currLower || currUpper)) return true\n return false\n}\n\n/**\n * Jaccard similarity between two token multisets. Formula: |A ∩ B| / |A ∪ B|.\n * Empty on both sides returns 1.0 (both are \"no tokens\", which are equivalent for the\n * purpose of §3.4.1). Empty on one side only returns 0.0.\n */\nexport function jaccard(a: readonly string[], b: readonly string[]): number {\n if (a.length === 0 && b.length === 0) return 1\n if (a.length === 0 || b.length === 0) return 0\n const setA = new Set(a)\n const setB = new Set(b)\n let intersection = 0\n for (const t of setA) if (setB.has(t)) intersection++\n const union = setA.size + setB.size - intersection\n return intersection / union\n}\n\n/** name-token Jaccard shortcut used by nameSimilarity and ownerSimilarity call sites. */\nexport function jaccardTokens(a: string, b: string): number {\n return jaccard(tokenizeName(a), tokenizeName(b))\n}\n\n/**\n * §3.4.1 — Jaccard over the tokens of the full qualified name. The last segment is not\n * given extra weight; the entire path contributes uniformly because the intersection is\n * what the score should reward.\n */\nexport function nameSimilarity(baseName: string, headName: string): number {\n return jaccardTokens(baseName, headName)\n}\n\n/**\n * §3.4.6 — owner similarity: same as nameSimilarity but restricted to everything **before**\n * the last segment. Compensates for the R-8 same-name-different-class collision, where\n * `UserRepo.getUser` should not be pair-swapped with `AdminRepo.getUser`.\n *\n * Owner extraction:\n * - `Class::method` → `Class`\n * - `A.B.C.method` → `A.B.C`\n * - `topLevel` → `` (empty)\n *\n * When both owners are empty (top-level functions), returns 1.0 because both live in the\n * same \"no-owner\" bucket. When one is empty and the other is not, returns 0.0 because the\n * two Symbols live in structurally different scopes.\n */\nexport function ownerSimilarity(baseName: string, headName: string): number {\n const baseOwner = extractOwner(baseName)\n const headOwner = extractOwner(headName)\n if (baseOwner === \"\" && headOwner === \"\") return 1\n if (baseOwner === \"\" || headOwner === \"\") return 0\n return jaccardTokens(baseOwner, headOwner)\n}\n\nfunction extractOwner(qname: string): string {\n const staticIdx = qname.indexOf(\"::\")\n if (staticIdx >= 0) return qname.slice(0, staticIdx)\n const lastDot = qname.lastIndexOf(\".\")\n if (lastDot >= 0) return qname.slice(0, lastDot)\n return \"\"\n}\n\n/** §3.4.1 tail note — used by drop-4.5 weak matcher and threshold lookup. */\nexport function lastSegment(qname: string): string {\n const staticIdx = qname.indexOf(\"::\")\n if (staticIdx >= 0) return qname.slice(staticIdx + 2)\n const lastDot = qname.lastIndexOf(\".\")\n if (lastDot >= 0) return qname.slice(lastDot + 1)\n return qname\n}\n","import { trySymbolId } from \"@aburi/core\"\nimport type { Symbol as IRSymbol, MatchRationale, SymbolId } from \"@aburi/types\"\nimport { signatureSimilarity } from \"./signature\"\nimport { lastSegment, nameSimilarity, ownerSimilarity, tokenizeName } from \"./similarity\"\n\n/**\n * A concluded pairing of one base Symbol with one head Symbol, along with the\n * `MatchRationale` that placed them in the same pair. Stage 4 uses this shape both to\n * carry results out and to feed the delta / status classifier downstream.\n */\nexport interface SymbolPair {\n base: IRSymbol\n head: IRSymbol\n rationale: MatchRationale\n}\n\n/** Optional map returned by git for stage 2 (`old path` → `new path`). */\nexport type GitRenameMap = ReadonlyMap<string, string>\n\n/** Fingerprint for the 12-hex-char \"everything zero\" (i.e. dropped) canonical hash. */\nconst ZERO_LOGIC_FP = \"000000000000\"\n\n/**\n * §3.1 — segments Symbols with identical `id` into paired matches. Runs first because it\n * is the highest-confidence signal (no heuristics, just hash lookup).\n */\nexport function matchStageId(\n base: readonly IRSymbol[],\n head: readonly IRSymbol[],\n): {\n matched: SymbolPair[]\n remainingBase: IRSymbol[]\n remainingHead: IRSymbol[]\n} {\n const headById = new Map<SymbolId, IRSymbol>()\n for (const s of head) headById.set(s.id, s)\n const matched: SymbolPair[] = []\n const remainingBase: IRSymbol[] = []\n const usedHead = new Set<SymbolId>()\n for (const b of base) {\n const h = headById.get(b.id)\n if (h !== undefined) {\n matched.push({ base: b, head: h, rationale: \"id-match\" })\n usedHead.add(b.id)\n } else {\n remainingBase.push(b)\n }\n }\n const remainingHead = head.filter((h) => !usedHead.has(h.id))\n return { matched, remainingBase, remainingHead }\n}\n\n/**\n * §3.2 — git rename detection. When a rename map is available (typical of ref-driven\n * scans), rewrite the base id with the head-side file path and look for a hit.\n *\n * The rewriter only touches the `file` portion of the id — the `<language>:` prefix and\n * the trailing `#<qualified-name>` segment stay unchanged so a moved file with the same\n * qualified name is picked up correctly. When a Symbol's file is not in the rename map,\n * it is left for later stages.\n */\nexport function matchStageGitRename(\n remainingBase: readonly IRSymbol[],\n remainingHead: readonly IRSymbol[],\n renameMap: GitRenameMap | null,\n): {\n matched: SymbolPair[]\n remainingBase: IRSymbol[]\n remainingHead: IRSymbol[]\n} {\n if (renameMap === null || renameMap.size === 0) {\n return { matched: [], remainingBase: [...remainingBase], remainingHead: [...remainingHead] }\n }\n const headById = new Map<SymbolId, IRSymbol>()\n for (const h of remainingHead) headById.set(h.id, h)\n const matched: SymbolPair[] = []\n const usedHead = new Set<SymbolId>()\n const remaining: IRSymbol[] = []\n for (const b of remainingBase) {\n const newPath = renameMap.get(b.source.file)\n if (newPath === undefined) {\n remaining.push(b)\n continue\n }\n const expectedId = rewriteIdFile(b.id, b.source.file, newPath)\n const h = expectedId === null ? undefined : headById.get(expectedId)\n if (h === undefined || usedHead.has(h.id)) {\n remaining.push(b)\n continue\n }\n matched.push({ base: b, head: h, rationale: \"git-rename\" })\n usedHead.add(h.id)\n }\n const remainingHeadOut = remainingHead.filter((h) => !usedHead.has(h.id))\n return { matched, remainingBase: remaining, remainingHead: remainingHeadOut }\n}\n\n/**\n * Predict the id a base Symbol would carry after git moved its file. The second Symbol-id\n * derivation in the codebase after `makeSymbolId` itself, so it goes back through the same\n * constructor rather than re-concatenating the parts: `trySymbolId` rejects a rename target\n * the id grammar cannot express (a backslash path, say) instead of minting an id no head\n * Symbol can ever equal.\n *\n * Returns `null` for that case, and the unchanged id when no rename applies — the caller\n * treats a null the same way it treats a lookup miss, leaving the pair for stage 3.\n */\nfunction rewriteIdFile(id: SymbolId, oldPath: string, newPath: string): SymbolId | null {\n const colon = id.indexOf(\":\")\n const hash = id.indexOf(\"#\")\n if (colon < 0 || hash < 0 || hash < colon) return id\n const filePart = id.slice(colon + 1, hash)\n if (filePart !== oldPath) return id\n return trySymbolId({\n language: id.slice(0, colon),\n file: newPath,\n qualifiedName: id.slice(hash + 1),\n })\n}\n\n/**\n * §3.3 — group base Symbols by `fingerprint.logic` and match head Symbols whose logic\n * fingerprint is in the map. Two disambiguation branches:\n * - single candidate → paired with `logic-fingerprint`\n * - multiple candidates → best `nameSimilarity` wins if ≥ 0.85; otherwise leave both\n * sides in remaining for stage 4 to re-evaluate\n *\n * Dropped symbols are excluded (§3.3 tail) — their logic fingerprint is the sentinel\n * `\"000000000000\"` and would collide with every other dropped Symbol in the workspace.\n * They flow to the stage-4.5 weak matcher instead.\n */\nexport function matchStageLogicFingerprint(\n remainingBase: readonly IRSymbol[],\n remainingHead: readonly IRSymbol[],\n): {\n matched: SymbolPair[]\n remainingBase: IRSymbol[]\n remainingHead: IRSymbol[]\n} {\n const logicMap = new Map<string, IRSymbol[]>()\n const carryBase: IRSymbol[] = []\n for (const b of remainingBase) {\n if (b.dropped || b.fingerprint.logic === ZERO_LOGIC_FP) {\n carryBase.push(b)\n continue\n }\n const bucket = logicMap.get(b.fingerprint.logic) ?? []\n bucket.push(b)\n logicMap.set(b.fingerprint.logic, bucket)\n }\n const matched: SymbolPair[] = []\n const carryHead: IRSymbol[] = []\n const usedBaseIds = new Set<SymbolId>()\n for (const h of remainingHead) {\n if (h.dropped || h.fingerprint.logic === ZERO_LOGIC_FP) {\n carryHead.push(h)\n continue\n }\n const candidates = logicMap.get(h.fingerprint.logic)\n if (candidates === undefined || candidates.length === 0) {\n carryHead.push(h)\n continue\n }\n if (candidates.length === 1) {\n const only = candidates[0]\n if (only === undefined) continue\n matched.push({ base: only, head: h, rationale: \"logic-fingerprint\" })\n usedBaseIds.add(only.id)\n logicMap.delete(h.fingerprint.logic)\n continue\n }\n let best: { symbol: IRSymbol; score: number } | null = null\n for (const c of candidates) {\n const score = nameSimilarity(c.name, h.name)\n if (best === null || score > best.score) best = { symbol: c, score }\n }\n if (best !== null && best.score >= 0.85) {\n matched.push({\n base: best.symbol,\n head: h,\n rationale: \"logic-fingerprint+name-disambiguation\",\n })\n usedBaseIds.add(best.symbol.id)\n const remainingBucket = candidates.filter((c) => c.id !== best?.symbol.id)\n if (remainingBucket.length === 0) logicMap.delete(h.fingerprint.logic)\n else logicMap.set(h.fingerprint.logic, remainingBucket)\n } else {\n carryHead.push(h)\n }\n }\n const finalBase: IRSymbol[] = [...carryBase]\n for (const bucket of logicMap.values()) {\n for (const b of bucket) {\n if (!usedBaseIds.has(b.id)) finalBase.push(b)\n }\n }\n return { matched, remainingBase: finalBase, remainingHead: carryHead }\n}\n\n/**\n * §3.4 — name + signature similarity with `(kind, signatureNullness)` bucket pre-filter.\n * `signatureNullness` distinguishes symbols with no signature (`interface`, `type`,\n * `class`) from callable ones so that a class body is never paired against a function.\n *\n * The composite score:\n * 0.5 * nameSimilarity + 0.3 * signatureSimilarity + 0.2 * ownerSimilarity\n *\n * §3.4.3 threshold table:\n * - 1-token name → 1.0 (unreachable → never paired; false-positive shield)\n * - 2-token name → 0.95 (strict, avoids `getUser` vs `getUsers`)\n * - default → 0.85\n *\n * §3.4.3 tail — if both sides are signature-less, we skip pairing entirely because\n * signatureSimilarity always returns 1.0 for `null + null` and would flood the bucket with\n * false pairings.\n */\nexport function matchStageNameSignature(\n remainingBase: readonly IRSymbol[],\n remainingHead: readonly IRSymbol[],\n): {\n matched: SymbolPair[]\n remainingBase: IRSymbol[]\n remainingHead: IRSymbol[]\n} {\n type BucketKey = string\n const buckets = new Map<BucketKey, IRSymbol[]>()\n for (const b of remainingBase) {\n if (b.dropped) continue\n const key = bucketKey(b)\n const bucket = buckets.get(key) ?? []\n bucket.push(b)\n buckets.set(key, bucket)\n }\n const matched: SymbolPair[] = []\n const remainingHeadOut: IRSymbol[] = []\n const usedBaseIds = new Set<SymbolId>()\n for (const h of remainingHead) {\n if (h.dropped) {\n remainingHeadOut.push(h)\n continue\n }\n const key = bucketKey(h)\n const bucket = buckets.get(key)\n if (bucket === undefined || bucket.length === 0) {\n remainingHeadOut.push(h)\n continue\n }\n const headSigNull = h.signature === null || h.signature === undefined\n if (headSigNull && bucket.every((c) => c.signature === null || c.signature === undefined)) {\n remainingHeadOut.push(h)\n continue\n }\n let best: { symbol: IRSymbol; score: number } | null = null\n for (const c of bucket) {\n const score =\n 0.5 * nameSimilarity(c.name, h.name) +\n 0.3 * signatureSimilarity(c.signature ?? null, h.signature ?? null) +\n 0.2 * ownerSimilarity(c.name, h.name)\n if (best === null || score > best.score) best = { symbol: c, score }\n }\n const threshold = thresholdFor(h.name)\n if (best !== null && best.score >= threshold) {\n matched.push({ base: best.symbol, head: h, rationale: \"name-signature\" })\n usedBaseIds.add(best.symbol.id)\n buckets.set(\n key,\n bucket.filter((c) => c.id !== best?.symbol.id),\n )\n } else {\n remainingHeadOut.push(h)\n }\n }\n const remainingBaseOut: IRSymbol[] = []\n for (const b of remainingBase) {\n if (b.dropped) {\n remainingBaseOut.push(b)\n continue\n }\n if (!usedBaseIds.has(b.id)) remainingBaseOut.push(b)\n }\n return { matched, remainingBase: remainingBaseOut, remainingHead: remainingHeadOut }\n}\n\nfunction bucketKey(s: IRSymbol): string {\n const sig = s.signature === null || s.signature === undefined ? \"no-sig\" : \"has-sig\"\n return `${s.kind}::${sig}`\n}\n\nfunction thresholdFor(qname: string): number {\n const tokens = tokenizeName(lastSegment(qname)).length\n if (tokens <= 1) return 1\n if (tokens === 2) return 0.95\n return 0.85\n}\n\n/**\n * §3.4.5 — dropped-only weak matcher. For dropped Symbols the fingerprint is zeroed and\n * name/signature are the only remaining signals. Score is a coarse \"did the last name\n * segment or file basename survive?\" — if either half matches we pair (threshold 0.5).\n *\n * This is deliberately lax; diff-algorithm.md §3.4.5 already accepts that dropped Symbols live\n * outside the main IR review surface, so occasional false pairings only affect the\n * \"Drop-rule variation\" fold-out section in the Markdown projection.\n */\nexport function matchStageDroppedWeak(\n remainingBase: readonly IRSymbol[],\n remainingHead: readonly IRSymbol[],\n): {\n matched: SymbolPair[]\n remainingBase: IRSymbol[]\n remainingHead: IRSymbol[]\n} {\n const droppedBase = remainingBase.filter((s) => s.dropped)\n const nonDroppedBase = remainingBase.filter((s) => !s.dropped)\n const droppedHead = remainingHead.filter((s) => s.dropped)\n const nonDroppedHead = remainingHead.filter((s) => !s.dropped)\n const matched: SymbolPair[] = []\n const usedBaseIds = new Set<SymbolId>()\n const carryHead: IRSymbol[] = []\n for (const h of droppedHead) {\n let best: { symbol: IRSymbol; score: number } | null = null\n for (const b of droppedBase) {\n if (b.kind !== h.kind) continue\n if (usedBaseIds.has(b.id)) continue\n const nameHit = lastSegment(b.name) === lastSegment(h.name) ? 1 : 0\n const fileHit = basename(b.source.file) === basename(h.source.file) ? 1 : 0\n const score = 0.5 * nameHit + 0.5 * fileHit\n if (best === null || score > best.score) best = { symbol: b, score }\n }\n if (best !== null && best.score >= 0.5) {\n matched.push({ base: best.symbol, head: h, rationale: \"dropped-weak-match\" })\n usedBaseIds.add(best.symbol.id)\n } else {\n carryHead.push(h)\n }\n }\n const carryBase: IRSymbol[] = [\n ...nonDroppedBase,\n ...droppedBase.filter((b) => !usedBaseIds.has(b.id)),\n ]\n return {\n matched,\n remainingBase: carryBase,\n remainingHead: [...nonDroppedHead, ...carryHead],\n }\n}\n\nfunction basename(path: string): string {\n const slash = path.lastIndexOf(\"/\")\n return slash >= 0 ? path.slice(slash + 1) : path\n}\n","import { type CallEdge, computeWeaklyConnectedComponents, RESERVED_LANGUAGE_IDS } from \"@aburi/core\"\nimport type { SliceId, SliceRecord, SymbolChange, SymbolId } from \"@aburi/types\"\nimport { DiffError } from \"./errors\"\n\n/** The three inputs of the Slice View pass — docs/design/slice-view.md §3. */\nexport interface SliceInput {\n /** SymbolChange records produced by `buildDiff` (pre-sort is not required). */\n changes: readonly SymbolChange[]\n /**\n * Resolved call edges from the base IR. Typically produced by\n * `reconstructCallEdgesFromIR(baseIR)` inside `buildDiff` — Slice View\n * consumes only resolved edges (§5.4), never `Symbol.calls[]` directly.\n */\n baseCallEdges: readonly CallEdge[]\n /** Resolved call edges from the head IR (same source rule as base). */\n headCallEdges: readonly CallEdge[]\n}\n\n/**\n * Slice View clustering pass — docs/design/slice-view.md §2, §4–§8.\n *\n * Groups the changed-Symbol set of a diff into weakly-connected components\n * over the union of base and head call edges. Emits a `SliceRecord[]` whose\n * cluster ids are `\"slice:\" + <smallest-member Symbol id>`, both `slices[]`\n * and each `members[]` sorted ascending.\n *\n * Pure function: `(changes, baseCallEdges, headCallEdges)` → `SliceRecord[]`.\n * Determinism, idempotence, input-order insensitivity, and locality are all\n * guaranteed — see `computeWeaklyConnectedComponents` in `@aburi/core`.\n *\n * Returns `[]` when no SymbolChange is Node-eligible per §4.1; callers should\n * still serialise the empty array — the Markdown projection omits the section\n * (§9.4 / §12.5) but the JSON always emits the key so consumers never\n * distinguish \"field absent\" from \"no slices\" (§11.2).\n *\n * Every returned record has passed `assertSliceRecordInvariant` (§7.4).\n */\nexport function computeSlices(input: SliceInput): SliceRecord[] {\n const nodeIds = collectNodeIds(input.changes)\n if (nodeIds.length === 0) return []\n\n const nodeIdSet = new Set<SymbolId>(nodeIds)\n const edges = collectEdges(input.baseCallEdges, input.headCallEdges, nodeIdSet)\n\n const components = computeWeaklyConnectedComponents<SymbolId>(nodeIds, edges, (id) => id)\n\n return components.map(makeSliceRecord)\n}\n\nconst SLICE_ID_PREFIX = \"slice:\"\n\n/**\n * §7.1 — the single place production code derives a Slice id. Everything else\n * in `src/` either receives an id or renders one. (Tests spell the prefix out\n * literally on purpose: an expectation written in terms of the function under\n * test would agree with it no matter what it produced.)\n *\n * The cast is the one place a `SliceId` comes into existence. `SliceId` and\n * `SymbolId` are separate brands precisely so this concatenation cannot be\n * open-coded anywhere else: a bare `\"slice:\" + x` evaluates to `string`, which\n * `SliceRecord.id` no longer accepts.\n */\nfunction sliceIdFor(anchor: SymbolId): SliceId {\n return `${SLICE_ID_PREFIX}${anchor}` as SliceId\n}\n\n/**\n * Build one SliceRecord from an ascending-sorted component and check its own\n * post-condition before letting it out (§7.4 layer 1).\n *\n * The check is not defending against untrusted input — this function builds\n * the id itself, so the derivation clause is true by construction here and\n * only the two clauses about `members` can actually fire. Those are the point:\n * `members[0]` is the anchor only because `computeWeaklyConnectedComponents`\n * returns each component sorted ascending, a guarantee that lives one layer\n * down and is invisible from this file. If it ever stops holding, the diff\n * would otherwise keep emitting well-formed-looking SliceRecords naming the\n * wrong anchor. Here it fails instead.\n */\nfunction makeSliceRecord(members: readonly SymbolId[]): SliceRecord {\n const anchor = members[0]\n if (anchor === undefined) {\n throw new DiffError(\n \"computeSlices: the clustering utility returned an empty component; every weakly-connected \" +\n \"component contains at least the node that seeded it (slice-view.md §6).\",\n { code: \"slice-invariant-violated\" },\n )\n }\n const record: SliceRecord = { id: sliceIdFor(anchor), members: [...members] }\n assertSliceRecordInvariant(record)\n return record\n}\n\n/**\n * §7.1 — the anchor of a Slice: its lexicographically smallest member.\n *\n * Answers from `members[0]`. `id` is derived from the anchor, so\n * reconstructing the anchor by stripping the `\"slice:\"` prefix is circular at\n * best and, for a record that broke the derivation, silently wrong: it would\n * name a Symbol that is not in the Slice. `id` is read here only to name the\n * offending record in the error message. Consumers that need the anchor call\n * this; consumers that need a label keep using `id` directly.\n */\nexport function sliceAnchor(record: SliceRecord): SymbolId {\n const anchor = record.members[0]\n if (anchor === undefined) {\n const violation = emptyMembersViolation(record.id)\n throw new DiffError(violation.message, {\n code: \"slice-invariant-violated\",\n value: violation.subject,\n })\n }\n return anchor\n}\n\n/** Which clause of the §7.1 / §8.2 invariant a `SliceRecord` broke. */\nexport type SliceViolationKind =\n /** Not a `SliceRecord` at all: not an object, or `id` / `members` of the wrong type. */\n | \"malformed-shape\"\n /** `members[]` has no entries, so the Slice has no anchor (§11.1). */\n | \"members-empty\"\n /** `members[]` is not in strictly ascending order, so `members[0]` need not be the smallest (§8.2, §11.1). */\n | \"members-unordered\"\n /** `id` is not `\"slice:\" + members[0]` (§7.1). */\n | \"id-not-derived\"\n /** The anchor is itself in a reserved id namespace, so `id` would read as a doubled prefix (§7.5). */\n | \"anchor-in-reserved-namespace\"\n\nexport interface SliceRecordViolation {\n kind: SliceViolationKind\n /** The offending record's `id` when it has a usable one, else a short stand-in. */\n subject: string\n /** Human-facing explanation, suitable for an error message or a validator error. */\n message: string\n}\n\n/**\n * Report which clause of the §7.1 / §8.2 invariant a value breaks, or `null`\n * when it is a well-formed `SliceRecord`. Non-throwing counterpart of\n * `assertSliceRecordInvariant`: the pass wants an exception, a schema\n * validator wants a verdict it can turn into its own error.\n *\n * Takes `unknown` rather than `SliceRecord` deliberately. §7.4 layer 2 points\n * this at documents written by third-party or older producers — data that has\n * not been type-checked by definition — so anything that assumed a well-typed\n * argument would crash on exactly the input it exists to reject.\n */\nexport function sliceRecordViolation(value: unknown): SliceRecordViolation | null {\n if (typeof value !== \"object\" || value === null) {\n return {\n kind: \"malformed-shape\",\n subject: \"<not an object>\",\n message: `Expected a SliceRecord object; got ${value === null ? \"null\" : typeof value}.`,\n }\n }\n const { id, members } = value as { id?: unknown; members?: unknown }\n const subject = typeof id === \"string\" ? id : \"<missing id>\"\n if (typeof id !== \"string\") {\n return {\n kind: \"malformed-shape\",\n subject,\n message: `SliceRecord ${subject}: id must be a string; got ${typeof id}.`,\n }\n }\n if (!Array.isArray(members) || members.some((member) => typeof member !== \"string\")) {\n return {\n kind: \"malformed-shape\",\n subject,\n message: `SliceRecord ${subject}: members must be an array of strings.`,\n }\n }\n\n const anchor = members[0]\n if (anchor === undefined) return emptyMembersViolation(subject)\n // A Symbol id in the `slice:` namespace derives to `slice:slice:…`, which is\n // self-consistent — it passes the derivation clause below — but names an id no reader can\n // tell from a Slice id. `makeSymbolId` refuses to build such a Symbol id and\n // `checkIRIntegrity` #16 rejects one read from disk, but `buildDiff` is public API and\n // runs no integrity check, so the doubled prefix is caught here too.\n const reservedAnchor = reservedNamespaceOf(anchor)\n if (reservedAnchor !== null) {\n return {\n kind: \"anchor-in-reserved-namespace\",\n subject,\n message:\n `SliceRecord anchor \"${anchor}\" uses the reserved language token \"${reservedAnchor}\", ` +\n `so its Slice id would repeat the prefix (ir-schema.md §3.5, slice-view.md §7.5).`,\n }\n }\n for (let i = 1; i < members.length; i++) {\n const previous = members[i - 1] as string\n const current = members[i] as string\n if (previous < current) continue\n return {\n kind: \"members-unordered\",\n subject,\n message:\n `SliceRecord ${subject}: members[] is not in strictly ascending order at index ${i} ` +\n `(\"${current}\" follows \"${previous}\"), so members[0] is not necessarily the anchor ` +\n `(slice-view.md §8.2 for the order, §11.1 for uniqueness).`,\n }\n }\n // The one place outside `sliceIdFor` that asserts an id brand, and the reason this\n // function takes `unknown`: the anchor has been checked to be a string and nothing more.\n // Whether it is a well-formed Symbol id is not this check's question — a record whose\n // members are gibberish still has to be told apart from one whose id disagrees with them.\n const expected = sliceIdFor(anchor as SymbolId)\n if (id !== expected) {\n return {\n kind: \"id-not-derived\",\n subject,\n message:\n `SliceRecord id \"${id}\" is not derived from the anchor \"${anchor}\"; ` +\n `expected \"${expected}\" (slice-view.md §7.1).`,\n }\n }\n return null\n}\n\n/** The reserved token an id opens with, or `null`. Shares the list `@aburi/core` enforces. */\nfunction reservedNamespaceOf(id: string): string | null {\n const colon = id.indexOf(\":\")\n if (colon < 0) return null\n const token = id.slice(0, colon)\n return RESERVED_LANGUAGE_IDS.has(token) ? token : null\n}\n\n/** Single source of the empty-`members[]` wording, shared with `sliceAnchor`. */\nfunction emptyMembersViolation(subject: string): SliceRecordViolation {\n return {\n kind: \"members-empty\",\n subject,\n message:\n `SliceRecord ${subject}: members[] is empty, so the Slice has no anchor ` +\n `(slice-view.md §11.1 requires at least one member).`,\n }\n}\n\n/**\n * Throwing form of `sliceRecordViolation`. Raised as a coded `DiffError` so\n * callers branch on `code` rather than parsing the message; which clause broke\n * is `SliceRecordViolation.kind`, for callers that need to tell them apart.\n */\nexport function assertSliceRecordInvariant(record: SliceRecord): void {\n const violation = sliceRecordViolation(record)\n if (violation === null) return\n throw new DiffError(violation.message, {\n code: \"slice-invariant-violated\",\n value: violation.subject,\n })\n}\n\n/**\n * §4.1 Node selection: keep every SymbolChange whose status is\n * added / removed / changed / moved+changed / dropped-toggled. The identity\n * is the head-side Symbol id where present (added / changed / moved+changed /\n * dropped-toggled), the base-side id for removed. Pure moved and unchanged\n * (already stripped upstream) are excluded — §4.1 / §4.3.\n *\n * The returned array is deliberately not deduplicated: a well-formed\n * SymbolChange list from `buildDiff` never mentions the same Symbol id\n * twice, and Union-Find would coalesce any accidental duplicate anyway.\n */\nfunction collectNodeIds(changes: readonly SymbolChange[]): SymbolId[] {\n const ids: SymbolId[] = []\n for (const change of changes) {\n const id = nodeIdOf(change)\n if (id === null) continue\n ids.push(id)\n }\n return ids\n}\n\nfunction nodeIdOf(change: SymbolChange): SymbolId | null {\n switch (change.status) {\n case \"added\":\n return change.symbol.id\n case \"removed\":\n // §4.1 — removed uses base-side id because there is no head symbol.\n return change.symbol.id\n case \"changed\":\n case \"moved+changed\":\n case \"dropped-toggled\":\n return change.after.id\n case \"moved\":\n // §4.3 — pure moved is not a Node and is not used for bridging either.\n return null\n }\n // Exhaustive: if SymbolChange grows a new status the compile error above\n // fires before this fallback is reachable.\n}\n\n/**\n * §5.1 Edge selection: union of base and head, restricted to edges whose\n * BOTH endpoints are Nodes, canonicalised to `(u, v)` with `u < v` (self-\n * loops implicitly dropped). Multi-edges collapse naturally inside the WCC\n * primitive — no explicit dedup needed.\n *\n * The union is the load-bearing rule of §5.3: a controller that called an\n * old service in base and a new service in head needs both edges to land\n * all three Symbols in a single Slice.\n */\nfunction collectEdges(\n baseEdges: readonly CallEdge[],\n headEdges: readonly CallEdge[],\n nodeIds: ReadonlySet<SymbolId>,\n): [SymbolId, SymbolId][] {\n const pairs: [SymbolId, SymbolId][] = []\n for (const edge of baseEdges) appendEdgeIfBothNodes(edge, nodeIds, pairs)\n for (const edge of headEdges) appendEdgeIfBothNodes(edge, nodeIds, pairs)\n return pairs\n}\n\nfunction appendEdgeIfBothNodes(\n edge: CallEdge,\n nodeIds: ReadonlySet<SymbolId>,\n out: [SymbolId, SymbolId][],\n): void {\n if (!nodeIds.has(edge.from) || !nodeIds.has(edge.to)) return\n out.push([edge.from, edge.to])\n}\n","import type { Symbol as IRSymbol } from \"@aburi/types\"\n\nexport type SymbolStatus = \"unchanged\" | \"moved\" | \"changed\" | \"moved+changed\" | \"dropped-toggled\"\n\n/**\n * §4 — determine which of the five statuses a matched pair falls into. `dropped-toggled`\n * has absolute priority: whenever `dropped` differs between base and head, every other\n * signal is ignored because the underlying fingerprint change is a consequence of the\n * drop-rule flip, not a genuine code edit (§4.1 rationale).\n *\n * \"pathChanged\" catches both physical file relocation AND in-file rename: any Symbol id\n * mismatch between base and head means the identifier moved, and DF9 (\"method rename\n * (same file, same logic) → moved\") relies on this. Stage-1 pairs cannot trigger the id branch\n * (they match by exact id), so this only affects stage-2+ pairings where the pair was\n * discovered via fingerprint or name/signature similarity.\n */\nexport function classifyStatus(base: IRSymbol, head: IRSymbol): SymbolStatus {\n const droppedToggled = base.dropped !== head.dropped\n if (droppedToggled) return \"dropped-toggled\"\n const pathChanged = base.source.file !== head.source.file || base.id !== head.id\n const fingerprintChanged =\n base.fingerprint.api !== head.fingerprint.api ||\n base.fingerprint.logic !== head.fingerprint.logic ||\n base.fingerprint.syntax !== head.fingerprint.syntax\n if (pathChanged && fingerprintChanged) return \"moved+changed\"\n if (pathChanged) return \"moved\"\n if (fingerprintChanged) return \"changed\"\n return \"unchanged\"\n}\n\n/** Direction of a `dropped-toggled` transition (§7.1 SymbolDroppedToggled). */\nexport type DropDirection = \"to-dropped\" | \"to-kept\"\n\nexport function dropDirection(head: IRSymbol): DropDirection {\n return head.dropped ? \"to-dropped\" : \"to-kept\"\n}\n","import { reconstructCallEdgesFromIR, type SerializeOptions, serializeCanonical } from \"@aburi/core\"\nimport type { DiffResult, IR, IRRef, Summary, SymbolChange } from \"@aburi/types\"\nimport { diffComponents, diffDependencies } from \"./components\"\nimport { computeSymbolDelta, type DeltaOptions } from \"./delta\"\nimport { DiffError } from \"./errors\"\nimport {\n type GitRenameMap,\n matchStageDroppedWeak,\n matchStageGitRename,\n matchStageId,\n matchStageLogicFingerprint,\n matchStageNameSignature,\n type SymbolPair,\n} from \"./match\"\nimport { computeSlices } from \"./slice\"\nimport { classifyStatus, dropDirection } from \"./status\"\n\nconst DIFF_SCHEMA = \"https://aburi.dev/schema/aburi.diff.v1.json\"\n\nexport interface DiffInput {\n baseIR: IR\n headIR: IR\n /** IR reference metadata (git ref / file path) for provenance. */\n base: IRRef\n head: IRRef\n /** Generator record for the diff output. Defaults to `{name: \"aburi\", version: \"0.0.0\"}`. */\n generator?: { name: string; version: string }\n /** Optional stage-2 rename table. When null/undefined stage 2 is skipped. */\n gitRenames?: GitRenameMap | null\n /** Passed through to computeSymbolDelta (§5.2.1 line fuzz). */\n delta?: DeltaOptions\n}\n\nconst DEFAULT_GENERATOR = { name: \"aburi\", version: \"0.0.0\" }\n\n/**\n * Top-level entry: run the 5-stage matcher, classify each pair, produce array deltas,\n * fold in Component / Dependency diffs, and assemble the `aburi.diff.v1` JSON projection.\n * The function is pure; write-to-disk is delegated to `writeCanonicalDiff` so callers can\n * pipe the result through additional steps (Markdown projection, `--fail-on` gate) before\n * serialisation.\n */\nexport function buildDiff(input: DiffInput): DiffResult {\n assertIRShape(input.baseIR, \"baseIR\")\n assertIRShape(input.headIR, \"headIR\")\n ensureSchemasAgree(input.baseIR, input.headIR)\n const stage1 = matchStageId(input.baseIR.symbols, input.headIR.symbols)\n const stage2 = matchStageGitRename(\n stage1.remainingBase,\n stage1.remainingHead,\n input.gitRenames ?? null,\n )\n const stage3 = matchStageLogicFingerprint(stage2.remainingBase, stage2.remainingHead)\n const stage4 = matchStageNameSignature(stage3.remainingBase, stage3.remainingHead)\n const stage4_5 = matchStageDroppedWeak(stage4.remainingBase, stage4.remainingHead)\n\n const pairs: SymbolPair[] = [\n ...stage1.matched,\n ...stage2.matched,\n ...stage3.matched,\n ...stage4.matched,\n ...stage4_5.matched,\n ]\n\n const summary: Summary = {\n added: 0,\n removed: 0,\n moved: 0,\n movedChanged: 0,\n changed: 0,\n droppedToggled: 0,\n unchanged: 0,\n droppedAdded: 0,\n droppedRemoved: 0,\n componentsAdded: 0,\n componentsRemoved: 0,\n componentsChanged: 0,\n depsAdded: 0,\n depsRemoved: 0,\n }\n\n const symbols: SymbolChange[] = []\n for (const pair of pairs) {\n const status = classifyStatus(pair.base, pair.head)\n if (status === \"unchanged\") {\n summary.unchanged++\n continue\n }\n if (status === \"dropped-toggled\") {\n summary.droppedToggled++\n symbols.push({\n status: \"dropped-toggled\",\n before: pair.base,\n after: pair.head,\n direction: dropDirection(pair.head),\n })\n continue\n }\n if (status === \"moved\") {\n summary.moved++\n symbols.push({\n status: \"moved\",\n before: pair.base,\n after: pair.head,\n rationale: pair.rationale,\n })\n continue\n }\n if (status === \"changed\") {\n summary.changed++\n symbols.push({\n status: \"changed\",\n before: pair.base,\n after: pair.head,\n delta: computeSymbolDelta(pair.base, pair.head, input.delta ?? {}),\n })\n continue\n }\n // moved+changed\n summary.movedChanged++\n symbols.push({\n status: \"moved+changed\",\n before: pair.base,\n after: pair.head,\n rationale: pair.rationale,\n delta: computeSymbolDelta(pair.base, pair.head, input.delta ?? {}),\n })\n }\n\n const finalRemainingHead = stage4_5.remainingHead\n const finalRemainingBase = stage4_5.remainingBase\n\n for (const h of finalRemainingHead) {\n if (h.dropped) summary.droppedAdded++\n else {\n summary.added++\n symbols.push({ status: \"added\", symbol: h })\n }\n }\n for (const b of finalRemainingBase) {\n if (b.dropped) summary.droppedRemoved++\n else {\n summary.removed++\n symbols.push({ status: \"removed\", symbol: b })\n }\n }\n\n const components = diffComponents(input.baseIR.components, input.headIR.components)\n summary.componentsAdded = components.added.length\n summary.componentsRemoved = components.removed.length\n summary.componentsChanged = components.changed.length\n\n const dependencies = diffDependencies(input.baseIR.dependencies, input.headIR.dependencies)\n summary.depsAdded = dependencies.added.length\n summary.depsRemoved = dependencies.removed.length\n\n symbols.sort(compareSymbolChange)\n\n // Slice View clustering (docs/design/slice-view.md §2). Runs after status /\n // delta computation and before Markdown projection. Consumes the resolved\n // CallEdge[] reconstructed from each IR — §5.4 requires only resolved edges,\n // never `Symbol.calls[]` directly. Emits `slices[]` unconditionally (§11.2);\n // the Markdown side omits the section when the array is empty (§12.5).\n const slices = computeSlices({\n changes: symbols,\n baseCallEdges: reconstructCallEdgesFromIR(input.baseIR),\n headCallEdges: reconstructCallEdgesFromIR(input.headIR),\n })\n\n return {\n $schema: DIFF_SCHEMA,\n generator: input.generator ?? DEFAULT_GENERATOR,\n base: input.base,\n head: input.head,\n summary,\n symbols,\n components,\n dependencies,\n slices,\n }\n}\n\n/** §9.1 — refuse to diff across schema versions. */\nfunction ensureSchemasAgree(base: IR, head: IR): void {\n if (base.$schema !== head.$schema) {\n throw new DiffError(\n `Base IR schema \"${base.$schema}\" does not match head IR schema \"${head.$schema}\"; a diff across schema versions is not supported.`,\n { code: \"schema-mismatch\", value: base.$schema },\n )\n }\n}\n\n/**\n * Top-level shape check. Refuses to enter the 5-stage matcher when a caller hands in an\n * IR-shaped object that is missing one of the collections the matcher requires as an\n * array. Without this, a malformed `baseIR = { symbols: undefined, ... }` would crash\n * deep inside `matchStageId` with `TypeError: undefined is not iterable`, leaving the\n * root cause obscured. Not a full schema validation — just an entry-point smoke test.\n */\nfunction assertIRShape(ir: IR, name: \"baseIR\" | \"headIR\"): void {\n if (ir === null || typeof ir !== \"object\") {\n throw new DiffError(`${name} must be an IR object; got ${typeof ir}.`, {\n code: \"ir-shape-invalid\",\n value: name,\n })\n }\n if (typeof ir.$schema !== \"string\" || ir.$schema.length === 0) {\n throw new DiffError(`${name}.$schema must be a non-empty schema URL.`, {\n code: \"ir-shape-invalid\",\n value: `${name}.$schema`,\n })\n }\n for (const field of [\"symbols\", \"components\", \"dependencies\"] as const) {\n if (!Array.isArray(ir[field])) {\n throw new DiffError(`${name}.${field} must be an array.`, {\n code: \"ir-shape-invalid\",\n value: `${name}.${field}`,\n })\n }\n }\n}\n\n/**\n * Deterministic ordering of the `symbols[]` output:\n * 1. by status (alphabetical) — pins added/changed/moved sections\n * 2. by the \"reference\" id (`after` for change/move/toggle, otherwise `symbol`)\n *\n * The result is stable byte-for-byte for equal inputs, matching the canonicalisation\n * guarantee the diff JSON must uphold when persisted to `out/diff.json`.\n */\nfunction compareSymbolChange(a: SymbolChange, b: SymbolChange): number {\n if (a.status !== b.status) return a.status < b.status ? -1 : 1\n const idA = referenceId(a)\n const idB = referenceId(b)\n return idA < idB ? -1 : idA > idB ? 1 : 0\n}\n\nfunction referenceId(change: SymbolChange): string {\n if (change.status === \"added\" || change.status === \"removed\") return change.symbol.id\n return change.after.id\n}\n\n/**\n * Byte-deterministic serialiser for a DiffResult. Delegates to `@aburi/core`\n * `serializeCanonical` so the sort order, NFC normalisation, and codepoint key sort are\n * shared with the IR side.\n */\nexport function writeCanonicalDiff(diff: DiffResult, options: SerializeOptions = {}): string {\n return serializeCanonical(diff, options)\n}\n"],"mappings":"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"}
package/src/components.ts DELETED
@@ -1,111 +0,0 @@
1
- import type {
2
- Component,
3
- ComponentDiff,
4
- ComponentId,
5
- Dependency,
6
- DependencyDiff,
7
- } from "@aburi/types"
8
-
9
- /**
10
- * §6.1 — Component diff. Component identity is `id`; when a Component is present in both
11
- * base and head with the same id, the three delta booleans describe which axes actually
12
- * moved (roots reshuffle, public API globs, or framework hint list). `modified` deltas
13
- * are intentionally absent from this diff (§5.2.3): fields are reported as before/after
14
- * pairs via the `changed[]` entries so consumers can render them without stringifying
15
- * arrays.
16
- */
17
- export function diffComponents(
18
- base: readonly Component[],
19
- head: readonly Component[],
20
- ): ComponentDiff {
21
- const baseById = new Map<ComponentId, Component>()
22
- for (const c of base) baseById.set(c.id, c)
23
- const headById = new Map<ComponentId, Component>()
24
- for (const c of head) headById.set(c.id, c)
25
-
26
- const added: Component[] = []
27
- const removed: Component[] = []
28
- const changed: ComponentDiff["changed"] = []
29
-
30
- for (const [id, headComp] of headById) {
31
- const baseComp = baseById.get(id)
32
- if (baseComp === undefined) {
33
- added.push(headComp)
34
- continue
35
- }
36
- const rootsChanged = !stringArraysEqual(baseComp.roots, headComp.roots)
37
- const publicApiChanged = !stringArraysEqual(baseComp.publicApi ?? [], headComp.publicApi ?? [])
38
- const frameworksChanged = !stringArraysEqual(
39
- baseComp.frameworks ?? [],
40
- headComp.frameworks ?? [],
41
- )
42
- if (rootsChanged || publicApiChanged || frameworksChanged) {
43
- changed.push({
44
- before: baseComp,
45
- after: headComp,
46
- delta: { rootsChanged, publicApiChanged, frameworksChanged },
47
- })
48
- }
49
- }
50
- for (const [id, baseComp] of baseById) {
51
- if (!headById.has(id)) removed.push(baseComp)
52
- }
53
- sortById(added)
54
- sortById(removed)
55
- changed.sort((a, b) => (a.after.id < b.after.id ? -1 : a.after.id > b.after.id ? 1 : 0))
56
- return { added, removed, changed }
57
- }
58
-
59
- /**
60
- * §6.2 — Dependency diff. Identity is the composite `(from, to, via)` triple; direction
61
- * and effect changes are surfaced as an added + removed pair so `modified` is not part of
62
- * the schema (§6.2 tail).
63
- */
64
- export function diffDependencies(
65
- base: readonly Dependency[],
66
- head: readonly Dependency[],
67
- ): DependencyDiff {
68
- const baseKeys = new Map<string, Dependency>()
69
- for (const d of base) baseKeys.set(dependencyKey(d), d)
70
- const headKeys = new Map<string, Dependency>()
71
- for (const d of head) headKeys.set(dependencyKey(d), d)
72
- const added: Dependency[] = []
73
- const removed: Dependency[] = []
74
- for (const [key, dep] of headKeys) {
75
- const b = baseKeys.get(key)
76
- if (b === undefined) {
77
- added.push(dep)
78
- continue
79
- }
80
- if (b.direction !== dep.direction || (b.effect ?? null) !== (dep.effect ?? null)) {
81
- removed.push(b)
82
- added.push(dep)
83
- }
84
- }
85
- for (const [key, dep] of baseKeys) {
86
- if (!headKeys.has(key)) removed.push(dep)
87
- }
88
- added.sort(compareDependencies)
89
- removed.sort(compareDependencies)
90
- return { added, removed }
91
- }
92
-
93
- function dependencyKey(d: Dependency): string {
94
- return `${d.from}::${d.to}::${d.via}`
95
- }
96
-
97
- function compareDependencies(a: Dependency, b: Dependency): number {
98
- const keyA = dependencyKey(a)
99
- const keyB = dependencyKey(b)
100
- return keyA < keyB ? -1 : keyA > keyB ? 1 : 0
101
- }
102
-
103
- function stringArraysEqual(a: readonly string[], b: readonly string[]): boolean {
104
- if (a.length !== b.length) return false
105
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false
106
- return true
107
- }
108
-
109
- function sortById<T extends { id: string }>(items: T[]): void {
110
- items.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0))
111
- }
package/src/delta.ts DELETED
@@ -1,304 +0,0 @@
1
- import type {
2
- ArrayDelta,
3
- Call,
4
- Decorator,
5
- Effect,
6
- Symbol as IRSymbol,
7
- Rule,
8
- Signature,
9
- SignatureDelta,
10
- SymbolDelta,
11
- } from "@aburi/types"
12
- import { DiffError } from "./errors"
13
-
14
- export const DEFAULT_LINE_FUZZ = 2
15
- export const MAX_LINE_FUZZ = 10
16
- export const MIN_LINE_FUZZ = 0
17
-
18
- export interface DeltaOptions {
19
- /**
20
- * §5.2.1 line fuzz for rule/call identity. Must be an integer in
21
- * `[MIN_LINE_FUZZ, MAX_LINE_FUZZ]` (`0..10`); anything outside — or a non-finite value —
22
- * throws `DiffError({ code: "invalid-line-fuzz" })`. Setting `0` disables fuzz; omitting
23
- * the field falls back to `DEFAULT_LINE_FUZZ` (2).
24
- */
25
- lineFuzz?: number
26
- }
27
-
28
- /**
29
- * §5 — Compute the full per-Symbol delta between two paired Symbols. The three axis
30
- * booleans (`apiChanged` / `logicChanged` / `syntaxChanged`) come from fingerprint
31
- * comparison; the array deltas (`rules` / `effects` / `calls` / `decorators`) come from
32
- * identity-preserving diff with configurable line fuzz.
33
- */
34
- export function computeSymbolDelta(
35
- base: IRSymbol,
36
- head: IRSymbol,
37
- options: DeltaOptions = {},
38
- ): SymbolDelta {
39
- const fuzz = validateLineFuzz(options.lineFuzz ?? DEFAULT_LINE_FUZZ)
40
- const delta: SymbolDelta = {
41
- apiChanged: base.fingerprint.api !== head.fingerprint.api,
42
- logicChanged: base.fingerprint.logic !== head.fingerprint.logic,
43
- syntaxChanged: base.fingerprint.syntax !== head.fingerprint.syntax,
44
- componentChanged: (base.component ?? null) !== (head.component ?? null),
45
- visibilityChanged: base.visibility !== head.visibility,
46
- }
47
- delta.rules = diffRules(base.rules, head.rules, fuzz)
48
- delta.effects = diffEffects(base.effects, head.effects)
49
- delta.calls = diffCalls(base.calls, head.calls, fuzz)
50
- delta.decorators = diffDecorators(base.decorators, head.decorators, fuzz)
51
- const sigDelta = diffSignature(base.signature ?? null, head.signature ?? null)
52
- delta.signature = sigDelta
53
- return delta
54
- }
55
-
56
- /**
57
- * §5.2.1 range check. Loud on violation rather than silently clamping so a config typo
58
- * (`lineFuzz: 999`) or an upstream numeric bug (`NaN`, `Infinity`) surfaces at the diff
59
- * boundary instead of quietly rounding into a range that produces the wrong deltas.
60
- */
61
- function validateLineFuzz(value: number): number {
62
- if (!Number.isFinite(value) || !Number.isInteger(value)) {
63
- throw new DiffError(
64
- `config.diff.lineFuzz must be an integer in [${MIN_LINE_FUZZ}, ${MAX_LINE_FUZZ}]; got ${String(value)}.`,
65
- { code: "invalid-line-fuzz", value: String(value) },
66
- )
67
- }
68
- if (value < MIN_LINE_FUZZ || value > MAX_LINE_FUZZ) {
69
- throw new DiffError(
70
- `config.diff.lineFuzz must be within [${MIN_LINE_FUZZ}, ${MAX_LINE_FUZZ}]; got ${value}.`,
71
- { code: "invalid-line-fuzz", value: String(value) },
72
- )
73
- }
74
- return value
75
- }
76
-
77
- interface Identified<T> {
78
- item: T
79
- key: string
80
- line: number
81
- }
82
-
83
- /**
84
- * Two-index diff: build key-indexed maps for both sides, then classify each key into
85
- * `added` / `removed` / `modified`. Line-fuzzed identities are matched within tolerance
86
- * so cosmetic line shifts do not produce added/removed noise. `modified` only fires when
87
- * the identity key stays the same but the content differs.
88
- */
89
- function differentiate<T>(
90
- base: readonly Identified<T>[],
91
- head: readonly Identified<T>[],
92
- isEqual: (a: T, b: T) => boolean,
93
- lineFuzz: number,
94
- ): ArrayDelta {
95
- const added: T[] = []
96
- const removed: T[] = []
97
- const modified: T[] = []
98
- const consumedBase = new Set<number>()
99
- for (const h of head) {
100
- let matchIdx = -1
101
- for (let j = 0; j < base.length; j++) {
102
- if (consumedBase.has(j)) continue
103
- const b = base[j]
104
- if (b === undefined) continue
105
- if (b.key !== h.key) continue
106
- if (Math.abs(b.line - h.line) > lineFuzz) continue
107
- matchIdx = j
108
- break
109
- }
110
- if (matchIdx === -1) {
111
- added.push(h.item)
112
- continue
113
- }
114
- consumedBase.add(matchIdx)
115
- const bmatch = base[matchIdx]
116
- if (bmatch === undefined) continue
117
- if (!isEqual(bmatch.item, h.item)) modified.push(h.item)
118
- }
119
- for (let j = 0; j < base.length; j++) {
120
- if (consumedBase.has(j)) continue
121
- const b = base[j]
122
- if (b === undefined) continue
123
- removed.push(b.item)
124
- }
125
- return { added, removed, modified }
126
- }
127
-
128
- function diffRules(base: readonly Rule[], head: readonly Rule[], lineFuzz: number): ArrayDelta {
129
- const mapper = (r: Rule): Identified<Rule> => ({
130
- item: r,
131
- key: r.type,
132
- line: r.line,
133
- })
134
- return differentiate(base.map(mapper), head.map(mapper), rulesEqual, lineFuzz)
135
- }
136
-
137
- function rulesEqual(a: Rule, b: Rule): boolean {
138
- return (
139
- a.type === b.type &&
140
- (a.condition ?? null) === (b.condition ?? null) &&
141
- (a.what ?? null) === (b.what ?? null) &&
142
- (a.expr ?? null) === (b.expr ?? null) &&
143
- (a.loopKind ?? null) === (b.loopKind ?? null)
144
- )
145
- }
146
-
147
- function diffEffects(base: readonly Effect[], head: readonly Effect[]): ArrayDelta {
148
- const mapper = (e: Effect): Identified<Effect> => ({
149
- item: e,
150
- key: `${e.id}::${e.target}`,
151
- // Propagated entries (effect-propagation.md §5.1) omit `line`; the diff
152
- // matcher tolerates missing lines by treating them as position-0. Effect
153
- // pairing here uses lineFuzz=Infinity anyway, so any placeholder is safe.
154
- line: e.line ?? 0,
155
- })
156
- return differentiate(base.map(mapper), head.map(mapper), effectsEqual, Number.POSITIVE_INFINITY)
157
- }
158
-
159
- function effectsEqual(a: Effect, b: Effect): boolean {
160
- return (
161
- a.id === b.id && a.target === b.target && a.plugin === b.plugin && a.confidence === b.confidence
162
- )
163
- }
164
-
165
- function diffCalls(base: readonly Call[], head: readonly Call[], lineFuzz: number): ArrayDelta {
166
- const mapper = (c: Call): Identified<Call> => ({
167
- item: c,
168
- key: c.target,
169
- line: c.line,
170
- })
171
- return differentiate(base.map(mapper), head.map(mapper), callsEqual, lineFuzz)
172
- }
173
-
174
- function callsEqual(a: Call, b: Call): boolean {
175
- return a.target === b.target && (a.resolved ?? null) === (b.resolved ?? null)
176
- }
177
-
178
- /**
179
- * §5.2.2 — decorator diff. Identity is `name`; when the same-named decorator is present
180
- * on both sides within the line-fuzz window, the argument list determines whether the
181
- * entry is a modified pair or an implicit no-op.
182
- */
183
- function diffDecorators(
184
- base: readonly Decorator[],
185
- head: readonly Decorator[],
186
- lineFuzz: number,
187
- ): ArrayDelta {
188
- const mapper = (d: Decorator): Identified<Decorator> => ({
189
- item: d,
190
- key: d.name,
191
- line: d.line,
192
- })
193
- return differentiate(base.map(mapper), head.map(mapper), decoratorsEqual, lineFuzz)
194
- }
195
-
196
- function decoratorsEqual(a: Decorator, b: Decorator): boolean {
197
- if (a.name !== b.name) return false
198
- if (a.arguments.length !== b.arguments.length) return false
199
- for (let i = 0; i < a.arguments.length; i++) {
200
- if (a.arguments[i] !== b.arguments[i]) return false
201
- }
202
- return true
203
- }
204
-
205
- /**
206
- * §5.3 — signature delta. Three cases:
207
- * - both sides `null` → `null` (nothing to compare)
208
- * - exactly one side `null` → the present side is emitted verbatim as
209
- * `added` (when only head has a signature) or `removed` (when only base has one);
210
- * the three axis booleans compare the missing side against `false` defaults
211
- * - both sides non-null → per-list sub-deltas:
212
- * - `inputs`: strict positional compare via `differentiate(..., lineFuzz: 0)` — index
213
- * used as the line key so ordered parameter lists match position-for-position
214
- * - `outputs`: positional compare (added/removed emitted when the arrays diverge at
215
- * an index), no `modified` category
216
- * - `throws`: unordered set diff — duplicates collapse per Set semantics
217
- * - `async` / `generator` / `typeParameters` change flags follow the raw fields
218
- */
219
- function diffSignature(base: Signature | null, head: Signature | null): SignatureDelta | null {
220
- if (base === null && head === null) return null
221
- if (base === null || head === null) {
222
- const empty: ArrayDelta = { added: [], removed: [], modified: [] }
223
- const present = base ?? head
224
- if (present === null) return null
225
- const isBaseNull = base === null
226
- const inputs: ArrayDelta = isBaseNull
227
- ? { added: [...present.inputs], removed: [], modified: [] }
228
- : { added: [], removed: [...present.inputs], modified: [] }
229
- const outputs: ArrayDelta = isBaseNull
230
- ? { added: [...present.outputs], removed: [], modified: [] }
231
- : { added: [], removed: [...present.outputs], modified: [] }
232
- const throws: ArrayDelta = isBaseNull
233
- ? { added: [...present.throws], removed: [], modified: [] }
234
- : { added: [], removed: [...present.throws], modified: [] }
235
- return {
236
- inputs,
237
- outputs,
238
- throws,
239
- asyncChanged: (base?.async ?? false) !== (head?.async ?? false),
240
- generatorChanged: (base?.generator ?? false) !== (head?.generator ?? false),
241
- typeParametersChanged: !arraysEqual(
242
- base?.typeParameters ?? empty.added.map(String),
243
- head?.typeParameters ?? empty.added.map(String),
244
- ),
245
- }
246
- }
247
- const inputMapper = (
248
- input: { name: string; type: string },
249
- index: number,
250
- ): Identified<{ name: string; type: string }> => ({
251
- item: input,
252
- key: `${index}:${input.name}`,
253
- line: index,
254
- })
255
- const inputs = differentiate(
256
- base.inputs.map(inputMapper),
257
- head.inputs.map(inputMapper),
258
- (a, b) => a.name === b.name && a.type === b.type,
259
- 0,
260
- )
261
- const outputs = diffStringList(base.outputs, head.outputs)
262
- const throws = diffStringSet(base.throws, head.throws)
263
- return {
264
- inputs,
265
- outputs,
266
- throws,
267
- asyncChanged: base.async !== head.async,
268
- generatorChanged: base.generator !== head.generator,
269
- typeParametersChanged: !arraysEqual(base.typeParameters, head.typeParameters),
270
- }
271
- }
272
-
273
- function diffStringList(base: readonly string[], head: readonly string[]): ArrayDelta {
274
- const added: string[] = []
275
- const removed: string[] = []
276
- const max = Math.max(base.length, head.length)
277
- for (let i = 0; i < max; i++) {
278
- const b = i < base.length ? base[i] : undefined
279
- const h = i < head.length ? head[i] : undefined
280
- if (b === undefined && h !== undefined) added.push(h)
281
- else if (h === undefined && b !== undefined) removed.push(b)
282
- else if (b !== h) {
283
- if (b !== undefined) removed.push(b)
284
- if (h !== undefined) added.push(h)
285
- }
286
- }
287
- return { added, removed, modified: [] }
288
- }
289
-
290
- function diffStringSet(base: readonly string[], head: readonly string[]): ArrayDelta {
291
- const setB = new Set(base)
292
- const setH = new Set(head)
293
- const added: string[] = []
294
- const removed: string[] = []
295
- for (const h of setH) if (!setB.has(h)) added.push(h)
296
- for (const b of setB) if (!setH.has(b)) removed.push(b)
297
- return { added, removed, modified: [] }
298
- }
299
-
300
- function arraysEqual(a: readonly string[], b: readonly string[]): boolean {
301
- if (a.length !== b.length) return false
302
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false
303
- return true
304
- }