@descent-vtt/spec-graph 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +355 -0
- package/README.md +89 -6
- package/dist/cli.d.ts +14 -5
- package/dist/cli.js +253 -63
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +33 -0
- package/dist/config.js +40 -1
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts +120 -0
- package/dist/diff.js +404 -0
- package/dist/diff.js.map +1 -0
- package/dist/directives.d.ts +18 -1
- package/dist/directives.js +79 -0
- package/dist/directives.js.map +1 -1
- package/dist/extract.d.ts +20 -0
- package/dist/extract.js +43 -8
- package/dist/extract.js.map +1 -1
- package/dist/glob.d.ts +34 -0
- package/dist/glob.js +51 -4
- package/dist/glob.js.map +1 -1
- package/dist/index.d.ts +11 -9
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/regex.d.ts +76 -0
- package/dist/regex.js +822 -0
- package/dist/regex.js.map +1 -0
- package/dist/report.d.ts +29 -0
- package/dist/report.js +111 -0
- package/dist/report.js.map +1 -1
- package/dist/resolve.js +11 -0
- package/dist/resolve.js.map +1 -1
- package/dist/rules.d.ts +13 -0
- package/dist/rules.js +26 -0
- package/dist/rules.js.map +1 -1
- package/dist/select.d.ts +8 -0
- package/dist/select.js +33 -14
- package/dist/select.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/select.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* the relationship.
|
|
21
21
|
*/
|
|
22
22
|
import { receptivityOf } from './lifecycle.js';
|
|
23
|
+
import { compilePattern, PatternError } from './regex.js';
|
|
23
24
|
import { documentOf } from './resolve.js';
|
|
24
25
|
import { EDGE_KINDS } from './types.js';
|
|
25
26
|
export class QueryError extends Error {
|
|
@@ -110,10 +111,26 @@ class Parser {
|
|
|
110
111
|
}
|
|
111
112
|
const operator = operatorMatch[0];
|
|
112
113
|
this.position += operator.length;
|
|
114
|
+
const valueStart = this.position;
|
|
113
115
|
const value = this.parseValue();
|
|
114
116
|
if (this.source[this.position] !== ']')
|
|
115
117
|
throw new QueryError('expected "]" to close the predicate', this.position);
|
|
116
118
|
this.position += 1;
|
|
119
|
+
// A pattern is checked here rather than where it runs, because here is the
|
|
120
|
+
// only place that knows where in the selector it was written. It used to be
|
|
121
|
+
// compiled on first use and an unparseable one silently matched nothing,
|
|
122
|
+
// which is the same silence a misspelled relation would have produced if
|
|
123
|
+
// this grammar tolerated one. See ADR-0017.
|
|
124
|
+
if (operator === '~=') {
|
|
125
|
+
try {
|
|
126
|
+
pattern(value);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (!(error instanceof PatternError))
|
|
130
|
+
throw error;
|
|
131
|
+
throw new QueryError(error.message, valueStart + error.offset + (quoted(this.source[valueStart]) ? 1 : 0));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
117
134
|
return { key, operator, value };
|
|
118
135
|
}
|
|
119
136
|
parseValue() {
|
|
@@ -313,27 +330,29 @@ function testPredicate(node, predicate, graph) {
|
|
|
313
330
|
case '*=':
|
|
314
331
|
return value.includes(needle);
|
|
315
332
|
case '~=':
|
|
316
|
-
return
|
|
333
|
+
return pattern(predicate.value).test(raw);
|
|
317
334
|
default:
|
|
318
335
|
return false;
|
|
319
336
|
}
|
|
320
337
|
});
|
|
321
338
|
}
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
339
|
+
const patterns = new Map();
|
|
340
|
+
/**
|
|
341
|
+
* Compiles a `~=` pattern, once per distinct pattern.
|
|
342
|
+
*
|
|
343
|
+
* Cached because a rule runs its predicate against every node in the corpus,
|
|
344
|
+
* and because compiling is the expensive half now that matching is linear.
|
|
345
|
+
*/
|
|
346
|
+
export function pattern(source) {
|
|
347
|
+
const cached = patterns.get(source);
|
|
325
348
|
if (cached)
|
|
326
349
|
return cached;
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
expression = /(?!)/;
|
|
334
|
-
}
|
|
335
|
-
regexCache.set(source, expression);
|
|
336
|
-
return expression;
|
|
350
|
+
const compiled = compilePattern(source);
|
|
351
|
+
patterns.set(source, compiled);
|
|
352
|
+
return compiled;
|
|
353
|
+
}
|
|
354
|
+
function quoted(char) {
|
|
355
|
+
return char === '"' || char === "'";
|
|
337
356
|
}
|
|
338
357
|
/** True when a node satisfies a matcher. */
|
|
339
358
|
export function matches(node, matcher, graph) {
|
package/dist/select.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The selector language.\n *\n * Rules and users ask the graph the same kinds of question, so they use the same\n * machinery. Every built-in diagnostic is a path query plus a message; anything\n * a rule can find, `spec-graph query` can find too, which means a team can\n * express a convention spec-graph never anticipated without writing a plugin.\n *\n * ```text\n * item[openness=open] -delegates-to-> document[phase=retired]\n * document[phase=active] -assumes,depends-on-> document[phase=retired]\n * document =supersedes=> document[phase=active]\n * item[state=obviated] <-assumes- *\n * *[path^=docs/adr] -blocked-by-> item[openness=open]\n * ```\n *\n * A query evaluates to **paths**, not endpoints. A rule that has the whole path\n * can say \"ADR-0004 line 31 delegates this to ADR-0002, which was retired in\n * March\" instead of naming two documents and leaving the reader to reconstruct\n * the relationship.\n */\n\nimport { receptivityOf } from './lifecycle.js';\nimport { documentOf } from './resolve.js';\nimport type { SpecGraph } from './graph.js';\nimport { EDGE_KINDS, type Edge, type EdgeKind, type NodeKind, type SpecNode } from './types.js';\n\n/* -------------------------------------------------------------------------- */\n/* Shapes */\n/* -------------------------------------------------------------------------- */\n\nexport type Operator = '=' | '!=' | '^=' | '$=' | '*=' | '~=' | 'exists';\n\nexport interface Predicate {\n readonly key: string;\n readonly operator: Operator;\n readonly value: string;\n}\n\nexport interface NodeMatcher {\n /** `null` matches both documents and items. */\n readonly kind: NodeKind | null;\n readonly predicates: readonly Predicate[];\n}\n\nexport interface StepSpec {\n readonly direction: 'out' | 'in';\n /** `null` matches any edge kind. */\n readonly kinds: readonly EdgeKind[] | null;\n /** Follow the relation transitively, taking the shortest path to each node. */\n readonly transitive: boolean;\n readonly node: NodeMatcher;\n}\n\nexport interface QuerySpec {\n readonly start: NodeMatcher;\n readonly steps: readonly StepSpec[];\n}\n\n/** One path through the graph. `edges` always has one fewer entry than `nodes`. */\nexport interface Match {\n readonly nodes: readonly SpecNode[];\n readonly edges: readonly Edge[];\n}\n\nexport class QueryError extends Error {\n /** Offset into the query string where the problem starts. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.name = 'QueryError';\n this.offset = offset;\n }\n}\n\n/** Guards against a pathological query fanning out over a huge corpus. */\nexport const DEFAULT_MATCH_LIMIT = 10_000;\n\n/* -------------------------------------------------------------------------- */\n/* Parsing */\n/* -------------------------------------------------------------------------- */\n\nconst NODE_KINDS: Readonly<Record<string, NodeKind | null>> = {\n document: 'document',\n documents: 'document',\n doc: 'document',\n docs: 'document',\n item: 'item',\n items: 'item',\n node: null,\n nodes: null,\n any: null,\n '*': null,\n};\n\n/** Compiles a selector string into a {@link QuerySpec}. */\nexport function parseQuery(source: string): QuerySpec {\n const parser = new Parser(source);\n const start = parser.parseNodeMatcher();\n const steps: StepSpec[] = [];\n parser.skipSpace();\n while (!parser.atEnd()) {\n steps.push(parser.parseStep());\n parser.skipSpace();\n }\n return { start, steps };\n}\n\nclass Parser {\n private readonly source: string;\n private position = 0;\n\n constructor(source: string) {\n this.source = source;\n }\n\n atEnd(): boolean {\n return this.position >= this.source.length;\n }\n\n skipSpace(): void {\n while (this.position < this.source.length && /\\s/.test(this.source[this.position] as string)) this.position += 1;\n }\n\n parseNodeMatcher(): NodeMatcher {\n this.skipSpace();\n let kind: NodeKind | null = null;\n const word = /^[A-Za-z]+|^\\*/.exec(this.source.slice(this.position));\n if (word) {\n const text = (word[0] as string).toLowerCase();\n if (!(text in NODE_KINDS)) {\n throw new QueryError(\n `unknown node type \"${word[0]}\", expected one of: document, item, * (any)`,\n this.position,\n );\n }\n kind = NODE_KINDS[text] as NodeKind | null;\n this.position += (word[0] as string).length;\n } else if (this.source[this.position] !== '[') {\n throw new QueryError('expected a node type (document, item or *) or a [predicate]', this.position);\n }\n\n const predicates: Predicate[] = [];\n while (this.source[this.position] === '[') predicates.push(this.parsePredicate());\n return { kind, predicates };\n }\n\n private parsePredicate(): Predicate {\n this.position += 1;\n const keyMatch = /^[A-Za-z_][A-Za-z0-9_.:-]*/.exec(this.source.slice(this.position));\n if (!keyMatch) throw new QueryError('expected an attribute name after \"[\"', this.position);\n const key = (keyMatch[0] as string).toLowerCase();\n this.position += (keyMatch[0] as string).length;\n\n if (this.source[this.position] === ']') {\n this.position += 1;\n return { key, operator: 'exists', value: '' };\n }\n\n const operatorMatch = /^(!=|\\^=|\\$=|\\*=|~=|=)/.exec(this.source.slice(this.position));\n if (!operatorMatch) {\n throw new QueryError(\n `expected an operator (=, !=, ^=, $=, *=, ~=) or \"]\" after \"${key}\"`,\n this.position,\n );\n }\n const operator = operatorMatch[0] as Operator;\n this.position += operator.length;\n\n const value = this.parseValue();\n if (this.source[this.position] !== ']') throw new QueryError('expected \"]\" to close the predicate', this.position);\n this.position += 1;\n return { key, operator, value };\n }\n\n private parseValue(): string {\n const quote = this.source[this.position];\n if (quote === '\"' || quote === \"'\") {\n this.position += 1;\n let out = '';\n while (this.position < this.source.length && this.source[this.position] !== quote) {\n if (this.source[this.position] === '\\\\') this.position += 1;\n out += this.source[this.position] ?? '';\n this.position += 1;\n }\n if (this.source[this.position] !== quote) throw new QueryError('unterminated quoted value', this.position);\n this.position += 1;\n return out;\n }\n const match = /^[^\\]\\s]*/.exec(this.source.slice(this.position));\n const value = (match?.[0] ?? '') as string;\n this.position += value.length;\n return value;\n }\n\n parseStep(): StepSpec {\n const rest = this.source.slice(this.position);\n // The forward form is lazy because its terminator is two characters (`->`),\n // which cannot be confused with the hyphen inside `delegates-to`. The\n // backward terminator is a single `-`, so it must be greedy and backtrack:\n // matching lazily would read `<-delegates-to-` as the relation `delegates`.\n const forward = /^(-|=)([A-Za-z,*-]*?)(->|=>)/.exec(rest);\n const backward = /^(<-|<=)([A-Za-z,*-]*)(-|=)(?=\\s|$|[A-Za-z*[])/.exec(rest);\n\n if (forward && (!backward || (forward.index ?? 0) <= (backward.index ?? 0))) {\n const transitive = (forward[1] as string) === '=';\n const closing = forward[3] as string;\n if ((closing === '=>') !== transitive) {\n throw new QueryError('mismatched arrow: use -kind-> for one hop or =kind=> for transitive', this.position);\n }\n const kinds = this.parseKinds(forward[2] as string, this.position + 1);\n this.position += (forward[0] as string).length;\n return { direction: 'out', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n if (backward) {\n const transitive = (backward[1] as string) === '<=';\n const closing = backward[3] as string;\n if ((closing === '=') !== transitive) {\n throw new QueryError('mismatched arrow: use <-kind- for one hop or <=kind= for transitive', this.position);\n }\n const kinds = this.parseKinds(backward[2] as string, this.position + 2);\n this.position += (backward[0] as string).length;\n return { direction: 'in', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n throw new QueryError(\n 'expected a relation step such as -delegates-to->, <-assumes- or =supersedes=>',\n this.position,\n );\n }\n\n private parseKinds(raw: string, offset: number): readonly EdgeKind[] | null {\n const trimmed = raw.trim();\n if (trimmed.length === 0 || trimmed === '*') return null;\n const kinds: EdgeKind[] = [];\n for (const part of trimmed.split(',')) {\n const name = part.trim().toLowerCase();\n if (name.length === 0) continue;\n if (!(EDGE_KINDS as readonly string[]).includes(name)) {\n throw new QueryError(`unknown relation \"${name}\", expected one of: ${EDGE_KINDS.join(', ')}`, offset);\n }\n kinds.push(name as EdgeKind);\n }\n return kinds.length > 0 ? kinds : null;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Attributes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Every attribute key a node can answer to, excluding the open `fm.` namespace.\n *\n * Selectors do not validate their keys - an unknown one simply matches nothing,\n * which is the forgiving reading a query typed at a prompt wants. A message\n * template is the opposite case: `{1.phse}` would render as an empty string in\n * a diagnostic somebody has to act on, silently and forever. So the list is\n * published here, next to the switch it mirrors, for the one caller that needs\n * to be strict. See ADR-0016.\n */\nexport const SELECTOR_KEYS: readonly string[] = Object.freeze([\n 'alias',\n 'body',\n 'conflicted',\n 'disposition',\n 'document',\n 'evidence',\n 'file',\n 'id',\n 'kind',\n 'line',\n 'openness',\n 'path',\n 'phase',\n 'receptivity',\n 'section',\n 'state',\n 'status',\n 'text',\n 'title',\n]);\n\n/**\n * The values a predicate key sees on a node.\n *\n * Multi-valued keys (`alias`, `section`) match when *any* value matches, which\n * is what makes `[section=Open Questions]` read the way people expect.\n */\nexport function attributesOf(node: SpecNode, key: string, graph?: SpecGraph): string[] {\n switch (key) {\n case 'id':\n return [node.id];\n case 'kind':\n return [node.kind];\n case 'title':\n return [node.title];\n case 'file':\n return [node.at.file];\n case 'line':\n return [String(node.at.span.start.line)];\n case 'document':\n return [node.kind === 'item' ? node.document : node.id];\n default:\n break;\n }\n\n if (node.kind === 'document') {\n switch (key) {\n case 'path':\n return [node.path];\n case 'phase':\n return [node.phase];\n case 'status':\n return node.rawStatus === null ? [] : [node.rawStatus];\n case 'receptivity':\n return [receptivityOf(node.phase)];\n case 'alias':\n return [...node.aliases];\n default:\n break;\n }\n if (key.startsWith('fm.')) {\n const value = node.frontMatter[key.slice(3)];\n if (value === undefined) return [];\n return typeof value === 'string' ? [value] : [...value];\n }\n return [];\n }\n\n // An item inherits its document's lifecycle. Without this, the two rules that\n // matter most could not be written as queries at all, because the question\n // they ask - \"is the document holding this obligation still open?\" - is about\n // the owner, not the item.\n if (key === 'phase' || key === 'receptivity' || key === 'status' || key === 'path' || key === 'alias') {\n const owner = graph?.owningDocument(node.id);\n if (owner) return attributesOf(owner, key, graph);\n return key === 'path' ? [node.at.file] : [];\n }\n\n switch (key) {\n case 'state':\n case 'disposition':\n return [node.disposition];\n case 'openness':\n return [node.openness];\n case 'section':\n return [...node.section];\n case 'text':\n return [node.text];\n case 'body':\n return [node.body];\n case 'evidence':\n return [node.evidence.source];\n case 'conflicted':\n return [node.conflicts.length > 0 ? 'true' : 'false'];\n default:\n return [];\n }\n}\n\nfunction testPredicate(node: SpecNode, predicate: Predicate, graph?: SpecGraph): boolean {\n const values = attributesOf(node, predicate.key, graph);\n const needle = predicate.value.toLowerCase();\n\n if (predicate.operator === 'exists') return values.some((value) => value.length > 0);\n if (predicate.operator === '!=') return !values.some((value) => value.toLowerCase() === needle);\n\n return values.some((raw) => {\n const value = raw.toLowerCase();\n switch (predicate.operator) {\n case '=':\n return value === needle;\n case '^=':\n return value.startsWith(needle);\n case '$=':\n return value.endsWith(needle);\n case '*=':\n return value.includes(needle);\n case '~=':\n return safeRegExp(predicate.value).test(raw);\n default:\n return false;\n }\n });\n}\n\nconst regexCache = new Map<string, RegExp>();\n\nfunction safeRegExp(source: string): RegExp {\n const cached = regexCache.get(source);\n if (cached) return cached;\n let expression: RegExp;\n try {\n expression = new RegExp(source, 'i');\n } catch {\n // An invalid pattern matches nothing rather than crashing a whole run.\n expression = /(?!)/;\n }\n regexCache.set(source, expression);\n return expression;\n}\n\n/** True when a node satisfies a matcher. */\nexport function matches(node: SpecNode, matcher: NodeMatcher, graph?: SpecGraph): boolean {\n if (matcher.kind !== null && node.kind !== matcher.kind) return false;\n return matcher.predicates.every((predicate) => testPredicate(node, predicate, graph));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Execution */\n/* -------------------------------------------------------------------------- */\n\nexport interface ExecuteOptions {\n /**\n * Cap on the paths produced by each traversal step.\n *\n * It bounds *expansion*, not the initial selection: a query with no steps\n * returns every matching node. That asymmetry is deliberate. The limit exists\n * so a query across a dense graph cannot fan out unboundedly, and the rules\n * run through this engine - truncating the set of nodes a rule starts from\n * would silently drop findings, whereas truncating a runaway expansion only\n * ever drops paths that were already beyond reading.\n */\n readonly limit?: number | undefined;\n /**\n * Whether a step may stay inside one document.\n *\n * Off by default for reflexive edges: a document that links to itself is a\n * formatting quirk, not a relationship, and letting those through turns every\n * table of contents into a finding.\n */\n readonly allowReflexive?: boolean | undefined;\n}\n\n/** Runs a compiled query and returns every matching path. */\nexport function execute(graph: SpecGraph, spec: QuerySpec, options: ExecuteOptions = {}): Match[] {\n const limit = options.limit ?? DEFAULT_MATCH_LIMIT;\n const allowReflexive = options.allowReflexive ?? false;\n\n let frontier: Match[] = [];\n for (const node of graph.nodes.values()) {\n if (matches(node, spec.start, graph)) frontier.push({ nodes: [node], edges: [] });\n }\n\n for (const step of spec.steps) {\n const next: Match[] = [];\n const seen = new Set<string>();\n\n for (const match of frontier) {\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n for (const { node, path } of expand(graph, tail, step, allowReflexive)) {\n if (!matches(node, step.node, graph)) continue;\n const key = `${match.nodes.map((n) => n.id).join('>')}>${node.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n next.push({ nodes: [...match.nodes, node], edges: [...match.edges, ...path] });\n if (next.length >= limit) break;\n }\n if (next.length >= limit) break;\n }\n\n frontier = next;\n if (frontier.length === 0) break;\n }\n\n return frontier;\n}\n\ninterface Expansion {\n readonly node: SpecNode;\n readonly path: readonly Edge[];\n}\n\nfunction expand(graph: SpecGraph, from: SpecNode, step: StepSpec, allowReflexive: boolean): Expansion[] {\n const kinds = step.kinds ?? [];\n const out: Expansion[] = [];\n\n if (!step.transitive) {\n const edges = step.direction === 'out' ? graph.out(from.id, kinds) : graph.in(from.id, kinds);\n for (const edge of edges) {\n if (!allowReflexive && edge.reflexive) continue;\n const targetId = step.direction === 'out' ? edge.to : edge.from;\n const node = graph.node(targetId);\n if (node) out.push({ node, path: [edge] });\n }\n return out;\n }\n\n // Transitive steps take the shortest path to each reachable node, which keeps\n // the result deterministic and the reported evidence minimal.\n const reached =\n step.direction === 'out'\n ? graph.reach(from.id, kinds)\n : reachIncoming(graph, from.id, kinds);\n\n for (const [id, path] of reached) {\n if (!allowReflexive && path.every((edge) => edge.reflexive)) continue;\n const node = graph.node(id);\n if (node) out.push({ node, path });\n }\n return out;\n}\n\nfunction reachIncoming(graph: SpecGraph, id: string, kinds: readonly EdgeKind[]): Map<string, readonly Edge[]> {\n const paths = new Map<string, readonly Edge[]>();\n const pathTo = new Map<string, readonly Edge[]>([[id, []]]);\n const queue: string[] = [id];\n\n while (queue.length > 0) {\n const current = queue.shift() as string;\n const prefix = pathTo.get(current) as readonly Edge[];\n for (const edge of graph.in(current, kinds)) {\n if (pathTo.has(edge.from) && edge.from !== id) continue;\n const path = [...prefix, edge];\n if (edge.from === id) {\n if (!paths.has(id)) paths.set(id, path);\n continue;\n }\n pathTo.set(edge.from, path);\n paths.set(edge.from, path);\n queue.push(edge.from);\n }\n }\n return paths;\n}\n\n/** Parses and runs a selector in one call. */\nexport function query(graph: SpecGraph, selector: string, options?: ExecuteOptions): Match[] {\n return execute(graph, parseQuery(selector), options);\n}\n\n/** Renders a match as `A -kind-> B`, for reports and for `--explain`. */\nexport function renderMatch(match: Match): string {\n const parts: string[] = [match.nodes[0]?.id ?? ''];\n for (let i = 0; i < match.edges.length; i += 1) {\n const edge = match.edges[i] as Edge;\n const node = match.nodes[i + 1];\n parts.push(`-${edge.kind}->`, node?.id ?? documentOf(edge.to));\n }\n return parts.join(' ');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../src/select.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The selector language.\n *\n * Rules and users ask the graph the same kinds of question, so they use the same\n * machinery. Every built-in diagnostic is a path query plus a message; anything\n * a rule can find, `spec-graph query` can find too, which means a team can\n * express a convention spec-graph never anticipated without writing a plugin.\n *\n * ```text\n * item[openness=open] -delegates-to-> document[phase=retired]\n * document[phase=active] -assumes,depends-on-> document[phase=retired]\n * document =supersedes=> document[phase=active]\n * item[state=obviated] <-assumes- *\n * *[path^=docs/adr] -blocked-by-> item[openness=open]\n * ```\n *\n * A query evaluates to **paths**, not endpoints. A rule that has the whole path\n * can say \"ADR-0004 line 31 delegates this to ADR-0002, which was retired in\n * March\" instead of naming two documents and leaving the reader to reconstruct\n * the relationship.\n */\n\nimport { receptivityOf } from './lifecycle.js';\nimport { compilePattern, PatternError, type Matcher } from './regex.js';\nimport { documentOf } from './resolve.js';\nimport type { SpecGraph } from './graph.js';\nimport { EDGE_KINDS, type Edge, type EdgeKind, type NodeKind, type SpecNode } from './types.js';\n\n/* -------------------------------------------------------------------------- */\n/* Shapes */\n/* -------------------------------------------------------------------------- */\n\nexport type Operator = '=' | '!=' | '^=' | '$=' | '*=' | '~=' | 'exists';\n\nexport interface Predicate {\n readonly key: string;\n readonly operator: Operator;\n readonly value: string;\n}\n\nexport interface NodeMatcher {\n /** `null` matches both documents and items. */\n readonly kind: NodeKind | null;\n readonly predicates: readonly Predicate[];\n}\n\nexport interface StepSpec {\n readonly direction: 'out' | 'in';\n /** `null` matches any edge kind. */\n readonly kinds: readonly EdgeKind[] | null;\n /** Follow the relation transitively, taking the shortest path to each node. */\n readonly transitive: boolean;\n readonly node: NodeMatcher;\n}\n\nexport interface QuerySpec {\n readonly start: NodeMatcher;\n readonly steps: readonly StepSpec[];\n}\n\n/** One path through the graph. `edges` always has one fewer entry than `nodes`. */\nexport interface Match {\n readonly nodes: readonly SpecNode[];\n readonly edges: readonly Edge[];\n}\n\nexport class QueryError extends Error {\n /** Offset into the query string where the problem starts. */\n readonly offset: number;\n\n constructor(message: string, offset: number) {\n super(message);\n this.name = 'QueryError';\n this.offset = offset;\n }\n}\n\n/** Guards against a pathological query fanning out over a huge corpus. */\nexport const DEFAULT_MATCH_LIMIT = 10_000;\n\n/* -------------------------------------------------------------------------- */\n/* Parsing */\n/* -------------------------------------------------------------------------- */\n\nconst NODE_KINDS: Readonly<Record<string, NodeKind | null>> = {\n document: 'document',\n documents: 'document',\n doc: 'document',\n docs: 'document',\n item: 'item',\n items: 'item',\n node: null,\n nodes: null,\n any: null,\n '*': null,\n};\n\n/** Compiles a selector string into a {@link QuerySpec}. */\nexport function parseQuery(source: string): QuerySpec {\n const parser = new Parser(source);\n const start = parser.parseNodeMatcher();\n const steps: StepSpec[] = [];\n parser.skipSpace();\n while (!parser.atEnd()) {\n steps.push(parser.parseStep());\n parser.skipSpace();\n }\n return { start, steps };\n}\n\nclass Parser {\n private readonly source: string;\n private position = 0;\n\n constructor(source: string) {\n this.source = source;\n }\n\n atEnd(): boolean {\n return this.position >= this.source.length;\n }\n\n skipSpace(): void {\n while (this.position < this.source.length && /\\s/.test(this.source[this.position] as string)) this.position += 1;\n }\n\n parseNodeMatcher(): NodeMatcher {\n this.skipSpace();\n let kind: NodeKind | null = null;\n const word = /^[A-Za-z]+|^\\*/.exec(this.source.slice(this.position));\n if (word) {\n const text = (word[0] as string).toLowerCase();\n if (!(text in NODE_KINDS)) {\n throw new QueryError(\n `unknown node type \"${word[0]}\", expected one of: document, item, * (any)`,\n this.position,\n );\n }\n kind = NODE_KINDS[text] as NodeKind | null;\n this.position += (word[0] as string).length;\n } else if (this.source[this.position] !== '[') {\n throw new QueryError('expected a node type (document, item or *) or a [predicate]', this.position);\n }\n\n const predicates: Predicate[] = [];\n while (this.source[this.position] === '[') predicates.push(this.parsePredicate());\n return { kind, predicates };\n }\n\n private parsePredicate(): Predicate {\n this.position += 1;\n const keyMatch = /^[A-Za-z_][A-Za-z0-9_.:-]*/.exec(this.source.slice(this.position));\n if (!keyMatch) throw new QueryError('expected an attribute name after \"[\"', this.position);\n const key = (keyMatch[0] as string).toLowerCase();\n this.position += (keyMatch[0] as string).length;\n\n if (this.source[this.position] === ']') {\n this.position += 1;\n return { key, operator: 'exists', value: '' };\n }\n\n const operatorMatch = /^(!=|\\^=|\\$=|\\*=|~=|=)/.exec(this.source.slice(this.position));\n if (!operatorMatch) {\n throw new QueryError(\n `expected an operator (=, !=, ^=, $=, *=, ~=) or \"]\" after \"${key}\"`,\n this.position,\n );\n }\n const operator = operatorMatch[0] as Operator;\n this.position += operator.length;\n\n const valueStart = this.position;\n const value = this.parseValue();\n if (this.source[this.position] !== ']') throw new QueryError('expected \"]\" to close the predicate', this.position);\n this.position += 1;\n // A pattern is checked here rather than where it runs, because here is the\n // only place that knows where in the selector it was written. It used to be\n // compiled on first use and an unparseable one silently matched nothing,\n // which is the same silence a misspelled relation would have produced if\n // this grammar tolerated one. See ADR-0017.\n if (operator === '~=') {\n try {\n pattern(value);\n } catch (error) {\n if (!(error instanceof PatternError)) throw error;\n throw new QueryError(error.message, valueStart + error.offset + (quoted(this.source[valueStart]) ? 1 : 0));\n }\n }\n return { key, operator, value };\n }\n\n private parseValue(): string {\n const quote = this.source[this.position];\n if (quote === '\"' || quote === \"'\") {\n this.position += 1;\n let out = '';\n while (this.position < this.source.length && this.source[this.position] !== quote) {\n if (this.source[this.position] === '\\\\') this.position += 1;\n out += this.source[this.position] ?? '';\n this.position += 1;\n }\n if (this.source[this.position] !== quote) throw new QueryError('unterminated quoted value', this.position);\n this.position += 1;\n return out;\n }\n const match = /^[^\\]\\s]*/.exec(this.source.slice(this.position));\n const value = (match?.[0] ?? '') as string;\n this.position += value.length;\n return value;\n }\n\n parseStep(): StepSpec {\n const rest = this.source.slice(this.position);\n // The forward form is lazy because its terminator is two characters (`->`),\n // which cannot be confused with the hyphen inside `delegates-to`. The\n // backward terminator is a single `-`, so it must be greedy and backtrack:\n // matching lazily would read `<-delegates-to-` as the relation `delegates`.\n const forward = /^(-|=)([A-Za-z,*-]*?)(->|=>)/.exec(rest);\n const backward = /^(<-|<=)([A-Za-z,*-]*)(-|=)(?=\\s|$|[A-Za-z*[])/.exec(rest);\n\n if (forward && (!backward || (forward.index ?? 0) <= (backward.index ?? 0))) {\n const transitive = (forward[1] as string) === '=';\n const closing = forward[3] as string;\n if ((closing === '=>') !== transitive) {\n throw new QueryError('mismatched arrow: use -kind-> for one hop or =kind=> for transitive', this.position);\n }\n const kinds = this.parseKinds(forward[2] as string, this.position + 1);\n this.position += (forward[0] as string).length;\n return { direction: 'out', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n if (backward) {\n const transitive = (backward[1] as string) === '<=';\n const closing = backward[3] as string;\n if ((closing === '=') !== transitive) {\n throw new QueryError('mismatched arrow: use <-kind- for one hop or <=kind= for transitive', this.position);\n }\n const kinds = this.parseKinds(backward[2] as string, this.position + 2);\n this.position += (backward[0] as string).length;\n return { direction: 'in', kinds, transitive, node: this.parseNodeMatcher() };\n }\n\n throw new QueryError(\n 'expected a relation step such as -delegates-to->, <-assumes- or =supersedes=>',\n this.position,\n );\n }\n\n private parseKinds(raw: string, offset: number): readonly EdgeKind[] | null {\n const trimmed = raw.trim();\n if (trimmed.length === 0 || trimmed === '*') return null;\n const kinds: EdgeKind[] = [];\n for (const part of trimmed.split(',')) {\n const name = part.trim().toLowerCase();\n if (name.length === 0) continue;\n if (!(EDGE_KINDS as readonly string[]).includes(name)) {\n throw new QueryError(`unknown relation \"${name}\", expected one of: ${EDGE_KINDS.join(', ')}`, offset);\n }\n kinds.push(name as EdgeKind);\n }\n return kinds.length > 0 ? kinds : null;\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Attributes */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Every attribute key a node can answer to, excluding the open `fm.` namespace.\n *\n * Selectors do not validate their keys - an unknown one simply matches nothing,\n * which is the forgiving reading a query typed at a prompt wants. A message\n * template is the opposite case: `{1.phse}` would render as an empty string in\n * a diagnostic somebody has to act on, silently and forever. So the list is\n * published here, next to the switch it mirrors, for the one caller that needs\n * to be strict. See ADR-0016.\n */\nexport const SELECTOR_KEYS: readonly string[] = Object.freeze([\n 'alias',\n 'body',\n 'conflicted',\n 'disposition',\n 'document',\n 'evidence',\n 'file',\n 'id',\n 'kind',\n 'line',\n 'openness',\n 'path',\n 'phase',\n 'receptivity',\n 'section',\n 'state',\n 'status',\n 'text',\n 'title',\n]);\n\n/**\n * The values a predicate key sees on a node.\n *\n * Multi-valued keys (`alias`, `section`) match when *any* value matches, which\n * is what makes `[section=Open Questions]` read the way people expect.\n */\nexport function attributesOf(node: SpecNode, key: string, graph?: SpecGraph): string[] {\n switch (key) {\n case 'id':\n return [node.id];\n case 'kind':\n return [node.kind];\n case 'title':\n return [node.title];\n case 'file':\n return [node.at.file];\n case 'line':\n return [String(node.at.span.start.line)];\n case 'document':\n return [node.kind === 'item' ? node.document : node.id];\n default:\n break;\n }\n\n if (node.kind === 'document') {\n switch (key) {\n case 'path':\n return [node.path];\n case 'phase':\n return [node.phase];\n case 'status':\n return node.rawStatus === null ? [] : [node.rawStatus];\n case 'receptivity':\n return [receptivityOf(node.phase)];\n case 'alias':\n return [...node.aliases];\n default:\n break;\n }\n if (key.startsWith('fm.')) {\n const value = node.frontMatter[key.slice(3)];\n if (value === undefined) return [];\n return typeof value === 'string' ? [value] : [...value];\n }\n return [];\n }\n\n // An item inherits its document's lifecycle. Without this, the two rules that\n // matter most could not be written as queries at all, because the question\n // they ask - \"is the document holding this obligation still open?\" - is about\n // the owner, not the item.\n if (key === 'phase' || key === 'receptivity' || key === 'status' || key === 'path' || key === 'alias') {\n const owner = graph?.owningDocument(node.id);\n if (owner) return attributesOf(owner, key, graph);\n return key === 'path' ? [node.at.file] : [];\n }\n\n switch (key) {\n case 'state':\n case 'disposition':\n return [node.disposition];\n case 'openness':\n return [node.openness];\n case 'section':\n return [...node.section];\n case 'text':\n return [node.text];\n case 'body':\n return [node.body];\n case 'evidence':\n return [node.evidence.source];\n case 'conflicted':\n return [node.conflicts.length > 0 ? 'true' : 'false'];\n default:\n return [];\n }\n}\n\nfunction testPredicate(node: SpecNode, predicate: Predicate, graph?: SpecGraph): boolean {\n const values = attributesOf(node, predicate.key, graph);\n const needle = predicate.value.toLowerCase();\n\n if (predicate.operator === 'exists') return values.some((value) => value.length > 0);\n if (predicate.operator === '!=') return !values.some((value) => value.toLowerCase() === needle);\n\n return values.some((raw) => {\n const value = raw.toLowerCase();\n switch (predicate.operator) {\n case '=':\n return value === needle;\n case '^=':\n return value.startsWith(needle);\n case '$=':\n return value.endsWith(needle);\n case '*=':\n return value.includes(needle);\n case '~=':\n return pattern(predicate.value).test(raw);\n default:\n return false;\n }\n });\n}\n\nconst patterns = new Map<string, Matcher>();\n\n/**\n * Compiles a `~=` pattern, once per distinct pattern.\n *\n * Cached because a rule runs its predicate against every node in the corpus,\n * and because compiling is the expensive half now that matching is linear.\n */\nexport function pattern(source: string): Matcher {\n const cached = patterns.get(source);\n if (cached) return cached;\n const compiled = compilePattern(source);\n patterns.set(source, compiled);\n return compiled;\n}\n\nfunction quoted(char: string | undefined): boolean {\n return char === '\"' || char === \"'\";\n}\n\n/** True when a node satisfies a matcher. */\nexport function matches(node: SpecNode, matcher: NodeMatcher, graph?: SpecGraph): boolean {\n if (matcher.kind !== null && node.kind !== matcher.kind) return false;\n return matcher.predicates.every((predicate) => testPredicate(node, predicate, graph));\n}\n\n/* -------------------------------------------------------------------------- */\n/* Execution */\n/* -------------------------------------------------------------------------- */\n\nexport interface ExecuteOptions {\n /**\n * Cap on the paths produced by each traversal step.\n *\n * It bounds *expansion*, not the initial selection: a query with no steps\n * returns every matching node. That asymmetry is deliberate. The limit exists\n * so a query across a dense graph cannot fan out unboundedly, and the rules\n * run through this engine - truncating the set of nodes a rule starts from\n * would silently drop findings, whereas truncating a runaway expansion only\n * ever drops paths that were already beyond reading.\n */\n readonly limit?: number | undefined;\n /**\n * Whether a step may stay inside one document.\n *\n * Off by default for reflexive edges: a document that links to itself is a\n * formatting quirk, not a relationship, and letting those through turns every\n * table of contents into a finding.\n */\n readonly allowReflexive?: boolean | undefined;\n}\n\n/** Runs a compiled query and returns every matching path. */\nexport function execute(graph: SpecGraph, spec: QuerySpec, options: ExecuteOptions = {}): Match[] {\n const limit = options.limit ?? DEFAULT_MATCH_LIMIT;\n const allowReflexive = options.allowReflexive ?? false;\n\n let frontier: Match[] = [];\n for (const node of graph.nodes.values()) {\n if (matches(node, spec.start, graph)) frontier.push({ nodes: [node], edges: [] });\n }\n\n for (const step of spec.steps) {\n const next: Match[] = [];\n const seen = new Set<string>();\n\n for (const match of frontier) {\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n for (const { node, path } of expand(graph, tail, step, allowReflexive)) {\n if (!matches(node, step.node, graph)) continue;\n const key = `${match.nodes.map((n) => n.id).join('>')}>${node.id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n next.push({ nodes: [...match.nodes, node], edges: [...match.edges, ...path] });\n if (next.length >= limit) break;\n }\n if (next.length >= limit) break;\n }\n\n frontier = next;\n if (frontier.length === 0) break;\n }\n\n return frontier;\n}\n\ninterface Expansion {\n readonly node: SpecNode;\n readonly path: readonly Edge[];\n}\n\nfunction expand(graph: SpecGraph, from: SpecNode, step: StepSpec, allowReflexive: boolean): Expansion[] {\n const kinds = step.kinds ?? [];\n const out: Expansion[] = [];\n\n if (!step.transitive) {\n const edges = step.direction === 'out' ? graph.out(from.id, kinds) : graph.in(from.id, kinds);\n for (const edge of edges) {\n if (!allowReflexive && edge.reflexive) continue;\n const targetId = step.direction === 'out' ? edge.to : edge.from;\n const node = graph.node(targetId);\n if (node) out.push({ node, path: [edge] });\n }\n return out;\n }\n\n // Transitive steps take the shortest path to each reachable node, which keeps\n // the result deterministic and the reported evidence minimal.\n const reached =\n step.direction === 'out'\n ? graph.reach(from.id, kinds)\n : reachIncoming(graph, from.id, kinds);\n\n for (const [id, path] of reached) {\n if (!allowReflexive && path.every((edge) => edge.reflexive)) continue;\n const node = graph.node(id);\n if (node) out.push({ node, path });\n }\n return out;\n}\n\nfunction reachIncoming(graph: SpecGraph, id: string, kinds: readonly EdgeKind[]): Map<string, readonly Edge[]> {\n const paths = new Map<string, readonly Edge[]>();\n const pathTo = new Map<string, readonly Edge[]>([[id, []]]);\n const queue: string[] = [id];\n\n while (queue.length > 0) {\n const current = queue.shift() as string;\n const prefix = pathTo.get(current) as readonly Edge[];\n for (const edge of graph.in(current, kinds)) {\n if (pathTo.has(edge.from) && edge.from !== id) continue;\n const path = [...prefix, edge];\n if (edge.from === id) {\n if (!paths.has(id)) paths.set(id, path);\n continue;\n }\n pathTo.set(edge.from, path);\n paths.set(edge.from, path);\n queue.push(edge.from);\n }\n }\n return paths;\n}\n\n/** Parses and runs a selector in one call. */\nexport function query(graph: SpecGraph, selector: string, options?: ExecuteOptions): Match[] {\n return execute(graph, parseQuery(selector), options);\n}\n\n/** Renders a match as `A -kind-> B`, for reports and for `--explain`. */\nexport function renderMatch(match: Match): string {\n const parts: string[] = [match.nodes[0]?.id ?? ''];\n for (let i = 0; i < match.edges.length; i += 1) {\n const edge = match.edges[i] as Edge;\n const node = match.nodes[i + 1];\n parts.push(`-${edge.kind}->`, node?.id ?? documentOf(edge.to));\n }\n return parts.join(' ');\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -199,6 +199,14 @@ export interface DocumentNode extends NodeBase {
|
|
|
199
199
|
}
|
|
200
200
|
export interface ItemNode extends NodeBase {
|
|
201
201
|
readonly kind: 'item';
|
|
202
|
+
/**
|
|
203
|
+
* Whether the id was written by an author rather than numbered by position.
|
|
204
|
+
*
|
|
205
|
+
* A numbered id is the item's ordinal within its section, so inserting one
|
|
206
|
+
* item renumbers every item after it. Only a declared id names the same item
|
|
207
|
+
* in two states of a repository (ADR-0020).
|
|
208
|
+
*/
|
|
209
|
+
readonly declared: boolean;
|
|
202
210
|
/** Owning document id. */
|
|
203
211
|
readonly document: string;
|
|
204
212
|
/** Heading path the item sits under, outermost first. */
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA4FH,MAAM,CAAC,MAAM,YAAY,GAA2B,MAAM,CAAC,MAAM,CAAC;IAChE,YAAY;IACZ,UAAU;IACV,WAAW;IACX,WAAW;IACX,eAAe;IACf,UAAU;IACV,UAAU;CACX,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,WAAW,GAA4C,MAAM,CAAC,MAAM,CAAC;IAChF,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,QAAQ;IACnB,eAAe,EAAE,QAAQ;IACzB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,QAAQ;CACnB,CAAC,CAAC;AAkDH,MAAM,CAAC,MAAM,WAAW,GAA2C,MAAM,CAAC,MAAM,CAAC;IAC/E,QAAQ,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE;IAClG,UAAU,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACtG,YAAY,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACxG,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACvG,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;IAC/F,cAAc,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE;IAC3G,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE;IACzG,UAAU,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE;IACrG,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;CAC7F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAwB,CAAC;AA6M1E,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC","sourcesContent":["/**\n * The spec-graph data model.\n *\n * Three ideas carry the whole design:\n *\n * 1. **Documents have a lifecycle phase**, normalised from whatever vocabulary\n * the repository already uses (MADR `accepted`, KEP `implementable`, RFC\n * `final`, plain English `archived`). The phase answers one question that\n * matters more than any other: *can this document still absorb new work?*\n *\n * 2. **Items are obligations with nuanced state.** A checkbox is not a boolean.\n * An item can be open, narrowed to a smaller scope, satisfied, consciously\n * accepted as debt, or obviated because the premise it rested on evaporated.\n * Collapsing those five into `[ ]` / `[x]` is what makes ad-hoc greps lie.\n *\n * 3. **Edges are typed and carry provenance.** Every relation records where it\n * was *declared*, which is frequently not the node it points at. That is what\n * lets a diagnostic say \"ADR-0009 line 12 claims to supersede ADR-0003\"\n * instead of \"something, somewhere, is inconsistent\".\n */\n\n/* -------------------------------------------------------------------------- */\n/* Source positions */\n/* -------------------------------------------------------------------------- */\n\n/** A point in a source file. `offset` is a UTF-16 code-unit index. */\nexport interface Position {\n readonly offset: number;\n /** 1-based. */\n readonly line: number;\n /** 1-based, counted in UTF-16 code units. */\n readonly column: number;\n}\n\n/** A half-open `[start, end)` range of a source file. */\nexport interface Span {\n readonly start: Position;\n readonly end: Position;\n}\n\n/** A span plus the file it belongs to. Everything user-visible carries one. */\nexport interface SourceRef {\n /** Repository-relative POSIX path. */\n readonly file: string;\n readonly span: Span;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Lifecycle */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The normalised document lifecycle, as a four-state lattice.\n *\n * Ecosystem vocabularies are large and mutually incompatible; the *consequences*\n * of a status are not. These four phases are chosen so that every question\n * spec-graph asks can be answered from the phase alone.\n *\n * - `draft` - not yet binding. Mutable. Absorbs new work.\n * - `active` - binding and mutable. Absorbs new work.\n * - `frozen` - binding and immutable. Changes require a new document.\n * - `retired` - no longer binding: superseded, rejected, withdrawn, archived.\n *\n * `unknown` is a fifth, deliberately non-lattice value for documents that never\n * declared a status. Rules treat it permissively rather than guessing.\n */\nexport type Phase = 'draft' | 'active' | 'frozen' | 'retired' | 'record' | 'unknown';\n\n/**\n * Can a document in this phase take on a *new* open obligation?\n *\n * This is the single predicate behind ghost-handover detection. Delegating an\n * open question into a `frozen` or `retired` document is a write to a closed\n * ledger: nobody will ever read it again.\n */\nexport type Receptivity = 'receptive' | 'sealed' | 'unknown';\n\n/* -------------------------------------------------------------------------- */\n/* Item state */\n/* -------------------------------------------------------------------------- */\n\n/**\n * How much of an obligation is left.\n *\n * Kept separate from {@link Disposition} because the two answer different\n * questions: `openness` is \"does someone still owe work?\", `disposition` is\n * \"what happened to it?\". Rules almost always want the former; humans reading a\n * report always want the latter.\n */\nexport type Openness = 'open' | 'partial' | 'closed';\n\n/** Why an item is in the state it is in. */\nexport type Disposition =\n /** Nothing has happened yet. */\n | 'unresolved'\n /** Scope was cut down, but a remainder is still owed. */\n | 'narrowed'\n /** Ownership moved elsewhere. Still owed - by somebody else. */\n | 'delegated'\n /** Done, answered, decided. */\n | 'satisfied'\n /** Knowingly not done. A closed item with a scar. */\n | 'accepted-debt'\n /** Decided against. */\n | 'rejected'\n /**\n * The premise evaporated, so the question stopped being a question.\n * Load-bearing citations to an obviated item are stale premises.\n */\n | 'obviated';\n\nexport const DISPOSITIONS: readonly Disposition[] = Object.freeze([\n 'unresolved',\n 'narrowed',\n 'delegated',\n 'satisfied',\n 'accepted-debt',\n 'rejected',\n 'obviated',\n]);\n\n/** The openness implied by each disposition. */\nexport const OPENNESS_OF: Readonly<Record<Disposition, Openness>> = Object.freeze({\n unresolved: 'open',\n narrowed: 'partial',\n delegated: 'open',\n satisfied: 'closed',\n 'accepted-debt': 'closed',\n rejected: 'closed',\n obviated: 'closed',\n});\n\n/* -------------------------------------------------------------------------- */\n/* Edges */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Relation kinds.\n *\n * The set is small on purpose. Two orthogonal properties - declared once in\n * {@link EDGE_TRAITS} - do the actual work, so adding a kind never means\n * touching a rule.\n */\nexport type EdgeKind =\n /** Structural: a document contains an item. Never inferred from prose. */\n | 'contains'\n /** A neutral citation. */\n | 'references'\n /** Explicitly *not* load-bearing: \"See also\", \"History\", \"Prior art\". */\n | 'relates-to'\n /** The source design depends on the target content. */\n | 'depends-on'\n /** The source rests on a claim made by the target. */\n | 'assumes'\n /** The source hands an obligation to the target. */\n | 'delegates-to'\n /** The source cannot proceed until the target does. */\n | 'blocked-by'\n /** The source replaces the target. */\n | 'supersedes'\n /** The source modifies the target without replacing it. */\n | 'amends';\n\nexport interface EdgeTraits {\n /**\n * Does the source continued validity depend on what the target says?\n *\n * Load-bearing edges into retired documents are stale premises. Non\n * load-bearing ones (\"see also\") are just history, and reporting them would\n * bury the real findings - which is exactly how ad-hoc linters lose trust.\n */\n readonly loadBearing: boolean;\n /** Does the edge move an obligation from source to target? */\n readonly transfersObligation: boolean;\n /** Does the edge assert something about the target lifecycle? */\n readonly lifecycle: boolean;\n /** Human-readable phrase used in diagnostics: \"ADR-1 <phrase> ADR-2\". */\n readonly phrase: string;\n}\n\nexport const EDGE_TRAITS: Readonly<Record<EdgeKind, EdgeTraits>> = Object.freeze({\n contains: { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'contains' },\n references: { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'references' },\n 'relates-to': { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'relates to' },\n 'depends-on': { loadBearing: true, transfersObligation: false, lifecycle: false, phrase: 'depends on' },\n assumes: { loadBearing: true, transfersObligation: false, lifecycle: false, phrase: 'assumes' },\n 'delegates-to': { loadBearing: false, transfersObligation: true, lifecycle: false, phrase: 'delegates to' },\n 'blocked-by': { loadBearing: true, transfersObligation: true, lifecycle: false, phrase: 'is blocked by' },\n supersedes: { loadBearing: false, transfersObligation: false, lifecycle: true, phrase: 'supersedes' },\n amends: { loadBearing: true, transfersObligation: false, lifecycle: true, phrase: 'amends' },\n});\n\nexport const EDGE_KINDS = Object.keys(EDGE_TRAITS) as readonly EdgeKind[];\n\n/** How an edge came to exist. Reported verbatim so findings are auditable. */\nexport type EdgeOrigin =\n /** A `<!-- @spec-edge -->` directive. */\n | 'directive'\n /** A front-matter field such as `supersedes:` or `superseded-by:`. */\n | 'front-matter'\n /** A Markdown link, classified by the section it sits in. */\n | 'link'\n /** A bare textual identifier such as `ADR-0007` in prose. */\n | 'text'\n /** Synthesised by the extractor (document -> item). */\n | 'structural';\n\nexport interface Edge {\n readonly kind: EdgeKind;\n /** Node id. */\n readonly from: string;\n /** Node id. */\n readonly to: string;\n readonly origin: EdgeOrigin;\n /**\n * Where the relation was written down. Not necessarily inside `from`: a\n * `superseded-by: ADR-9` field in ADR-3 declares `ADR-9 supersedes ADR-3`,\n * and the only place a human can go fix it is ADR-3.\n */\n readonly declaredAt: SourceRef;\n /** The raw text that produced the edge, for diagnostics. */\n readonly raw: string;\n /**\n * Every file that declares this relation.\n *\n * A supersession stated only in the superseding document leaves the\n * superseded one silently presenting itself as current, which is a finding in\n * its own right - so the set of declaration sites has to survive de-duplication.\n */\n readonly declaredIn: readonly string[];\n /** True when `from` and `to` are the same node. Kept, never traversed. */\n readonly reflexive: boolean;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Nodes */\n/* -------------------------------------------------------------------------- */\n\nexport type NodeKind = 'document' | 'item';\n\ninterface NodeBase {\n /** Canonical, graph-unique. Documents: `ADR-0007`. Items: `ADR-0007#q.1`. */\n readonly id: string;\n readonly kind: NodeKind;\n /** Short human label used in reports. */\n readonly title: string;\n readonly at: SourceRef;\n}\n\nexport interface DocumentNode extends NodeBase {\n readonly kind: 'document';\n /** Repository-relative POSIX path. */\n readonly path: string;\n /** Every spelling that resolves to this document, lowercased. */\n readonly aliases: readonly string[];\n readonly phase: Phase;\n /** The status string as written, before normalisation. `null` if absent. */\n readonly rawStatus: string | null;\n /** Where the status was declared, when it was declared anywhere. */\n readonly statusAt: SourceRef | null;\n /** Front-matter fields, flattened to strings and string arrays. */\n readonly frontMatter: Readonly<Record<string, string | readonly string[]>>;\n}\n\nexport interface ItemNode extends NodeBase {\n readonly kind: 'item';\n /** Owning document id. */\n readonly document: string;\n /** Heading path the item sits under, outermost first. */\n readonly section: readonly string[];\n /** First line of the item, normalised to one line for reports. */\n readonly text: string;\n /** The full multi-line body, continuations included. */\n readonly body: string;\n readonly disposition: Disposition;\n readonly openness: Openness;\n /** Which signal decided the disposition. */\n readonly evidence: StateSignal;\n /** Conflicting signals found while resolving the state. */\n readonly conflicts: readonly StateSignal[];\n}\n\nexport type SpecNode = DocumentNode | ItemNode;\n\n/** One observation about an item state. */\nexport interface StateSignal {\n /** Higher wins. See `SIGNAL_PRIORITY` in `state.ts`. */\n readonly source: 'directive' | 'marker' | 'strikethrough' | 'checkbox' | 'section' | 'default';\n readonly disposition: Disposition;\n /** The literal text that produced the signal. */\n readonly raw: string;\n readonly at: SourceRef;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Unresolved references */\n/* -------------------------------------------------------------------------- */\n\nexport type DanglingReason =\n /** Nothing in the corpus answers to this identifier. */\n | 'unknown-target'\n /** More than one document answers to it. */\n | 'ambiguous'\n /** The path exists on disk but is not a parsed specification. */\n | 'not-a-spec'\n /** The document resolved, but the `#anchor` did not. */\n | 'unknown-anchor';\n\n/** A citation that failed foreign-key validation. */\nexport interface DanglingRef {\n readonly kind: EdgeKind;\n readonly from: string;\n /** The identifier as written. */\n readonly target: string;\n readonly reason: DanglingReason;\n readonly origin: EdgeOrigin;\n readonly declaredAt: SourceRef;\n readonly raw: string;\n /** Candidate ids when `reason` is `ambiguous`, or near-misses otherwise. */\n readonly candidates: readonly string[];\n}\n\n/**\n * A citation that failed to resolve and that configuration told us to ignore.\n *\n * Kept so a repository can audit its own silence. `ignoreReferences` and\n * `ignoreFamilies` are the two places a team can tell spec-graph to stop\n * reporting something, and a single over-broad glob can quietly turn the whole\n * check off - which looks exactly like a clean repository. The opportunistic\n * rule from [ADR-0004] is deliberately not counted here: reading `SHA-256` as\n * prose is spec-graph's own judgement, not a decision anybody wrote down, and\n * listing it would bury the handful of entries that are. See ADR-0008.\n */\nexport interface SuppressedRef {\n /** The identifier as written. */\n readonly target: string;\n /** The document that wrote it. */\n readonly from: string;\n readonly at: SourceRef;\n /** Which declaration silenced it. */\n readonly by: 'reference' | 'family';\n}\n\n/**\n * A front-matter key that reads as a relation and is not one.\n *\n * Front matter is an open vocabulary - `title`, `tags`, `sidebar_position` and\n * whatever else the site generator wants all live there - so an unrecognised key\n * is normally none of spec-graph's business. This is the exception: a key one\n * character away from a relation, carrying something shaped like a citation, is\n * a relation the author believes they declared. The edge does not exist, and\n * nothing else would ever say so.\n */\nexport interface MisreadKey {\n /** The key as written. */\n readonly key: string;\n /** The relation key it is a hair away from. */\n readonly suggestion: string;\n /** The document the key was written in. */\n readonly from: string;\n readonly at: SourceRef;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Diagnostics */\n/* -------------------------------------------------------------------------- */\n\nexport type Severity = 'error' | 'warn' | 'info' | 'off';\n\nexport type RuleId =\n | 'ghost-handover'\n | 'stale-premise'\n | 'broken-reference'\n | 'reference-outside-corpus'\n | 'ambiguous-reference'\n | 'circular-delegation'\n | 'orphaned-obligation'\n | 'unreciprocated-supersession'\n | 'live-supersession'\n | 'state-conflict'\n | 'self-reference'\n | 'unknown-relation-key';\n\n/**\n * A rule a repository wrote for itself, as a selector plus a sentence.\n *\n * Namespaced, and the namespace is load-bearing rather than decorative. A\n * built-in id can never contain a colon, so a project rule can never collide\n * with one however it is named - which means `--rule`, the severity table, a\n * baseline entry and a SARIF `ruleId` all keep working without anyone having to\n * reserve a vocabulary. See ADR-0016.\n */\nexport type ProjectRuleId = `project:${string}`;\n\n/** Any rule that can produce a finding: built in, or written by a repository. */\nexport type AnyRuleId = RuleId | ProjectRuleId;\n\n/** True for an id in the project namespace, as opposed to a built-in. */\nexport function isProjectRule(rule: AnyRuleId): rule is ProjectRuleId {\n return rule.startsWith(PROJECT_RULE_PREFIX);\n}\n\nexport const PROJECT_RULE_PREFIX = 'project:';\n\nexport interface RelatedLocation {\n readonly at: SourceRef;\n readonly note: string;\n}\n\nexport interface Diagnostic {\n readonly rule: AnyRuleId;\n readonly severity: Exclude<Severity, 'off'>;\n /** One line, no trailing period. The headline of the finding. */\n readonly message: string;\n /** Where a human should go to fix it. */\n readonly at: SourceRef;\n /** Node ids involved, most relevant first. */\n readonly nodes: readonly string[];\n /**\n * What within the document this finding is about, where that is not a node.\n *\n * `null` everywhere else, where `nodes` already names the subject. It exists\n * because a broken reference names something that is not a node - that is\n * what makes it broken - and a baseline still has to be able to tell one\n * broken reference from another. A misread front-matter key is the same\n * shape: a spelling, not a node. See ADR-0012.\n */\n readonly target: string | null;\n /** Supporting locations: the other end of an edge, a cycle members. */\n readonly related: readonly RelatedLocation[];\n /** Concrete next action. */\n readonly hint: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Analysis output */\n/* -------------------------------------------------------------------------- */\n\n/** A problem with the input itself, as opposed to a finding about the graph. */\nexport interface ParseProblem {\n readonly message: string;\n readonly at: SourceRef;\n}\n\nexport interface AnalysisSummary {\n readonly documents: number;\n readonly items: number;\n readonly edges: number;\n readonly openObligations: number;\n readonly errors: number;\n readonly warnings: number;\n readonly infos: number;\n /** Milliseconds, wall clock. */\n readonly durationMs: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA4FH,MAAM,CAAC,MAAM,YAAY,GAA2B,MAAM,CAAC,MAAM,CAAC;IAChE,YAAY;IACZ,UAAU;IACV,WAAW;IACX,WAAW;IACX,eAAe;IACf,UAAU;IACV,UAAU;CACX,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,MAAM,WAAW,GAA4C,MAAM,CAAC,MAAM,CAAC;IAChF,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,SAAS;IACnB,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,QAAQ;IACnB,eAAe,EAAE,QAAQ;IACzB,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,QAAQ;CACnB,CAAC,CAAC;AAkDH,MAAM,CAAC,MAAM,WAAW,GAA2C,MAAM,CAAC,MAAM,CAAC;IAC/E,QAAQ,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE;IAClG,UAAU,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACtG,YAAY,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACxG,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE;IACvG,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE;IAC/F,cAAc,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE;IAC3G,YAAY,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE;IACzG,UAAU,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE;IACrG,MAAM,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;CAC7F,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAwB,CAAC;AAqN1E,yEAAyE;AACzE,MAAM,UAAU,aAAa,CAAC,IAAe;IAC3C,OAAO,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC","sourcesContent":["/**\n * The spec-graph data model.\n *\n * Three ideas carry the whole design:\n *\n * 1. **Documents have a lifecycle phase**, normalised from whatever vocabulary\n * the repository already uses (MADR `accepted`, KEP `implementable`, RFC\n * `final`, plain English `archived`). The phase answers one question that\n * matters more than any other: *can this document still absorb new work?*\n *\n * 2. **Items are obligations with nuanced state.** A checkbox is not a boolean.\n * An item can be open, narrowed to a smaller scope, satisfied, consciously\n * accepted as debt, or obviated because the premise it rested on evaporated.\n * Collapsing those five into `[ ]` / `[x]` is what makes ad-hoc greps lie.\n *\n * 3. **Edges are typed and carry provenance.** Every relation records where it\n * was *declared*, which is frequently not the node it points at. That is what\n * lets a diagnostic say \"ADR-0009 line 12 claims to supersede ADR-0003\"\n * instead of \"something, somewhere, is inconsistent\".\n */\n\n/* -------------------------------------------------------------------------- */\n/* Source positions */\n/* -------------------------------------------------------------------------- */\n\n/** A point in a source file. `offset` is a UTF-16 code-unit index. */\nexport interface Position {\n readonly offset: number;\n /** 1-based. */\n readonly line: number;\n /** 1-based, counted in UTF-16 code units. */\n readonly column: number;\n}\n\n/** A half-open `[start, end)` range of a source file. */\nexport interface Span {\n readonly start: Position;\n readonly end: Position;\n}\n\n/** A span plus the file it belongs to. Everything user-visible carries one. */\nexport interface SourceRef {\n /** Repository-relative POSIX path. */\n readonly file: string;\n readonly span: Span;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Lifecycle */\n/* -------------------------------------------------------------------------- */\n\n/**\n * The normalised document lifecycle, as a four-state lattice.\n *\n * Ecosystem vocabularies are large and mutually incompatible; the *consequences*\n * of a status are not. These four phases are chosen so that every question\n * spec-graph asks can be answered from the phase alone.\n *\n * - `draft` - not yet binding. Mutable. Absorbs new work.\n * - `active` - binding and mutable. Absorbs new work.\n * - `frozen` - binding and immutable. Changes require a new document.\n * - `retired` - no longer binding: superseded, rejected, withdrawn, archived.\n *\n * `unknown` is a fifth, deliberately non-lattice value for documents that never\n * declared a status. Rules treat it permissively rather than guessing.\n */\nexport type Phase = 'draft' | 'active' | 'frozen' | 'retired' | 'record' | 'unknown';\n\n/**\n * Can a document in this phase take on a *new* open obligation?\n *\n * This is the single predicate behind ghost-handover detection. Delegating an\n * open question into a `frozen` or `retired` document is a write to a closed\n * ledger: nobody will ever read it again.\n */\nexport type Receptivity = 'receptive' | 'sealed' | 'unknown';\n\n/* -------------------------------------------------------------------------- */\n/* Item state */\n/* -------------------------------------------------------------------------- */\n\n/**\n * How much of an obligation is left.\n *\n * Kept separate from {@link Disposition} because the two answer different\n * questions: `openness` is \"does someone still owe work?\", `disposition` is\n * \"what happened to it?\". Rules almost always want the former; humans reading a\n * report always want the latter.\n */\nexport type Openness = 'open' | 'partial' | 'closed';\n\n/** Why an item is in the state it is in. */\nexport type Disposition =\n /** Nothing has happened yet. */\n | 'unresolved'\n /** Scope was cut down, but a remainder is still owed. */\n | 'narrowed'\n /** Ownership moved elsewhere. Still owed - by somebody else. */\n | 'delegated'\n /** Done, answered, decided. */\n | 'satisfied'\n /** Knowingly not done. A closed item with a scar. */\n | 'accepted-debt'\n /** Decided against. */\n | 'rejected'\n /**\n * The premise evaporated, so the question stopped being a question.\n * Load-bearing citations to an obviated item are stale premises.\n */\n | 'obviated';\n\nexport const DISPOSITIONS: readonly Disposition[] = Object.freeze([\n 'unresolved',\n 'narrowed',\n 'delegated',\n 'satisfied',\n 'accepted-debt',\n 'rejected',\n 'obviated',\n]);\n\n/** The openness implied by each disposition. */\nexport const OPENNESS_OF: Readonly<Record<Disposition, Openness>> = Object.freeze({\n unresolved: 'open',\n narrowed: 'partial',\n delegated: 'open',\n satisfied: 'closed',\n 'accepted-debt': 'closed',\n rejected: 'closed',\n obviated: 'closed',\n});\n\n/* -------------------------------------------------------------------------- */\n/* Edges */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Relation kinds.\n *\n * The set is small on purpose. Two orthogonal properties - declared once in\n * {@link EDGE_TRAITS} - do the actual work, so adding a kind never means\n * touching a rule.\n */\nexport type EdgeKind =\n /** Structural: a document contains an item. Never inferred from prose. */\n | 'contains'\n /** A neutral citation. */\n | 'references'\n /** Explicitly *not* load-bearing: \"See also\", \"History\", \"Prior art\". */\n | 'relates-to'\n /** The source design depends on the target content. */\n | 'depends-on'\n /** The source rests on a claim made by the target. */\n | 'assumes'\n /** The source hands an obligation to the target. */\n | 'delegates-to'\n /** The source cannot proceed until the target does. */\n | 'blocked-by'\n /** The source replaces the target. */\n | 'supersedes'\n /** The source modifies the target without replacing it. */\n | 'amends';\n\nexport interface EdgeTraits {\n /**\n * Does the source continued validity depend on what the target says?\n *\n * Load-bearing edges into retired documents are stale premises. Non\n * load-bearing ones (\"see also\") are just history, and reporting them would\n * bury the real findings - which is exactly how ad-hoc linters lose trust.\n */\n readonly loadBearing: boolean;\n /** Does the edge move an obligation from source to target? */\n readonly transfersObligation: boolean;\n /** Does the edge assert something about the target lifecycle? */\n readonly lifecycle: boolean;\n /** Human-readable phrase used in diagnostics: \"ADR-1 <phrase> ADR-2\". */\n readonly phrase: string;\n}\n\nexport const EDGE_TRAITS: Readonly<Record<EdgeKind, EdgeTraits>> = Object.freeze({\n contains: { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'contains' },\n references: { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'references' },\n 'relates-to': { loadBearing: false, transfersObligation: false, lifecycle: false, phrase: 'relates to' },\n 'depends-on': { loadBearing: true, transfersObligation: false, lifecycle: false, phrase: 'depends on' },\n assumes: { loadBearing: true, transfersObligation: false, lifecycle: false, phrase: 'assumes' },\n 'delegates-to': { loadBearing: false, transfersObligation: true, lifecycle: false, phrase: 'delegates to' },\n 'blocked-by': { loadBearing: true, transfersObligation: true, lifecycle: false, phrase: 'is blocked by' },\n supersedes: { loadBearing: false, transfersObligation: false, lifecycle: true, phrase: 'supersedes' },\n amends: { loadBearing: true, transfersObligation: false, lifecycle: true, phrase: 'amends' },\n});\n\nexport const EDGE_KINDS = Object.keys(EDGE_TRAITS) as readonly EdgeKind[];\n\n/** How an edge came to exist. Reported verbatim so findings are auditable. */\nexport type EdgeOrigin =\n /** A `<!-- @spec-edge -->` directive. */\n | 'directive'\n /** A front-matter field such as `supersedes:` or `superseded-by:`. */\n | 'front-matter'\n /** A Markdown link, classified by the section it sits in. */\n | 'link'\n /** A bare textual identifier such as `ADR-0007` in prose. */\n | 'text'\n /** Synthesised by the extractor (document -> item). */\n | 'structural';\n\nexport interface Edge {\n readonly kind: EdgeKind;\n /** Node id. */\n readonly from: string;\n /** Node id. */\n readonly to: string;\n readonly origin: EdgeOrigin;\n /**\n * Where the relation was written down. Not necessarily inside `from`: a\n * `superseded-by: ADR-9` field in ADR-3 declares `ADR-9 supersedes ADR-3`,\n * and the only place a human can go fix it is ADR-3.\n */\n readonly declaredAt: SourceRef;\n /** The raw text that produced the edge, for diagnostics. */\n readonly raw: string;\n /**\n * Every file that declares this relation.\n *\n * A supersession stated only in the superseding document leaves the\n * superseded one silently presenting itself as current, which is a finding in\n * its own right - so the set of declaration sites has to survive de-duplication.\n */\n readonly declaredIn: readonly string[];\n /** True when `from` and `to` are the same node. Kept, never traversed. */\n readonly reflexive: boolean;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Nodes */\n/* -------------------------------------------------------------------------- */\n\nexport type NodeKind = 'document' | 'item';\n\ninterface NodeBase {\n /** Canonical, graph-unique. Documents: `ADR-0007`. Items: `ADR-0007#q.1`. */\n readonly id: string;\n readonly kind: NodeKind;\n /** Short human label used in reports. */\n readonly title: string;\n readonly at: SourceRef;\n}\n\nexport interface DocumentNode extends NodeBase {\n readonly kind: 'document';\n /** Repository-relative POSIX path. */\n readonly path: string;\n /** Every spelling that resolves to this document, lowercased. */\n readonly aliases: readonly string[];\n readonly phase: Phase;\n /** The status string as written, before normalisation. `null` if absent. */\n readonly rawStatus: string | null;\n /** Where the status was declared, when it was declared anywhere. */\n readonly statusAt: SourceRef | null;\n /** Front-matter fields, flattened to strings and string arrays. */\n readonly frontMatter: Readonly<Record<string, string | readonly string[]>>;\n}\n\nexport interface ItemNode extends NodeBase {\n readonly kind: 'item';\n /**\n * Whether the id was written by an author rather than numbered by position.\n *\n * A numbered id is the item's ordinal within its section, so inserting one\n * item renumbers every item after it. Only a declared id names the same item\n * in two states of a repository (ADR-0020).\n */\n readonly declared: boolean;\n /** Owning document id. */\n readonly document: string;\n /** Heading path the item sits under, outermost first. */\n readonly section: readonly string[];\n /** First line of the item, normalised to one line for reports. */\n readonly text: string;\n /** The full multi-line body, continuations included. */\n readonly body: string;\n readonly disposition: Disposition;\n readonly openness: Openness;\n /** Which signal decided the disposition. */\n readonly evidence: StateSignal;\n /** Conflicting signals found while resolving the state. */\n readonly conflicts: readonly StateSignal[];\n}\n\nexport type SpecNode = DocumentNode | ItemNode;\n\n/** One observation about an item state. */\nexport interface StateSignal {\n /** Higher wins. See `SIGNAL_PRIORITY` in `state.ts`. */\n readonly source: 'directive' | 'marker' | 'strikethrough' | 'checkbox' | 'section' | 'default';\n readonly disposition: Disposition;\n /** The literal text that produced the signal. */\n readonly raw: string;\n readonly at: SourceRef;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Unresolved references */\n/* -------------------------------------------------------------------------- */\n\nexport type DanglingReason =\n /** Nothing in the corpus answers to this identifier. */\n | 'unknown-target'\n /** More than one document answers to it. */\n | 'ambiguous'\n /** The path exists on disk but is not a parsed specification. */\n | 'not-a-spec'\n /** The document resolved, but the `#anchor` did not. */\n | 'unknown-anchor';\n\n/** A citation that failed foreign-key validation. */\nexport interface DanglingRef {\n readonly kind: EdgeKind;\n readonly from: string;\n /** The identifier as written. */\n readonly target: string;\n readonly reason: DanglingReason;\n readonly origin: EdgeOrigin;\n readonly declaredAt: SourceRef;\n readonly raw: string;\n /** Candidate ids when `reason` is `ambiguous`, or near-misses otherwise. */\n readonly candidates: readonly string[];\n}\n\n/**\n * A citation that failed to resolve and that configuration told us to ignore.\n *\n * Kept so a repository can audit its own silence. `ignoreReferences` and\n * `ignoreFamilies` are the two places a team can tell spec-graph to stop\n * reporting something, and a single over-broad glob can quietly turn the whole\n * check off - which looks exactly like a clean repository. The opportunistic\n * rule from [ADR-0004] is deliberately not counted here: reading `SHA-256` as\n * prose is spec-graph's own judgement, not a decision anybody wrote down, and\n * listing it would bury the handful of entries that are. See ADR-0008.\n */\nexport interface SuppressedRef {\n /** The identifier as written. */\n readonly target: string;\n /** The document that wrote it. */\n readonly from: string;\n readonly at: SourceRef;\n /** Which declaration silenced it. */\n readonly by: 'reference' | 'family';\n}\n\n/**\n * A front-matter key that reads as a relation and is not one.\n *\n * Front matter is an open vocabulary - `title`, `tags`, `sidebar_position` and\n * whatever else the site generator wants all live there - so an unrecognised key\n * is normally none of spec-graph's business. This is the exception: a key one\n * character away from a relation, carrying something shaped like a citation, is\n * a relation the author believes they declared. The edge does not exist, and\n * nothing else would ever say so.\n */\nexport interface MisreadKey {\n /** The key as written. */\n readonly key: string;\n /** The relation key it is a hair away from. */\n readonly suggestion: string;\n /** The document the key was written in. */\n readonly from: string;\n readonly at: SourceRef;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Diagnostics */\n/* -------------------------------------------------------------------------- */\n\nexport type Severity = 'error' | 'warn' | 'info' | 'off';\n\nexport type RuleId =\n | 'ghost-handover'\n | 'stale-premise'\n | 'broken-reference'\n | 'reference-outside-corpus'\n | 'ambiguous-reference'\n | 'circular-delegation'\n | 'orphaned-obligation'\n | 'unreciprocated-supersession'\n | 'live-supersession'\n | 'state-conflict'\n | 'self-reference'\n | 'unknown-relation-key';\n\n/**\n * A rule a repository wrote for itself, as a selector plus a sentence.\n *\n * Namespaced, and the namespace is load-bearing rather than decorative. A\n * built-in id can never contain a colon, so a project rule can never collide\n * with one however it is named - which means `--rule`, the severity table, a\n * baseline entry and a SARIF `ruleId` all keep working without anyone having to\n * reserve a vocabulary. See ADR-0016.\n */\nexport type ProjectRuleId = `project:${string}`;\n\n/** Any rule that can produce a finding: built in, or written by a repository. */\nexport type AnyRuleId = RuleId | ProjectRuleId;\n\n/** True for an id in the project namespace, as opposed to a built-in. */\nexport function isProjectRule(rule: AnyRuleId): rule is ProjectRuleId {\n return rule.startsWith(PROJECT_RULE_PREFIX);\n}\n\nexport const PROJECT_RULE_PREFIX = 'project:';\n\nexport interface RelatedLocation {\n readonly at: SourceRef;\n readonly note: string;\n}\n\nexport interface Diagnostic {\n readonly rule: AnyRuleId;\n readonly severity: Exclude<Severity, 'off'>;\n /** One line, no trailing period. The headline of the finding. */\n readonly message: string;\n /** Where a human should go to fix it. */\n readonly at: SourceRef;\n /** Node ids involved, most relevant first. */\n readonly nodes: readonly string[];\n /**\n * What within the document this finding is about, where that is not a node.\n *\n * `null` everywhere else, where `nodes` already names the subject. It exists\n * because a broken reference names something that is not a node - that is\n * what makes it broken - and a baseline still has to be able to tell one\n * broken reference from another. A misread front-matter key is the same\n * shape: a spelling, not a node. See ADR-0012.\n */\n readonly target: string | null;\n /** Supporting locations: the other end of an edge, a cycle members. */\n readonly related: readonly RelatedLocation[];\n /** Concrete next action. */\n readonly hint: string;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Analysis output */\n/* -------------------------------------------------------------------------- */\n\n/** A problem with the input itself, as opposed to a finding about the graph. */\nexport interface ParseProblem {\n readonly message: string;\n readonly at: SourceRef;\n}\n\nexport interface AnalysisSummary {\n readonly documents: number;\n readonly items: number;\n readonly edges: number;\n readonly openObligations: number;\n readonly errors: number;\n readonly warnings: number;\n readonly infos: number;\n /** Milliseconds, wall clock. */\n readonly durationMs: number;\n}\n"]}
|
package/package.json
CHANGED