@descent-vtt/spec-brief 0.8.0 → 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 +102 -0
- package/README.md +56 -6
- package/dist/archive.js.map +1 -1
- package/dist/brief.js.map +1 -1
- package/dist/cli.d.ts +18 -1
- package/dist/cli.js +273 -55
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -2
- package/dist/config.js.map +1 -1
- package/dist/corpus.js +1 -0
- package/dist/corpus.js.map +1 -1
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +74 -12
- package/dist/engine.js.map +1 -1
- package/dist/fs.d.ts +15 -1
- package/dist/fs.js +21 -8
- package/dist/fs.js.map +1 -1
- package/dist/git.d.ts +11 -0
- package/dist/git.js +38 -4
- package/dist/git.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lint.js.map +1 -1
- package/dist/literal.d.ts +11 -0
- package/dist/literal.js +14 -0
- package/dist/literal.js.map +1 -0
- package/dist/markdown.js.map +1 -1
- package/dist/plugins.js +2 -0
- package/dist/plugins.js.map +1 -1
- package/dist/report.js +3 -2
- package/dist/report.js.map +1 -1
- package/dist/rules.js +2 -1
- package/dist/rules.js.map +1 -1
- package/dist/schedule.js.map +1 -1
- package/dist/schema.js.map +1 -1
- package/dist/scope.js.map +1 -1
- package/dist/text.js.map +1 -1
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -1
- package/dist/vendor/spec-core/markdown/headings.js.map +1 -1
- package/dist/vendor/spec-core/markdown/links.js.map +1 -1
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -1
- package/dist/vendor/spec-core/pattern/automaton.js.map +1 -1
- package/dist/vendor/spec-core/pattern/charset.js.map +1 -1
- package/dist/vendor/spec-core/pattern/regex.js.map +1 -1
- package/dist/vendor/spec-core/text/lines.js.map +1 -1
- package/dist/vendor/spec-core/text/width.js.map +1 -1
- package/package.json +1 -1
package/dist/corpus.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corpus.js","sourceRoot":"","sources":["../src/corpus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Every brief of a repository at once, and the dependency graph between them.\n */\n\nimport { parseBrief, type Brief } from './brief.js';\nimport type { Config } from './config.js';\nimport { normalisePath } from './links.js';\nimport type { Phase } from './types.js';\n\nexport interface SourceFile {\n readonly path: string;\n readonly text: string;\n readonly phase: Phase;\n}\n\nexport interface Corpus {\n readonly config: Config;\n /** Sorted by path. */\n readonly briefs: readonly Brief[];\n readonly live: readonly Brief[];\n readonly archived: readonly Brief[];\n}\n\nexport function buildCorpus(files: readonly SourceFile[], config: Config): Corpus {\n // Paths are unique, so no two compare equal.\n const briefs = [...files]\n .sort((a, b) => (a.path < b.path ? -1 : 1))\n .map((file) => parseBrief(file.path, file.text, config, file.phase));\n return {\n config,\n briefs,\n live: briefs.filter((b) => b.phase === 'live'),\n archived: briefs.filter((b) => b.phase === 'archived'),\n };\n}\n\n/**\n * The form in which ids are compared. Numeric ids compare by value, so a\n * dependency written `34` - which YAML would read as a number anyway - names\n * brief `034`.\n */\nexport function idKey(id: string): string {\n return /^\\d+$/.test(id) ? String(Number(id)) : id;\n}\n\n/** Briefs an id, a file name or a path names. More than one is an ambiguity the caller reports. */\nexport function findBriefs(corpus: Corpus, reference: string): Brief[] {\n const key = idKey(reference.trim());\n // The null test is for the types: a missing id is no key, and equals none.\n const byId = corpus.briefs.filter((b) => b.id !== null && idKey(b.id) === key);\n if (byId.length > 0) return byId;\n try {\n const path = normalisePath(reference);\n return corpus.briefs.filter((b) => b.file === path || b.name === path);\n } catch {\n // A path that leaves the repository names no brief in it.\n return [];\n }\n}\n\n/** The brief a dependency names, preferring a live one when an id is ambiguous. */\nexport function resolveDependency(corpus: Corpus, id: string): Brief | undefined {\n const found = findBriefs(corpus, id);\n return found.find((b) => b.phase === 'live') ?? found[0];\n}\n\n/** Briefs grouped by id, for the ids more than one file claims. */\nexport function duplicateIds(corpus: Corpus): Map<string, Brief[]> {\n const groups = new Map<string, Brief[]>();\n for (const brief of corpus.briefs) {\n if (brief.id === null) continue;\n const key = idKey(brief.id);\n groups.set(key, [...(groups.get(key) ?? []), brief]);\n }\n return new Map([...groups].filter(([, members]) => members.length > 1));\n}\n\n/** The live dependencies of a brief that are not archived yet. */\nexport function pendingDependencies(corpus: Corpus, brief: Brief): string[] {\n return brief.dependsOn.filter((dependency) => {\n const target = resolveDependency(corpus, dependency);\n return target !== undefined && target.phase === 'live';\n });\n}\n\n/**\n * A live brief whose every dependency is archived can be executed now, unless\n * it is still being written or is deferred until its trigger.\n */\nexport function isReady(corpus: Corpus, brief: Brief): boolean {\n if (brief.phase !== 'live' || brief.status === 'draft' || brief.status === 'deferred') return false;\n return brief.dependsOn.every((dependency) => resolveDependency(corpus, dependency)?.phase === 'archived');\n}\n\nfunction label(brief: Brief): string {\n return brief.id ?? brief.name;\n}\n\n/** Briefs by id, numbers by value; by path where two ids agree. */\nexport function byId(a: Brief, b: Brief): number {\n const x = label(a);\n const y = label(b);\n const numeric = /^\\d+$/.test(x) && /^\\d+$/.test(y);\n if (numeric && Number(x) !== Number(y)) return Number(x) - Number(y);\n if (x < y) return -1;\n if (x > y) return 1;\n return a.file < b.file ? -1 : a.file > b.file ? 1 : 0;\n}\n\n/** The live briefs a brief depends on, by id. Archived ones are done, and unknown ones are the dependency rule's. */\nexport function liveDependencies(corpus: Corpus, brief: Brief): Brief[] {\n const targets = brief.dependsOn\n .map((d) => resolveDependency(corpus, d))\n .filter((t): t is Brief => t !== undefined && t !== brief && t.phase === 'live');\n return [...new Set(targets)].sort(byId);\n}\n\n/** A live brief that cannot start before deferred work, and the dependency it waits through. */\nexport interface Waiting {\n readonly brief: Brief;\n /** The deferred brief it depends on, or the brief through which it waits on one; the first by id. */\n readonly waitsOn: Brief;\n}\n\n/**\n * Live briefs that wait on deferred work: each depends on a deferred brief,\n * or on a brief that waits on one. Neither can start before an event nobody\n * has scheduled, so neither runs in a wave (ADR-0011): `schedule` places them\n * nowhere and `matrix` compares them with nothing. By id.\n */\nexport function waitingOnDeferred(corpus: Corpus): Waiting[] {\n const candidates = corpus.live.filter((b) => b.status !== 'deferred');\n const dependencies = new Map(candidates.map((b) => [b, liveDependencies(corpus, b)]));\n const held = new Set<Brief>();\n const holds = (d: Brief): boolean => d.status === 'deferred' || held.has(d);\n // Each pass holds another brief or is the last, so there is at most one pass more than there are briefs.\n for (let grew = true; grew; ) {\n grew = false;\n for (const brief of candidates) {\n if (held.has(brief) || !(dependencies.get(brief) as Brief[]).some(holds)) continue;\n held.add(brief);\n grew = true;\n }\n }\n return [...held].sort(byId).map((brief) => ({ brief, waitsOn: (dependencies.get(brief) as Brief[]).find(holds) as Brief }));\n}\n\n/**\n * Dependency cycles among live briefs, each as the ids along it with the first\n * repeated at the end. Found with Tarjan's algorithm; each strongly connected\n * component is reported once, starting from its first brief by path.\n */\nexport function dependencyCycles(corpus: Corpus): Brief[][] {\n const nodes = corpus.live;\n const edges = new Map<Brief, Brief[]>();\n for (const brief of nodes) {\n // A brief that depends on itself is the dependency rule's finding; left\n // in, the edge would be the shortest way round any cycle it is part of.\n const targets = brief.dependsOn\n .map((d) => resolveDependency(corpus, d))\n .filter((t): t is Brief => t !== undefined && t !== brief && t.phase === 'live');\n edges.set(brief, targets);\n }\n\n const index = new Map<Brief, number>();\n const low = new Map<Brief, number>();\n // A component is popped down to the brief it started from, so nothing under that brief is ever read.\n const stack: Brief[] = [];\n const onStack = new Set<Brief>();\n const components: Brief[][] = [];\n let counter = 0;\n\n // Recursion depth is bounded by the number of live briefs.\n const connect = (v: Brief): void => {\n index.set(v, counter);\n low.set(v, counter);\n counter += 1;\n stack.push(v);\n onStack.add(v);\n for (const w of edges.get(v) as Brief[]) {\n if (!index.has(w)) {\n connect(w);\n low.set(v, Math.min(low.get(v) as number, low.get(w) as number));\n } else if (onStack.has(w)) {\n low.set(v, Math.min(low.get(v) as number, index.get(w) as number));\n }\n }\n if (low.get(v) === index.get(v)) {\n const component: Brief[] = [];\n for (;;) {\n const w = stack.pop() as Brief;\n onStack.delete(w);\n component.push(w);\n if (w === v) break;\n }\n if (component.length > 1) components.push(component);\n }\n };\n // Visited again, a brief would be a component of one, which is no cycle: the test saves the visit.\n for (const brief of nodes) if (!index.has(brief)) connect(brief);\n\n return components.map((component) => {\n const start = [...component].sort((a, b) => (a.file < b.file ? -1 : 1))[0] as Brief;\n return cyclePath(start, edges);\n });\n}\n\n/**\n * A cycle as a finding's subject: the briefs on it, each once, in order. `lint`\n * and `schedule` word a cycle differently and report it as one problem.\n */\nexport function cycleSubject(cycle: readonly Brief[]): string {\n return [...new Set(cycle.map((b) => b.id ?? b.name))].sort().join(', ');\n}\n\n/**\n * The shortest path from a brief in a cycle back to itself, found\n * breadth-first. Every brief on a way back is in the start's component, and\n * no brief the search reaches outside the component leads back into it, so\n * the search need not be told which briefs those are: the others are visited\n * and lead nowhere.\n */\nfunction cyclePath(start: Brief, edges: ReadonlyMap<Brief, Brief[]>): Brief[] {\n const previous = new Map<Brief, Brief>();\n const queue: Brief[] = [start];\n // Ends by construction: a component of two or more always leads back to its start.\n for (let head = 0; ; head += 1) {\n const node = queue[head] as Brief;\n for (const next of edges.get(node) as Brief[]) {\n if (next === start) {\n const path: Brief[] = [start];\n // Every brief queued was reached from the start, so the walk back ends there.\n for (let at = node; at !== start; at = previous.get(at) as Brief) path.splice(1, 0, at);\n return [...path, start];\n }\n if (!previous.has(next)) {\n previous.set(next, node);\n queue.push(next);\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"corpus.js","sourceRoot":"","sources":["../src/corpus.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Every brief of a repository at once, and the dependency graph between them.\n */\n\nimport { parseBrief, type Brief } from './brief.js';\nimport type { Config } from './config.js';\nimport { normalisePath } from './links.js';\nimport type { Phase } from './types.js';\n\nexport interface SourceFile {\n readonly path: string;\n readonly text: string;\n readonly phase: Phase;\n}\n\nexport interface Corpus {\n readonly config: Config;\n /** Sorted by path. */\n readonly briefs: readonly Brief[];\n readonly live: readonly Brief[];\n readonly archived: readonly Brief[];\n}\n\nexport function buildCorpus(files: readonly SourceFile[], config: Config): Corpus {\n // Paths are unique, so no two compare equal.\n const briefs = [...files]\n .sort((a, b) => (a.path < b.path ? -1 : 1))\n .map((file) => parseBrief(file.path, file.text, config, file.phase));\n return {\n config,\n briefs,\n live: briefs.filter((b) => b.phase === 'live'),\n archived: briefs.filter((b) => b.phase === 'archived'),\n };\n}\n\n/**\n * The form in which ids are compared. Numeric ids compare by value, so a\n * dependency written `34` - which YAML would read as a number anyway - names\n * brief `034`.\n */\nexport function idKey(id: string): string {\n return /^\\d+$/.test(id) ? String(Number(id)) : id;\n}\n\n/** Briefs an id, a file name or a path names. More than one is an ambiguity the caller reports. */\nexport function findBriefs(corpus: Corpus, reference: string): Brief[] {\n const key = idKey(reference.trim());\n // The null test is for the types: a missing id is no key, and equals none.\n const byId = corpus.briefs.filter((b) => b.id !== null && idKey(b.id) === key);\n if (byId.length > 0) return byId;\n try {\n const path = normalisePath(reference);\n return corpus.briefs.filter((b) => b.file === path || b.name === path);\n } catch {\n // A path that leaves the repository names no brief in it.\n return [];\n }\n}\n\n/** The brief a dependency names, preferring a live one when an id is ambiguous. */\nexport function resolveDependency(corpus: Corpus, id: string): Brief | undefined {\n const found = findBriefs(corpus, id);\n return found.find((b) => b.phase === 'live') ?? found[0];\n}\n\n/** Briefs grouped by id, for the ids more than one file claims. */\nexport function duplicateIds(corpus: Corpus): Map<string, Brief[]> {\n const groups = new Map<string, Brief[]>();\n for (const brief of corpus.briefs) {\n if (brief.id === null) continue;\n const key = idKey(brief.id);\n groups.set(key, [...(groups.get(key) ?? []), brief]);\n }\n return new Map([...groups].filter(([, members]) => members.length > 1));\n}\n\n/** The live dependencies of a brief that are not archived yet. */\nexport function pendingDependencies(corpus: Corpus, brief: Brief): string[] {\n return brief.dependsOn.filter((dependency) => {\n const target = resolveDependency(corpus, dependency);\n return target !== undefined && target.phase === 'live';\n });\n}\n\n/**\n * A live brief whose every dependency is archived can be executed now, unless\n * it is still being written or is deferred until its trigger.\n */\nexport function isReady(corpus: Corpus, brief: Brief): boolean {\n if (brief.phase !== 'live' || brief.status === 'draft' || brief.status === 'deferred') return false;\n return brief.dependsOn.every((dependency) => resolveDependency(corpus, dependency)?.phase === 'archived');\n}\n\nfunction label(brief: Brief): string {\n return brief.id ?? brief.name;\n}\n\n/** Briefs by id, numbers by value; by path where two ids agree. */\nexport function byId(a: Brief, b: Brief): number {\n const x = label(a);\n const y = label(b);\n const numeric = /^\\d+$/.test(x) && /^\\d+$/.test(y);\n if (numeric && Number(x) !== Number(y)) return Number(x) - Number(y);\n if (x < y) return -1;\n if (x > y) return 1;\n return a.file < b.file ? -1 : a.file > b.file ? 1 : 0;\n}\n\n/** The live briefs a brief depends on, by id. Archived ones are done, and unknown ones are the dependency rule's. */\nexport function liveDependencies(corpus: Corpus, brief: Brief): Brief[] {\n const targets = brief.dependsOn\n .map((d) => resolveDependency(corpus, d))\n .filter((t): t is Brief => t !== undefined && t !== brief && t.phase === 'live');\n return [...new Set(targets)].sort(byId);\n}\n\n/** A live brief that cannot start before deferred work, and the dependency it waits through. */\nexport interface Waiting {\n readonly brief: Brief;\n /** The deferred brief it depends on, or the brief through which it waits on one; the first by id. */\n readonly waitsOn: Brief;\n}\n\n/**\n * Live briefs that wait on deferred work: each depends on a deferred brief,\n * or on a brief that waits on one. Neither can start before an event nobody\n * has scheduled, so neither runs in a wave (ADR-0011): `schedule` places them\n * nowhere and `matrix` compares them with nothing. By id.\n */\nexport function waitingOnDeferred(corpus: Corpus): Waiting[] {\n const candidates = corpus.live.filter((b) => b.status !== 'deferred');\n const dependencies = new Map(candidates.map((b) => [b, liveDependencies(corpus, b)]));\n const held = new Set<Brief>();\n const holds = (d: Brief): boolean => d.status === 'deferred' || held.has(d);\n // Each pass holds another brief or is the last, so there is at most one pass more than there are briefs.\n for (let grew = true; grew; ) {\n grew = false;\n for (const brief of candidates) {\n if (held.has(brief) || !(dependencies.get(brief) as Brief[]).some(holds)) continue;\n held.add(brief);\n grew = true;\n }\n }\n return [...held].sort(byId).map((brief) => ({ brief, waitsOn: (dependencies.get(brief) as Brief[]).find(holds) as Brief }));\n}\n\n/**\n * Dependency cycles among live briefs, each as the ids along it with the first\n * repeated at the end. Found with Tarjan's algorithm; each strongly connected\n * component is reported once, starting from its first brief by path.\n */\nexport function dependencyCycles(corpus: Corpus): Brief[][] {\n const nodes = corpus.live;\n const edges = new Map<Brief, Brief[]>();\n for (const brief of nodes) {\n // A brief that depends on itself is the dependency rule's finding; left\n // in, the edge would be the shortest way round any cycle it is part of.\n const targets = brief.dependsOn\n .map((d) => resolveDependency(corpus, d))\n .filter((t): t is Brief => t !== undefined && t !== brief && t.phase === 'live');\n edges.set(brief, targets);\n }\n\n const index = new Map<Brief, number>();\n const low = new Map<Brief, number>();\n // A component is popped down to the brief it started from, so nothing under that brief is ever read.\n const stack: Brief[] = [];\n const onStack = new Set<Brief>();\n const components: Brief[][] = [];\n let counter = 0;\n\n // Recursion depth is bounded by the number of live briefs.\n const connect = (v: Brief): void => {\n index.set(v, counter);\n low.set(v, counter);\n counter += 1;\n stack.push(v);\n onStack.add(v);\n for (const w of edges.get(v) as Brief[]) {\n if (!index.has(w)) {\n connect(w);\n low.set(v, Math.min(low.get(v) as number, low.get(w) as number));\n } else if (onStack.has(w)) {\n low.set(v, Math.min(low.get(v) as number, index.get(w) as number));\n }\n }\n if (low.get(v) === index.get(v)) {\n const component: Brief[] = [];\n for (;;) {\n const w = stack.pop() as Brief;\n onStack.delete(w);\n component.push(w);\n if (w === v) break;\n }\n if (component.length > 1) components.push(component);\n }\n };\n // Visited again, a brief would be a component of one, which is no cycle: the test saves the visit.\n for (const brief of nodes) if (!index.has(brief)) connect(brief);\n\n return components.map((component) => {\n // Two briefs of a component are two files, so no two compare equal.\n const start = [...component].sort((a, b) => (a.file < b.file ? -1 : 1))[0]!;\n return cyclePath(start, edges);\n });\n}\n\n/**\n * A cycle as a finding's subject: the briefs on it, each once, in order. `lint`\n * and `schedule` word a cycle differently and report it as one problem.\n */\nexport function cycleSubject(cycle: readonly Brief[]): string {\n return [...new Set(cycle.map((b) => b.id ?? b.name))].sort().join(', ');\n}\n\n/**\n * The shortest path from a brief in a cycle back to itself, found\n * breadth-first. Every brief on a way back is in the start's component, and\n * no brief the search reaches outside the component leads back into it, so\n * the search need not be told which briefs those are: the others are visited\n * and lead nowhere.\n */\nfunction cyclePath(start: Brief, edges: ReadonlyMap<Brief, Brief[]>): Brief[] {\n const previous = new Map<Brief, Brief>();\n const queue: Brief[] = [start];\n // Ends by construction: a component of two or more always leads back to its start.\n for (let head = 0; ; head += 1) {\n const node = queue[head] as Brief;\n for (const next of edges.get(node) as Brief[]) {\n if (next === start) {\n const path: Brief[] = [start];\n // Every brief queued was reached from the start, so the walk back ends there.\n for (let at = node; at !== start; at = previous.get(at) as Brief) path.splice(1, 0, at);\n return [...path, start];\n }\n if (!previous.has(next)) {\n previous.set(next, node);\n queue.push(next);\n }\n }\n }\n}\n"]}
|
package/dist/engine.d.ts
CHANGED
|
@@ -42,6 +42,36 @@ export interface OpenOptions {
|
|
|
42
42
|
/** `null` runs without git. Defaults to git when the root is inside a working tree. */
|
|
43
43
|
readonly git?: Git | null;
|
|
44
44
|
readonly fs?: FileSystem;
|
|
45
|
+
/**
|
|
46
|
+
* `false` where nothing the caller asks of the engine reads git, as `list`
|
|
47
|
+
* asks it nothing: where a configuration file names the root, git is then
|
|
48
|
+
* not asked where the work tree's top is, and the engine has no git.
|
|
49
|
+
* Without one, git is still asked, since its answer is the root.
|
|
50
|
+
*/
|
|
51
|
+
readonly needsGit?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Everything an answer was read from, for a tool that would give it again
|
|
55
|
+
* without starting spec-brief (ADR-0002, amended 2026-10-19). Each file is
|
|
56
|
+
* named with the SHA-256 of its bytes, or `null` where it was looked for and
|
|
57
|
+
* was not there; each directory with the names of the files in it, sorted,
|
|
58
|
+
* or `null` where there was no directory to list. Each `null` is a path
|
|
59
|
+
* that must still not be there. Paths are from the root, which wherever `keep` is true is the directory
|
|
60
|
+
* the run started in. `git` is `null`, since no answer named here asks git
|
|
61
|
+
* anything. `keep` is `false`, and the lists are empty, wherever that cannot
|
|
62
|
+
* be named whole.
|
|
63
|
+
*/
|
|
64
|
+
export interface Inputs {
|
|
65
|
+
readonly keep: boolean;
|
|
66
|
+
readonly files: readonly {
|
|
67
|
+
readonly path: string;
|
|
68
|
+
readonly sha256: string | null;
|
|
69
|
+
}[];
|
|
70
|
+
readonly directories: readonly {
|
|
71
|
+
readonly path: string;
|
|
72
|
+
readonly entries: readonly string[] | null;
|
|
73
|
+
}[];
|
|
74
|
+
readonly git: null;
|
|
45
75
|
}
|
|
46
76
|
export interface ArchiveOptions {
|
|
47
77
|
readonly pr?: number | undefined;
|
|
@@ -85,6 +115,13 @@ export declare class BriefEngine {
|
|
|
85
115
|
readonly git: Git | null;
|
|
86
116
|
private plugins;
|
|
87
117
|
private loaded;
|
|
118
|
+
/**
|
|
119
|
+
* What the engine has read, where `open` made it, and whether that is
|
|
120
|
+
* every input of an answer made of the briefs and the configuration alone.
|
|
121
|
+
* Set false by a read that is not on it: the files git or a walk lists,
|
|
122
|
+
* and a plugin's module.
|
|
123
|
+
*/
|
|
124
|
+
private reading;
|
|
88
125
|
/** Known once the briefs are read, which every command does before it asks. */
|
|
89
126
|
private missingBriefs;
|
|
90
127
|
constructor(setup: EngineSetup);
|
|
@@ -127,6 +164,12 @@ export declare class BriefEngine {
|
|
|
127
164
|
written: string[];
|
|
128
165
|
refused: Finding[];
|
|
129
166
|
}>;
|
|
167
|
+
/**
|
|
168
|
+
* Every input of an answer made of the briefs and the configuration alone,
|
|
169
|
+
* as `list` gives one, read since the engine was opened (ADR-0002, amended
|
|
170
|
+
* 2026-10-19).
|
|
171
|
+
*/
|
|
172
|
+
inputs(): Inputs;
|
|
130
173
|
/** Live briefs that can run now: every dependency archived, and neither a draft nor deferred. */
|
|
131
174
|
ready(): Brief[];
|
|
132
175
|
/** The live dependencies holding a brief back. */
|
package/dist/engine.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Everything they compose is a pure function that takes those as arguments,
|
|
9
9
|
* so an engine can equally run over a `MemoryFileSystem` and no git at all.
|
|
10
10
|
*/
|
|
11
|
-
import { access } from 'node:fs/promises';
|
|
11
|
+
import { access, stat } from 'node:fs/promises';
|
|
12
12
|
import { basename, dirname, relative, resolve, sep } from 'node:path';
|
|
13
13
|
import { applyPlan } from './apply.js';
|
|
14
14
|
import { applyWaivers, planArchive, planUnarchive, WAIVABLE, } from './archive.js';
|
|
@@ -105,6 +105,13 @@ export class BriefEngine {
|
|
|
105
105
|
git;
|
|
106
106
|
plugins;
|
|
107
107
|
loaded;
|
|
108
|
+
/**
|
|
109
|
+
* What the engine has read, where `open` made it, and whether that is
|
|
110
|
+
* every input of an answer made of the briefs and the configuration alone.
|
|
111
|
+
* Set false by a read that is not on it: the files git or a walk lists,
|
|
112
|
+
* and a plugin's module.
|
|
113
|
+
*/
|
|
114
|
+
reading;
|
|
108
115
|
/** Known once the briefs are read, which every command does before it asks. */
|
|
109
116
|
missingBriefs;
|
|
110
117
|
constructor(setup) {
|
|
@@ -124,19 +131,38 @@ export class BriefEngine {
|
|
|
124
131
|
// Named as git names them, so the root and the work tree compare.
|
|
125
132
|
const cwd = await canonicalPath(resolve(options.cwd ?? process.cwd()));
|
|
126
133
|
const explicit = options.config === undefined ? undefined : await canonicalPath(resolve(cwd, options.config));
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
134
|
+
// Each name looked for, so that an answer can say which were not there.
|
|
135
|
+
const looked = [];
|
|
136
|
+
const configPath = options.noConfig === true
|
|
137
|
+
? undefined
|
|
138
|
+
: (explicit ??
|
|
139
|
+
(await locateConfig(cwd, (path) => {
|
|
140
|
+
looked.push(path);
|
|
141
|
+
return exists(path);
|
|
142
|
+
})));
|
|
143
|
+
// Asking git costs a process; without git there is nothing to ask, and
|
|
144
|
+
// with a root a configuration file names nothing to ask it where the
|
|
145
|
+
// caller reads no git.
|
|
146
|
+
const asksGit = options.git !== null && (configPath === undefined || options.needsGit !== false);
|
|
147
|
+
const toplevel = asksGit ? await NodeGit.toplevel(cwd) : null;
|
|
130
148
|
const root = configPath === undefined ? (toplevel ?? cwd) : dirname(configPath);
|
|
149
|
+
const reads = { files: new Map(), directories: new Map() };
|
|
150
|
+
// Each is named as not there, and the one found again by its read below.
|
|
151
|
+
// Wherever an answer is kept, each was looked for in the root itself, so
|
|
152
|
+
// its path from there is its name.
|
|
153
|
+
for (const path of looked)
|
|
154
|
+
reads.files.set(relative(root, path), null);
|
|
131
155
|
let config = DEFAULT_CONFIG;
|
|
132
156
|
if (configPath !== undefined) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
// Only a file is read. A named pipe under the configuration's name was
|
|
158
|
+
// read without end, by every command; and a directory, found or named
|
|
159
|
+
// with --config, ended the run on Node's EISDIR. Each is a
|
|
160
|
+
// configuration that does not load, and no defect to report a stack for.
|
|
161
|
+
const found = await stat(configPath).catch(() => null);
|
|
162
|
+
if (found !== null && !found.isFile()) {
|
|
163
|
+
throw new ConfigError(relative(cwd, configPath), [`is ${found.isDirectory() ? 'a directory' : 'neither a file nor a directory'}, not a configuration file`]);
|
|
164
|
+
}
|
|
165
|
+
const text = await new NodeFileSystem(root, reads).read(basename(configPath));
|
|
140
166
|
if (text === null)
|
|
141
167
|
throw new EngineError('not-found', `${configPath} does not exist`);
|
|
142
168
|
config = parseConfig(text, relative(cwd, configPath));
|
|
@@ -150,9 +176,18 @@ export class BriefEngine {
|
|
|
150
176
|
config,
|
|
151
177
|
// The root is the configuration's directory, so its path from there is its name.
|
|
152
178
|
configFile: configPath === undefined ? null : basename(configPath),
|
|
153
|
-
fs: options.fs ?? new NodeFileSystem(root),
|
|
179
|
+
fs: options.fs ?? new NodeFileSystem(root, reads),
|
|
154
180
|
git: options.git === undefined ? (inTree ? new NodeGit(root) : null) : options.git,
|
|
155
181
|
});
|
|
182
|
+
// Named whole only where each input is read from the directory the run
|
|
183
|
+
// started in, so that a reader finds it by the path named: the engine
|
|
184
|
+
// reads through its own disk, the root is that directory, and a
|
|
185
|
+
// configuration file there names it, or neither a configuration nor git
|
|
186
|
+
// was asked. A configuration not found was looked for above the root as
|
|
187
|
+
// well, and a root git names is decided by what git reads to find the
|
|
188
|
+
// top of a work tree, which no list here names.
|
|
189
|
+
const whole = options.fs === undefined && !asksGit && root === cwd && (configPath !== undefined || options.noConfig === true);
|
|
190
|
+
engine.reading = { reads, whole };
|
|
156
191
|
await engine.load();
|
|
157
192
|
return engine;
|
|
158
193
|
}
|
|
@@ -200,6 +235,8 @@ export class BriefEngine {
|
|
|
200
235
|
}
|
|
201
236
|
/** The plugins the configuration names, loaded once. */
|
|
202
237
|
async loadedPlugins() {
|
|
238
|
+
if (this.reading !== undefined)
|
|
239
|
+
this.reading.whole = false;
|
|
203
240
|
this.plugins ??= await loadPlugins(this.config.plugins, this.root);
|
|
204
241
|
return this.plugins;
|
|
205
242
|
}
|
|
@@ -215,6 +252,8 @@ export class BriefEngine {
|
|
|
215
252
|
}
|
|
216
253
|
/** The files git sees, or every file when there is no git; `null` when neither can be read. */
|
|
217
254
|
async repoFiles() {
|
|
255
|
+
if (this.reading !== undefined)
|
|
256
|
+
this.reading.whole = false;
|
|
218
257
|
try {
|
|
219
258
|
return this.git === null ? await this.fs.walk() : await this.git.files();
|
|
220
259
|
}
|
|
@@ -247,6 +286,29 @@ export class BriefEngine {
|
|
|
247
286
|
await this.load();
|
|
248
287
|
return { written: plan.ops.map((op) => op.path), refused: [] };
|
|
249
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Every input of an answer made of the briefs and the configuration alone,
|
|
291
|
+
* as `list` gives one, read since the engine was opened (ADR-0002, amended
|
|
292
|
+
* 2026-10-19).
|
|
293
|
+
*/
|
|
294
|
+
inputs() {
|
|
295
|
+
const reading = this.reading;
|
|
296
|
+
if (reading?.whole !== true)
|
|
297
|
+
return { keep: false, files: [], directories: [], git: null };
|
|
298
|
+
// Paths are unique, so no two compare equal.
|
|
299
|
+
const byPath = (a, b) => (a.path < b.path ? -1 : 1);
|
|
300
|
+
return {
|
|
301
|
+
keep: true,
|
|
302
|
+
files: [...reading.reads.files].map(([path, sha256]) => ({ path, sha256 })).sort(byPath),
|
|
303
|
+
// The root is "." where a briefs directory is the root itself. One that
|
|
304
|
+
// was not there, an archive not yet made, is named with no entries: one
|
|
305
|
+
// made with a brief in it changes what `list --archived` answers.
|
|
306
|
+
directories: [...reading.reads.directories]
|
|
307
|
+
.map(([path, entries]) => ({ path: path === '' ? '.' : path, entries: entries === null ? null : [...entries].sort() }))
|
|
308
|
+
.sort(byPath),
|
|
309
|
+
git: null,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
250
312
|
/** Live briefs that can run now: every dependency archived, and neither a draft nor deferred. */
|
|
251
313
|
ready() {
|
|
252
314
|
return this.corpus.live.filter((b) => isReady(this.corpus, b));
|
package/dist/engine.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The engine: one repository's briefs, with the operations the CLI offers and\n * the same ones for a harness or an orchestrator that would rather call a\n * function than parse a terminal.\n *\n * This module and the CLI are where the pieces meet their implementations -\n * the real filesystem, the real git, the plugins a configuration names.\n * Everything they compose is a pure function that takes those as arguments,\n * so an engine can equally run over a `MemoryFileSystem` and no git at all.\n */\n\nimport { access } from 'node:fs/promises';\nimport { basename, dirname, relative, resolve, sep } from 'node:path';\n\nimport { applyPlan } from './apply.js';\nimport {\n applyWaivers,\n planArchive,\n type Plan,\n planUnarchive,\n type PullRequest,\n type UnarchiveRequest,\n type Unmeasured,\n WAIVABLE,\n type Waiver,\n} from './archive.js';\nimport type { Brief } from './brief.js';\nimport { collisionFindings, collisions, type CollisionOptions, type CollisionReport } from './collisions.js';\nimport { type Config, ConfigError, DEFAULT_CONFIG, locateConfig, parseConfig, typeSections } from './config.js';\nimport { buildCorpus, type Corpus, findBriefs, isReady, pendingDependencies, type SourceFile } from './corpus.js';\nimport { canonicalPath, type FileSystem, NodeFileSystem } from './fs.js';\nimport { type CommitInfo, type FileChange, type Git, NodeGit, pullRequestUrl } from './git.js';\nimport { matchGlob, parseGlob } from './glob.js';\nimport { normalisePath } from './links.js';\nimport { lint, type Plugin } from './lint.js';\nimport { asWaivers, loadPlugins } from './plugins.js';\nimport { fileNameFor, nextId, renderNewBrief } from './scaffold.js';\nimport { planWaves, type Schedule, schedule, scheduleFindings, type ScheduleOptions } from './schedule.js';\nimport type { Finding, Phase } from './types.js';\n\nexport class EngineError extends Error {\n readonly code: 'not-found' | 'ambiguous' | 'invalid' | 'refused' | 'exists';\n\n constructor(code: EngineError['code'], message: string) {\n super(message);\n this.name = 'EngineError';\n this.code = code;\n }\n}\n\nexport interface EngineSetup {\n /** Absolute root: the directory holding the configuration. */\n readonly root: string;\n readonly config: Config;\n /** Repository-relative configuration path, or `null` when the defaults apply. */\n readonly configFile: string | null;\n readonly fs: FileSystem;\n readonly git: Git | null;\n readonly plugins?: readonly Plugin[];\n}\n\nexport interface OpenOptions {\n /** Where discovery starts. Defaults to the process's directory. */\n readonly cwd?: string;\n /** An explicit configuration file; discovery is skipped. */\n readonly config?: string | undefined;\n /** Use the defaults even where a configuration file exists. */\n readonly noConfig?: boolean;\n /** `null` runs without git. Defaults to git when the root is inside a working tree. */\n readonly git?: Git | null;\n readonly fs?: FileSystem;\n}\n\nexport interface ArchiveOptions {\n readonly pr?: number | undefined;\n /** The commit the round landed as. */\n readonly commit?: string | undefined;\n /** The branch the round started from; the diff runs from its merge base. */\n readonly base?: string | undefined;\n readonly summary?: string | undefined;\n readonly date?: string | undefined;\n readonly allowDirty?: boolean | undefined;\n readonly strict?: boolean | undefined;\n /** Leave git out, even where there is a repository. */\n readonly noGit?: boolean | undefined;\n}\n\nexport interface NewOptions {\n readonly title: string;\n readonly id?: string | undefined;\n readonly type?: string | undefined;\n readonly wave?: number | undefined;\n readonly dependsOn?: readonly string[] | undefined;\n readonly date?: string | undefined;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n // Absent, or not to be reached: the search goes on either way. An empty\n // catch would answer `undefined`, which the search reads the same.\n return false;\n }\n}\n\n/** The last second of 9999-12-31, the last date written YYYY-MM-DD. */\nconst LAST_SECOND = 253402300799;\n\n/**\n * Today, in UTC. `SOURCE_DATE_EPOCH` wins where it is set, which is what makes\n * a banner written in a reproducible pipeline reproducible.\n *\n * Set, it is what its specification says it is: whole seconds since\n * 1970-01-01 in digits alone, as `date +%s` prints them, and here no later\n * than the last date written YYYY-MM-DD. Anything else is refused by name,\n * an empty value too, which is what a command substitution that failed\n * leaves. Read as unset, it had the run write the clock's date where a\n * pipeline asked for its own, and say nothing.\n */\nexport function today(env: Readonly<Record<string, string | undefined>> = process.env): string {\n const epoch = env['SOURCE_DATE_EPOCH'];\n let when = new Date();\n if (epoch !== undefined) {\n // Quoted as JSON, so a newline or a tab in it is seen, and the refusal stays one line.\n const held = `SOURCE_DATE_EPOCH is ${JSON.stringify(epoch)}`;\n if (!/^\\d+$/.test(epoch)) {\n throw new EngineError('invalid', `${held}, which is not a whole number of seconds since 1970-01-01; set it to one in digits alone, as \"date +%s\" prints it, or unset it`);\n }\n if (Number(epoch) > LAST_SECOND) {\n throw new EngineError('invalid', `${held}, which is after 9999-12-31, the last date written YYYY-MM-DD; set it to ${LAST_SECOND} or less, or unset it`);\n }\n when = new Date(Number(epoch) * 1000);\n }\n return when.toISOString().slice(0, 10);\n}\n\n/**\n * A deep copy of a value in which every object and array is frozen. Plain\n * data only, as `structuredClone` takes it; the recursion ends at the data's\n * own depth.\n */\nfunction frozenCopy<T>(value: T): T {\n const freeze = (item: unknown): void => {\n if (typeof item !== 'object' || item === null) return;\n for (const child of Object.values(item)) freeze(child);\n Object.freeze(item);\n };\n const copy = structuredClone(value);\n freeze(copy);\n return copy;\n}\n\nexport function isDate(text: string): boolean {\n // The shape keeps out the one other text that survives the round trip\n // below: a year past 9999 with a month, \"+010000-01\". Its anchors are for\n // the reader, since nothing longer than a date comes back as it went in.\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(text)) return false;\n const parsed = new Date(`${text}T00:00:00Z`);\n return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === text;\n}\n\nexport class BriefEngine {\n readonly root: string;\n readonly config: Config;\n readonly configFile: string | null;\n readonly fs: FileSystem;\n readonly git: Git | null;\n private plugins: readonly Plugin[] | undefined;\n private loaded: Corpus | undefined;\n /** Known once the briefs are read, which every command does before it asks. */\n private missingBriefs: boolean | undefined;\n\n constructor(setup: EngineSetup) {\n this.root = setup.root;\n this.config = setup.config;\n this.configFile = setup.configFile;\n this.fs = setup.fs;\n this.git = setup.git;\n this.plugins = setup.plugins;\n }\n\n /**\n * Finds the configuration, the root it defines and the repository around it,\n * and reads every brief. The root is the directory holding the\n * configuration; without one it is the working tree's top, or `cwd`.\n */\n static async open(options: OpenOptions = {}): Promise<BriefEngine> {\n // Named as git names them, so the root and the work tree compare.\n const cwd = await canonicalPath(resolve(options.cwd ?? process.cwd()));\n const explicit = options.config === undefined ? undefined : await canonicalPath(resolve(cwd, options.config));\n const configPath = options.noConfig === true ? undefined : (explicit ?? (await locateConfig(cwd, exists)));\n // Asking git costs a process; without git there is nothing to ask.\n const toplevel = options.git === null ? null : await NodeGit.toplevel(cwd);\n const root = configPath === undefined ? (toplevel ?? cwd) : dirname(configPath);\n let config = DEFAULT_CONFIG;\n if (configPath !== undefined) {\n const text = await new NodeFileSystem(root).read(basename(configPath)).catch((error: NodeJS.ErrnoException) => {\n // A directory of that name, found or named with --config, is a\n // configuration that does not load, and no defect to report a stack for.\n if (error.code !== 'EISDIR') throw error;\n throw new ConfigError(relative(cwd, configPath), ['is a directory, not a configuration file']);\n });\n if (text === null) throw new EngineError('not-found', `${configPath} does not exist`);\n config = parseConfig(text, relative(cwd, configPath));\n }\n // Outside the work tree is its parent or a path through it, \"..\", \"../x\":\n // a directory inside it may itself be named \"..drafts\".\n const fromTop = toplevel === null ? null : relative(toplevel, root);\n const inTree = fromTop !== null && fromTop !== '..' && !fromTop.startsWith(`..${sep}`);\n const engine = new BriefEngine({\n root,\n config,\n // The root is the configuration's directory, so its path from there is its name.\n configFile: configPath === undefined ? null : basename(configPath),\n fs: options.fs ?? new NodeFileSystem(root),\n git: options.git === undefined ? (inTree ? new NodeGit(root) : null) : options.git,\n });\n await engine.load();\n return engine;\n }\n\n get corpus(): Corpus {\n if (this.loaded === undefined) throw new Error('the engine has not loaded its briefs; call load() first');\n return this.loaded;\n }\n\n /** Reads every brief again, as after an archival. */\n async load(): Promise<Corpus> {\n const files: SourceFile[] = [];\n const accept = parseGlob(this.config.files);\n const reject = this.config.exclude.map((pattern) => parseGlob(pattern));\n const isBrief = (name: string): boolean =>\n accept.ok && matchGlob(accept.glob, name) && !reject.some((r) => r.ok && matchGlob(r.glob, name));\n const read = async (directory: string, phase: Phase): Promise<boolean> => {\n const dir = normalisePath(directory);\n const names = await this.fs.list(dir);\n if (names === null) return false;\n for (const name of names) {\n if (!isBrief(name)) continue;\n const path = dir === '' ? name : `${dir}/${name}`;\n const text = await this.fs.read(path);\n // A file listed a moment ago and gone now was removed under the run; there is nothing to read.\n if (text !== null) files.push({ path, text, phase });\n }\n return true;\n };\n this.missingBriefs = !(await read(this.config.briefs, 'live'));\n await read(this.config.archive, 'archived');\n this.loaded = buildCorpus(files, this.config);\n return this.loaded;\n }\n\n /**\n * Refuses to report on a briefs directory that does not exist. A check over\n * nothing looks exactly like a clean one, and the likelier cause is a\n * configuration pointing at the wrong place.\n */\n requireBriefs(): void {\n if (this.missingBriefs) {\n throw new EngineError('not-found', `${normalisePath(this.config.briefs)}/ does not exist; run \"spec-brief init\", or set \"briefs\" in the configuration`);\n }\n }\n\n /** The plugins the configuration names, loaded once. */\n async loadedPlugins(): Promise<readonly Plugin[]> {\n this.plugins ??= await loadPlugins(this.config.plugins, this.root);\n return this.plugins;\n }\n\n /** The one brief a reference names: an id, a file name or a path. */\n find(reference: string): Brief {\n const found = findBriefs(this.corpus, reference);\n if (found.length === 0) throw new EngineError('not-found', `no brief is named \"${reference}\"`);\n if (found.length > 1) {\n throw new EngineError('ambiguous', `\"${reference}\" names ${found.length} briefs: ${found.map((b) => b.file).join(', ')}`);\n }\n return found[0] as Brief;\n }\n\n /** The files git sees, or every file when there is no git; `null` when neither can be read. */\n async repoFiles(): Promise<string[] | null> {\n try {\n return this.git === null ? await this.fs.walk() : await this.git.files();\n } catch {\n return null;\n }\n }\n\n async lint(references: readonly string[] = []): Promise<Finding[]> {\n const only = references.length === 0 ? undefined : references.map((r) => this.find(r));\n return lint(this.corpus, { plugins: await this.loadedPlugins(), repoFiles: await this.repoFiles(), ...(only ? { only } : {}) });\n }\n\n async collisions(options: CollisionOptions = {}): Promise<{ report: CollisionReport; findings: Finding[] }> {\n const report = collisions(this.corpus, { repoFiles: await this.repoFiles(), ...options });\n return { report, findings: collisionFindings(this.corpus, report) };\n }\n\n async schedule(options: ScheduleOptions = {}): Promise<{ schedule: Schedule; findings: Finding[] }> {\n const computed = schedule(this.corpus, { repoFiles: await this.repoFiles(), ...options });\n return { schedule: computed, findings: scheduleFindings(this.corpus, computed) };\n }\n\n /**\n * Writes each brief's proposed wave into its front matter, every other line\n * as it was, as one transaction. Refused, with nothing written, when the\n * dependencies form a cycle or a front matter cannot be edited.\n */\n async writeWaves(computed: Schedule): Promise<{ written: string[]; refused: Finding[] }> {\n const plan = planWaves(computed);\n if (computed.cycles.length > 0 || plan.refused.length > 0) return { written: [], refused: [...plan.refused] };\n await applyPlan(this.fs, plan.ops);\n await this.load();\n return { written: plan.ops.map((op) => op.path), refused: [] };\n }\n\n /** Live briefs that can run now: every dependency archived, and neither a draft nor deferred. */\n ready(): Brief[] {\n return this.corpus.live.filter((b) => isReady(this.corpus, b));\n }\n\n /** The live dependencies holding a brief back. */\n waitingOn(brief: Brief): string[] {\n return pendingDependencies(this.corpus, brief);\n }\n\n async planArchive(reference: string, options: ArchiveOptions = {}): Promise<Plan> {\n const brief = this.find(reference);\n const date = options.date ?? today();\n if (!isDate(date)) throw new EngineError('invalid', `\"${date}\" is not a date written YYYY-MM-DD`);\n if (options.noGit === true && (options.commit !== undefined || options.base !== undefined)) {\n throw new EngineError('invalid', 'a commit or a base needs git; drop --no-git, or drop --commit and --base');\n }\n const git = options.noGit === true ? null : this.git;\n const base = options.base ?? this.config.archiving.base ?? undefined;\n const findings = await this.lint();\n\n let commit: CommitInfo | undefined;\n let changes: FileChange[] | undefined;\n let unmeasured: Unmeasured | undefined;\n let dirty: string[] | undefined;\n let pr: PullRequest | undefined;\n if (git !== null) {\n const revision = options.commit ?? (base === undefined ? undefined : 'HEAD');\n if (revision !== undefined) {\n commit = (await git.commit(revision)) ?? undefined;\n if (commit === undefined) throw new EngineError('not-found', `\"${revision}\" names no commit`);\n let from: string | null = null;\n if (base !== undefined) {\n from = await git.mergeBase(base, commit.sha);\n // No merge base: a base with a history of its own is diffed from\n // directly, and one that names nothing is a typo to report, not a\n // diff for git to fail.\n if (from === null) {\n if ((await git.commit(base)) === null) {\n throw new EngineError('not-found', `the base \"${base}\" names no commit; check --base and \"archiving.base\"`);\n }\n from = base;\n }\n }\n // A commit that is its own merge base with the base branch is already\n // in it: the diff is empty whatever the round changed, and an empty\n // diff would pass every scope check without measuring one.\n if (from === commit.sha) unmeasured = { reason: 'merged', base: base as string, commit: commit.sha };\n else changes = await git.changes(from, commit.sha);\n }\n // With neither a commit nor a base, nothing was read, and the planner\n // says so itself of a request that brings no changes and no reason.\n dirty = await git.dirty();\n } else {\n unmeasured = { reason: 'git' };\n }\n if (options.pr !== undefined) {\n pr = { number: options.pr, url: git === null ? null : pullRequestUrl(await git.remoteUrl('origin'), options.pr) };\n }\n const plan = planArchive(this.corpus, brief, {\n date,\n summary: options.summary,\n pr,\n commit,\n changes,\n unmeasured,\n dirty,\n allowDirty: options.allowDirty,\n strict: options.strict,\n findings,\n });\n return this.waive(plan, base ?? null, commit?.sha ?? null, options.strict === true);\n }\n\n /**\n * Asks each plugin with a `waive` hook which of a plan's refusals its own\n * check lifts, and turns those into notes. Asked only when the plan has a\n * refusal a plugin may lift: a hook may read git and verify signatures, and a\n * plan with nothing to waive has no question for it. A hook that throws, or\n * answers in the wrong shape, stops the run as a plugin that fails to load\n * does: an archival decided without the check it asked for is not one to\n * trust.\n *\n * A hook is handed a frozen copy of the refusals, and its waivers are\n * matched against the plan's own. Handed the plan's list, a hook could drop\n * a refusal from it, or relabel an open task as a protected file and waive\n * that, and so lift what only spec-brief may; writing to the copy throws,\n * which stops the run like any failing hook.\n */\n private async waive(plan: Plan, base: string | null, commit: string | null, strict: boolean): Promise<Plan> {\n if (!plan.blocking.some((f) => WAIVABLE.includes(f.rule))) return plan;\n const context = frozenCopy({\n root: this.root,\n brief: { id: plan.brief.id, file: plan.brief.file, text: plan.brief.text },\n findings: plan.blocking,\n base,\n commit,\n });\n const waivers: { plugin: string; waiver: Waiver }[] = [];\n for (const plugin of await this.loadedPlugins()) {\n if (plugin.waive === undefined) continue;\n let answer: unknown;\n try {\n answer = await plugin.waive(context);\n } catch (error) {\n throw new ConfigError(`plugin \"${plugin.name}\"`, [`\"waive\" failed: ${error instanceof Error ? error.message : String(error)}`]);\n }\n for (const waiver of asWaivers(answer, plugin.name)) waivers.push({ plugin: plugin.name, waiver });\n }\n return applyWaivers(plan, waivers, strict);\n }\n\n planUnarchive(reference: string, options: UnarchiveRequest = {}): Plan {\n return planUnarchive(this.corpus, this.find(reference), options);\n }\n\n /** Executes a plan and reads the briefs again. A refused plan is never applied. */\n async apply(plan: Plan): Promise<void> {\n if (plan.blocking.length > 0) {\n throw new EngineError('refused', `${plan.action} of ${plan.brief.file} is refused: ${(plan.blocking[0] as Finding).message}`);\n }\n if (plan.done) return;\n await applyPlan(this.fs, plan.ops);\n await this.load();\n }\n\n /** Scaffolds a brief and writes it; the file is new, or nothing is written. */\n async create(options: NewOptions): Promise<{ file: string; content: string }> {\n const title = options.title.trim();\n if (title === '') throw new EngineError('invalid', 'a brief needs a title');\n const id = options.id ?? nextId(this.corpus);\n if (id === null) throw new EngineError('invalid', 'the ids here are not numbers, so there is no next one; pass --id');\n if (/[\\s/\\\\]/.test(id) || id.includes(this.config.id.separator)) {\n throw new EngineError('invalid', `\"${id}\" cannot be an id: no whitespace, slashes or \"${this.config.id.separator}\"`);\n }\n if (findBriefs(this.corpus, id).some((b) => b.id !== null)) throw new EngineError('exists', `the id \"${id}\" is taken`);\n if (options.type !== undefined && typeSections(this.config, options.type) === undefined) {\n const names = Object.keys(this.config.types);\n throw new EngineError('invalid', `\"${options.type}\" is not a brief type here; use one of ${names.join(', ') || '(none)'}`);\n }\n const date = options.date ?? today();\n if (!isDate(date)) throw new EngineError('invalid', `\"${date}\" is not a date written YYYY-MM-DD`);\n const template = this.config.template === null ? null : await this.fs.read(this.config.template);\n if (this.config.template !== null && template === null) {\n throw new EngineError('not-found', `the template ${this.config.template} does not exist`);\n }\n const content = renderNewBrief(\n this.config,\n { id, title, date, type: options.type, wave: options.wave, dependsOn: options.dependsOn },\n template,\n );\n const dir = normalisePath(this.config.briefs);\n const file = `${dir}/${fileNameFor(this.config, id, title)}`;\n if (await this.fs.exists(file)) throw new EngineError('exists', `${file} already exists`);\n await this.fs.write(file, content);\n await this.load();\n return { file, content };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The engine: one repository's briefs, with the operations the CLI offers and\n * the same ones for a harness or an orchestrator that would rather call a\n * function than parse a terminal.\n *\n * This module and the CLI are where the pieces meet their implementations -\n * the real filesystem, the real git, the plugins a configuration names.\n * Everything they compose is a pure function that takes those as arguments,\n * so an engine can equally run over a `MemoryFileSystem` and no git at all.\n */\n\nimport { access, stat } from 'node:fs/promises';\nimport { basename, dirname, relative, resolve, sep } from 'node:path';\n\nimport { applyPlan } from './apply.js';\nimport {\n applyWaivers,\n planArchive,\n type Plan,\n planUnarchive,\n type PullRequest,\n type UnarchiveRequest,\n type Unmeasured,\n WAIVABLE,\n type Waiver,\n} from './archive.js';\nimport type { Brief } from './brief.js';\nimport { collisionFindings, collisions, type CollisionOptions, type CollisionReport } from './collisions.js';\nimport { type Config, ConfigError, DEFAULT_CONFIG, locateConfig, parseConfig, typeSections } from './config.js';\nimport { buildCorpus, type Corpus, findBriefs, isReady, pendingDependencies, type SourceFile } from './corpus.js';\nimport { canonicalPath, type FileSystem, NodeFileSystem, type Reads } from './fs.js';\nimport { type CommitInfo, type FileChange, type Git, NodeGit, pullRequestUrl } from './git.js';\nimport { matchGlob, parseGlob } from './glob.js';\nimport { normalisePath } from './links.js';\nimport { lint, type Plugin } from './lint.js';\nimport { asWaivers, loadPlugins } from './plugins.js';\nimport { fileNameFor, nextId, renderNewBrief } from './scaffold.js';\nimport { planWaves, type Schedule, schedule, scheduleFindings, type ScheduleOptions } from './schedule.js';\nimport type { Finding, Phase } from './types.js';\n\nexport class EngineError extends Error {\n readonly code: 'not-found' | 'ambiguous' | 'invalid' | 'refused' | 'exists';\n\n constructor(code: EngineError['code'], message: string) {\n super(message);\n this.name = 'EngineError';\n this.code = code;\n }\n}\n\nexport interface EngineSetup {\n /** Absolute root: the directory holding the configuration. */\n readonly root: string;\n readonly config: Config;\n /** Repository-relative configuration path, or `null` when the defaults apply. */\n readonly configFile: string | null;\n readonly fs: FileSystem;\n readonly git: Git | null;\n readonly plugins?: readonly Plugin[];\n}\n\nexport interface OpenOptions {\n /** Where discovery starts. Defaults to the process's directory. */\n readonly cwd?: string;\n /** An explicit configuration file; discovery is skipped. */\n readonly config?: string | undefined;\n /** Use the defaults even where a configuration file exists. */\n readonly noConfig?: boolean;\n /** `null` runs without git. Defaults to git when the root is inside a working tree. */\n readonly git?: Git | null;\n readonly fs?: FileSystem;\n /**\n * `false` where nothing the caller asks of the engine reads git, as `list`\n * asks it nothing: where a configuration file names the root, git is then\n * not asked where the work tree's top is, and the engine has no git.\n * Without one, git is still asked, since its answer is the root.\n */\n readonly needsGit?: boolean;\n}\n\n/**\n * Everything an answer was read from, for a tool that would give it again\n * without starting spec-brief (ADR-0002, amended 2026-10-19). Each file is\n * named with the SHA-256 of its bytes, or `null` where it was looked for and\n * was not there; each directory with the names of the files in it, sorted,\n * or `null` where there was no directory to list. Each `null` is a path\n * that must still not be there. Paths are from the root, which wherever `keep` is true is the directory\n * the run started in. `git` is `null`, since no answer named here asks git\n * anything. `keep` is `false`, and the lists are empty, wherever that cannot\n * be named whole.\n */\nexport interface Inputs {\n readonly keep: boolean;\n readonly files: readonly { readonly path: string; readonly sha256: string | null }[];\n readonly directories: readonly { readonly path: string; readonly entries: readonly string[] | null }[];\n readonly git: null;\n}\n\nexport interface ArchiveOptions {\n readonly pr?: number | undefined;\n /** The commit the round landed as. */\n readonly commit?: string | undefined;\n /** The branch the round started from; the diff runs from its merge base. */\n readonly base?: string | undefined;\n readonly summary?: string | undefined;\n readonly date?: string | undefined;\n readonly allowDirty?: boolean | undefined;\n readonly strict?: boolean | undefined;\n /** Leave git out, even where there is a repository. */\n readonly noGit?: boolean | undefined;\n}\n\nexport interface NewOptions {\n readonly title: string;\n readonly id?: string | undefined;\n readonly type?: string | undefined;\n readonly wave?: number | undefined;\n readonly dependsOn?: readonly string[] | undefined;\n readonly date?: string | undefined;\n}\n\nasync function exists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n // Absent, or not to be reached: the search goes on either way. An empty\n // catch would answer `undefined`, which the search reads the same.\n return false;\n }\n}\n\n/** The last second of 9999-12-31, the last date written YYYY-MM-DD. */\nconst LAST_SECOND = 253402300799;\n\n/**\n * Today, in UTC. `SOURCE_DATE_EPOCH` wins where it is set, which is what makes\n * a banner written in a reproducible pipeline reproducible.\n *\n * Set, it is what its specification says it is: whole seconds since\n * 1970-01-01 in digits alone, as `date +%s` prints them, and here no later\n * than the last date written YYYY-MM-DD. Anything else is refused by name,\n * an empty value too, which is what a command substitution that failed\n * leaves. Read as unset, it had the run write the clock's date where a\n * pipeline asked for its own, and say nothing.\n */\nexport function today(env: Readonly<Record<string, string | undefined>> = process.env): string {\n const epoch = env['SOURCE_DATE_EPOCH'];\n let when = new Date();\n if (epoch !== undefined) {\n // Quoted as JSON, so a newline or a tab in it is seen, and the refusal stays one line.\n const held = `SOURCE_DATE_EPOCH is ${JSON.stringify(epoch)}`;\n if (!/^\\d+$/.test(epoch)) {\n throw new EngineError('invalid', `${held}, which is not a whole number of seconds since 1970-01-01; set it to one in digits alone, as \"date +%s\" prints it, or unset it`);\n }\n if (Number(epoch) > LAST_SECOND) {\n throw new EngineError('invalid', `${held}, which is after 9999-12-31, the last date written YYYY-MM-DD; set it to ${LAST_SECOND} or less, or unset it`);\n }\n when = new Date(Number(epoch) * 1000);\n }\n return when.toISOString().slice(0, 10);\n}\n\n/**\n * A deep copy of a value in which every object and array is frozen. Plain\n * data only, as `structuredClone` takes it; the recursion ends at the data's\n * own depth.\n */\nfunction frozenCopy<T>(value: T): T {\n const freeze = (item: unknown): void => {\n if (typeof item !== 'object' || item === null) return;\n for (const child of Object.values(item)) freeze(child);\n Object.freeze(item);\n };\n const copy = structuredClone(value);\n freeze(copy);\n return copy;\n}\n\nexport function isDate(text: string): boolean {\n // The shape keeps out the one other text that survives the round trip\n // below: a year past 9999 with a month, \"+010000-01\". Its anchors are for\n // the reader, since nothing longer than a date comes back as it went in.\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(text)) return false;\n const parsed = new Date(`${text}T00:00:00Z`);\n return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === text;\n}\n\nexport class BriefEngine {\n readonly root: string;\n readonly config: Config;\n readonly configFile: string | null;\n readonly fs: FileSystem;\n readonly git: Git | null;\n private plugins: readonly Plugin[] | undefined;\n private loaded: Corpus | undefined;\n /**\n * What the engine has read, where `open` made it, and whether that is\n * every input of an answer made of the briefs and the configuration alone.\n * Set false by a read that is not on it: the files git or a walk lists,\n * and a plugin's module.\n */\n private reading: { readonly reads: Reads; whole: boolean } | undefined;\n /** Known once the briefs are read, which every command does before it asks. */\n private missingBriefs: boolean | undefined;\n\n constructor(setup: EngineSetup) {\n this.root = setup.root;\n this.config = setup.config;\n this.configFile = setup.configFile;\n this.fs = setup.fs;\n this.git = setup.git;\n this.plugins = setup.plugins;\n }\n\n /**\n * Finds the configuration, the root it defines and the repository around it,\n * and reads every brief. The root is the directory holding the\n * configuration; without one it is the working tree's top, or `cwd`.\n */\n static async open(options: OpenOptions = {}): Promise<BriefEngine> {\n // Named as git names them, so the root and the work tree compare.\n const cwd = await canonicalPath(resolve(options.cwd ?? process.cwd()));\n const explicit = options.config === undefined ? undefined : await canonicalPath(resolve(cwd, options.config));\n // Each name looked for, so that an answer can say which were not there.\n const looked: string[] = [];\n const configPath =\n options.noConfig === true\n ? undefined\n : (explicit ??\n (await locateConfig(cwd, (path) => {\n looked.push(path);\n return exists(path);\n })));\n // Asking git costs a process; without git there is nothing to ask, and\n // with a root a configuration file names nothing to ask it where the\n // caller reads no git.\n const asksGit = options.git !== null && (configPath === undefined || options.needsGit !== false);\n const toplevel = asksGit ? await NodeGit.toplevel(cwd) : null;\n const root = configPath === undefined ? (toplevel ?? cwd) : dirname(configPath);\n const reads: Reads = { files: new Map(), directories: new Map() };\n // Each is named as not there, and the one found again by its read below.\n // Wherever an answer is kept, each was looked for in the root itself, so\n // its path from there is its name.\n for (const path of looked) reads.files.set(relative(root, path), null);\n let config = DEFAULT_CONFIG;\n if (configPath !== undefined) {\n // Only a file is read. A named pipe under the configuration's name was\n // read without end, by every command; and a directory, found or named\n // with --config, ended the run on Node's EISDIR. Each is a\n // configuration that does not load, and no defect to report a stack for.\n const found = await stat(configPath).catch(() => null);\n if (found !== null && !found.isFile()) {\n throw new ConfigError(relative(cwd, configPath), [`is ${found.isDirectory() ? 'a directory' : 'neither a file nor a directory'}, not a configuration file`]);\n }\n const text = await new NodeFileSystem(root, reads).read(basename(configPath));\n if (text === null) throw new EngineError('not-found', `${configPath} does not exist`);\n config = parseConfig(text, relative(cwd, configPath));\n }\n // Outside the work tree is its parent or a path through it, \"..\", \"../x\":\n // a directory inside it may itself be named \"..drafts\".\n const fromTop = toplevel === null ? null : relative(toplevel, root);\n const inTree = fromTop !== null && fromTop !== '..' && !fromTop.startsWith(`..${sep}`);\n const engine = new BriefEngine({\n root,\n config,\n // The root is the configuration's directory, so its path from there is its name.\n configFile: configPath === undefined ? null : basename(configPath),\n fs: options.fs ?? new NodeFileSystem(root, reads),\n git: options.git === undefined ? (inTree ? new NodeGit(root) : null) : options.git,\n });\n // Named whole only where each input is read from the directory the run\n // started in, so that a reader finds it by the path named: the engine\n // reads through its own disk, the root is that directory, and a\n // configuration file there names it, or neither a configuration nor git\n // was asked. A configuration not found was looked for above the root as\n // well, and a root git names is decided by what git reads to find the\n // top of a work tree, which no list here names.\n const whole = options.fs === undefined && !asksGit && root === cwd && (configPath !== undefined || options.noConfig === true);\n engine.reading = { reads, whole };\n await engine.load();\n return engine;\n }\n\n get corpus(): Corpus {\n if (this.loaded === undefined) throw new Error('the engine has not loaded its briefs; call load() first');\n return this.loaded;\n }\n\n /** Reads every brief again, as after an archival. */\n async load(): Promise<Corpus> {\n const files: SourceFile[] = [];\n const accept = parseGlob(this.config.files);\n const reject = this.config.exclude.map((pattern) => parseGlob(pattern));\n const isBrief = (name: string): boolean =>\n accept.ok && matchGlob(accept.glob, name) && !reject.some((r) => r.ok && matchGlob(r.glob, name));\n const read = async (directory: string, phase: Phase): Promise<boolean> => {\n const dir = normalisePath(directory);\n const names = await this.fs.list(dir);\n if (names === null) return false;\n for (const name of names) {\n if (!isBrief(name)) continue;\n const path = dir === '' ? name : `${dir}/${name}`;\n const text = await this.fs.read(path);\n // A file listed a moment ago and gone now was removed under the run; there is nothing to read.\n if (text !== null) files.push({ path, text, phase });\n }\n return true;\n };\n this.missingBriefs = !(await read(this.config.briefs, 'live'));\n await read(this.config.archive, 'archived');\n this.loaded = buildCorpus(files, this.config);\n return this.loaded;\n }\n\n /**\n * Refuses to report on a briefs directory that does not exist. A check over\n * nothing looks exactly like a clean one, and the likelier cause is a\n * configuration pointing at the wrong place.\n */\n requireBriefs(): void {\n if (this.missingBriefs) {\n throw new EngineError('not-found', `${normalisePath(this.config.briefs)}/ does not exist; run \"spec-brief init\", or set \"briefs\" in the configuration`);\n }\n }\n\n /** The plugins the configuration names, loaded once. */\n async loadedPlugins(): Promise<readonly Plugin[]> {\n if (this.reading !== undefined) this.reading.whole = false;\n this.plugins ??= await loadPlugins(this.config.plugins, this.root);\n return this.plugins;\n }\n\n /** The one brief a reference names: an id, a file name or a path. */\n find(reference: string): Brief {\n const found = findBriefs(this.corpus, reference);\n if (found.length === 0) throw new EngineError('not-found', `no brief is named \"${reference}\"`);\n if (found.length > 1) {\n throw new EngineError('ambiguous', `\"${reference}\" names ${found.length} briefs: ${found.map((b) => b.file).join(', ')}`);\n }\n return found[0] as Brief;\n }\n\n /** The files git sees, or every file when there is no git; `null` when neither can be read. */\n async repoFiles(): Promise<string[] | null> {\n if (this.reading !== undefined) this.reading.whole = false;\n try {\n return this.git === null ? await this.fs.walk() : await this.git.files();\n } catch {\n return null;\n }\n }\n\n async lint(references: readonly string[] = []): Promise<Finding[]> {\n const only = references.length === 0 ? undefined : references.map((r) => this.find(r));\n return lint(this.corpus, { plugins: await this.loadedPlugins(), repoFiles: await this.repoFiles(), ...(only ? { only } : {}) });\n }\n\n async collisions(options: CollisionOptions = {}): Promise<{ report: CollisionReport; findings: Finding[] }> {\n const report = collisions(this.corpus, { repoFiles: await this.repoFiles(), ...options });\n return { report, findings: collisionFindings(this.corpus, report) };\n }\n\n async schedule(options: ScheduleOptions = {}): Promise<{ schedule: Schedule; findings: Finding[] }> {\n const computed = schedule(this.corpus, { repoFiles: await this.repoFiles(), ...options });\n return { schedule: computed, findings: scheduleFindings(this.corpus, computed) };\n }\n\n /**\n * Writes each brief's proposed wave into its front matter, every other line\n * as it was, as one transaction. Refused, with nothing written, when the\n * dependencies form a cycle or a front matter cannot be edited.\n */\n async writeWaves(computed: Schedule): Promise<{ written: string[]; refused: Finding[] }> {\n const plan = planWaves(computed);\n if (computed.cycles.length > 0 || plan.refused.length > 0) return { written: [], refused: [...plan.refused] };\n await applyPlan(this.fs, plan.ops);\n await this.load();\n return { written: plan.ops.map((op) => op.path), refused: [] };\n }\n\n /**\n * Every input of an answer made of the briefs and the configuration alone,\n * as `list` gives one, read since the engine was opened (ADR-0002, amended\n * 2026-10-19).\n */\n inputs(): Inputs {\n const reading = this.reading;\n if (reading?.whole !== true) return { keep: false, files: [], directories: [], git: null };\n // Paths are unique, so no two compare equal.\n const byPath = (a: { readonly path: string }, b: { readonly path: string }): number => (a.path < b.path ? -1 : 1);\n return {\n keep: true,\n files: [...reading.reads.files].map(([path, sha256]) => ({ path, sha256 })).sort(byPath),\n // The root is \".\" where a briefs directory is the root itself. One that\n // was not there, an archive not yet made, is named with no entries: one\n // made with a brief in it changes what `list --archived` answers.\n directories: [...reading.reads.directories]\n .map(([path, entries]) => ({ path: path === '' ? '.' : path, entries: entries === null ? null : [...entries].sort() }))\n .sort(byPath),\n git: null,\n };\n }\n\n /** Live briefs that can run now: every dependency archived, and neither a draft nor deferred. */\n ready(): Brief[] {\n return this.corpus.live.filter((b) => isReady(this.corpus, b));\n }\n\n /** The live dependencies holding a brief back. */\n waitingOn(brief: Brief): string[] {\n return pendingDependencies(this.corpus, brief);\n }\n\n async planArchive(reference: string, options: ArchiveOptions = {}): Promise<Plan> {\n const brief = this.find(reference);\n const date = options.date ?? today();\n if (!isDate(date)) throw new EngineError('invalid', `\"${date}\" is not a date written YYYY-MM-DD`);\n if (options.noGit === true && (options.commit !== undefined || options.base !== undefined)) {\n throw new EngineError('invalid', 'a commit or a base needs git; drop --no-git, or drop --commit and --base');\n }\n const git = options.noGit === true ? null : this.git;\n const base = options.base ?? this.config.archiving.base ?? undefined;\n const findings = await this.lint();\n\n let commit: CommitInfo | undefined;\n let changes: FileChange[] | undefined;\n let unmeasured: Unmeasured | undefined;\n let dirty: string[] | undefined;\n let pr: PullRequest | undefined;\n if (git !== null) {\n const revision = options.commit ?? (base === undefined ? undefined : 'HEAD');\n if (revision !== undefined) {\n commit = (await git.commit(revision)) ?? undefined;\n if (commit === undefined) throw new EngineError('not-found', `\"${revision}\" names no commit`);\n let from: string | null = null;\n if (base !== undefined) {\n from = await git.mergeBase(base, commit.sha);\n // No merge base: a base with a history of its own is diffed from\n // directly, and one that names nothing is a typo to report, not a\n // diff for git to fail.\n if (from === null) {\n if ((await git.commit(base)) === null) {\n throw new EngineError('not-found', `the base \"${base}\" names no commit; check --base and \"archiving.base\"`);\n }\n from = base;\n }\n }\n // A commit that is its own merge base with the base branch is already\n // in it: the diff is empty whatever the round changed, and an empty\n // diff would pass every scope check without measuring one.\n if (from === commit.sha) unmeasured = { reason: 'merged', base: base as string, commit: commit.sha };\n else changes = await git.changes(from, commit.sha);\n }\n // With neither a commit nor a base, nothing was read, and the planner\n // says so itself of a request that brings no changes and no reason.\n dirty = await git.dirty();\n } else {\n unmeasured = { reason: 'git' };\n }\n if (options.pr !== undefined) {\n pr = { number: options.pr, url: git === null ? null : pullRequestUrl(await git.remoteUrl('origin'), options.pr) };\n }\n const plan = planArchive(this.corpus, brief, {\n date,\n summary: options.summary,\n pr,\n commit,\n changes,\n unmeasured,\n dirty,\n allowDirty: options.allowDirty,\n strict: options.strict,\n findings,\n });\n return this.waive(plan, base ?? null, commit?.sha ?? null, options.strict === true);\n }\n\n /**\n * Asks each plugin with a `waive` hook which of a plan's refusals its own\n * check lifts, and turns those into notes. Asked only when the plan has a\n * refusal a plugin may lift: a hook may read git and verify signatures, and a\n * plan with nothing to waive has no question for it. A hook that throws, or\n * answers in the wrong shape, stops the run as a plugin that fails to load\n * does: an archival decided without the check it asked for is not one to\n * trust.\n *\n * A hook is handed a frozen copy of the refusals, and its waivers are\n * matched against the plan's own. Handed the plan's list, a hook could drop\n * a refusal from it, or relabel an open task as a protected file and waive\n * that, and so lift what only spec-brief may; writing to the copy throws,\n * which stops the run like any failing hook.\n */\n private async waive(plan: Plan, base: string | null, commit: string | null, strict: boolean): Promise<Plan> {\n if (!plan.blocking.some((f) => WAIVABLE.includes(f.rule))) return plan;\n const context = frozenCopy({\n root: this.root,\n brief: { id: plan.brief.id, file: plan.brief.file, text: plan.brief.text },\n findings: plan.blocking,\n base,\n commit,\n });\n const waivers: { plugin: string; waiver: Waiver }[] = [];\n for (const plugin of await this.loadedPlugins()) {\n if (plugin.waive === undefined) continue;\n let answer: unknown;\n try {\n answer = await plugin.waive(context);\n } catch (error) {\n throw new ConfigError(`plugin \"${plugin.name}\"`, [`\"waive\" failed: ${error instanceof Error ? error.message : String(error)}`]);\n }\n for (const waiver of asWaivers(answer, plugin.name)) waivers.push({ plugin: plugin.name, waiver });\n }\n return applyWaivers(plan, waivers, strict);\n }\n\n planUnarchive(reference: string, options: UnarchiveRequest = {}): Plan {\n return planUnarchive(this.corpus, this.find(reference), options);\n }\n\n /** Executes a plan and reads the briefs again. A refused plan is never applied. */\n async apply(plan: Plan): Promise<void> {\n if (plan.blocking.length > 0) {\n throw new EngineError('refused', `${plan.action} of ${plan.brief.file} is refused: ${(plan.blocking[0] as Finding).message}`);\n }\n if (plan.done) return;\n await applyPlan(this.fs, plan.ops);\n await this.load();\n }\n\n /** Scaffolds a brief and writes it; the file is new, or nothing is written. */\n async create(options: NewOptions): Promise<{ file: string; content: string }> {\n const title = options.title.trim();\n if (title === '') throw new EngineError('invalid', 'a brief needs a title');\n const id = options.id ?? nextId(this.corpus);\n if (id === null) throw new EngineError('invalid', 'the ids here are not numbers, so there is no next one; pass --id');\n if (/[\\s/\\\\]/.test(id) || id.includes(this.config.id.separator)) {\n throw new EngineError('invalid', `\"${id}\" cannot be an id: no whitespace, slashes or \"${this.config.id.separator}\"`);\n }\n if (findBriefs(this.corpus, id).some((b) => b.id !== null)) throw new EngineError('exists', `the id \"${id}\" is taken`);\n if (options.type !== undefined && typeSections(this.config, options.type) === undefined) {\n const names = Object.keys(this.config.types);\n throw new EngineError('invalid', `\"${options.type}\" is not a brief type here; use one of ${names.join(', ') || '(none)'}`);\n }\n const date = options.date ?? today();\n if (!isDate(date)) throw new EngineError('invalid', `\"${date}\" is not a date written YYYY-MM-DD`);\n const template = this.config.template === null ? null : await this.fs.read(this.config.template);\n if (this.config.template !== null && template === null) {\n throw new EngineError('not-found', `the template ${this.config.template} does not exist`);\n }\n const content = renderNewBrief(\n this.config,\n { id, title, date, type: options.type, wave: options.wave, dependsOn: options.dependsOn },\n template,\n );\n const dir = normalisePath(this.config.briefs);\n const file = `${dir}/${fileNameFor(this.config, id, title)}`;\n if (await this.fs.exists(file)) throw new EngineError('exists', `${file} already exists`);\n await this.fs.write(file, content);\n await this.load();\n return { file, content };\n }\n}\n"]}
|
package/dist/fs.d.ts
CHANGED
|
@@ -21,6 +21,18 @@ export interface FileSystem {
|
|
|
21
21
|
walk(): Promise<string[]>;
|
|
22
22
|
exists(path: string): Promise<boolean>;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* What a run read from the disk, by repository-relative path: each file with
|
|
26
|
+
* the SHA-256 of its bytes, or `null` where it was looked for and was not
|
|
27
|
+
* there, and each directory with the names of the files it listed, or
|
|
28
|
+
* `null` where there was none. An answer that names every input it was read
|
|
29
|
+
* from is one another tool may give again without starting this one
|
|
30
|
+
* (ADR-0002, amended 2026-10-19).
|
|
31
|
+
*/
|
|
32
|
+
export interface Reads {
|
|
33
|
+
readonly files: Map<string, string | null>;
|
|
34
|
+
readonly directories: Map<string, readonly string[] | null>;
|
|
35
|
+
}
|
|
24
36
|
/**
|
|
25
37
|
* The path as the filesystem names it: links and junctions followed, and on
|
|
26
38
|
* Windows short names such as `RUNNER~1` expanded. Git reports the top of a
|
|
@@ -32,7 +44,9 @@ export declare function canonicalPath(path: string): Promise<string>;
|
|
|
32
44
|
export declare const IGNORED_DIRECTORIES: ReadonlySet<string>;
|
|
33
45
|
export declare class NodeFileSystem implements FileSystem {
|
|
34
46
|
readonly root: string;
|
|
35
|
-
|
|
47
|
+
private readonly reads;
|
|
48
|
+
/** `reads`, where it is given, is told every file read and every directory listed. */
|
|
49
|
+
constructor(root: string, reads?: Reads);
|
|
36
50
|
private host;
|
|
37
51
|
read(path: string): Promise<string | null>;
|
|
38
52
|
write(path: string, content: string): Promise<void>;
|
package/dist/fs.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* written, and how the transaction's rollback is exercised with a failure on
|
|
9
9
|
* exactly the operation that matters.
|
|
10
10
|
*/
|
|
11
|
-
import { randomBytes } from 'node:crypto';
|
|
11
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
12
12
|
import { mkdir, readdir, readFile, realpath, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
13
13
|
import { dirname, join, relative, resolve, sep } from 'node:path';
|
|
14
14
|
import { normalisePath as repoPath } from './links.js';
|
|
@@ -48,21 +48,30 @@ async function pause(ms) {
|
|
|
48
48
|
}
|
|
49
49
|
export class NodeFileSystem {
|
|
50
50
|
root;
|
|
51
|
-
|
|
51
|
+
reads;
|
|
52
|
+
/** `reads`, where it is given, is told every file read and every directory listed. */
|
|
53
|
+
constructor(root, reads) {
|
|
52
54
|
this.root = resolve(root);
|
|
55
|
+
this.reads = reads;
|
|
53
56
|
}
|
|
54
57
|
host(path) {
|
|
55
58
|
return join(this.root, ...repoPath(path).split('/'));
|
|
56
59
|
}
|
|
57
60
|
async read(path) {
|
|
61
|
+
let bytes;
|
|
58
62
|
try {
|
|
59
|
-
|
|
63
|
+
bytes = await readFile(this.host(path));
|
|
60
64
|
}
|
|
61
65
|
catch (error) {
|
|
62
|
-
if (error.code
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
if (error.code !== 'ENOENT')
|
|
67
|
+
throw error;
|
|
68
|
+
this.reads?.files.set(repoPath(path), null);
|
|
69
|
+
return null;
|
|
65
70
|
}
|
|
71
|
+
// The digest is of the bytes the text is decoded from, so that nothing
|
|
72
|
+
// written between a read and a digest of it can go unseen.
|
|
73
|
+
this.reads?.files.set(repoPath(path), createHash('sha256').update(bytes).digest('hex'));
|
|
74
|
+
return bytes.toString('utf8');
|
|
66
75
|
}
|
|
67
76
|
async write(path, content) {
|
|
68
77
|
const target = this.host(path);
|
|
@@ -100,12 +109,16 @@ export class NodeFileSystem {
|
|
|
100
109
|
async list(directory) {
|
|
101
110
|
try {
|
|
102
111
|
const entries = await readdir(this.host(directory), { withFileTypes: true });
|
|
103
|
-
|
|
112
|
+
const names = entries.filter((e) => e.isFile()).map((e) => e.name);
|
|
113
|
+
this.reads?.directories.set(repoPath(directory), names);
|
|
114
|
+
return names;
|
|
104
115
|
}
|
|
105
116
|
catch (error) {
|
|
106
117
|
const code = error.code;
|
|
107
|
-
if (code === 'ENOENT' || code === 'ENOTDIR')
|
|
118
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
119
|
+
this.reads?.directories.set(repoPath(directory), null);
|
|
108
120
|
return null;
|
|
121
|
+
}
|
|
109
122
|
// A directory that may not be read must not read as "no briefs".
|
|
110
123
|
throw error;
|
|
111
124
|
}
|