@descent-vtt/spec-graph 0.7.0 → 0.9.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 +477 -0
- package/README.md +109 -17
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +38 -19
- package/dist/cli.js.map +1 -1
- package/dist/directives.d.ts +9 -2
- package/dist/directives.js +15 -4
- package/dist/directives.js.map +1 -1
- package/dist/extract.d.ts +1 -1
- package/dist/extract.js +50 -16
- package/dist/extract.js.map +1 -1
- package/dist/glob.d.ts +44 -44
- package/dist/glob.js +152 -116
- package/dist/glob.js.map +1 -1
- package/dist/identity.d.ts +12 -4
- package/dist/identity.js +32 -8
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.js +10 -2
- package/dist/lifecycle.js.map +1 -1
- package/dist/markdown.d.ts +53 -161
- package/dist/markdown.js +42 -864
- package/dist/markdown.js.map +1 -1
- package/dist/report.d.ts +23 -0
- package/dist/report.js +80 -0
- package/dist/report.js.map +1 -1
- package/dist/rules.js +48 -14
- package/dist/rules.js.map +1 -1
- package/dist/sections.d.ts +1 -1
- package/dist/sections.js +18 -4
- package/dist/sections.js.map +1 -1
- package/dist/select.d.ts +2 -2
- package/dist/select.js +3 -3
- package/dist/select.js.map +1 -1
- package/dist/source.d.ts +6 -19
- package/dist/source.js +4 -70
- package/dist/source.js.map +1 -1
- package/dist/types.d.ts +23 -3
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vendor/spec-core/markdown/frontmatter.d.ts +103 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
- package/dist/vendor/spec-core/markdown/headings.d.ts +45 -0
- package/dist/vendor/spec-core/markdown/headings.js +199 -0
- package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
- package/dist/vendor/spec-core/markdown/index.d.ts +5 -0
- package/dist/vendor/spec-core/markdown/index.js +4 -0
- package/dist/vendor/spec-core/markdown/index.js.map +1 -0
- package/dist/vendor/spec-core/markdown/layout.d.ts +39 -0
- package/dist/vendor/spec-core/markdown/layout.js +28 -0
- package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
- package/dist/vendor/spec-core/markdown/links.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/links.js +364 -0
- package/dist/vendor/spec-core/markdown/links.js.map +1 -0
- package/dist/vendor/spec-core/markdown/lists.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/lists.js +127 -0
- package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
- package/dist/vendor/spec-core/markdown/scan.d.ts +36 -0
- package/dist/vendor/spec-core/markdown/scan.js +439 -0
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
- package/dist/vendor/spec-core/markdown/syntax.d.ts +75 -0
- package/dist/vendor/spec-core/markdown/syntax.js +181 -0
- package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
- package/dist/vendor/spec-core/markdown/tables.d.ts +18 -0
- package/dist/vendor/spec-core/markdown/tables.js +86 -0
- package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
- package/dist/vendor/spec-core/markdown/types.d.ts +287 -0
- package/dist/vendor/spec-core/markdown/types.js +9 -0
- package/dist/vendor/spec-core/markdown/types.js.map +1 -0
- package/dist/vendor/spec-core/path/index.d.ts +1 -0
- package/dist/vendor/spec-core/path/index.js +2 -0
- package/dist/vendor/spec-core/path/index.js.map +1 -0
- package/dist/vendor/spec-core/path/posix.d.ts +83 -0
- package/dist/vendor/spec-core/path/posix.js +150 -0
- package/dist/vendor/spec-core/path/posix.js.map +1 -0
- package/dist/vendor/spec-core/pattern/automaton.d.ts +129 -0
- package/dist/vendor/spec-core/pattern/automaton.js +340 -0
- package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
- package/dist/vendor/spec-core/pattern/charset.d.ts +58 -0
- package/dist/vendor/spec-core/pattern/charset.js +116 -0
- package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
- package/dist/vendor/spec-core/pattern/glob.d.ts +121 -0
- package/dist/vendor/spec-core/pattern/glob.js +481 -0
- package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
- package/dist/vendor/spec-core/pattern/index.d.ts +6 -0
- package/dist/vendor/spec-core/pattern/index.js +4 -0
- package/dist/vendor/spec-core/pattern/index.js.map +1 -0
- package/dist/{regex.d.ts → vendor/spec-core/pattern/regex.d.ts} +20 -29
- package/dist/{regex.js → vendor/spec-core/pattern/regex.js} +40 -49
- package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
- package/dist/vendor/spec-core/text/index.d.ts +2 -0
- package/dist/vendor/spec-core/text/index.js +2 -0
- package/dist/vendor/spec-core/text/index.js.map +1 -0
- package/dist/vendor/spec-core/text/lines.d.ts +72 -0
- package/dist/vendor/spec-core/text/lines.js +156 -0
- package/dist/vendor/spec-core/text/lines.js.map +1 -0
- package/dist/yaml.d.ts +44 -12
- package/dist/yaml.js +115 -206
- package/dist/yaml.js.map +1 -1
- package/package.json +3 -2
- package/src/vendor/spec-core/LICENSE +21 -0
- package/src/vendor/spec-core/README.md +8 -0
- package/dist/regex.js.map +0 -1
package/dist/identity.d.ts
CHANGED
|
@@ -35,11 +35,14 @@ export interface IdentityInput {
|
|
|
35
35
|
/** The document H1, if any. */
|
|
36
36
|
readonly heading: string | null;
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Whether this identity is the file's own. Default true.
|
|
39
39
|
*
|
|
40
40
|
* A specification that is a *region* of a file must not claim the file's
|
|
41
41
|
* path: the file already answers to it, and two nodes answering to one path
|
|
42
|
-
* would make every link to that file ambiguous.
|
|
42
|
+
* would make every link to that file ambiguous. The file's *name* is the same
|
|
43
|
+
* claim spelled differently, so a region does not read a number out of it
|
|
44
|
+
* either: every row of a register kept in `0042-open-issues.md` answered to
|
|
45
|
+
* ADR-0042, and so did the file.
|
|
43
46
|
*/
|
|
44
47
|
readonly includePathAliases?: boolean | undefined;
|
|
45
48
|
}
|
|
@@ -47,8 +50,13 @@ export interface IdentityInput {
|
|
|
47
50
|
* Derives a document's canonical id and every alias that should reach it.
|
|
48
51
|
*
|
|
49
52
|
* Evidence is used in order of how deliberate it is: an explicit declaration,
|
|
50
|
-
* then the file name, then the H1.
|
|
51
|
-
*
|
|
53
|
+
* then the file name, then the H1. The first two both *name* the document, so a
|
|
54
|
+
* declaration that carries no number still takes the number from the file name
|
|
55
|
+
* beside it - `slug: sharding-the-write-path` in `0007-sharding.md` is ADR-0007.
|
|
56
|
+
* A title only *describes* it, so it is read as a name only where nothing else
|
|
57
|
+
* has given one. A losing declaration or file name still contributes aliases,
|
|
58
|
+
* so a reference written in either form resolves; a title that lost does not,
|
|
59
|
+
* because the identifier it opens with belongs to the document it is about.
|
|
52
60
|
*/
|
|
53
61
|
export declare function identify(input: IdentityInput): DocumentIdentity;
|
|
54
62
|
/**
|
package/dist/identity.js
CHANGED
|
@@ -46,8 +46,13 @@ const HAS_LETTER_OR_DIGIT = /[\p{L}\p{N}]/u;
|
|
|
46
46
|
* Derives a document's canonical id and every alias that should reach it.
|
|
47
47
|
*
|
|
48
48
|
* Evidence is used in order of how deliberate it is: an explicit declaration,
|
|
49
|
-
* then the file name, then the H1.
|
|
50
|
-
*
|
|
49
|
+
* then the file name, then the H1. The first two both *name* the document, so a
|
|
50
|
+
* declaration that carries no number still takes the number from the file name
|
|
51
|
+
* beside it - `slug: sharding-the-write-path` in `0007-sharding.md` is ADR-0007.
|
|
52
|
+
* A title only *describes* it, so it is read as a name only where nothing else
|
|
53
|
+
* has given one. A losing declaration or file name still contributes aliases,
|
|
54
|
+
* so a reference written in either form resolves; a title that lost does not,
|
|
55
|
+
* because the identifier it opens with belongs to the document it is about.
|
|
51
56
|
*/
|
|
52
57
|
export function identify(input) {
|
|
53
58
|
// A declaration carrying no letter or digit is discarded rather than honoured.
|
|
@@ -57,6 +62,9 @@ export function identify(input) {
|
|
|
57
62
|
// exports as invalid Mermaid, and names itself in findings a reader cannot act
|
|
58
63
|
// on. There is nothing to gain by taking such a value seriously.
|
|
59
64
|
const declaredId = input.declaredId !== null && HAS_LETTER_OR_DIGIT.test(input.declaredId) ? input.declaredId : null;
|
|
65
|
+
// What the flag decides, named for what it means: the file's path and its
|
|
66
|
+
// name are one claim, and a region makes neither of them.
|
|
67
|
+
const ownsFile = input.includePathAliases !== false;
|
|
60
68
|
const stem = fileStem(input.path);
|
|
61
69
|
const directoryFamily = familyFromPath(input.path);
|
|
62
70
|
const aliases = new Set();
|
|
@@ -96,9 +104,26 @@ export function identify(input) {
|
|
|
96
104
|
};
|
|
97
105
|
consider(declaredId, true);
|
|
98
106
|
// `0007-sharding-the-write-path.md` and `kep-1234-foo.md` both start with the
|
|
99
|
-
// identifier; take the leading token rather than the whole stem.
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
// identifier; take the leading token rather than the whole stem. Only for the
|
|
108
|
+
// file's own identity: the name names the file, and a region reading its
|
|
109
|
+
// number is the collision ADR-0009 forbids, reached by a second route.
|
|
110
|
+
if (ownsFile)
|
|
111
|
+
consider(leadingToken(stem), true);
|
|
112
|
+
// A title is prose *about* a document, so it names one only where nothing has
|
|
113
|
+
// named it yet, and where it does not it registers no alias either - an alias
|
|
114
|
+
// is the same mistake one step removed, landing on a document that is real.
|
|
115
|
+
// A number disqualifies it, because a different number in a title is about a
|
|
116
|
+
// different document; so does being a region, because a row's id column
|
|
117
|
+
// exists to hold an identifier while its title cell is prose about the rest
|
|
118
|
+
// of the corpus. `| OI-V-05 | ADR-040's enforcement point has no browser
|
|
119
|
+
// test |` became ADR-040, collided with the real one and left the issue with
|
|
120
|
+
// no node at all: 27 of 232 rows of one register, nine inventing a `STAGE-1`
|
|
121
|
+
// nobody had written. A *file* keeps the looser reading deliberately - there
|
|
122
|
+
// the two cases are one shape, `slug:` holds a URL segment rather than an id,
|
|
123
|
+
// and `slug: sharding` under `# ADR-0007` has to stay ADR-0007. See the fifth
|
|
124
|
+
// asymmetry in ADR-0004 for what that costs and why it is not guessed at.
|
|
125
|
+
const titleId = ownsFile && !candidates.some((c) => c.number !== null) ? headingId(input.heading) : null;
|
|
126
|
+
consider(titleId, false);
|
|
102
127
|
const numbered = candidates.find((c) => c.number !== null);
|
|
103
128
|
const family = numbered?.family ?? directoryFamily;
|
|
104
129
|
const number = numbered?.number ?? null;
|
|
@@ -123,7 +148,7 @@ export function identify(input) {
|
|
|
123
148
|
id = stem.length > 0 ? stem : input.path;
|
|
124
149
|
}
|
|
125
150
|
add(id);
|
|
126
|
-
if (
|
|
151
|
+
if (ownsFile) {
|
|
127
152
|
add(stem);
|
|
128
153
|
add(input.path);
|
|
129
154
|
add(stripExtension(input.path));
|
|
@@ -131,8 +156,7 @@ export function identify(input) {
|
|
|
131
156
|
add(declaredId);
|
|
132
157
|
for (const alias of input.declaredAliases)
|
|
133
158
|
add(alias);
|
|
134
|
-
|
|
135
|
-
add(headingId(input.heading));
|
|
159
|
+
add(titleId);
|
|
136
160
|
if (number !== null) {
|
|
137
161
|
const spellings = numberSpellings(number, witnesses);
|
|
138
162
|
if (family !== null) {
|
package/dist/identity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Document identity and reference normalisation.\n *\n * Foreign-key validation across a corpus of Markdown only works if `ADR-7`,\n * `ADR-0007`, `adr 7`, `[[0007-sharding]]` and `../adr/0007-sharding.md` are\n * understood to name the same document. Getting that wrong in either direction\n * is fatal: too strict and every real repository drowns in false \"broken\n * reference\" findings, too loose and `RFC 7` silently resolves to `ADR-7`.\n *\n * The resolution here is deliberately asymmetric. **Textual aliases** (`adr-7`)\n * are global, because a prefix already names the family. **Bare numbers** (`7`,\n * `#0007`) are resolved only within the citing document's own family, because a\n * repository with both `adr/0007` and `rfc/0007` is normal and guessing between\n * them would be worse than reporting nothing.\n */\n\n/** Directory names that name a specification family, mapped to a canonical prefix. */\nconst FAMILY_DIRECTORIES: Readonly<Record<string, string>> = {\n adr: 'ADR',\n adrs: 'ADR',\n 'architecture-decisions': 'ADR',\n 'decision-records': 'ADR',\n decisions: 'ADR',\n rfc: 'RFC',\n rfcs: 'RFC',\n kep: 'KEP',\n keps: 'KEP',\n enhancements: 'KEP',\n proposal: 'PROPOSAL',\n proposals: 'PROPOSAL',\n design: 'DESIGN',\n designs: 'DESIGN',\n prd: 'PRD',\n prds: 'PRD',\n spec: 'SPEC',\n specs: 'SPEC',\n};\n\n/** Front-matter keys that may carry an explicit identifier, in preference order. */\nexport const ID_KEYS: readonly string[] = ['id', 'adr', 'adr-id', 'rfc', 'rfc-id', 'kep-number', 'number', 'slug'];\n\n/** `ADR-0007`, `KEP 1234`, `rfc2119`: a family prefix followed by a number. */\nconst PREFIXED_ID = /^([A-Za-z][A-Za-z0-9_]{0,15}?)[\\s._-]*(\\d{1,6})$/;\n/** A bare number, optionally hash-prefixed: `7`, `#0007`. */\nconst BARE_NUMBER = /^#?(\\d{1,6})$/;\n\nexport interface DocumentIdentity {\n /** The canonical id: `ADR-0007`, or a path when nothing better exists. */\n readonly id: string;\n /** Family prefix, upper-cased. `null` when the document is not numbered. */\n readonly family: string | null;\n /** Sequence number without leading zeros. `null` when not numbered. */\n readonly number: number | null;\n /** Every lower-cased spelling that should resolve to this document. */\n readonly aliases: readonly string[];\n}\n\nexport interface IdentityInput {\n /** Repository-relative POSIX path. */\n readonly path: string;\n /** An id declared by a directive or front matter, if any. */\n readonly declaredId: string | null;\n /** Extra aliases declared by a directive or front matter. */\n readonly declaredAliases: readonly string[];\n /** The document H1, if any. */\n readonly heading: string | null;\n /**\n * Register the file path and stem as aliases. Default true.\n *\n * A specification that is a *region* of a file must not claim the file's\n * path: the file already answers to it, and two nodes answering to one path\n * would make every link to that file ambiguous.\n */\n readonly includePathAliases?: boolean | undefined;\n}\n\n/** An id has to be a name. Punctuation alone is what a bad parse leaves behind. */\nconst HAS_LETTER_OR_DIGIT = /[\\p{L}\\p{N}]/u;\n\n/**\n * Derives a document's canonical id and every alias that should reach it.\n *\n * Evidence is used in order of how deliberate it is: an explicit declaration,\n * then the file name, then the H1. Whatever loses still contributes aliases, so\n * a reference written in any of those forms still resolves.\n */\nexport function identify(input: IdentityInput): DocumentIdentity {\n // A declaration carrying no letter or digit is discarded rather than honoured.\n // `<!-- @spec-node id=\\\"ADR-9\\\" -->`, written inside a JavaScript string in a\n // worked example, parses its bare value as a lone backslash - and a document\n // whose id is `\\` collides with every other one that made the same mistake,\n // exports as invalid Mermaid, and names itself in findings a reader cannot act\n // on. There is nothing to gain by taking such a value seriously.\n const declaredId = input.declaredId !== null && HAS_LETTER_OR_DIGIT.test(input.declaredId) ? input.declaredId : null;\n const stem = fileStem(input.path);\n const directoryFamily = familyFromPath(input.path);\n const aliases = new Set<string>();\n\n const add = (value: string | null | undefined): void => {\n if (!value) return;\n const key = normaliseRef(value);\n if (key.length > 0) aliases.add(key);\n };\n\n const candidates: { family: string | null; number: number | null; label: string }[] = [];\n\n const consider = (raw: string | null, allowBare: boolean): void => {\n if (!raw) return;\n const cleaned = raw.trim();\n if (cleaned.length === 0) return;\n const prefixed = PREFIXED_ID.exec(cleaned);\n if (prefixed) {\n candidates.push({\n family: (prefixed[1] as string).toUpperCase(),\n number: Number.parseInt(prefixed[2] as string, 10),\n label: cleaned,\n });\n return;\n }\n const bare = BARE_NUMBER.exec(cleaned);\n if (bare && allowBare) {\n candidates.push({\n family: directoryFamily,\n number: Number.parseInt(bare[1] as string, 10),\n label: cleaned,\n });\n return;\n }\n candidates.push({ family: null, number: null, label: cleaned });\n };\n\n consider(declaredId, true);\n // `0007-sharding-the-write-path.md` and `kep-1234-foo.md` both start with the\n // identifier; take the leading token rather than the whole stem.\n consider(leadingToken(stem), true);\n consider(headingId(input.heading), false);\n\n const numbered = candidates.find((c) => c.number !== null);\n const family = numbered?.family ?? directoryFamily;\n const number = numbered?.number ?? null;\n\n // Witnesses to the local zero-padding convention, most authoritative first.\n // The spelling that actually produced the number comes first: an id read from\n // the H1 of `sharding.md` must still render as ADR-0007, not ADR-7.\n const witnesses = [numbered?.label, declaredId, stem];\n\n let id: string;\n if (number !== null && family !== null) {\n id = `${family}-${padNumber(number, witnesses)}`;\n } else if (declaredId) {\n id = declaredId.trim();\n } else if (number !== null) {\n // Numbered but with no family to prefix - a Rust RFC in `text/0001-foo.md`,\n // say. The file stem is a far better identity than a bare \"1\": it is what\n // people actually type, and it stays unique across the repository.\n id = stem.length > 0 ? stem : String(number);\n } else {\n id = stem.length > 0 ? stem : input.path;\n }\n\n add(id);\n if (input.includePathAliases !== false) {\n add(stem);\n add(input.path);\n add(stripExtension(input.path));\n }\n add(declaredId);\n for (const alias of input.declaredAliases) add(alias);\n if (input.heading) add(headingId(input.heading));\n\n if (number !== null) {\n const spellings = numberSpellings(number, witnesses);\n if (family !== null) {\n // Every spelling a human might type. `normaliseRef` folds separators, so\n // `adr-7`, `adr 7` and `adr7` collapse to one key; the padding variants do\n // not, and each needs registering.\n for (const digits of spellings) add(`${family}-${digits}`);\n } else {\n // With no family there is only one number space in the repository, so a\n // bare `0001` is unambiguous and worth resolving.\n for (const digits of spellings) add(digits);\n }\n }\n\n return { id, family, number, aliases: [...aliases] };\n}\n\n/**\n * Folds a reference to its lookup key.\n *\n * Case, separators and surrounding punctuation carry no meaning in a citation,\n * so `ADR-0007`, `adr 0007` and `Adr_0007` all become `adr0007`.\n */\nexport function normaliseRef(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/^[#<([]+|[)\\]>.,;:]+$/g, '')\n .replace(/[\\s._-]+/g, '')\n .trim();\n}\n\n/** Splits a `target#anchor` reference. */\nexport function splitAnchor(target: string): { target: string; anchor: string | null } {\n const hash = target.indexOf('#');\n if (hash === -1) return { target, anchor: null };\n // A leading `#` is an in-document anchor, not a separator.\n if (hash === 0) return { target: '', anchor: target.slice(1) };\n return { target: target.slice(0, hash), anchor: target.slice(hash + 1) };\n}\n\n/** Parses `ADR-0007` into its family and number, or returns `null`. */\nexport function parsePrefixedRef(value: string): { family: string; number: number } | null {\n const match = PREFIXED_ID.exec(value.trim().replace(/^[#([]+/, ''));\n if (!match) return null;\n return { family: (match[1] as string).toUpperCase(), number: Number.parseInt(match[2] as string, 10) };\n}\n\n/** Parses a bare `7` or `#0007`, or returns `null`. */\nexport function parseBareRef(value: string): number | null {\n const match = BARE_NUMBER.exec(value.trim());\n return match ? Number.parseInt(match[1] as string, 10) : null;\n}\n\n/** True when a reference target names a location rather than an identifier. */\nexport function looksLikePath(target: string): boolean {\n return target.includes('/') || DOCUMENT_EXTENSION.test(target);\n}\n\nconst DOCUMENT_EXTENSION = /\\.(md|markdown|mdx|txt|rst|adoc|asciidoc)$/i;\n\n/**\n * True when a path-like target could name a specification.\n *\n * A specification linking to `../../src/rules.ts` is citing source code, which\n * is a different tool's job. Treating it as a dangling specification reference\n * would report an error on one of the most ordinary things a design document\n * does, so targets carrying a non-document extension are not references at all.\n */\nexport function isDocumentTarget(target: string): boolean {\n const withoutAnchor = splitAnchor(target).target;\n const base = withoutAnchor.slice(withoutAnchor.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n // No extension: a directory, or an extensionless spec such as `rfcs/0007`.\n if (dot <= 0) return true;\n const extension = base.slice(dot);\n // Only a plausible file extension disqualifies a target. An identifier like\n // `v1.2` or `ADR-0007.1` keeps its dot and is still resolved as an id.\n if (!/^\\.[A-Za-z][A-Za-z0-9]{0,5}$/.test(extension)) return true;\n return DOCUMENT_EXTENSION.test(base);\n}\n\n/** True when a reference target points outside the repository. */\nexport function isExternal(target: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(target) && !/^[a-z]:[\\\\/]/i.test(target);\n}\n\n/** The family prefix implied by a document's directory, if any. */\nexport function familyFromPath(posixPath: string): string | null {\n const segments = posixPath.toLowerCase().split('/');\n // Nearest enclosing directory wins: `docs/rfcs/adr/0007.md` is an ADR.\n for (let i = segments.length - 2; i >= 0; i -= 1) {\n const found = FAMILY_DIRECTORIES[segments[i] as string];\n if (found) return found;\n }\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Internals */\n/* -------------------------------------------------------------------------- */\n\nfunction fileStem(posixPath: string): string {\n const base = posixPath.slice(posixPath.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n const stem = dot > 0 ? base.slice(0, dot) : base;\n // `docs/adr/0007-sharding/README.md` is identified by its directory - but a\n // README at the repository root has no directory to be named after, and must\n // keep its own name.\n const slash = posixPath.lastIndexOf('/');\n if (slash > 0 && /^(readme|index)$/i.test(stem)) {\n const parent = posixPath.slice(0, slash);\n return parent.slice(parent.lastIndexOf('/') + 1);\n }\n return stem;\n}\n\nfunction stripExtension(posixPath: string): string {\n const dot = posixPath.lastIndexOf('.');\n const slash = posixPath.lastIndexOf('/');\n return dot > slash ? posixPath.slice(0, dot) : posixPath;\n}\n\n/** `kep-1234-foo` -> `kep-1234`; `0007-sharding` -> `0007`; `sharding` -> `sharding`. */\nfunction leadingToken(stem: string): string {\n const prefixed = /^([A-Za-z]{1,15}[\\s._-]?\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (prefixed) return prefixed[1] as string;\n const bare = /^(\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (bare) return bare[1] as string;\n return stem;\n}\n\n/** `# ADR-0007: Sharding the write path` -> `ADR-0007`. */\nfunction headingId(heading: string | null): string | null {\n if (!heading) return null;\n const match = /^\\s*([A-Za-z]{1,15}[\\s._-]?\\d{1,6})\\b/.exec(heading);\n return match ? (match[1] as string) : null;\n}\n\n/**\n * Chooses the digit width of the canonical id.\n *\n * A repository that writes `0007` everywhere should see `ADR-0007` in reports,\n * not `ADR-7`. The file name is the most reliable witness of the local\n * convention, so it decides.\n */\nfunction padNumber(number: number, witnesses: readonly (string | null | undefined)[]): string {\n const width = paddingWidth(number, witnesses) ?? String(number).length;\n return String(number).padStart(width, '0');\n}\n\n/** The digit width of the first witness that actually spells this number. */\nfunction paddingWidth(number: number, witnesses: readonly (string | null | undefined)[]): number | null {\n for (const witness of witnesses) {\n if (!witness) continue;\n const digits = /(\\d{1,6})/.exec(witness)?.[1];\n if (digits && Number.parseInt(digits, 10) === number) return digits.length;\n }\n return null;\n}\n\n/** Zero-padded spellings a citation might reasonably use. */\nfunction numberSpellings(number: number, witnesses: readonly (string | null | undefined)[]): string[] {\n const out = new Set<string>([String(number)]);\n const observed = paddingWidth(number, witnesses);\n if (observed !== null) out.add(String(number).padStart(observed, '0'));\n for (const width of [3, 4]) out.add(String(number).padStart(width, '0'));\n return [...out];\n}\n\n/**\n * True when two folded spellings are one edit apart.\n *\n * One edit, not two, and a transposition counts as one: `supercedesby` for\n * `supercededby` is a slip of the finger, `categories` for `dependencies` is a\n * different word. Written out rather than as a distance matrix because the\n * answer is a yes or a no and the bound is one - the general algorithm would be\n * more code, not less.\n */\nexport function withinOneEdit(a: string, b: string): boolean {\n if (a === b) return false;\n if (a.length === b.length) {\n let first = -1;\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] === b[i]) continue;\n if (first === -1) {\n first = i;\n continue;\n }\n // A second difference is allowed only if the two are a swapped pair, and\n // only if there is no third.\n return first === i - 1 && a[first] === b[i] && a[i] === b[first] && a.slice(i + 1) === b.slice(i + 1);\n }\n return true;\n }\n const [short, long] = a.length < b.length ? [a, b] : [b, a];\n if (long.length - short.length !== 1) return false;\n for (let i = 0; i < short.length; i += 1) {\n if (short[i] === long[i]) continue;\n return short.slice(i) === long.slice(i + 1);\n }\n return true;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Document identity and reference normalisation.\n *\n * Foreign-key validation across a corpus of Markdown only works if `ADR-7`,\n * `ADR-0007`, `adr 7`, `[[0007-sharding]]` and `../adr/0007-sharding.md` are\n * understood to name the same document. Getting that wrong in either direction\n * is fatal: too strict and every real repository drowns in false \"broken\n * reference\" findings, too loose and `RFC 7` silently resolves to `ADR-7`.\n *\n * The resolution here is deliberately asymmetric. **Textual aliases** (`adr-7`)\n * are global, because a prefix already names the family. **Bare numbers** (`7`,\n * `#0007`) are resolved only within the citing document's own family, because a\n * repository with both `adr/0007` and `rfc/0007` is normal and guessing between\n * them would be worse than reporting nothing.\n */\n\n/** Directory names that name a specification family, mapped to a canonical prefix. */\nconst FAMILY_DIRECTORIES: Readonly<Record<string, string>> = {\n adr: 'ADR',\n adrs: 'ADR',\n 'architecture-decisions': 'ADR',\n 'decision-records': 'ADR',\n decisions: 'ADR',\n rfc: 'RFC',\n rfcs: 'RFC',\n kep: 'KEP',\n keps: 'KEP',\n enhancements: 'KEP',\n proposal: 'PROPOSAL',\n proposals: 'PROPOSAL',\n design: 'DESIGN',\n designs: 'DESIGN',\n prd: 'PRD',\n prds: 'PRD',\n spec: 'SPEC',\n specs: 'SPEC',\n};\n\n/** Front-matter keys that may carry an explicit identifier, in preference order. */\nexport const ID_KEYS: readonly string[] = ['id', 'adr', 'adr-id', 'rfc', 'rfc-id', 'kep-number', 'number', 'slug'];\n\n/** `ADR-0007`, `KEP 1234`, `rfc2119`: a family prefix followed by a number. */\nconst PREFIXED_ID = /^([A-Za-z][A-Za-z0-9_]{0,15}?)[\\s._-]*(\\d{1,6})$/;\n/** A bare number, optionally hash-prefixed: `7`, `#0007`. */\nconst BARE_NUMBER = /^#?(\\d{1,6})$/;\n\nexport interface DocumentIdentity {\n /** The canonical id: `ADR-0007`, or a path when nothing better exists. */\n readonly id: string;\n /** Family prefix, upper-cased. `null` when the document is not numbered. */\n readonly family: string | null;\n /** Sequence number without leading zeros. `null` when not numbered. */\n readonly number: number | null;\n /** Every lower-cased spelling that should resolve to this document. */\n readonly aliases: readonly string[];\n}\n\nexport interface IdentityInput {\n /** Repository-relative POSIX path. */\n readonly path: string;\n /** An id declared by a directive or front matter, if any. */\n readonly declaredId: string | null;\n /** Extra aliases declared by a directive or front matter. */\n readonly declaredAliases: readonly string[];\n /** The document H1, if any. */\n readonly heading: string | null;\n /**\n * Whether this identity is the file's own. Default true.\n *\n * A specification that is a *region* of a file must not claim the file's\n * path: the file already answers to it, and two nodes answering to one path\n * would make every link to that file ambiguous. The file's *name* is the same\n * claim spelled differently, so a region does not read a number out of it\n * either: every row of a register kept in `0042-open-issues.md` answered to\n * ADR-0042, and so did the file.\n */\n readonly includePathAliases?: boolean | undefined;\n}\n\n/** An id has to be a name. Punctuation alone is what a bad parse leaves behind. */\nconst HAS_LETTER_OR_DIGIT = /[\\p{L}\\p{N}]/u;\n\n/**\n * Derives a document's canonical id and every alias that should reach it.\n *\n * Evidence is used in order of how deliberate it is: an explicit declaration,\n * then the file name, then the H1. The first two both *name* the document, so a\n * declaration that carries no number still takes the number from the file name\n * beside it - `slug: sharding-the-write-path` in `0007-sharding.md` is ADR-0007.\n * A title only *describes* it, so it is read as a name only where nothing else\n * has given one. A losing declaration or file name still contributes aliases,\n * so a reference written in either form resolves; a title that lost does not,\n * because the identifier it opens with belongs to the document it is about.\n */\nexport function identify(input: IdentityInput): DocumentIdentity {\n // A declaration carrying no letter or digit is discarded rather than honoured.\n // `<!-- @spec-node id=\\\"ADR-9\\\" -->`, written inside a JavaScript string in a\n // worked example, parses its bare value as a lone backslash - and a document\n // whose id is `\\` collides with every other one that made the same mistake,\n // exports as invalid Mermaid, and names itself in findings a reader cannot act\n // on. There is nothing to gain by taking such a value seriously.\n const declaredId = input.declaredId !== null && HAS_LETTER_OR_DIGIT.test(input.declaredId) ? input.declaredId : null;\n // What the flag decides, named for what it means: the file's path and its\n // name are one claim, and a region makes neither of them.\n const ownsFile = input.includePathAliases !== false;\n const stem = fileStem(input.path);\n const directoryFamily = familyFromPath(input.path);\n const aliases = new Set<string>();\n\n const add = (value: string | null | undefined): void => {\n if (!value) return;\n const key = normaliseRef(value);\n if (key.length > 0) aliases.add(key);\n };\n\n const candidates: { family: string | null; number: number | null; label: string }[] = [];\n\n const consider = (raw: string | null, allowBare: boolean): void => {\n if (!raw) return;\n const cleaned = raw.trim();\n if (cleaned.length === 0) return;\n const prefixed = PREFIXED_ID.exec(cleaned);\n if (prefixed) {\n candidates.push({\n family: (prefixed[1] as string).toUpperCase(),\n number: Number.parseInt(prefixed[2] as string, 10),\n label: cleaned,\n });\n return;\n }\n const bare = BARE_NUMBER.exec(cleaned);\n if (bare && allowBare) {\n candidates.push({\n family: directoryFamily,\n number: Number.parseInt(bare[1] as string, 10),\n label: cleaned,\n });\n return;\n }\n candidates.push({ family: null, number: null, label: cleaned });\n };\n\n consider(declaredId, true);\n // `0007-sharding-the-write-path.md` and `kep-1234-foo.md` both start with the\n // identifier; take the leading token rather than the whole stem. Only for the\n // file's own identity: the name names the file, and a region reading its\n // number is the collision ADR-0009 forbids, reached by a second route.\n if (ownsFile) consider(leadingToken(stem), true);\n\n // A title is prose *about* a document, so it names one only where nothing has\n // named it yet, and where it does not it registers no alias either - an alias\n // is the same mistake one step removed, landing on a document that is real.\n // A number disqualifies it, because a different number in a title is about a\n // different document; so does being a region, because a row's id column\n // exists to hold an identifier while its title cell is prose about the rest\n // of the corpus. `| OI-V-05 | ADR-040's enforcement point has no browser\n // test |` became ADR-040, collided with the real one and left the issue with\n // no node at all: 27 of 232 rows of one register, nine inventing a `STAGE-1`\n // nobody had written. A *file* keeps the looser reading deliberately - there\n // the two cases are one shape, `slug:` holds a URL segment rather than an id,\n // and `slug: sharding` under `# ADR-0007` has to stay ADR-0007. See the fifth\n // asymmetry in ADR-0004 for what that costs and why it is not guessed at.\n const titleId = ownsFile && !candidates.some((c) => c.number !== null) ? headingId(input.heading) : null;\n consider(titleId, false);\n\n const numbered = candidates.find((c) => c.number !== null);\n const family = numbered?.family ?? directoryFamily;\n const number = numbered?.number ?? null;\n\n // Witnesses to the local zero-padding convention, most authoritative first.\n // The spelling that actually produced the number comes first: an id read from\n // the H1 of `sharding.md` must still render as ADR-0007, not ADR-7.\n const witnesses = [numbered?.label, declaredId, stem];\n\n let id: string;\n if (number !== null && family !== null) {\n id = `${family}-${padNumber(number, witnesses)}`;\n } else if (declaredId) {\n id = declaredId.trim();\n } else if (number !== null) {\n // Numbered but with no family to prefix - a Rust RFC in `text/0001-foo.md`,\n // say. The file stem is a far better identity than a bare \"1\": it is what\n // people actually type, and it stays unique across the repository.\n id = stem.length > 0 ? stem : String(number);\n } else {\n id = stem.length > 0 ? stem : input.path;\n }\n\n add(id);\n if (ownsFile) {\n add(stem);\n add(input.path);\n add(stripExtension(input.path));\n }\n add(declaredId);\n for (const alias of input.declaredAliases) add(alias);\n add(titleId);\n\n if (number !== null) {\n const spellings = numberSpellings(number, witnesses);\n if (family !== null) {\n // Every spelling a human might type. `normaliseRef` folds separators, so\n // `adr-7`, `adr 7` and `adr7` collapse to one key; the padding variants do\n // not, and each needs registering.\n for (const digits of spellings) add(`${family}-${digits}`);\n } else {\n // With no family there is only one number space in the repository, so a\n // bare `0001` is unambiguous and worth resolving.\n for (const digits of spellings) add(digits);\n }\n }\n\n return { id, family, number, aliases: [...aliases] };\n}\n\n/**\n * Folds a reference to its lookup key.\n *\n * Case, separators and surrounding punctuation carry no meaning in a citation,\n * so `ADR-0007`, `adr 0007` and `Adr_0007` all become `adr0007`.\n */\nexport function normaliseRef(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/^[#<([]+|[)\\]>.,;:]+$/g, '')\n .replace(/[\\s._-]+/g, '')\n .trim();\n}\n\n/** Splits a `target#anchor` reference. */\nexport function splitAnchor(target: string): { target: string; anchor: string | null } {\n const hash = target.indexOf('#');\n if (hash === -1) return { target, anchor: null };\n // A leading `#` is an in-document anchor, not a separator.\n if (hash === 0) return { target: '', anchor: target.slice(1) };\n return { target: target.slice(0, hash), anchor: target.slice(hash + 1) };\n}\n\n/** Parses `ADR-0007` into its family and number, or returns `null`. */\nexport function parsePrefixedRef(value: string): { family: string; number: number } | null {\n const match = PREFIXED_ID.exec(value.trim().replace(/^[#([]+/, ''));\n if (!match) return null;\n return { family: (match[1] as string).toUpperCase(), number: Number.parseInt(match[2] as string, 10) };\n}\n\n/** Parses a bare `7` or `#0007`, or returns `null`. */\nexport function parseBareRef(value: string): number | null {\n const match = BARE_NUMBER.exec(value.trim());\n return match ? Number.parseInt(match[1] as string, 10) : null;\n}\n\n/** True when a reference target names a location rather than an identifier. */\nexport function looksLikePath(target: string): boolean {\n return target.includes('/') || DOCUMENT_EXTENSION.test(target);\n}\n\nconst DOCUMENT_EXTENSION = /\\.(md|markdown|mdx|txt|rst|adoc|asciidoc)$/i;\n\n/**\n * True when a path-like target could name a specification.\n *\n * A specification linking to `../../src/rules.ts` is citing source code, which\n * is a different tool's job. Treating it as a dangling specification reference\n * would report an error on one of the most ordinary things a design document\n * does, so targets carrying a non-document extension are not references at all.\n */\nexport function isDocumentTarget(target: string): boolean {\n const withoutAnchor = splitAnchor(target).target;\n const base = withoutAnchor.slice(withoutAnchor.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n // No extension: a directory, or an extensionless spec such as `rfcs/0007`.\n if (dot <= 0) return true;\n const extension = base.slice(dot);\n // Only a plausible file extension disqualifies a target. An identifier like\n // `v1.2` or `ADR-0007.1` keeps its dot and is still resolved as an id.\n if (!/^\\.[A-Za-z][A-Za-z0-9]{0,5}$/.test(extension)) return true;\n return DOCUMENT_EXTENSION.test(base);\n}\n\n/** True when a reference target points outside the repository. */\nexport function isExternal(target: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(target) && !/^[a-z]:[\\\\/]/i.test(target);\n}\n\n/** The family prefix implied by a document's directory, if any. */\nexport function familyFromPath(posixPath: string): string | null {\n const segments = posixPath.toLowerCase().split('/');\n // Nearest enclosing directory wins: `docs/rfcs/adr/0007.md` is an ADR.\n for (let i = segments.length - 2; i >= 0; i -= 1) {\n const found = FAMILY_DIRECTORIES[segments[i] as string];\n if (found) return found;\n }\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Internals */\n/* -------------------------------------------------------------------------- */\n\nfunction fileStem(posixPath: string): string {\n const base = posixPath.slice(posixPath.lastIndexOf('/') + 1);\n const dot = base.lastIndexOf('.');\n const stem = dot > 0 ? base.slice(0, dot) : base;\n // `docs/adr/0007-sharding/README.md` is identified by its directory - but a\n // README at the repository root has no directory to be named after, and must\n // keep its own name.\n const slash = posixPath.lastIndexOf('/');\n if (slash > 0 && /^(readme|index)$/i.test(stem)) {\n const parent = posixPath.slice(0, slash);\n return parent.slice(parent.lastIndexOf('/') + 1);\n }\n return stem;\n}\n\nfunction stripExtension(posixPath: string): string {\n const dot = posixPath.lastIndexOf('.');\n const slash = posixPath.lastIndexOf('/');\n return dot > slash ? posixPath.slice(0, dot) : posixPath;\n}\n\n/** `kep-1234-foo` -> `kep-1234`; `0007-sharding` -> `0007`; `sharding` -> `sharding`. */\nfunction leadingToken(stem: string): string {\n const prefixed = /^([A-Za-z]{1,15}[\\s._-]?\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (prefixed) return prefixed[1] as string;\n const bare = /^(\\d{1,6})(?:[\\s._-]|$)/.exec(stem);\n if (bare) return bare[1] as string;\n return stem;\n}\n\n/** `# ADR-0007: Sharding the write path` -> `ADR-0007`. */\nfunction headingId(heading: string | null): string | null {\n if (!heading) return null;\n const match = /^\\s*([A-Za-z]{1,15}[\\s._-]?\\d{1,6})\\b/.exec(heading);\n return match ? (match[1] as string) : null;\n}\n\n/**\n * Chooses the digit width of the canonical id.\n *\n * A repository that writes `0007` everywhere should see `ADR-0007` in reports,\n * not `ADR-7`. The file name is the most reliable witness of the local\n * convention, so it decides.\n */\nfunction padNumber(number: number, witnesses: readonly (string | null | undefined)[]): string {\n const width = paddingWidth(number, witnesses) ?? String(number).length;\n return String(number).padStart(width, '0');\n}\n\n/** The digit width of the first witness that actually spells this number. */\nfunction paddingWidth(number: number, witnesses: readonly (string | null | undefined)[]): number | null {\n for (const witness of witnesses) {\n if (!witness) continue;\n const digits = /(\\d{1,6})/.exec(witness)?.[1];\n if (digits && Number.parseInt(digits, 10) === number) return digits.length;\n }\n return null;\n}\n\n/** Zero-padded spellings a citation might reasonably use. */\nfunction numberSpellings(number: number, witnesses: readonly (string | null | undefined)[]): string[] {\n const out = new Set<string>([String(number)]);\n const observed = paddingWidth(number, witnesses);\n if (observed !== null) out.add(String(number).padStart(observed, '0'));\n for (const width of [3, 4]) out.add(String(number).padStart(width, '0'));\n return [...out];\n}\n\n/**\n * True when two folded spellings are one edit apart.\n *\n * One edit, not two, and a transposition counts as one: `supercedesby` for\n * `supercededby` is a slip of the finger, `categories` for `dependencies` is a\n * different word. Written out rather than as a distance matrix because the\n * answer is a yes or a no and the bound is one - the general algorithm would be\n * more code, not less.\n */\nexport function withinOneEdit(a: string, b: string): boolean {\n if (a === b) return false;\n if (a.length === b.length) {\n let first = -1;\n for (let i = 0; i < a.length; i += 1) {\n if (a[i] === b[i]) continue;\n if (first === -1) {\n first = i;\n continue;\n }\n // A second difference is allowed only if the two are a swapped pair, and\n // only if there is no third.\n return first === i - 1 && a[first] === b[i] && a[i] === b[first] && a.slice(i + 1) === b.slice(i + 1);\n }\n return true;\n }\n const [short, long] = a.length < b.length ? [a, b] : [b, a];\n if (long.length - short.length !== 1) return false;\n for (let i = 0; i < short.length; i += 1) {\n if (short[i] === long[i]) continue;\n return short.slice(i) === long.slice(i + 1);\n }\n return true;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,14 +32,14 @@ export type { AnalyseOptions, AnalyseSourcesOptions, AnalysisResult, Source } fr
|
|
|
32
32
|
export { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';
|
|
33
33
|
export type { SpecGraph } from './graph.js';
|
|
34
34
|
export { attributesOf, execute, matches, parseQuery, pattern, query, renderMatch, QueryError, DEFAULT_MATCH_LIMIT, SELECTOR_KEYS, } from './select.js';
|
|
35
|
-
export { compilePattern, PatternError } from './
|
|
36
|
-
export type { Matcher, PatternOptions } from './
|
|
35
|
+
export { compileRegex as compilePattern, RegexError as PatternError } from './vendor/spec-core/pattern/index.js';
|
|
36
|
+
export type { RegexMatcher as Matcher, RegexOptions as PatternOptions } from './vendor/spec-core/pattern/index.js';
|
|
37
37
|
export type { ExecuteOptions, Match, NodeMatcher, Operator, Predicate, QuerySpec, StepSpec, } from './select.js';
|
|
38
38
|
export { resolveStrict, runRules, sortDiagnostics, DEFAULT_SEVERITIES, RULE_DECISIONS, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
|
|
39
39
|
export type { RuleOptions } from './rules.js';
|
|
40
40
|
export { compileProjectRules, renderTemplate } from './project-rules.js';
|
|
41
41
|
export type { CompiledProjectRules, ProjectRule } from './project-rules.js';
|
|
42
|
-
export { createPainter, formatGraph, formatJson, formatMarkdown, formatReport, formatSarif, shouldUseAscii, shouldUseColor, } from './report.js';
|
|
42
|
+
export { createPainter, formatGitlab, formatGraph, formatJson, formatMarkdown, formatReport, formatSarif, shouldUseAscii, shouldUseColor, } from './report.js';
|
|
43
43
|
export type { ColorEnvironment, GraphExportOptions, GraphFormat, Painter, ReporterOptions } from './report.js';
|
|
44
44
|
export { extractDocument, extractSpecifications, classifyReference, inheritableFrontMatter, sectionPathAt, } from './extract.js';
|
|
45
45
|
export { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';
|
package/dist/index.js
CHANGED
|
@@ -27,10 +27,12 @@ export { main, parseArgs, HELP, UsageError, EXIT_OK, EXIT_FAILED, EXIT_ERROR } f
|
|
|
27
27
|
export { analyse, analyseSources, createFamilyFilter, createHistoryMatcher, withDiagnostics, DEFAULT_CONCURRENCY, DEFAULT_PATTERNS, } from './runner.js';
|
|
28
28
|
export { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';
|
|
29
29
|
export { attributesOf, execute, matches, parseQuery, pattern, query, renderMatch, QueryError, DEFAULT_MATCH_LIMIT, SELECTOR_KEYS, } from './select.js';
|
|
30
|
-
|
|
30
|
+
// The matcher behind `~=` lives in spec-core now (ADR-0017), and keeps the
|
|
31
|
+
// names it was published under here.
|
|
32
|
+
export { compileRegex as compilePattern, RegexError as PatternError } from './vendor/spec-core/pattern/index.js';
|
|
31
33
|
export { resolveStrict, runRules, sortDiagnostics, DEFAULT_SEVERITIES, RULE_DECISIONS, RULE_DESCRIPTIONS, RULE_IDS, RULE_QUERIES, } from './rules.js';
|
|
32
34
|
export { compileProjectRules, renderTemplate } from './project-rules.js';
|
|
33
|
-
export { createPainter, formatGraph, formatJson, formatMarkdown, formatReport, formatSarif, shouldUseAscii, shouldUseColor, } from './report.js';
|
|
35
|
+
export { createPainter, formatGitlab, formatGraph, formatJson, formatMarkdown, formatReport, formatSarif, shouldUseAscii, shouldUseColor, } from './report.js';
|
|
34
36
|
export { extractDocument, extractSpecifications, classifyReference, inheritableFrontMatter, sectionPathAt, } from './extract.js';
|
|
35
37
|
export { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';
|
|
36
38
|
export { resolveCorpus, documentOf } from './resolve.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGxG,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG/F,OAAO,EACL,OAAO,EACP,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG9E,OAAO,EACL,YAAY,EACZ,OAAO,EACP,OAAO,EACP,UAAU,EACV,OAAO,EACP,KAAK,EACL,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Programmatic API.\n *\n * The CLI is a shell over `analyse`; anything the CLI can do, a build script, a\n * custom reporter or an editor extension can do too.\n *\n * ```ts\n * import { analyse, formatReport } from '@descent-vtt/spec-graph';\n *\n * const result = await analyse({ root: process.cwd(), patterns: ['docs/**\\/*.md'] });\n * if (!result.ok) console.error(formatReport(result, { color: true }));\n * ```\n *\n * For a corpus that is already in memory - a monorepo tool, a language server,\n * a test - skip the filesystem entirely:\n *\n * ```ts\n * import { analyseSources, query } from '@descent-vtt/spec-graph';\n *\n * const { graph } = analyseSources([{ path: 'adr/0001.md', text: '...' }]);\n * const ghosts = query(graph, 'item[openness=open] -delegates-to-> document[phase=retired]');\n * ```\n */\n\nexport {\n applyBaseline,\n formatBaseline,\n fingerprintOf,\n parseBaseline,\n BASELINE_VERSION,\n EMPTY_BASELINE,\n} from './baseline.js';\nexport type { Baseline, BaselineEntry, BaselineOutcome, ParsedBaseline, StaleEntry } from './baseline.js';\n\nexport { discoverConfig, loadConfig, parseConfig, CONFIG_FILES, CONFIG_PACKAGE_KEY } from './config.js';\nexport type { DiscoveredConfig, DiscoveryIO, LoadedConfig, SpecGraphConfig } from './config.js';\n\nexport { main, parseArgs, HELP, UsageError, EXIT_OK, EXIT_FAILED, EXIT_ERROR } from './cli.js';\nexport type { CliIO, CliOptions, Command } from './cli.js';\n\nexport {\n analyse,\n analyseSources,\n createFamilyFilter,\n createHistoryMatcher,\n withDiagnostics,\n DEFAULT_CONCURRENCY,\n DEFAULT_PATTERNS,\n} from './runner.js';\nexport type { AnalyseOptions, AnalyseSourcesOptions, AnalysisResult, Source } from './runner.js';\n\nexport { buildGraph, LOAD_BEARING_EDGES, OBLIGATION_EDGES } from './graph.js';\nexport type { SpecGraph } from './graph.js';\n\nexport {\n attributesOf,\n execute,\n matches,\n parseQuery,\n pattern,\n query,\n renderMatch,\n QueryError,\n DEFAULT_MATCH_LIMIT,\n SELECTOR_KEYS,\n} from './select.js';\n\n// The matcher behind `~=` lives in spec-core now (ADR-0017), and keeps the\n// names it was published under here.\nexport { compileRegex as compilePattern, RegexError as PatternError } from './vendor/spec-core/pattern/index.js';\nexport type { RegexMatcher as Matcher, RegexOptions as PatternOptions } from './vendor/spec-core/pattern/index.js';\nexport type {\n ExecuteOptions,\n Match,\n NodeMatcher,\n Operator,\n Predicate,\n QuerySpec,\n StepSpec,\n} from './select.js';\n\nexport {\n resolveStrict,\n runRules,\n sortDiagnostics,\n DEFAULT_SEVERITIES,\n RULE_DECISIONS,\n RULE_DESCRIPTIONS,\n RULE_IDS,\n RULE_QUERIES,\n} from './rules.js';\nexport type { RuleOptions } from './rules.js';\n\nexport { compileProjectRules, renderTemplate } from './project-rules.js';\nexport type { CompiledProjectRules, ProjectRule } from './project-rules.js';\n\nexport {\n createPainter,\n formatGitlab,\n formatGraph,\n formatJson,\n formatMarkdown,\n formatReport,\n formatSarif,\n shouldUseAscii,\n shouldUseColor,\n} from './report.js';\nexport type { ColorEnvironment, GraphExportOptions, GraphFormat, Painter, ReporterOptions } from './report.js';\n\nexport {\n extractDocument,\n extractSpecifications,\n classifyReference,\n inheritableFrontMatter,\n sectionPathAt,\n} from './extract.js';\nexport { findSpecificationRegions, readRegionStatus, regionAt } from './sections.js';\nexport type { RegionStatus, SpecificationRegion } from './sections.js';\nexport type { Classification, ExtractedDocument, ExtractInput, ReferenceCandidate } from './extract.js';\n\nexport { resolveCorpus, documentOf } from './resolve.js';\nexport type { ResolvedCorpus, ResolveOptions } from './resolve.js';\n\nexport { scanMarkdown, slugify } from './markdown.js';\nexport type {\n FrontMatter,\n Heading,\n HtmlComment,\n Link,\n LinkForm,\n ListItem,\n Range,\n ScannedDocument,\n ScannedLine,\n} from './markdown.js';\n\nexport { parseDirectives, attr, attrList, bindItemDirectives, directiveFor } from './directives.js';\nexport type { Attribute, Directive, DirectiveName } from './directives.js';\n\nexport { parseFrontMatter, toRecord, valuesOf } from './yaml.js';\nexport type { YamlEntry } from './yaml.js';\n\nexport {\n isRetired,\n isStatusHeading,\n normaliseStatus,\n phaseFromPath,\n phaseOf,\n receptivityOf,\n supersessionTargetsIn,\n STATUS_KEYS,\n} from './lifecycle.js';\n\nexport { resolveItemState, KNOWN_STATE_WORDS } from './state.js';\nexport type { ResolvedState, StateInput } from './state.js';\n\nexport {\n familyFromPath,\n identify,\n isDocumentTarget,\n isExternal,\n looksLikePath,\n normaliseRef,\n parseBareRef,\n parsePrefixedRef,\n splitAnchor,\n ID_KEYS,\n} from './identity.js';\nexport type { DocumentIdentity, IdentityInput } from './identity.js';\n\nexport {\n compileGlob,\n createGlobMatcher,\n createReferenceFilter,\n globBase,\n globToRegExp,\n isGlob,\n walkFiles,\n DEFAULT_IGNORED_DIRECTORIES,\n MAX_FILE_SIZE,\n} from './glob.js';\nexport type { GlobMatcher, GlobOptions, WalkedFile, WalkOptions } from './glob.js';\n\nexport { compareRefs, createLineIndex, formatRef, refOf, spanOf } from './source.js';\nexport type { LineIndex } from './source.js';\n\nexport {\n basenamePosix,\n dirnamePosix,\n extnamePosix,\n isAbsolutePath,\n joinPosix,\n normalisePosix,\n resolveFrom,\n toPosix,\n} from './paths.js';\n\nexport { isProjectRule, DISPOSITIONS, EDGE_KINDS, EDGE_TRAITS, OPENNESS_OF, PROJECT_RULE_PREFIX } from './types.js';\nexport type {\n AnalysisSummary,\n AnyRuleId,\n DanglingReason,\n DanglingRef,\n Diagnostic,\n Disposition,\n DocumentNode,\n Edge,\n EdgeKind,\n EdgeOrigin,\n EdgeTraits,\n ItemNode,\n MisreadKey,\n NodeKind,\n Openness,\n ParseProblem,\n Phase,\n Position,\n Receptivity,\n ProjectRuleId,\n RelatedLocation,\n RuleId,\n Severity,\n SourceRef,\n Span,\n SpecNode,\n StateSignal,\n SuppressedRef,\n} from './types.js';\n"]}
|
package/dist/lifecycle.js
CHANGED
|
@@ -34,8 +34,6 @@ const VOCABULARY = [
|
|
|
34
34
|
'obsolete',
|
|
35
35
|
'obsoleted',
|
|
36
36
|
'retired',
|
|
37
|
-
'archived',
|
|
38
|
-
'archive',
|
|
39
37
|
'rejected',
|
|
40
38
|
'declined',
|
|
41
39
|
'withdrawn',
|
|
@@ -58,6 +56,16 @@ const VOCABULARY = [
|
|
|
58
56
|
'wontfix',
|
|
59
57
|
],
|
|
60
58
|
],
|
|
59
|
+
[
|
|
60
|
+
// A finished round of work kept as it was left: spec-brief archives a brief
|
|
61
|
+
// by writing this word and moving the file. Every spec-* tool reads it as a
|
|
62
|
+
// record rather than as a retired decision - depending on one is normal,
|
|
63
|
+
// its unticked boxes were never going to be ticked, and its links still
|
|
64
|
+
// have to go somewhere (ADR-0011). After retirement, so a brief written
|
|
65
|
+
// `archived, superseded by B-0007` is still retired.
|
|
66
|
+
'record',
|
|
67
|
+
['archived', 'archive'],
|
|
68
|
+
],
|
|
61
69
|
[
|
|
62
70
|
'frozen',
|
|
63
71
|
['final', 'finalised', 'finalized', 'frozen', 'locked', 'ratified', 'immutable', 'sealed', 'standard', 'published'],
|
package/dist/lifecycle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH;;;;;;GAMG;AACH,MAAM,UAAU,GAAqD;IACnE;QACE,SAAS;QACT;YACE,YAAY;YACZ,sEAAsE;YACtE,yCAAyC;YACzC,YAAY;YACZ,eAAe;YACf,UAAU;YACV,aAAa;YACb,YAAY;YACZ,UAAU;YACV,WAAW;YACX,SAAS;YACT,UAAU;YACV,SAAS;YACT,UAAU;YACV,UAAU;YACV,WAAW;YACX,WAAW;YACX,WAAW;YACX,UAAU;YACV,SAAS;YACT,SAAS;YACT,UAAU;YACV,OAAO;YACP,YAAY;YACZ,SAAS;YACT,UAAU;YACV,MAAM;YACN,MAAM;YACN,QAAQ;YACR,WAAW;YACX,UAAU;YACV,aAAa;YACb,SAAS;SACV;KACF;IACD;QACE,QAAQ;QACR,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC;KACpH;IACD;QACE,QAAQ;QACR;YACE,UAAU;YACV,QAAQ;YACR,UAAU;YACV,SAAS;YACT,QAAQ;YACR,eAAe;YACf,aAAa;YACb,cAAc;YACd,SAAS;YACT,WAAW;YACX,WAAW;YACX,UAAU;YACV,QAAQ;YACR,QAAQ;YACR,UAAU;YACV,SAAS;YACT,MAAM;YACN,MAAM;YACN,UAAU;YACV,WAAW;YACX,YAAY;YACZ,WAAW;SACZ;KACF;IACD;QACE,OAAO;QACP;YACE,OAAO;YACP,UAAU;YACV,UAAU;YACV,UAAU;YACV,aAAa;YACb,aAAa;YACb,KAAK;YACL,kBAAkB;YAClB,aAAa;YACb,QAAQ;YACR,WAAW;YACX,cAAc;YACd,WAAW;YACX,YAAY;YACZ,YAAY;YACZ,SAAS;YACT,MAAM;YACN,aAAa;YACb,WAAW;YACX,MAAM;YACN,KAAK;YACL,YAAY;YACZ,KAAK;YACL,cAAc;YACd,OAAO;YACP,MAAM;YACN,YAAY;YACZ,wBAAwB;SACzB;KACF;CACF,CAAC;AAEF,6DAA6D;AAC7D,MAAM,oBAAoB,GAAwB,IAAI,GAAG,CAAC;IACxD,SAAS;IACT,UAAU;IACV,OAAO;IACP,YAAY;IACZ,WAAW;IACX,YAAY;IACZ,UAAU;IACV,UAAU;IACV,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,WAAW;CACZ,CAAC,CAAC;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,WAAW,GAAsB,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAE/G,6DAA6D;AAC7D,MAAM,eAAe,GAAwB,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAElH;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,GAAG;SACP,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC;SACzB,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC;SACvC,OAAO,CAAC,6BAA6B,EAAE,GAAG,CAAC;SAC3C,OAAO,CAAC,oBAAoB,EAAE,GAAG,CAAC;SAClC,WAAW,EAAE;SACb,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC;SAChC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,OAAO,CAAC,GAA8B;IACpD,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAE3C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,UAAU,EAAE,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,OAAO,KAAK,CAAC;YAC9C,CAAC;iBAAM,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3B,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,KAAY;IACxC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ;YACX,OAAO,WAAW,CAAC;QACrB,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS,CAAC;QACf,yEAAyE;QACzE,0EAA0E;QAC1E,KAAK,QAAQ;YACX,OAAO,QAAQ,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,SAAS,CAAC,KAAY;IACpC,OAAO,KAAK,KAAK,SAAS,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;AACtF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,MAAM,QAAQ,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpD,6EAA6E;IAC7E,eAAe;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAW,CAAC;YAAE,OAAO,SAAS,CAAC;IACxE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,MAAM,OAAO,GAAG,gFAAgF,CAAC;IACjG,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC;IACvB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAW,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;QAC/C,MAAM,OAAO,GAAG,KAAK;aAClB,OAAO,CAAC,wBAAwB,EAAE,IAAI,CAAC;aACvC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC;aACzB,IAAI,EAAE,CAAC;QACV,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG;YAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["/**\n * Status vocabulary normalisation.\n *\n * Every specification ecosystem invented its own status words, and no two agree.\n * MADR says `accepted`; Kubernetes KEPs say `implementable`; Rust RFCs say\n * `merged`; a hundred internal wikis say `Live`, `Signed off`, or `DONE ✅`.\n *\n * spec-graph refuses to care. What it needs from a status is one bit - *can this\n * document still absorb an obligation?* - and that bit is recoverable from all\n * of those vocabularies at once. The tables below are the only place in the\n * codebase that knows any ecosystem-specific word, which is what keeps the\n * engine domain-agnostic: supporting a new convention is a table entry, never a\n * rule change.\n */\n\nimport type { Phase, Receptivity } from './types.js';\n\n/**\n * Recognised status words, most terminal first.\n *\n * Order matters. A status of \"accepted, superseded by ADR-0009\" is retired, not\n * active: retirement is terminal, so a retirement word anywhere in the string\n * wins over anything else in it.\n */\nconst VOCABULARY: readonly (readonly [Phase, readonly string[]])[] = [\n [\n 'retired',\n [\n 'superseded',\n // Misspelled in the wild often enough that omitting it would silently\n // mis-classify real documents as active.\n 'superceded',\n 'supersedes-by',\n 'replaced',\n 'replaced-by',\n 'deprecated',\n 'obsolete',\n 'obsoleted',\n 'retired',\n 'archived',\n 'archive',\n 'rejected',\n 'declined',\n 'withdrawn',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'dropped',\n 'revoked',\n 'reverted',\n 'moved',\n 'historical',\n 'defunct',\n 'inactive',\n 'dead',\n 'void',\n 'closed',\n 'postponed',\n 'deferred',\n 'not-planned',\n 'wontfix',\n ],\n ],\n [\n 'frozen',\n ['final', 'finalised', 'finalized', 'frozen', 'locked', 'ratified', 'immutable', 'sealed', 'standard', 'published'],\n ],\n [\n 'active',\n [\n 'accepted',\n 'active',\n 'approved',\n 'adopted',\n 'agreed',\n 'implementable',\n 'implemented',\n 'implementing',\n 'current',\n 'effective',\n 'in-effect',\n 'enforced',\n 'stable',\n 'merged',\n 'released',\n 'shipped',\n 'live',\n 'done',\n 'complete',\n 'completed',\n 'signed-off',\n 'committed',\n ],\n ],\n [\n 'draft',\n [\n 'draft',\n 'drafting',\n 'proposed',\n 'proposal',\n 'provisional',\n 'prospective',\n 'wip',\n 'work-in-progress',\n 'in-progress',\n 'review',\n 'in-review',\n 'under-review',\n 'reviewing',\n 'discussion',\n 'discussing',\n 'pending',\n 'idea',\n 'exploratory',\n 'candidate',\n 'open',\n 'new',\n 'unreviewed',\n 'rfc',\n 'experimental',\n 'alpha',\n 'beta',\n 'incubating',\n 'provisionally-accepted',\n ],\n ],\n];\n\n/** Path segments that retire every document beneath them. */\nconst RETIRING_DIRECTORIES: ReadonlySet<string> = new Set([\n 'archive',\n 'archived',\n 'attic',\n 'deprecated',\n 'graveyard',\n 'historical',\n 'obsolete',\n 'rejected',\n 'retired',\n 'superseded',\n 'superceded',\n 'withdrawn',\n]);\n\n/** Front-matter keys that may carry a status, in order of preference. */\nexport const STATUS_KEYS: readonly string[] = ['status', 'state', 'stage', 'lifecycle', 'phase', 'adr-status'];\n\n/** Heading names whose body declares the document status. */\nconst STATUS_HEADINGS: ReadonlySet<string> = new Set(['status', 'state', 'stage', 'lifecycle', 'current status']);\n\n/**\n * Reduces a status string to lookup form.\n *\n * Strips Markdown emphasis, badge syntax, emoji and dates, so `**Accepted**`,\n * `` `accepted` ``, `Accepted ✅` and `Accepted (2026-03-01)` all land on the\n * same token.\n */\nexport function normaliseStatus(raw: string): string {\n return raw\n .replace(/<[^>]*>/g, ' ')\n .replace(/[`*_~\"'#]/g, '')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\p{Extended_Pictographic}/gu, ' ')\n .replace(/\\d{4}-\\d{2}-\\d{2}/g, ' ')\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n}\n\n/**\n * Maps a raw status string onto a lifecycle phase.\n *\n * Returns `unknown` rather than guessing when nothing matches: a document with\n * an unrecognised status is not evidence of anything, and inventing a phase for\n * it would produce exactly the confident-but-wrong findings this tool exists to\n * eliminate.\n */\nexport function phaseOf(raw: string | null | undefined): Phase {\n if (raw === null || raw === undefined) return 'unknown';\n const text = normaliseStatus(raw);\n if (text.length === 0) return 'unknown';\n const words = new Set(text.split(' '));\n const hyphenated = text.replace(/ /g, '-');\n\n for (const [phase, terms] of VOCABULARY) {\n for (const term of terms) {\n if (term.includes('-')) {\n if (hyphenated.includes(term)) return phase;\n } else if (words.has(term)) {\n return phase;\n }\n }\n }\n return 'unknown';\n}\n\n/** Whether a document in this phase can take on a new open obligation. */\nexport function receptivityOf(phase: Phase): Receptivity {\n switch (phase) {\n case 'draft':\n case 'active':\n return 'receptive';\n case 'frozen':\n case 'retired':\n // A record was never asking for an obligation, so it cannot take one on.\n // Sealed for the same reason a retired document is, by a different route.\n case 'record':\n return 'sealed';\n case 'unknown':\n return 'unknown';\n }\n}\n\n/** True when the phase means the document no longer binds anybody. */\nexport function isRetired(phase: Phase): boolean {\n return phase === 'retired';\n}\n\n/** True when the heading names a section that declares the document status. */\nexport function isStatusHeading(text: string): boolean {\n return STATUS_HEADINGS.has(text.trim().toLowerCase().replace(/[:*_`]/g, '').trim());\n}\n\n/**\n * Infers retirement from where a file lives.\n *\n * Moving an ADR into `docs/adr/archive/` is the most common way a team retires a\n * decision without touching its front matter, and a graph that misses it will\n * happily let obligations be delegated into the archive.\n */\nexport function phaseFromPath(posixPath: string): Phase {\n const segments = posixPath.toLowerCase().split('/');\n // The file name itself is not a directory; a document called `archive.md` is\n // not retired.\n for (let i = 0; i < segments.length - 1; i += 1) {\n if (RETIRING_DIRECTORIES.has(segments[i] as string)) return 'retired';\n }\n return 'unknown';\n}\n\n/**\n * Pulls a supersession target out of a status string.\n *\n * `Superseded by ADR-0009` is the single most common way a supersession is ever\n * recorded, and it is written in the status field rather than as a link. Reading\n * it here means the relation reaches the graph even in repositories that never\n * adopted an explicit field for it.\n */\nexport function supersessionTargetsIn(raw: string): string[] {\n const out: string[] = [];\n const pattern = /(?:superse[dc]ed|replaced|obsoleted)\\s*(?:by|with|through)?\\s*[:\\-—]?\\s*(.+)$/i;\n const match = pattern.exec(raw.replace(/[`*_]/g, ''));\n if (!match) return out;\n const tail = match[1] as string;\n for (const token of tail.split(/[,;]|\\band\\b/)) {\n const cleaned = token\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/[()<>\"']/g, ' ')\n .trim();\n if (cleaned.length > 0 && cleaned.length < 200) out.push(cleaned);\n }\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../src/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Status vocabulary normalisation.\n *\n * Every specification ecosystem invented its own status words, and no two agree.\n * MADR says `accepted`; Kubernetes KEPs say `implementable`; Rust RFCs say\n * `merged`; a hundred internal wikis say `Live`, `Signed off`, or `DONE ✅`.\n *\n * spec-graph refuses to care. What it needs from a status is one bit - *can this\n * document still absorb an obligation?* - and that bit is recoverable from all\n * of those vocabularies at once. The tables below are the only place in the\n * codebase that knows any ecosystem-specific word, which is what keeps the\n * engine domain-agnostic: supporting a new convention is a table entry, never a\n * rule change.\n */\n\nimport type { Phase, Receptivity } from './types.js';\n\n/**\n * Recognised status words, most terminal first.\n *\n * Order matters. A status of \"accepted, superseded by ADR-0009\" is retired, not\n * active: retirement is terminal, so a retirement word anywhere in the string\n * wins over anything else in it.\n */\nconst VOCABULARY: readonly (readonly [Phase, readonly string[]])[] = [\n [\n 'retired',\n [\n 'superseded',\n // Misspelled in the wild often enough that omitting it would silently\n // mis-classify real documents as active.\n 'superceded',\n 'supersedes-by',\n 'replaced',\n 'replaced-by',\n 'deprecated',\n 'obsolete',\n 'obsoleted',\n 'retired',\n 'rejected',\n 'declined',\n 'withdrawn',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'dropped',\n 'revoked',\n 'reverted',\n 'moved',\n 'historical',\n 'defunct',\n 'inactive',\n 'dead',\n 'void',\n 'closed',\n 'postponed',\n 'deferred',\n 'not-planned',\n 'wontfix',\n ],\n ],\n [\n // A finished round of work kept as it was left: spec-brief archives a brief\n // by writing this word and moving the file. Every spec-* tool reads it as a\n // record rather than as a retired decision - depending on one is normal,\n // its unticked boxes were never going to be ticked, and its links still\n // have to go somewhere (ADR-0011). After retirement, so a brief written\n // `archived, superseded by B-0007` is still retired.\n 'record',\n ['archived', 'archive'],\n ],\n [\n 'frozen',\n ['final', 'finalised', 'finalized', 'frozen', 'locked', 'ratified', 'immutable', 'sealed', 'standard', 'published'],\n ],\n [\n 'active',\n [\n 'accepted',\n 'active',\n 'approved',\n 'adopted',\n 'agreed',\n 'implementable',\n 'implemented',\n 'implementing',\n 'current',\n 'effective',\n 'in-effect',\n 'enforced',\n 'stable',\n 'merged',\n 'released',\n 'shipped',\n 'live',\n 'done',\n 'complete',\n 'completed',\n 'signed-off',\n 'committed',\n ],\n ],\n [\n 'draft',\n [\n 'draft',\n 'drafting',\n 'proposed',\n 'proposal',\n 'provisional',\n 'prospective',\n 'wip',\n 'work-in-progress',\n 'in-progress',\n 'review',\n 'in-review',\n 'under-review',\n 'reviewing',\n 'discussion',\n 'discussing',\n 'pending',\n 'idea',\n 'exploratory',\n 'candidate',\n 'open',\n 'new',\n 'unreviewed',\n 'rfc',\n 'experimental',\n 'alpha',\n 'beta',\n 'incubating',\n 'provisionally-accepted',\n ],\n ],\n];\n\n/** Path segments that retire every document beneath them. */\nconst RETIRING_DIRECTORIES: ReadonlySet<string> = new Set([\n 'archive',\n 'archived',\n 'attic',\n 'deprecated',\n 'graveyard',\n 'historical',\n 'obsolete',\n 'rejected',\n 'retired',\n 'superseded',\n 'superceded',\n 'withdrawn',\n]);\n\n/** Front-matter keys that may carry a status, in order of preference. */\nexport const STATUS_KEYS: readonly string[] = ['status', 'state', 'stage', 'lifecycle', 'phase', 'adr-status'];\n\n/** Heading names whose body declares the document status. */\nconst STATUS_HEADINGS: ReadonlySet<string> = new Set(['status', 'state', 'stage', 'lifecycle', 'current status']);\n\n/**\n * Reduces a status string to lookup form.\n *\n * Strips Markdown emphasis, badge syntax, emoji and dates, so `**Accepted**`,\n * `` `accepted` ``, `Accepted ✅` and `Accepted (2026-03-01)` all land on the\n * same token.\n */\nexport function normaliseStatus(raw: string): string {\n return raw\n .replace(/<[^>]*>/g, ' ')\n .replace(/[`*_~\"'#]/g, '')\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\p{Extended_Pictographic}/gu, ' ')\n .replace(/\\d{4}-\\d{2}-\\d{2}/g, ' ')\n .toLowerCase()\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim();\n}\n\n/**\n * Maps a raw status string onto a lifecycle phase.\n *\n * Returns `unknown` rather than guessing when nothing matches: a document with\n * an unrecognised status is not evidence of anything, and inventing a phase for\n * it would produce exactly the confident-but-wrong findings this tool exists to\n * eliminate.\n */\nexport function phaseOf(raw: string | null | undefined): Phase {\n if (raw === null || raw === undefined) return 'unknown';\n const text = normaliseStatus(raw);\n if (text.length === 0) return 'unknown';\n const words = new Set(text.split(' '));\n const hyphenated = text.replace(/ /g, '-');\n\n for (const [phase, terms] of VOCABULARY) {\n for (const term of terms) {\n if (term.includes('-')) {\n if (hyphenated.includes(term)) return phase;\n } else if (words.has(term)) {\n return phase;\n }\n }\n }\n return 'unknown';\n}\n\n/** Whether a document in this phase can take on a new open obligation. */\nexport function receptivityOf(phase: Phase): Receptivity {\n switch (phase) {\n case 'draft':\n case 'active':\n return 'receptive';\n case 'frozen':\n case 'retired':\n // A record was never asking for an obligation, so it cannot take one on.\n // Sealed for the same reason a retired document is, by a different route.\n case 'record':\n return 'sealed';\n case 'unknown':\n return 'unknown';\n }\n}\n\n/** True when the phase means the document no longer binds anybody. */\nexport function isRetired(phase: Phase): boolean {\n return phase === 'retired';\n}\n\n/** True when the heading names a section that declares the document status. */\nexport function isStatusHeading(text: string): boolean {\n return STATUS_HEADINGS.has(text.trim().toLowerCase().replace(/[:*_`]/g, '').trim());\n}\n\n/**\n * Infers retirement from where a file lives.\n *\n * Moving an ADR into `docs/adr/archive/` is the most common way a team retires a\n * decision without touching its front matter, and a graph that misses it will\n * happily let obligations be delegated into the archive.\n */\nexport function phaseFromPath(posixPath: string): Phase {\n const segments = posixPath.toLowerCase().split('/');\n // The file name itself is not a directory; a document called `archive.md` is\n // not retired.\n for (let i = 0; i < segments.length - 1; i += 1) {\n if (RETIRING_DIRECTORIES.has(segments[i] as string)) return 'retired';\n }\n return 'unknown';\n}\n\n/**\n * Pulls a supersession target out of a status string.\n *\n * `Superseded by ADR-0009` is the single most common way a supersession is ever\n * recorded, and it is written in the status field rather than as a link. Reading\n * it here means the relation reaches the graph even in repositories that never\n * adopted an explicit field for it.\n */\nexport function supersessionTargetsIn(raw: string): string[] {\n const out: string[] = [];\n const pattern = /(?:superse[dc]ed|replaced|obsoleted)\\s*(?:by|with|through)?\\s*[:\\-—]?\\s*(.+)$/i;\n const match = pattern.exec(raw.replace(/[`*_]/g, ''));\n if (!match) return out;\n const tail = match[1] as string;\n for (const token of tail.split(/[,;]|\\band\\b/)) {\n const cleaned = token\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/[()<>\"']/g, ' ')\n .trim();\n if (cleaned.length > 0 && cleaned.length < 200) out.push(cleaned);\n }\n return out;\n}\n"]}
|