@descent-vtt/spec-graph 0.9.10 → 0.10.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 +68 -2
- package/README.md +522 -319
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +112 -67
- package/dist/cli.js.map +1 -1
- package/dist/extract.d.ts +14 -1
- package/dist/extract.js +114 -10
- package/dist/extract.js.map +1 -1
- package/dist/glob.d.ts +11 -0
- package/dist/glob.js +17 -1
- package/dist/glob.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +11 -1
- package/dist/lifecycle.js +187 -28
- package/dist/lifecycle.js.map +1 -1
- package/dist/report.d.ts +22 -0
- package/dist/report.js +50 -3
- package/dist/report.js.map +1 -1
- package/dist/sections.d.ts +7 -1
- package/dist/sections.js +10 -2
- package/dist/sections.js.map +1 -1
- package/dist/state.js +19 -3
- package/dist/state.js.map +1 -1
- package/dist/vendor/spec-core/markdown/frontmatter.d.ts +11 -1
- package/dist/vendor/spec-core/markdown/frontmatter.js +20 -3
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -1
- package/dist/vendor/spec-core/pattern/glob.d.ts +85 -0
- package/dist/vendor/spec-core/pattern/glob.js +207 -17
- package/dist/vendor/spec-core/pattern/glob.js.map +1 -1
- package/dist/vendor/spec-core/pattern/index.d.ts +2 -2
- package/dist/vendor/spec-core/pattern/index.js +1 -1
- package/dist/vendor/spec-core/pattern/index.js.map +1 -1
- package/dist/vendor/spec-core/text/index.d.ts +1 -0
- package/dist/vendor/spec-core/text/index.js +1 -0
- package/dist/vendor/spec-core/text/index.js.map +1 -1
- package/dist/vendor/spec-core/text/width.d.ts +30 -0
- package/dist/vendor/spec-core/text/width.js +113 -0
- package/dist/vendor/spec-core/text/width.js.map +1 -0
- package/dist/yaml.js +6 -2
- package/dist/yaml.js.map +1 -1
- package/package.json +1 -1
package/dist/sections.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Specification regions: the part of a file that one specification occupies.\n *\n * `spec-graph` began by assuming one specification per file, because that is\n * how MADR, KEPs and Rust RFCs are laid out. Plenty of organisations do not\n * work that way. A register, a whitepaper or a living architecture document\n * holds dozens of decisions in one file, each with its own identifier, its own\n * status and its own dependencies - and to an engine that binds a document to a\n * file, every one of them is invisible.\n *\n * The fix is small once stated: **a specification is a region of a file, not a\n * file.** A file yields at least one region - itself - and may yield more.\n * Everything downstream is unchanged, because a region produces exactly the\n * same `DocumentNode` a whole file produced.\n *\n * What this module decides is where those regions begin and end, and it is\n * deliberately hard to convince. A heading has to carry an identifier *and* the\n * section has to declare a status before it counts. One signal is not enough:\n * `## Q3 2026 Roadmap` parses as family `Q`, number 3, and `## v1.2.0` in a\n * changelog parses as family `v`, number 1. Neither is a specification, and\n * neither declares a status.\n */\n\nimport { attr, type Directive } from './directives.js';\nimport { HAS_LETTER_OR_DIGIT, isExternal, parsePrefixedRef } from './identity.js';\nimport { isStatusHeading } from './lifecycle.js';\nimport {\n isMarkdownLine,\n isOnlyComment,\n type Heading,\n type Range,\n type ScannedDocument,\n type Table,\n type TableCell,\n type TableRow,\n} from './markdown.js';\nimport type { EdgeKind } from './types.js';\n\n/** A status as written, and where it was written. */\nexport interface RegionStatus {\n readonly text: string;\n readonly start: number;\n readonly end: number;\n}\n\n/** A relation read from a table column rather than from prose. */\nexport interface TableRelation {\n readonly kind: EdgeKind;\n readonly inverted: boolean;\n readonly target: string;\n readonly start: number;\n readonly end: number;\n /** The column header that gave it its meaning, for the diagnostic. */\n readonly column: string;\n}\n\nexport interface SpecificationRegion {\n /** The heading that opens the region, or `null` for the file or a table row. */\n readonly heading: Heading | null;\n /** The table row that is the region, when it is one. */\n readonly row: TableRow | null;\n /** Offset the region starts at. */\n readonly start: number;\n /** Offset just past the region. */\n readonly end: number;\n /** Identifier read from the heading or the identifier column. */\n readonly declaredId: string | null;\n readonly title: string | null;\n readonly status: RegionStatus | null;\n /** A `@spec-node` directive inside the region, when there is one. */\n readonly directive: Directive | null;\n /** Relations declared by column, empty for a heading region. */\n readonly relations: readonly TableRelation[];\n /**\n * Cells whose content is already accounted for as a typed relation.\n *\n * Prose scanning skips these, so a link in a `Depends on` column produces one\n * typed edge rather than a typed edge and a neutral citation beside it.\n */\n readonly claimed: readonly Range[];\n}\n\n/** Column headers that name the row's identifier. */\nconst ID_COLUMNS = /^(?:id|ids|ref|reference|key|no|number|#|adr|rfc|kep|oi|decision|spec|specification)$/;\n\n/** Column headers that name the row's status. */\nconst STATUS_COLUMNS = /^(?:status|state|stage|lifecycle|phase)$/;\n\n/** Column headers that name the row's title. */\nconst TITLE_COLUMNS = /^(?:title|name|summary|description|subject|decision|topic)$/;\n\n/**\n * Column headers that declare a relation.\n *\n * Written the way a header is written - \"Depends on\", \"Superseded by\" - rather\n * than the way a front-matter key is, because that is what an author types at\n * the top of a column.\n */\nconst RELATION_COLUMNS: Readonly<Record<string, { kind: EdgeKind; inverted: boolean }>> = {\n 'depends on': { kind: 'depends-on', inverted: false },\n 'depends-on': { kind: 'depends-on', inverted: false },\n dependencies: { kind: 'depends-on', inverted: false },\n requires: { kind: 'depends-on', inverted: false },\n supersedes: { kind: 'supersedes', inverted: false },\n replaces: { kind: 'supersedes', inverted: false },\n 'superseded by': { kind: 'supersedes', inverted: true },\n 'replaced by': { kind: 'supersedes', inverted: true },\n 'blocked by': { kind: 'blocked-by', inverted: false },\n 'blocked on': { kind: 'blocked-by', inverted: false },\n blocks: { kind: 'blocked-by', inverted: true },\n amends: { kind: 'amends', inverted: false },\n extends: { kind: 'amends', inverted: false },\n assumes: { kind: 'assumes', inverted: false },\n 'delegates to': { kind: 'delegates-to', inverted: false },\n 'delegated to': { kind: 'delegates-to', inverted: false },\n 'tracked in': { kind: 'delegates-to', inverted: false },\n related: { kind: 'relates-to', inverted: false },\n 'related to': { kind: 'relates-to', inverted: false },\n 'see also': { kind: 'relates-to', inverted: false },\n references: { kind: 'references', inverted: false },\n};\n\n/** Cell values that mean \"nothing here\". */\nconst MARKDOWN_LINK = /!?\\[([^\\]]*)\\]\\(([^)]*)\\)/g;\nconst EMPTY_CELL = /^(?:-+|—|–|n\\/?a|none|nil|tbd|\\.|_+)$/i;\n\n/** `## ADR-0007: Sharding` - an identifier at the very start of a heading. */\nconst HEADING_ID = /^\\s*([A-Za-z]{1,15}[\\s._-]?\\d{1,6})\\b/;\n\n/** `**Status:** Accepted`, `Status: Accepted`, `*Status* : Accepted`. */\nconst INLINE_STATUS = /^[ \\t>]*[*_]{0,2}\\s*status\\s*[*_]{0,2}\\s*[::]\\s*(.+?)\\s*$/im;\n\n/**\n * Finds every specification in a file.\n *\n * The first entry is always the file itself, so a caller that ignores the rest\n * behaves exactly as it did before regions existed.\n */\nexport function findSpecificationRegions(\n scanned: ScannedDocument,\n directives: readonly Directive[],\n /** The file's own identifier, so its title heading is not read as a sub-region. */\n fileId: string,\n): SpecificationRegion[] {\n // The file's own relations come from its front matter and prose, never from\n // here, and it claims no cell: nothing reads either list off this region.\n const whole: SpecificationRegion = {\n heading: null,\n row: null,\n start: scanned.bodyStart,\n end: scanned.text.length,\n declaredId: null,\n title: null,\n status: null,\n directive: null,\n relations: [],\n claimed: [],\n };\n\n const regions: SpecificationRegion[] = [];\n const folded = fold(fileId);\n const fileRef = parsePrefixedRef(fileId);\n\n for (let i = 0; i < scanned.headings.length; i += 1) {\n const heading = scanned.headings[i] as Heading;\n const declaredId = HEADING_ID.exec(heading.text)?.[1];\n if (declaredId === undefined) continue;\n // The file's own title heading names the file, not a region inside it.\n if (fold(declaredId) === folded) continue;\n // Padding is not part of an identity anywhere else - `ADR-40`, `ADR-040`\n // and `ADR-0040` all resolve to one document - so a heading that spells the\n // number differently from the file name is still that file's own title.\n // Comparing the text alone made `# ADR-040` inside `0040-enforce.md` a\n // region within the file it names: one decision, two nodes, and a citation\n // arriving at whichever spelling it happened to use.\n // `HEADING_ID` captures only what `PREFIXED_ID` parses, so `ref` is never\n // null here; the check is for the type.\n const ref = parsePrefixedRef(declaredId);\n if (ref !== null && fileRef !== null && ref.family === fileRef.family && ref.number === fileRef.number) continue;\n\n const end = regionEnd(scanned, i);\n const directive = directiveIn(directives, heading.end, end);\n const status = readRegionStatus(scanned, heading.end, end);\n\n // Two independent signals are required. An identifier alone matches\n // `## Q3 2026 Roadmap` and `## v1.2.0`; a status alone matches every\n // ordinary `## Status` section in a normal ADR.\n if (status === null && directive === null) continue;\n\n // A directive's id wins, but only if it is a name. One that is not - the\n // lone backslash `id=\\\"ADR-9\\\"` parses to, written inside a string - would\n // be discarded when the region is identified, and a region with nothing\n // to go by falls back to the file's own name: the file then contained\n // itself, and the section vanished. The heading's id is the one to keep.\n const directiveId = directive ? attr(directive, 'id')?.value : undefined;\n\n regions.push({\n heading,\n row: null,\n start: heading.start,\n end,\n declaredId: directiveId !== undefined && HAS_LETTER_OR_DIGIT.test(directiveId) ? directiveId : declaredId,\n title: heading.text,\n status,\n directive,\n relations: [],\n // Prose inside a section is prose: a heading region claims no cell.\n claimed: [],\n });\n }\n\n return [whole, ...regions, ...findTableRegions(scanned)];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Registers kept as tables */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Reads a table whose columns describe specifications.\n *\n * A table qualifies when it has an identifier column *and* either a status\n * column or at least one relation column. Two signals again, for the same\n * reason: a table of identifiers and prose is a citation list, and promoting\n * every row of it to a specification would invent a register nobody wrote.\n */\nexport function findTableRegions(scanned: ScannedDocument): SpecificationRegion[] {\n const out: SpecificationRegion[] = [];\n\n for (const table of scanned.tables) {\n const schema = readSchema(table);\n if (schema === null) continue;\n\n for (const row of table.rows) {\n const idCell = row.cells[schema.id];\n if (!idCell) continue;\n const id = flatten(idCell.text);\n // A row is named by its id cell or not at all. An empty one, a\n // placeholder, or punctuation alone - `?` for a row nobody has numbered\n // yet - names nothing, and a region with no name fell back to the file's:\n // the register then contained itself, and the row vanished.\n if (!HAS_LETTER_OR_DIGIT.test(id) || EMPTY_CELL.test(id)) continue;\n\n // `row.cells[null]` is undefined too, so the null checks on the optional\n // columns are for the type; and `statusText` is read only beside a cell.\n const statusCell = schema.status === null ? undefined : row.cells[schema.status];\n const statusText = statusCell ? flatten(statusCell.text) : '';\n const status: RegionStatus | null =\n statusCell && statusText.length > 0 && !EMPTY_CELL.test(statusText)\n ? { text: statusText, start: statusCell.start, end: statusCell.end }\n : null;\n\n const titleCell = schema.title === null ? undefined : row.cells[schema.title];\n const title = titleCell ? flatten(titleCell.text) : null;\n\n const relations: TableRelation[] = [];\n // The status cell is left to prose, as a status line under a heading is:\n // `Superseded by ADR-0002` there names the row's successor, and claimed\n // it named nothing at all.\n const claimed: Range[] = [{ start: idCell.start, end: idCell.end }];\n\n for (const [column, relation] of schema.relations) {\n const cell = row.cells[column];\n if (!cell) continue;\n claimed.push({ start: cell.start, end: cell.end });\n for (const target of splitTargets(cell)) {\n relations.push({\n kind: relation.kind,\n inverted: relation.inverted,\n target: target.text,\n start: target.start,\n end: target.end,\n column: (table.headers[column] as TableCell).text,\n });\n }\n }\n\n out.push({\n heading: null,\n row,\n start: row.start,\n end: row.end,\n declaredId: id,\n title: title !== null && title.length > 0 ? title : null,\n status,\n directive: null,\n relations,\n claimed,\n });\n }\n }\n\n return out;\n}\n\ninterface TableSchema {\n readonly id: number;\n readonly status: number | null;\n readonly title: number | null;\n readonly relations: readonly (readonly [number, { kind: EdgeKind; inverted: boolean }])[];\n}\n\nfunction readSchema(table: Table): TableSchema | null {\n let id: number | null = null;\n let status: number | null = null;\n let title: number | null = null;\n const relations: [number, { kind: EdgeKind; inverted: boolean }][] = [];\n\n table.headers.forEach((header, column) => {\n const name = normaliseHeader(header.text);\n if (id === null && ID_COLUMNS.test(name)) {\n id = column;\n return;\n }\n if (status === null && STATUS_COLUMNS.test(name)) {\n status = column;\n return;\n }\n const relation = RELATION_COLUMNS[name];\n if (relation) {\n relations.push([column, relation]);\n return;\n }\n if (title === null && TITLE_COLUMNS.test(name)) title = column;\n });\n\n // Without an id column every row would be skipped for want of an id cell;\n // this says so before reading any of them.\n if (id === null) return null;\n if (status === null && relations.length === 0) return null;\n return { id, status, title, relations };\n}\n\nfunction normaliseHeader(text: string): string {\n return flatten(text)\n .toLowerCase()\n .replace(/[^a-z0-9/#\\s-]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** Splits a cell into the identifiers it lists, keeping each one's offset. */\nfunction splitTargets(cell: TableCell): { text: string; start: number; end: number }[] {\n const out: { text: string; start: number; end: number }[] = [];\n // Every target is reported at the cell's span. A cell is short, and an offset\n // recovered back through a flattening is an offset that can drift.\n const push = (raw: string): void => {\n const target = raw.trim();\n if (target.length === 0 || EMPTY_CELL.test(target)) return;\n out.push({ text: target, start: cell.start, end: cell.end });\n };\n const prose = (segment: string): void => {\n for (const piece of flatten(segment).split(/\\s*(?:[,;]|\\band\\b)\\s*/i)) push(piece);\n };\n\n // Links are taken out whole rather than flattened with everything else: a\n // path would otherwise be cut in half by its own slash, and its underscores\n // stripped as emphasis.\n // A fresh matcher per cell. `prose` calls `flatten`, which drives the shared\n // pattern; one /g regex advanced from inside its own scan resets its cursor\n // and never terminates.\n const links = new RegExp(MARKDOWN_LINK.source, 'g');\n let last = 0;\n for (let match = links.exec(cell.text); match !== null; match = links.exec(cell.text)) {\n prose(cell.text.slice(last, match.index));\n // Both groups always take part in a match, so the `?? ''` below and in the\n // label are for the type.\n const destination = (match[2] ?? '').trim();\n // A link states where its target lives; its label states only what the\n // target is called (ADR-0008). In a column the author has already typed,\n // the destination is the claim - `[see](adr/0050.md)` is a dependency on\n // that file, not on the word \"see\". An external or in-page destination\n // names no document here, so the label is what is left to try.\n const local = destination.length > 0 && !destination.startsWith('#') && !isExternal(destination);\n push(local ? destination : (match[1] ?? ''));\n last = match.index + match[0].length;\n }\n prose(cell.text.slice(last));\n return out;\n}\n\n/** Reduces a cell to its text: link labels rather than link syntax. */\nfunction flatten(text: string): string {\n return text\n .replace(MARKDOWN_LINK, '$1')\n .replace(/\\[\\[(?:[^\\]|]*\\|)?([^\\]]*)\\]\\]/g, '$1')\n .replace(/[`*_~]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** Where a heading's section ends: the next heading at the same level or above. */\nfunction regionEnd(scanned: ScannedDocument, index: number): number {\n const heading = scanned.headings[index] as Heading;\n for (let i = index + 1; i < scanned.headings.length; i += 1) {\n const next = scanned.headings[i] as Heading;\n if (next.level <= heading.level) return next.start;\n }\n return scanned.text.length;\n}\n\n/**\n * Reads a status declared inside a region.\n *\n * Two forms, both common: a labelled line (`**Status:** Accepted`) and a\n * sub-heading (`### Status` with the value beneath it). Only the region is\n * searched, so one decision in a register cannot inherit its neighbour's.\n */\nexport function readRegionStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n const labelled = readLabelledStatus(scanned, start, end);\n if (labelled) return labelled;\n return readHeadedStatus(scanned, start, end);\n}\n\nfunction readLabelledStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n for (const line of scanned.lines) {\n if (line.contentStart < start) continue;\n if (line.contentStart >= end) break;\n // A template's commented-out `Status: proposed | accepted` is a hint to\n // whoever fills it in, not a status the region declares, and one in code or\n // a <pre> block is an example.\n if (!isMarkdownLine(line) || line.blank || line.comment) continue;\n const match = INLINE_STATUS.exec(line.content);\n if (!match) continue;\n // `**Status:** Accepted` closes its emphasis *after* the colon, so the\n // closing marker lands in the captured value. Strip it rather than let it\n // reach the report as part of the status a reader is shown.\n const value = (match[1] as string).replace(/^[*_\\s]+/, '').replace(/[*_\\s]+$/, '');\n if (value.length === 0) continue;\n const offset = line.contentStart + line.content.lastIndexOf(value);\n return { text: value, start: offset, end: offset + value.length };\n }\n return null;\n}\n\nfunction readHeadedStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n // The upper bound decides nothing on its own: under a heading at or past\n // `end` there is no line before `end`, and the loop below stops there.\n const heading = scanned.headings.find((h) => h.start >= start && h.start < end && isStatusHeading(h.text));\n if (!heading) return null;\n\n for (const line of scanned.lines) {\n if (line.line <= heading.line) continue;\n if (line.contentStart >= end) break;\n if (line.blank || isOnlyComment(scanned, line)) continue;\n if (!isMarkdownLine(line)) return null;\n if (opensHeading(scanned, line.line)) return null;\n const trimmed = line.content.trim();\n // A bullet list under Status is a status history; the first entry is current.\n const cleaned = trimmed.replace(/^[-*+]\\s+/, '');\n const offset = line.contentStart + line.content.indexOf(trimmed) + (trimmed.length - cleaned.length);\n return { text: cleaned, start: offset, end: offset + cleaned.length };\n }\n return null;\n}\n\n/**\n * Whether a heading begins on this line, which ends a status section's search\n * for its value.\n *\n * Asked of the scanner rather than of the line's first character, because a\n * setext heading's text line starts with no `#`: under `### Status` it read as\n * the status, and a register took the next decision's title for its own.\n */\nexport function opensHeading(scanned: ScannedDocument, line: number): boolean {\n return scanned.headings.some((heading) => heading.line === line);\n}\n\n/**\n * The last `@spec-node` directive inside a region: the one written last, which\n * is the one a reader sees as the final word.\n *\n * Directives arrive in the order they were written, so each candidate starts\n * after the one before it and the comparison below only restates that. And\n * `start` is where a heading's line ends, a comment written on it included,\n * so no directive begins exactly there and `<` against `<=` decides nothing.\n */\nfunction directiveIn(directives: readonly Directive[], start: number, end: number): Directive | null {\n let found: Directive | null = null;\n for (const directive of directives) {\n if (directive.name !== 'spec-node') continue;\n if (directive.start < start || directive.end > end) continue;\n if (found === null || directive.start > found.start) found = directive;\n }\n return found;\n}\n\n/**\n * Case- and separator-insensitive comparison, matching reference folding.\n *\n * Applied to both sides of one comparison, so which case it folds to does not\n * matter; the separators take every space with them, so the trim does not\n * either; and `+` only saves work.\n */\nfunction fold(value: string): string {\n return value.trim().toLowerCase().replace(/[\\s._-]+/g, '');\n}\n\n/** The region containing an offset, innermost first. `null` when only the file does. */\nexport function regionAt(regions: readonly SpecificationRegion[], offset: number): SpecificationRegion | null {\n let best: SpecificationRegion | null = null;\n for (const region of regions) {\n // The whole-file region owns nothing in particular; it is the fallback.\n if (region.heading === null && region.row === null) continue;\n if (offset < region.start || offset >= region.end) continue;\n // No two regions of one file open at the same offset, so `>` and `>=`\n // agree here.\n if (best === null || region.start > best.start) best = region;\n }\n return best;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Specification regions: the part of a file that one specification occupies.\n *\n * `spec-graph` began by assuming one specification per file, because that is\n * how MADR, KEPs and Rust RFCs are laid out. Plenty of organisations do not\n * work that way. A register, a whitepaper or a living architecture document\n * holds dozens of decisions in one file, each with its own identifier, its own\n * status and its own dependencies - and to an engine that binds a document to a\n * file, every one of them is invisible.\n *\n * The fix is small once stated: **a specification is a region of a file, not a\n * file.** A file yields at least one region - itself - and may yield more.\n * Everything downstream is unchanged, because a region produces exactly the\n * same `DocumentNode` a whole file produced.\n *\n * What this module decides is where those regions begin and end, and it is\n * deliberately hard to convince. A heading has to carry an identifier *and* the\n * section has to declare a status before it counts. One signal is not enough:\n * `## Q3 2026 Roadmap` parses as family `Q`, number 3, and `## v1.2.0` in a\n * changelog parses as family `v`, number 1. Neither is a specification, and\n * neither declares a status.\n */\n\nimport { attr, type Directive } from './directives.js';\nimport { HAS_LETTER_OR_DIGIT, isExternal, parsePrefixedRef } from './identity.js';\nimport { isStatusHeading } from './lifecycle.js';\nimport {\n isMarkdownLine,\n isOnlyComment,\n type Heading,\n type Range,\n type ScannedDocument,\n type Table,\n type TableCell,\n type TableRow,\n} from './markdown.js';\nimport type { EdgeKind } from './types.js';\n\n/** A status as written, and where it was written. */\nexport interface RegionStatus {\n readonly text: string;\n readonly start: number;\n readonly end: number;\n}\n\n/** A relation read from a table column rather than from prose. */\nexport interface TableRelation {\n readonly kind: EdgeKind;\n readonly inverted: boolean;\n readonly target: string;\n readonly start: number;\n readonly end: number;\n /** The column header that gave it its meaning, for the diagnostic. */\n readonly column: string;\n}\n\nexport interface SpecificationRegion {\n /** The heading that opens the region, or `null` for the file or a table row. */\n readonly heading: Heading | null;\n /** The table row that is the region, when it is one. */\n readonly row: TableRow | null;\n /** Offset the region starts at. */\n readonly start: number;\n /** Offset just past the region. */\n readonly end: number;\n /** Identifier read from the heading or the identifier column. */\n readonly declaredId: string | null;\n readonly title: string | null;\n readonly status: RegionStatus | null;\n /** A `@spec-node` directive inside the region, when there is one. */\n readonly directive: Directive | null;\n /** Relations declared by column, empty for a heading region. */\n readonly relations: readonly TableRelation[];\n /**\n * Cells whose content is already accounted for as a typed relation.\n *\n * Prose scanning skips these, so a link in a `Depends on` column produces one\n * typed edge rather than a typed edge and a neutral citation beside it.\n */\n readonly claimed: readonly Range[];\n}\n\n/** Column headers that name the row's identifier. */\nconst ID_COLUMNS = /^(?:id|ids|ref|reference|key|no|number|#|adr|rfc|kep|oi|decision|spec|specification)$/;\n\n/** Column headers that name the row's status. */\nconst STATUS_COLUMNS = /^(?:status|state|stage|lifecycle|phase)$/;\n\n/** Column headers that name the row's title. */\nconst TITLE_COLUMNS = /^(?:title|name|summary|description|subject|decision|topic)$/;\n\n/**\n * Column headers that declare a relation.\n *\n * Written the way a header is written - \"Depends on\", \"Superseded by\" - rather\n * than the way a front-matter key is, because that is what an author types at\n * the top of a column.\n */\nconst RELATION_COLUMNS: Readonly<Record<string, { kind: EdgeKind; inverted: boolean }>> = {\n 'depends on': { kind: 'depends-on', inverted: false },\n 'depends-on': { kind: 'depends-on', inverted: false },\n dependencies: { kind: 'depends-on', inverted: false },\n requires: { kind: 'depends-on', inverted: false },\n supersedes: { kind: 'supersedes', inverted: false },\n replaces: { kind: 'supersedes', inverted: false },\n 'superseded by': { kind: 'supersedes', inverted: true },\n 'replaced by': { kind: 'supersedes', inverted: true },\n 'blocked by': { kind: 'blocked-by', inverted: false },\n 'blocked on': { kind: 'blocked-by', inverted: false },\n blocks: { kind: 'blocked-by', inverted: true },\n amends: { kind: 'amends', inverted: false },\n extends: { kind: 'amends', inverted: false },\n assumes: { kind: 'assumes', inverted: false },\n 'delegates to': { kind: 'delegates-to', inverted: false },\n 'delegated to': { kind: 'delegates-to', inverted: false },\n 'tracked in': { kind: 'delegates-to', inverted: false },\n related: { kind: 'relates-to', inverted: false },\n 'related to': { kind: 'relates-to', inverted: false },\n 'see also': { kind: 'relates-to', inverted: false },\n references: { kind: 'references', inverted: false },\n};\n\n/** Cell values that mean \"nothing here\". */\nconst MARKDOWN_LINK = /!?\\[([^\\]]*)\\]\\(([^)]*)\\)/g;\nconst EMPTY_CELL = /^(?:-+|—|–|n\\/?a|none|nil|tbd|\\.|_+)$/i;\n\n/** `## ADR-0007: Sharding` - an identifier at the very start of a heading. */\nconst HEADING_ID = /^\\s*([A-Za-z]{1,15}[\\s._-]?\\d{1,6})\\b/;\n\n/** `**Status:** Accepted`, `Status: Accepted`, `*Status* : Accepted`, `**狀態:** 已接受`. */\nconst INLINE_STATUS = /^[ \\t>]*[*_]{0,2}\\s*(?:status|狀態|状态)\\s*[*_]{0,2}\\s*[::]\\s*(.+?)\\s*$/im;\n\n/**\n * Finds every specification in a file.\n *\n * The first entry is always the file itself, so a caller that ignores the rest\n * behaves exactly as it did before regions existed.\n */\nexport function findSpecificationRegions(\n scanned: ScannedDocument,\n directives: readonly Directive[],\n /** The file's own identifier, so its title heading is not read as a sub-region. */\n fileId: string,\n): SpecificationRegion[] {\n // The file's own relations come from its front matter and prose, never from\n // here, and it claims no cell: nothing reads either list off this region.\n const whole: SpecificationRegion = {\n heading: null,\n row: null,\n start: scanned.bodyStart,\n end: scanned.text.length,\n declaredId: null,\n title: null,\n status: null,\n directive: null,\n relations: [],\n claimed: [],\n };\n\n const regions: SpecificationRegion[] = [];\n const folded = fold(fileId);\n const fileRef = parsePrefixedRef(fileId);\n\n for (let i = 0; i < scanned.headings.length; i += 1) {\n const heading = scanned.headings[i] as Heading;\n const declaredId = HEADING_ID.exec(heading.text)?.[1];\n if (declaredId === undefined) continue;\n // The file's own title heading names the file, not a region inside it.\n if (fold(declaredId) === folded) continue;\n // Padding is not part of an identity anywhere else - `ADR-40`, `ADR-040`\n // and `ADR-0040` all resolve to one document - so a heading that spells the\n // number differently from the file name is still that file's own title.\n // Comparing the text alone made `# ADR-040` inside `0040-enforce.md` a\n // region within the file it names: one decision, two nodes, and a citation\n // arriving at whichever spelling it happened to use.\n // `HEADING_ID` captures only what `PREFIXED_ID` parses, so `ref` is never\n // null here; the check is for the type.\n const ref = parsePrefixedRef(declaredId);\n if (ref !== null && fileRef !== null && ref.family === fileRef.family && ref.number === fileRef.number) continue;\n\n const end = regionEnd(scanned, i);\n const directive = directiveIn(directives, heading.end, end);\n const status = readRegionStatus(scanned, heading.end, end);\n\n // Two independent signals are required. An identifier alone matches\n // `## Q3 2026 Roadmap` and `## v1.2.0`; a status alone matches every\n // ordinary `## Status` section in a normal ADR.\n if (status === null && directive === null) continue;\n\n // A directive's id wins, but only if it is a name. One that is not - the\n // lone backslash `id=\\\"ADR-9\\\"` parses to, written inside a string - would\n // be discarded when the region is identified, and a region with nothing\n // to go by falls back to the file's own name: the file then contained\n // itself, and the section vanished. The heading's id is the one to keep.\n const directiveId = directive ? attr(directive, 'id')?.value : undefined;\n\n regions.push({\n heading,\n row: null,\n start: heading.start,\n end,\n declaredId: directiveId !== undefined && HAS_LETTER_OR_DIGIT.test(directiveId) ? directiveId : declaredId,\n title: heading.text,\n status,\n directive,\n relations: [],\n // Prose inside a section is prose: a heading region claims no cell.\n claimed: [],\n });\n }\n\n return [whole, ...regions, ...findTableRegions(scanned)];\n}\n\n/* -------------------------------------------------------------------------- */\n/* Registers kept as tables */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Reads a table whose columns describe specifications.\n *\n * A table qualifies when it has an identifier column *and* either a status\n * column or at least one relation column. Two signals again, for the same\n * reason: a table of identifiers and prose is a citation list, and promoting\n * every row of it to a specification would invent a register nobody wrote.\n */\nexport function findTableRegions(scanned: ScannedDocument): SpecificationRegion[] {\n const out: SpecificationRegion[] = [];\n\n for (const table of scanned.tables) {\n const schema = readSchema(table);\n if (schema === null) continue;\n\n for (const row of table.rows) {\n const idCell = row.cells[schema.id];\n if (!idCell) continue;\n const id = flatten(idCell.text);\n // A row is named by its id cell or not at all. An empty one, a\n // placeholder, or punctuation alone - `?` for a row nobody has numbered\n // yet - names nothing, and a region with no name fell back to the file's:\n // the register then contained itself, and the row vanished.\n if (!HAS_LETTER_OR_DIGIT.test(id) || EMPTY_CELL.test(id)) continue;\n\n // `row.cells[null]` is undefined too, so the null checks on the optional\n // columns are for the type; and `statusText` is read only beside a cell.\n const statusCell = schema.status === null ? undefined : row.cells[schema.status];\n const statusText = statusCell ? flatten(statusCell.text) : '';\n const status: RegionStatus | null =\n statusCell && statusText.length > 0 && !EMPTY_CELL.test(statusText)\n ? { text: statusText, start: statusCell.start, end: statusCell.end }\n : null;\n\n const titleCell = schema.title === null ? undefined : row.cells[schema.title];\n const title = titleCell ? flatten(titleCell.text) : null;\n\n const relations: TableRelation[] = [];\n // The status cell is left to prose, as a status line under a heading is:\n // `Superseded by ADR-0002` there names the row's successor, and claimed\n // it named nothing at all.\n const claimed: Range[] = [{ start: idCell.start, end: idCell.end }];\n\n for (const [column, relation] of schema.relations) {\n const cell = row.cells[column];\n if (!cell) continue;\n claimed.push({ start: cell.start, end: cell.end });\n for (const target of splitTargets(cell)) {\n relations.push({\n kind: relation.kind,\n inverted: relation.inverted,\n target: target.text,\n start: target.start,\n end: target.end,\n column: (table.headers[column] as TableCell).text,\n });\n }\n }\n\n out.push({\n heading: null,\n row,\n start: row.start,\n end: row.end,\n declaredId: id,\n title: title !== null && title.length > 0 ? title : null,\n status,\n directive: null,\n relations,\n claimed,\n });\n }\n }\n\n return out;\n}\n\n/**\n * Whether spec-graph reads a table as a register: an identifier column, and a\n * status column or a relation column beside it. Such a table's status column\n * describes its rows, never the document holding it.\n */\nexport function isRegisterTable(table: Table): boolean {\n return readSchema(table) !== null;\n}\n\ninterface TableSchema {\n readonly id: number;\n readonly status: number | null;\n readonly title: number | null;\n readonly relations: readonly (readonly [number, { kind: EdgeKind; inverted: boolean }])[];\n}\n\nfunction readSchema(table: Table): TableSchema | null {\n let id: number | null = null;\n let status: number | null = null;\n let title: number | null = null;\n const relations: [number, { kind: EdgeKind; inverted: boolean }][] = [];\n\n table.headers.forEach((header, column) => {\n const name = normaliseHeader(header.text);\n if (id === null && ID_COLUMNS.test(name)) {\n id = column;\n return;\n }\n if (status === null && STATUS_COLUMNS.test(name)) {\n status = column;\n return;\n }\n const relation = RELATION_COLUMNS[name];\n if (relation) {\n relations.push([column, relation]);\n return;\n }\n if (title === null && TITLE_COLUMNS.test(name)) title = column;\n });\n\n // Without an id column every row would be skipped for want of an id cell;\n // this says so before reading any of them.\n if (id === null) return null;\n if (status === null && relations.length === 0) return null;\n return { id, status, title, relations };\n}\n\nfunction normaliseHeader(text: string): string {\n return flatten(text)\n .toLowerCase()\n .replace(/[^a-z0-9/#\\s-]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** Splits a cell into the identifiers it lists, keeping each one's offset. */\nfunction splitTargets(cell: TableCell): { text: string; start: number; end: number }[] {\n const out: { text: string; start: number; end: number }[] = [];\n // Every target is reported at the cell's span. A cell is short, and an offset\n // recovered back through a flattening is an offset that can drift.\n const push = (raw: string): void => {\n const target = raw.trim();\n if (target.length === 0 || EMPTY_CELL.test(target)) return;\n out.push({ text: target, start: cell.start, end: cell.end });\n };\n const prose = (segment: string): void => {\n for (const piece of flatten(segment).split(/\\s*(?:[,;]|\\band\\b)\\s*/i)) push(piece);\n };\n\n // Links are taken out whole rather than flattened with everything else: a\n // path would otherwise be cut in half by its own slash, and its underscores\n // stripped as emphasis.\n // A fresh matcher per cell. `prose` calls `flatten`, which drives the shared\n // pattern; one /g regex advanced from inside its own scan resets its cursor\n // and never terminates.\n const links = new RegExp(MARKDOWN_LINK.source, 'g');\n let last = 0;\n for (let match = links.exec(cell.text); match !== null; match = links.exec(cell.text)) {\n prose(cell.text.slice(last, match.index));\n // Both groups always take part in a match, so the `?? ''` below and in the\n // label are for the type.\n const destination = (match[2] ?? '').trim();\n // A link states where its target lives; its label states only what the\n // target is called (ADR-0008). In a column the author has already typed,\n // the destination is the claim - `[see](adr/0050.md)` is a dependency on\n // that file, not on the word \"see\". An external or in-page destination\n // names no document here, so the label is what is left to try.\n const local = destination.length > 0 && !destination.startsWith('#') && !isExternal(destination);\n push(local ? destination : (match[1] ?? ''));\n last = match.index + match[0].length;\n }\n prose(cell.text.slice(last));\n return out;\n}\n\n/** Reduces a cell to its text: link labels rather than link syntax. */\nfunction flatten(text: string): string {\n return text\n .replace(MARKDOWN_LINK, '$1')\n .replace(/\\[\\[(?:[^\\]|]*\\|)?([^\\]]*)\\]\\]/g, '$1')\n .replace(/[`*_~]/g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** Where a heading's section ends: the next heading at the same level or above. */\nfunction regionEnd(scanned: ScannedDocument, index: number): number {\n const heading = scanned.headings[index] as Heading;\n for (let i = index + 1; i < scanned.headings.length; i += 1) {\n const next = scanned.headings[i] as Heading;\n if (next.level <= heading.level) return next.start;\n }\n return scanned.text.length;\n}\n\n/**\n * Reads a status declared inside a region.\n *\n * Two forms, both common: a labelled line (`**Status:** Accepted`) and a\n * sub-heading (`### Status` with the value beneath it). Only the region is\n * searched, so one decision in a register cannot inherit its neighbour's.\n */\nexport function readRegionStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n const labelled = readLabelledStatus(scanned, start, end);\n if (labelled) return labelled;\n return readHeadedStatus(scanned, start, end);\n}\n\nfunction readLabelledStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n for (const line of scanned.lines) {\n if (line.contentStart < start) continue;\n if (line.contentStart >= end) break;\n // A template's commented-out `Status: proposed | accepted` is a hint to\n // whoever fills it in, not a status the region declares, and one in code or\n // a <pre> block is an example.\n if (!isMarkdownLine(line) || line.blank || line.comment) continue;\n const match = INLINE_STATUS.exec(line.content);\n if (!match) continue;\n // `**Status:** Accepted` closes its emphasis *after* the colon, so the\n // closing marker lands in the captured value. Strip it rather than let it\n // reach the report as part of the status a reader is shown.\n const value = (match[1] as string).replace(/^[*_\\s]+/, '').replace(/[*_\\s]+$/, '');\n if (value.length === 0) continue;\n const offset = line.contentStart + line.content.lastIndexOf(value);\n return { text: value, start: offset, end: offset + value.length };\n }\n return null;\n}\n\nfunction readHeadedStatus(scanned: ScannedDocument, start: number, end: number): RegionStatus | null {\n // The upper bound decides nothing on its own: under a heading at or past\n // `end` there is no line before `end`, and the loop below stops there.\n const heading = scanned.headings.find((h) => h.start >= start && h.start < end && isStatusHeading(h.text));\n if (!heading) return null;\n\n for (const line of scanned.lines) {\n if (line.line <= heading.line) continue;\n if (line.contentStart >= end) break;\n if (line.blank || isOnlyComment(scanned, line)) continue;\n if (!isMarkdownLine(line)) return null;\n if (opensHeading(scanned, line.line)) return null;\n const trimmed = line.content.trim();\n // A bullet list under Status is a status history; the first entry is current.\n const cleaned = trimmed.replace(/^[-*+]\\s+/, '');\n const offset = line.contentStart + line.content.indexOf(trimmed) + (trimmed.length - cleaned.length);\n return { text: cleaned, start: offset, end: offset + cleaned.length };\n }\n return null;\n}\n\n/**\n * Whether a heading begins on this line, which ends a status section's search\n * for its value.\n *\n * Asked of the scanner rather than of the line's first character, because a\n * setext heading's text line starts with no `#`: under `### Status` it read as\n * the status, and a register took the next decision's title for its own.\n */\nexport function opensHeading(scanned: ScannedDocument, line: number): boolean {\n return scanned.headings.some((heading) => heading.line === line);\n}\n\n/**\n * The last `@spec-node` directive inside a region: the one written last, which\n * is the one a reader sees as the final word.\n *\n * Directives arrive in the order they were written, so each candidate starts\n * after the one before it and the comparison below only restates that. And\n * `start` is where a heading's line ends, a comment written on it included,\n * so no directive begins exactly there and `<` against `<=` decides nothing.\n */\nfunction directiveIn(directives: readonly Directive[], start: number, end: number): Directive | null {\n let found: Directive | null = null;\n for (const directive of directives) {\n if (directive.name !== 'spec-node') continue;\n if (directive.start < start || directive.end > end) continue;\n if (found === null || directive.start > found.start) found = directive;\n }\n return found;\n}\n\n/**\n * Case- and separator-insensitive comparison, matching reference folding.\n *\n * Applied to both sides of one comparison, so which case it folds to does not\n * matter; the separators take every space with them, so the trim does not\n * either; and `+` only saves work.\n */\nfunction fold(value: string): string {\n return value.trim().toLowerCase().replace(/[\\s._-]+/g, '');\n}\n\n/** The region containing an offset, innermost first. `null` when only the file does. */\nexport function regionAt(regions: readonly SpecificationRegion[], offset: number): SpecificationRegion | null {\n let best: SpecificationRegion | null = null;\n for (const region of regions) {\n // The whole-file region owns nothing in particular; it is the fallback.\n if (region.heading === null && region.row === null) continue;\n if (offset < region.start || offset >= region.end) continue;\n // No two regions of one file open at the same offset, so `>` and `>=`\n // agree here.\n if (best === null || region.start > best.start) best = region;\n }\n return best;\n}\n"]}
|
package/dist/state.js
CHANGED
|
@@ -54,7 +54,9 @@ const CHECKBOX_STATE = {
|
|
|
54
54
|
*
|
|
55
55
|
* Matched only in the qualified forms real documents use - `**Resolved:**`,
|
|
56
56
|
* `RESOLVED -`, `**Moot**` - never as bare words, so ordinary prose such as
|
|
57
|
-
* "we resolved to keep the queue" does not close an item.
|
|
57
|
+
* "we resolved to keep the queue" does not close an item. A Chinese marker,
|
|
58
|
+
* Traditional or Simplified, is the English one it translates, and has no
|
|
59
|
+
* case to shout in: `已決定:` or `**已決定**`, never `我們已決定採用`.
|
|
58
60
|
*/
|
|
59
61
|
const MARKERS = {
|
|
60
62
|
satisfied: [
|
|
@@ -72,6 +74,11 @@ const MARKERS = {
|
|
|
72
74
|
'implemented',
|
|
73
75
|
'solved',
|
|
74
76
|
'confirmed',
|
|
77
|
+
'已解決',
|
|
78
|
+
'已解决',
|
|
79
|
+
'已決定',
|
|
80
|
+
'已决定',
|
|
81
|
+
'已定案',
|
|
75
82
|
],
|
|
76
83
|
narrowed: [
|
|
77
84
|
'partially resolved',
|
|
@@ -87,6 +94,8 @@ const MARKERS = {
|
|
|
87
94
|
'in progress',
|
|
88
95
|
'remaining',
|
|
89
96
|
'split',
|
|
97
|
+
'部分解決',
|
|
98
|
+
'部分解决',
|
|
90
99
|
],
|
|
91
100
|
delegated: [
|
|
92
101
|
'delegated',
|
|
@@ -103,6 +112,7 @@ const MARKERS = {
|
|
|
103
112
|
'followup in',
|
|
104
113
|
'continued in',
|
|
105
114
|
'owned by',
|
|
115
|
+
'已移交',
|
|
106
116
|
],
|
|
107
117
|
'accepted-debt': [
|
|
108
118
|
'accepted debt',
|
|
@@ -119,6 +129,9 @@ const MARKERS = {
|
|
|
119
129
|
'by design',
|
|
120
130
|
'tolerated',
|
|
121
131
|
'living with it',
|
|
132
|
+
'技術債',
|
|
133
|
+
'技术债',
|
|
134
|
+
'已知限制',
|
|
122
135
|
],
|
|
123
136
|
rejected: [
|
|
124
137
|
'rejected',
|
|
@@ -273,8 +286,11 @@ function buildMarkerPattern() {
|
|
|
273
286
|
.map((phrase) => phrase.replace(/\\?[ -]/g, '[ _\\-]').replace(/'/g, "['’]"));
|
|
274
287
|
return new RegExp(
|
|
275
288
|
// A marker only counts at the start of a statement: the start of the item,
|
|
276
|
-
// a new line, after a sentence ends, or after an emphasis opener.
|
|
277
|
-
|
|
289
|
+
// a new line, after a sentence ends, or after an emphasis opener. A
|
|
290
|
+
// Chinese sentence ends at `。?!;` with a space after it or none -
|
|
291
|
+
// `是否分片?已決定:不分片。` - and the space before the phrase below
|
|
292
|
+
// takes any that follows.
|
|
293
|
+
String.raw `(?:^|\n|[.?!]\s+|[。?!;]|[(\[])` +
|
|
278
294
|
// Skip block-quote and list markers, but only when the marker character is
|
|
279
295
|
// followed by whitespace. Without that guard the `*` of `**Resolved**`
|
|
280
296
|
// reads as a bullet and the emphasis wrapper is lost.
|
package/dist/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Item state resolution.\n *\n * The premise of this module is that `- [ ]` and `- [x]` are the *least*\n * reliable statement a document makes about an obligation. Real specifications\n * record outcomes in prose, on a continuation line, days after the checkbox was\n * written and nobody went back to tick it:\n *\n * ```md\n * - [ ] Should the write path shard before the migration?\n * **Resolved (2026-03):** no - one node holds three years of growth.\n * ```\n *\n * A grep for `- [ ]` calls that an open question and re-opens a settled debate.\n * A grep for `- [x]` misses it entirely. spec-graph reads both signals, lets the\n * more specific one win, and *reports the disagreement* rather than hiding it,\n * because a checkbox that contradicts its own body is itself a defect worth\n * knowing about.\n */\n\nimport type { Directive } from './directives.js';\nimport { attr } from './directives.js';\nimport type { ListItem } from './markdown.js';\nimport { refOf, type LineIndex } from './source.js';\nimport { OPENNESS_OF, type Disposition, type Openness, type SourceRef, type StateSignal } from './types.js';\n\n/** Which signal wins when two disagree. Higher is more specific. */\nconst SIGNAL_PRIORITY: Readonly<Record<StateSignal['source'], number>> = {\n directive: 5,\n marker: 4,\n strikethrough: 3,\n checkbox: 2,\n section: 1,\n default: 0,\n};\n\n/**\n * Checkbox characters.\n *\n * `[ ]` and `[x]` are universal; the rest come from conventions that several\n * ecosystems share (`[~]` partial, `[-]` dropped, `[?]` question, `[/]` in\n * progress). Reading them costs nothing and refusing to would throw away the\n * one place a document states its nuance unambiguously.\n */\nconst CHECKBOX_STATE: Readonly<Record<string, Disposition>> = {\n ' ': 'unresolved',\n x: 'satisfied',\n X: 'satisfied',\n '+': 'satisfied',\n '~': 'narrowed',\n '/': 'narrowed',\n '-': 'rejected',\n '?': 'unresolved',\n '!': 'unresolved',\n '*': 'unresolved',\n};\n\n/**\n * Prose markers, by the state they announce.\n *\n * Matched only in the qualified forms real documents use - `**Resolved:**`,\n * `RESOLVED -`, `**Moot**` - never as bare words, so ordinary prose such as\n * \"we resolved to keep the queue\" does not close an item.\n */\nconst MARKERS: Readonly<Record<Disposition, readonly string[]>> = {\n satisfied: [\n 'resolved',\n 'answered',\n 'decided',\n 'settled',\n 'done',\n 'complete',\n 'completed',\n 'closed',\n 'fixed',\n 'shipped',\n 'addressed',\n 'implemented',\n 'solved',\n 'confirmed',\n ],\n narrowed: [\n 'partially resolved',\n 'partially answered',\n 'partially done',\n 'partly resolved',\n 'narrowed',\n 'narrowed to',\n 'scoped down',\n 'reduced scope',\n 'reduced to',\n 'partial',\n 'in progress',\n 'remaining',\n 'split',\n ],\n delegated: [\n 'delegated',\n 'delegated to',\n 'moved to',\n 'tracked in',\n 'tracked by',\n 'handed off',\n 'handed off to',\n 'handed to',\n 'deferred to',\n 'follow up in',\n 'follow-up in',\n 'followup in',\n 'continued in',\n 'owned by',\n ],\n 'accepted-debt': [\n 'accepted debt',\n 'accepted as debt',\n 'accepted risk',\n 'known limitation',\n 'known issue',\n 'known gap',\n 'technical debt',\n 'tech debt',\n 'wontfix',\n \"won't fix\",\n 'will not fix',\n 'by design',\n 'tolerated',\n 'living with it',\n ],\n rejected: [\n 'rejected',\n 'declined',\n 'dropped',\n 'not doing',\n 'will not do',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'withdrawn',\n ],\n obviated: [\n 'no longer applicable',\n 'no longer relevant',\n 'no longer needed',\n 'overtaken by events',\n 'premise invalid',\n 'obviated',\n 'obsolete',\n 'not applicable',\n 'moot',\n 'void',\n 'n/a',\n 'obe',\n ],\n unresolved: ['unresolved', 'still open', 'open question', 'tbd', 'undecided', 'reopened'],\n};\n\n/** Words accepted in a `state=` attribute, including friendly synonyms. */\nconst STATE_ALIASES: Readonly<Record<string, Disposition>> = {\n open: 'unresolved',\n unresolved: 'unresolved',\n todo: 'unresolved',\n narrowed: 'narrowed',\n partial: 'narrowed',\n delegated: 'delegated',\n satisfied: 'satisfied',\n resolved: 'satisfied',\n done: 'satisfied',\n closed: 'satisfied',\n 'accepted-debt': 'accepted-debt',\n debt: 'accepted-debt',\n wontfix: 'accepted-debt',\n rejected: 'rejected',\n declined: 'rejected',\n obviated: 'obviated',\n moot: 'obviated',\n};\n\nconst MARKER_PATTERN = buildMarkerPattern();\nconst MARKER_LOOKUP = markerLookup();\n\nexport interface StateInput {\n readonly file: string;\n readonly index: LineIndex;\n readonly item: ListItem;\n /** Heading path the item sits under. */\n readonly section: readonly string[];\n /** A `@spec-item` directive attached to the item, when there is one. */\n readonly directive: Directive | null;\n /** True when the item sits under a heading that declares obligations. */\n readonly inObligationSection: boolean;\n}\n\nexport interface ResolvedState {\n readonly disposition: Disposition;\n readonly openness: Openness;\n readonly evidence: StateSignal;\n /** Signals that disagree with `evidence` about how open the item is. */\n readonly conflicts: readonly StateSignal[];\n}\n\n/** Collects every state signal on an item and decides between them. */\nexport function resolveItemState(input: StateInput): ResolvedState {\n const { file, index, item } = input;\n const signals: StateSignal[] = [];\n\n const at = (start: number, end: number): SourceRef => refOf(file, index, start, end);\n\n if (input.directive) {\n const declared = attr(input.directive, 'state');\n if (declared) {\n const mapped = STATE_ALIASES[declared.value.toLowerCase()];\n if (mapped) {\n signals.push({\n source: 'directive',\n disposition: mapped,\n raw: declared.value,\n at: at(declared.start, declared.end),\n });\n }\n }\n }\n\n // Markers are searched in the masked body: a fenced example nested under an\n // item must never be able to close it.\n for (const found of findMarkers(item.maskedBody)) {\n signals.push({\n source: 'marker',\n disposition: found.disposition,\n raw: found.raw,\n at: at(item.textStart + found.start, item.textStart + found.end),\n });\n }\n\n const struck = strikethroughOf(item.firstLine);\n if (struck) {\n signals.push({\n source: 'strikethrough',\n disposition: 'satisfied',\n raw: struck,\n at: at(item.textStart, item.textStart + item.firstLine.length),\n });\n }\n\n // The scanner reads a checkbox only from the ten characters above, and gives\n // it a start exactly when it gives it a character: these guards narrow types\n // and decide nothing, so their mutants are equivalent.\n if (item.checkbox !== null && item.checkboxStart !== null) {\n const mapped = CHECKBOX_STATE[item.checkbox];\n if (mapped) {\n signals.push({\n source: 'checkbox',\n disposition: mapped,\n raw: `[${item.checkbox}]`,\n at: at(item.checkboxStart, item.checkboxStart + 3),\n });\n }\n }\n\n if (input.inObligationSection) {\n signals.push({\n source: 'section',\n disposition: 'unresolved',\n // An item in an obligation section is under a heading, so the fallback\n // narrows the type and is never read.\n raw: input.section[input.section.length - 1] ?? '',\n at: at(item.start, item.end),\n });\n }\n\n if (signals.length === 0) {\n const fallback: StateSignal = {\n source: 'default',\n disposition: 'unresolved',\n raw: '',\n at: at(item.start, item.end),\n };\n return { disposition: 'unresolved', openness: 'open', evidence: fallback, conflicts: [] };\n }\n\n // The most specific signal wins; ties go to the one written last, which is\n // the one a reader of the document sees as the final word.\n //\n // Signals are pushed most specific first and markers in document order, so\n // the winner only changes on a tie, where array order and document order\n // agree. Mutants of the comparisons are equivalent for that reason; they are\n // written out so that neither fact about the pushes above is load-bearing.\n let winner = signals[0] as StateSignal;\n for (const signal of signals) {\n const better = SIGNAL_PRIORITY[signal.source] - SIGNAL_PRIORITY[winner.source];\n if (better > 0 || (better === 0 && signal.at.span.start.offset > winner.at.span.start.offset)) {\n winner = signal;\n }\n }\n\n const openness = OPENNESS_OF[winner.disposition];\n // Only disagreement about *openness* is a conflict. A body that says\n // \"Resolved\" under a ticked checkbox agrees with it; one under an empty\n // checkbox does not, and that is worth a human looking at. The winner agrees\n // with itself, so it is never among them.\n const conflicts = signals.filter(\n (signal) => signal.source !== 'section' && OPENNESS_OF[signal.disposition] !== openness,\n );\n\n return { disposition: winner.disposition, openness, evidence: winner, conflicts };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Marker matching */\n/* -------------------------------------------------------------------------- */\n\ninterface MarkerHit {\n readonly disposition: Disposition;\n readonly raw: string;\n readonly start: number;\n readonly end: number;\n}\n\nfunction buildMarkerPattern(): RegExp {\n const phrases = Object.values(MARKERS)\n .flat()\n // Longest first so `partially resolved` is not eaten by `resolved`.\n .sort((a, b) => b.length - a.length)\n .map(escapeRegExp)\n // Allow the writer to space, hyphenate or underscore a multi-word marker\n // whichever way, and to type its apostrophe straight or curly.\n .map((phrase) => phrase.replace(/\\\\?[ -]/g, '[ _\\\\-]').replace(/'/g, \"['’]\"));\n\n return new RegExp(\n // A marker only counts at the start of a statement: the start of the item,\n // a new line, after a sentence ends, or after an emphasis opener.\n String.raw`(?:^|\\n|[.?!]\\s+|[(\\[])` +\n // Skip block-quote and list markers, but only when the marker character is\n // followed by whitespace. Without that guard the `*` of `**Resolved**`\n // reads as a bullet and the emphasis wrapper is lost.\n String.raw`(?:[ \\t>•]|(?:[-*+]|\\d{1,9}[.)])[ \\t]+)*` +\n // Optional emphasis wrapper, remembered so `**Moot**` qualifies without a colon.\n String.raw`(\\*{1,2}|_{1,2})?` +\n String.raw`\\s*(${phrases.join('|')})` +\n String.raw`(\\*{1,2}|_{1,2})?` +\n // An optional dated parenthetical: `Resolved (2026-03):`.\n String.raw`\\s*(?:[(\\[][^)\\]\\n]{0,60}[)\\]])?` +\n // Optional, and validated in code below: an emphasised or shouted marker\n // stands on its own, a bare lower-case word never does.\n String.raw`\\s*([::]|[-–—]\\s|\\.|$|\\n)?`,\n 'gim',\n );\n}\n\nfunction findMarkers(body: string): MarkerHit[] {\n const out: MarkerHit[] = [];\n\n MARKER_PATTERN.lastIndex = 0;\n for (let m = MARKER_PATTERN.exec(body); m !== null; m = MARKER_PATTERN.exec(body)) {\n const phrase = m[2] as string;\n const emphasisOpen = m[1];\n const emphasisClose = m[3];\n // A dash arrives with the space after it. An absent qualifier is none of the\n // four, so what stands in for it is equivalent as long as it is not one.\n // Only the dash alternative puts a dash in the qualifier at all, so the\n // anchor on the dash test decides nothing either.\n const qualifier = m[4] ?? '';\n\n const emphasised = emphasisOpen !== undefined && emphasisClose !== undefined;\n const punctuated = qualifier === ':' || qualifier === ':' || /^[-–—]/.test(qualifier) || qualifier === '.';\n const shouted = phrase === phrase.toUpperCase() && /[A-Z]/.test(phrase);\n\n // Without one of these three the match is ordinary prose. \"We resolved to\n // keep the queue\" must not close an item.\n if (!emphasised && !punctuated && !shouted) continue;\n\n // Every phrase the pattern matches is a table phrase respelled, so this\n // narrows the type and never skips: its mutant is equivalent.\n const disposition = MARKER_LOOKUP.get(normalisePhrase(phrase));\n if (!disposition) continue;\n\n const start = (m.index ?? 0) + (m[0] as string).indexOf(phrase);\n out.push({ disposition, raw: phrase, start, end: start + phrase.length });\n\n // Overlapping alternatives would double-count; resume after this match.\n MARKER_PATTERN.lastIndex = start + phrase.length;\n }\n\n return out;\n}\n\n/** Built once: this used to be rebuilt for every item in the corpus. */\nfunction markerLookup(): Map<string, Disposition> {\n const map = new Map<string, Disposition>();\n for (const [disposition, phrases] of Object.entries(MARKERS) as [Disposition, readonly string[]][]) {\n for (const phrase of phrases) map.set(normalisePhrase(phrase), disposition);\n }\n return map;\n}\n\n/**\n * The key a phrase is looked up by, from the table and from a match alike.\n *\n * Its case and separator mutants are equivalent, because it folds both sides\n * of one lookup: folded to upper case, or with the separator dropped, the same\n * phrases share a key - no two of the table's dispositions collide without\n * their spaces - and neither a table phrase nor a match ever holds two\n * separators in a row. The apostrophe is folded for the match alone: the table\n * spells it straight.\n */\nfunction normalisePhrase(phrase: string): string {\n return phrase.toLowerCase().replace(/’/g, \"'\").replace(/[\\s_-]+/g, ' ');\n}\n\n/** Returns the struck text when the whole line, which arrives trimmed, is wrapped in `~~`. */\nfunction strikethroughOf(line: string): string | null {\n if (!line.startsWith('~~') || !line.endsWith('~~')) return null;\n const inner = line.slice(2, -2);\n // Something has to be struck: `~~~~~` is the fence of a code block nested\n // under the item, and strikes nothing.\n return /[^~]/.test(inner) && !inner.includes('~~') ? line : null;\n}\n\n/**\n * No phrase in the table holds a metacharacter today, so blanking the\n * replacement is an equivalent mutant; this keeps the dots of a future `n.a.`\n * dots, rather than any character.\n */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Exposed for the query language, which lets users filter on these names. */\nexport const KNOWN_STATE_WORDS: readonly string[] = Object.keys(STATE_ALIASES);\n"]}
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Item state resolution.\n *\n * The premise of this module is that `- [ ]` and `- [x]` are the *least*\n * reliable statement a document makes about an obligation. Real specifications\n * record outcomes in prose, on a continuation line, days after the checkbox was\n * written and nobody went back to tick it:\n *\n * ```md\n * - [ ] Should the write path shard before the migration?\n * **Resolved (2026-03):** no - one node holds three years of growth.\n * ```\n *\n * A grep for `- [ ]` calls that an open question and re-opens a settled debate.\n * A grep for `- [x]` misses it entirely. spec-graph reads both signals, lets the\n * more specific one win, and *reports the disagreement* rather than hiding it,\n * because a checkbox that contradicts its own body is itself a defect worth\n * knowing about.\n */\n\nimport type { Directive } from './directives.js';\nimport { attr } from './directives.js';\nimport type { ListItem } from './markdown.js';\nimport { refOf, type LineIndex } from './source.js';\nimport { OPENNESS_OF, type Disposition, type Openness, type SourceRef, type StateSignal } from './types.js';\n\n/** Which signal wins when two disagree. Higher is more specific. */\nconst SIGNAL_PRIORITY: Readonly<Record<StateSignal['source'], number>> = {\n directive: 5,\n marker: 4,\n strikethrough: 3,\n checkbox: 2,\n section: 1,\n default: 0,\n};\n\n/**\n * Checkbox characters.\n *\n * `[ ]` and `[x]` are universal; the rest come from conventions that several\n * ecosystems share (`[~]` partial, `[-]` dropped, `[?]` question, `[/]` in\n * progress). Reading them costs nothing and refusing to would throw away the\n * one place a document states its nuance unambiguously.\n */\nconst CHECKBOX_STATE: Readonly<Record<string, Disposition>> = {\n ' ': 'unresolved',\n x: 'satisfied',\n X: 'satisfied',\n '+': 'satisfied',\n '~': 'narrowed',\n '/': 'narrowed',\n '-': 'rejected',\n '?': 'unresolved',\n '!': 'unresolved',\n '*': 'unresolved',\n};\n\n/**\n * Prose markers, by the state they announce.\n *\n * Matched only in the qualified forms real documents use - `**Resolved:**`,\n * `RESOLVED -`, `**Moot**` - never as bare words, so ordinary prose such as\n * \"we resolved to keep the queue\" does not close an item. A Chinese marker,\n * Traditional or Simplified, is the English one it translates, and has no\n * case to shout in: `已決定:` or `**已決定**`, never `我們已決定採用`.\n */\nconst MARKERS: Readonly<Record<Disposition, readonly string[]>> = {\n satisfied: [\n 'resolved',\n 'answered',\n 'decided',\n 'settled',\n 'done',\n 'complete',\n 'completed',\n 'closed',\n 'fixed',\n 'shipped',\n 'addressed',\n 'implemented',\n 'solved',\n 'confirmed',\n '已解決',\n '已解决',\n '已決定',\n '已决定',\n '已定案',\n ],\n narrowed: [\n 'partially resolved',\n 'partially answered',\n 'partially done',\n 'partly resolved',\n 'narrowed',\n 'narrowed to',\n 'scoped down',\n 'reduced scope',\n 'reduced to',\n 'partial',\n 'in progress',\n 'remaining',\n 'split',\n '部分解決',\n '部分解决',\n ],\n delegated: [\n 'delegated',\n 'delegated to',\n 'moved to',\n 'tracked in',\n 'tracked by',\n 'handed off',\n 'handed off to',\n 'handed to',\n 'deferred to',\n 'follow up in',\n 'follow-up in',\n 'followup in',\n 'continued in',\n 'owned by',\n '已移交',\n ],\n 'accepted-debt': [\n 'accepted debt',\n 'accepted as debt',\n 'accepted risk',\n 'known limitation',\n 'known issue',\n 'known gap',\n 'technical debt',\n 'tech debt',\n 'wontfix',\n \"won't fix\",\n 'will not fix',\n 'by design',\n 'tolerated',\n 'living with it',\n '技術債',\n '技术债',\n '已知限制',\n ],\n rejected: [\n 'rejected',\n 'declined',\n 'dropped',\n 'not doing',\n 'will not do',\n 'abandoned',\n 'cancelled',\n 'canceled',\n 'withdrawn',\n ],\n obviated: [\n 'no longer applicable',\n 'no longer relevant',\n 'no longer needed',\n 'overtaken by events',\n 'premise invalid',\n 'obviated',\n 'obsolete',\n 'not applicable',\n 'moot',\n 'void',\n 'n/a',\n 'obe',\n ],\n unresolved: ['unresolved', 'still open', 'open question', 'tbd', 'undecided', 'reopened'],\n};\n\n/** Words accepted in a `state=` attribute, including friendly synonyms. */\nconst STATE_ALIASES: Readonly<Record<string, Disposition>> = {\n open: 'unresolved',\n unresolved: 'unresolved',\n todo: 'unresolved',\n narrowed: 'narrowed',\n partial: 'narrowed',\n delegated: 'delegated',\n satisfied: 'satisfied',\n resolved: 'satisfied',\n done: 'satisfied',\n closed: 'satisfied',\n 'accepted-debt': 'accepted-debt',\n debt: 'accepted-debt',\n wontfix: 'accepted-debt',\n rejected: 'rejected',\n declined: 'rejected',\n obviated: 'obviated',\n moot: 'obviated',\n};\n\nconst MARKER_PATTERN = buildMarkerPattern();\nconst MARKER_LOOKUP = markerLookup();\n\nexport interface StateInput {\n readonly file: string;\n readonly index: LineIndex;\n readonly item: ListItem;\n /** Heading path the item sits under. */\n readonly section: readonly string[];\n /** A `@spec-item` directive attached to the item, when there is one. */\n readonly directive: Directive | null;\n /** True when the item sits under a heading that declares obligations. */\n readonly inObligationSection: boolean;\n}\n\nexport interface ResolvedState {\n readonly disposition: Disposition;\n readonly openness: Openness;\n readonly evidence: StateSignal;\n /** Signals that disagree with `evidence` about how open the item is. */\n readonly conflicts: readonly StateSignal[];\n}\n\n/** Collects every state signal on an item and decides between them. */\nexport function resolveItemState(input: StateInput): ResolvedState {\n const { file, index, item } = input;\n const signals: StateSignal[] = [];\n\n const at = (start: number, end: number): SourceRef => refOf(file, index, start, end);\n\n if (input.directive) {\n const declared = attr(input.directive, 'state');\n if (declared) {\n const mapped = STATE_ALIASES[declared.value.toLowerCase()];\n if (mapped) {\n signals.push({\n source: 'directive',\n disposition: mapped,\n raw: declared.value,\n at: at(declared.start, declared.end),\n });\n }\n }\n }\n\n // Markers are searched in the masked body: a fenced example nested under an\n // item must never be able to close it.\n for (const found of findMarkers(item.maskedBody)) {\n signals.push({\n source: 'marker',\n disposition: found.disposition,\n raw: found.raw,\n at: at(item.textStart + found.start, item.textStart + found.end),\n });\n }\n\n const struck = strikethroughOf(item.firstLine);\n if (struck) {\n signals.push({\n source: 'strikethrough',\n disposition: 'satisfied',\n raw: struck,\n at: at(item.textStart, item.textStart + item.firstLine.length),\n });\n }\n\n // The scanner reads a checkbox only from the ten characters above, and gives\n // it a start exactly when it gives it a character: these guards narrow types\n // and decide nothing, so their mutants are equivalent.\n if (item.checkbox !== null && item.checkboxStart !== null) {\n const mapped = CHECKBOX_STATE[item.checkbox];\n if (mapped) {\n signals.push({\n source: 'checkbox',\n disposition: mapped,\n raw: `[${item.checkbox}]`,\n at: at(item.checkboxStart, item.checkboxStart + 3),\n });\n }\n }\n\n if (input.inObligationSection) {\n signals.push({\n source: 'section',\n disposition: 'unresolved',\n // An item in an obligation section is under a heading, so the fallback\n // narrows the type and is never read.\n raw: input.section[input.section.length - 1] ?? '',\n at: at(item.start, item.end),\n });\n }\n\n if (signals.length === 0) {\n const fallback: StateSignal = {\n source: 'default',\n disposition: 'unresolved',\n raw: '',\n at: at(item.start, item.end),\n };\n return { disposition: 'unresolved', openness: 'open', evidence: fallback, conflicts: [] };\n }\n\n // The most specific signal wins; ties go to the one written last, which is\n // the one a reader of the document sees as the final word.\n //\n // Signals are pushed most specific first and markers in document order, so\n // the winner only changes on a tie, where array order and document order\n // agree. Mutants of the comparisons are equivalent for that reason; they are\n // written out so that neither fact about the pushes above is load-bearing.\n let winner = signals[0] as StateSignal;\n for (const signal of signals) {\n const better = SIGNAL_PRIORITY[signal.source] - SIGNAL_PRIORITY[winner.source];\n if (better > 0 || (better === 0 && signal.at.span.start.offset > winner.at.span.start.offset)) {\n winner = signal;\n }\n }\n\n const openness = OPENNESS_OF[winner.disposition];\n // Only disagreement about *openness* is a conflict. A body that says\n // \"Resolved\" under a ticked checkbox agrees with it; one under an empty\n // checkbox does not, and that is worth a human looking at. The winner agrees\n // with itself, so it is never among them.\n const conflicts = signals.filter(\n (signal) => signal.source !== 'section' && OPENNESS_OF[signal.disposition] !== openness,\n );\n\n return { disposition: winner.disposition, openness, evidence: winner, conflicts };\n}\n\n/* -------------------------------------------------------------------------- */\n/* Marker matching */\n/* -------------------------------------------------------------------------- */\n\ninterface MarkerHit {\n readonly disposition: Disposition;\n readonly raw: string;\n readonly start: number;\n readonly end: number;\n}\n\nfunction buildMarkerPattern(): RegExp {\n const phrases = Object.values(MARKERS)\n .flat()\n // Longest first so `partially resolved` is not eaten by `resolved`.\n .sort((a, b) => b.length - a.length)\n .map(escapeRegExp)\n // Allow the writer to space, hyphenate or underscore a multi-word marker\n // whichever way, and to type its apostrophe straight or curly.\n .map((phrase) => phrase.replace(/\\\\?[ -]/g, '[ _\\\\-]').replace(/'/g, \"['’]\"));\n\n return new RegExp(\n // A marker only counts at the start of a statement: the start of the item,\n // a new line, after a sentence ends, or after an emphasis opener. A\n // Chinese sentence ends at `。?!;` with a space after it or none -\n // `是否分片?已決定:不分片。` - and the space before the phrase below\n // takes any that follows.\n String.raw`(?:^|\\n|[.?!]\\s+|[。?!;]|[(\\[])` +\n // Skip block-quote and list markers, but only when the marker character is\n // followed by whitespace. Without that guard the `*` of `**Resolved**`\n // reads as a bullet and the emphasis wrapper is lost.\n String.raw`(?:[ \\t>•]|(?:[-*+]|\\d{1,9}[.)])[ \\t]+)*` +\n // Optional emphasis wrapper, remembered so `**Moot**` qualifies without a colon.\n String.raw`(\\*{1,2}|_{1,2})?` +\n String.raw`\\s*(${phrases.join('|')})` +\n String.raw`(\\*{1,2}|_{1,2})?` +\n // An optional dated parenthetical: `Resolved (2026-03):`.\n String.raw`\\s*(?:[(\\[][^)\\]\\n]{0,60}[)\\]])?` +\n // Optional, and validated in code below: an emphasised or shouted marker\n // stands on its own, a bare lower-case word never does.\n String.raw`\\s*([::]|[-–—]\\s|\\.|$|\\n)?`,\n 'gim',\n );\n}\n\nfunction findMarkers(body: string): MarkerHit[] {\n const out: MarkerHit[] = [];\n\n MARKER_PATTERN.lastIndex = 0;\n for (let m = MARKER_PATTERN.exec(body); m !== null; m = MARKER_PATTERN.exec(body)) {\n const phrase = m[2] as string;\n const emphasisOpen = m[1];\n const emphasisClose = m[3];\n // A dash arrives with the space after it. An absent qualifier is none of the\n // four, so what stands in for it is equivalent as long as it is not one.\n // Only the dash alternative puts a dash in the qualifier at all, so the\n // anchor on the dash test decides nothing either.\n const qualifier = m[4] ?? '';\n\n const emphasised = emphasisOpen !== undefined && emphasisClose !== undefined;\n const punctuated = qualifier === ':' || qualifier === ':' || /^[-–—]/.test(qualifier) || qualifier === '.';\n const shouted = phrase === phrase.toUpperCase() && /[A-Z]/.test(phrase);\n\n // Without one of these three the match is ordinary prose. \"We resolved to\n // keep the queue\" must not close an item.\n if (!emphasised && !punctuated && !shouted) continue;\n\n // Every phrase the pattern matches is a table phrase respelled, so this\n // narrows the type and never skips: its mutant is equivalent.\n const disposition = MARKER_LOOKUP.get(normalisePhrase(phrase));\n if (!disposition) continue;\n\n const start = (m.index ?? 0) + (m[0] as string).indexOf(phrase);\n out.push({ disposition, raw: phrase, start, end: start + phrase.length });\n\n // Overlapping alternatives would double-count; resume after this match.\n MARKER_PATTERN.lastIndex = start + phrase.length;\n }\n\n return out;\n}\n\n/** Built once: this used to be rebuilt for every item in the corpus. */\nfunction markerLookup(): Map<string, Disposition> {\n const map = new Map<string, Disposition>();\n for (const [disposition, phrases] of Object.entries(MARKERS) as [Disposition, readonly string[]][]) {\n for (const phrase of phrases) map.set(normalisePhrase(phrase), disposition);\n }\n return map;\n}\n\n/**\n * The key a phrase is looked up by, from the table and from a match alike.\n *\n * Its case and separator mutants are equivalent, because it folds both sides\n * of one lookup: folded to upper case, or with the separator dropped, the same\n * phrases share a key - no two of the table's dispositions collide without\n * their spaces - and neither a table phrase nor a match ever holds two\n * separators in a row. The apostrophe is folded for the match alone: the table\n * spells it straight.\n */\nfunction normalisePhrase(phrase: string): string {\n return phrase.toLowerCase().replace(/’/g, \"'\").replace(/[\\s_-]+/g, ' ');\n}\n\n/** Returns the struck text when the whole line, which arrives trimmed, is wrapped in `~~`. */\nfunction strikethroughOf(line: string): string | null {\n if (!line.startsWith('~~') || !line.endsWith('~~')) return null;\n const inner = line.slice(2, -2);\n // Something has to be struck: `~~~~~` is the fence of a code block nested\n // under the item, and strikes nothing.\n return /[^~]/.test(inner) && !inner.includes('~~') ? line : null;\n}\n\n/**\n * No phrase in the table holds a metacharacter today, so blanking the\n * replacement is an equivalent mutant; this keeps the dots of a future `n.a.`\n * dots, rather than any character.\n */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Exposed for the query language, which lets users filter on these names. */\nexport const KNOWN_STATE_WORDS: readonly string[] = Object.keys(STATE_ALIASES);\n"]}
|
|
@@ -87,7 +87,17 @@ export declare function readFrontMatter(source: string, options?: ReadOptions):
|
|
|
87
87
|
export declare function parseInline(text: string): YamlValue;
|
|
88
88
|
/** Null in the YAML 1.2 core schema: empty, `~` or `null` in any of its three spellings. */
|
|
89
89
|
export declare function isNull(scalar: YamlScalar): boolean;
|
|
90
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* Renders a string as a scalar that reads back as the same string.
|
|
92
|
+
*
|
|
93
|
+
* Plain only as a YAML plain scalar that reads back as itself: a letter
|
|
94
|
+
* first, of any script, then letters, marks, digits, spaces and `._/+-` - so
|
|
95
|
+
* a status of `封存` or `已接受` is written as it is typed - with no space at
|
|
96
|
+
* the end, and none of `true`, `false`, `null`, `yes`, `no`, `on`, `off` or
|
|
97
|
+
* `~` in any case, which YAML 1.2 or 1.1 reads as a boolean or a null.
|
|
98
|
+
* Anything else is quoted: an indicator, `: ` or ` #`, a number, a space at
|
|
99
|
+
* either end, a full-width space or punctuation.
|
|
100
|
+
*/
|
|
91
101
|
export declare function renderScalar(value: string): string;
|
|
92
102
|
export declare function findEntry(frontMatter: FrontMatter | null, key: string): FrontMatterEntry | undefined;
|
|
93
103
|
/**
|
|
@@ -30,7 +30,14 @@ export function frontMatterKind(firstLine) {
|
|
|
30
30
|
export function frontMatterCloses(line, kind) {
|
|
31
31
|
return (kind === 'yaml' ? YAML_CLOSE : TOML_FENCE).test(line);
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* A key and what follows its colon. A key is a word of any script, as a YAML
|
|
35
|
+
* plain key may be: a letter or `_` first, then letters, marks, digits and
|
|
36
|
+
* `_.-`, so `狀態` and `状态` are keys as `status` is. Nothing else is one: a
|
|
37
|
+
* digit or a mark first, a space, punctuation, and a full-width colon, which
|
|
38
|
+
* YAML does not read as one.
|
|
39
|
+
*/
|
|
40
|
+
const KEY = /^([\p{L}_][\p{L}\p{M}\p{N}_.-]*)[ \t]*:(?:[ \t]+(.*))?$/u;
|
|
34
41
|
const SEQUENCE_ITEM = /^([ \t]*)-(?:[ \t]+(.*))?$/;
|
|
35
42
|
const BLANK_OR_COMMENT = /^[ \t]*(?:#.*)?$/;
|
|
36
43
|
export function keyName(key) {
|
|
@@ -415,10 +422,20 @@ function parseFlowSequence(text) {
|
|
|
415
422
|
export function isNull(scalar) {
|
|
416
423
|
return !scalar.quoted && ['', '~', 'null', 'Null', 'NULL'].includes(scalar.text);
|
|
417
424
|
}
|
|
418
|
-
/**
|
|
425
|
+
/**
|
|
426
|
+
* Renders a string as a scalar that reads back as the same string.
|
|
427
|
+
*
|
|
428
|
+
* Plain only as a YAML plain scalar that reads back as itself: a letter
|
|
429
|
+
* first, of any script, then letters, marks, digits, spaces and `._/+-` - so
|
|
430
|
+
* a status of `封存` or `已接受` is written as it is typed - with no space at
|
|
431
|
+
* the end, and none of `true`, `false`, `null`, `yes`, `no`, `on`, `off` or
|
|
432
|
+
* `~` in any case, which YAML 1.2 or 1.1 reads as a boolean or a null.
|
|
433
|
+
* Anything else is quoted: an indicator, `: ` or ` #`, a number, a space at
|
|
434
|
+
* either end, a full-width space or punctuation.
|
|
435
|
+
*/
|
|
419
436
|
export function renderScalar(value) {
|
|
420
437
|
const reserved = /^(?:true|false|null|yes|no|on|off|~)$/i.test(value);
|
|
421
|
-
const plain =
|
|
438
|
+
const plain = /^\p{L}[\p{L}\p{M}\p{N} ._/+-]*$/u.test(value) && !value.endsWith(' ');
|
|
422
439
|
return plain && !reserved ? value : JSON.stringify(value);
|
|
423
440
|
}
|
|
424
441
|
export function findEntry(frontMatter, key) {
|