@descent-vtt/spec-brief 0.6.0 → 0.7.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.
@@ -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/**\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 */\nexport function today(env: Readonly<Record<string, string | undefined>> = process.env): string {\n const epoch = env['SOURCE_DATE_EPOCH'];\n // The check for a variable that is not set is the compiler's: unset, it would read as \"undefined\", which is no number.\n const when = epoch !== undefined && /^\\d+$/.test(epoch) ? new Date(Number(epoch) * 1000) : new Date();\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));\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":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EACL,YAAY,EACZ,WAAW,EAEX,aAAa,EAIb,QAAQ,GAET,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAA+C,MAAM,iBAAiB,CAAC;AAC7G,OAAO,EAAe,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,WAAW,EAAe,UAAU,EAAE,OAAO,EAAE,mBAAmB,EAAmB,MAAM,aAAa,CAAC;AAClH,OAAO,EAAE,aAAa,EAAmB,cAAc,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAA8C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAe,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,SAAS,EAAiB,QAAQ,EAAE,gBAAgB,EAAwB,MAAM,eAAe,CAAC;AAG3G,MAAM,OAAO,WAAY,SAAQ,KAAK;IAC3B,IAAI,CAA+D;IAE5E,YAAY,IAAyB,EAAE,OAAe;QACpD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAgDD,KAAK,UAAU,MAAM,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;QACxE,mEAAmE;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,MAAM,WAAW,GAAG,YAAY,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CAAC,GAAG,GAAiD,OAAO,CAAC,GAAG;IACnF,MAAM,KAAK,GAAG,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;IACtB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,uFAAuF;QACvF,MAAM,IAAI,GAAG,wBAAwB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,GAAG,IAAI,gIAAgI,CAAC,CAAC;QAC5K,CAAC;QACD,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,WAAW,EAAE,CAAC;YAChC,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,GAAG,IAAI,4EAA4E,WAAW,uBAAuB,CAAC,CAAC;QAC1J,CAAC;QACD,IAAI,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAS,UAAU,CAAI,KAAQ;IAC7B,MAAM,MAAM,GAAG,CAAC,IAAa,EAAQ,EAAE;QACrC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO;QACtD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,CAAC,IAAI,CAAC,CAAC;IACb,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,IAAY;IACjC,sEAAsE;IACtE,0EAA0E;IAC1E,yEAAyE;IACzE,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,IAAI,YAAY,CAAC,CAAC;IAC7C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC;AACvF,CAAC;AAED,MAAM,OAAO,WAAW;IACb,IAAI,CAAS;IACb,MAAM,CAAS;IACf,UAAU,CAAgB;IAC1B,EAAE,CAAa;IACf,GAAG,CAAa;IACjB,OAAO,CAAgC;IACvC,MAAM,CAAqB;IACnC,+EAA+E;IACvE,aAAa,CAAsB;IAE3C,YAAY,KAAkB;QAC5B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,GAAgB,EAAE;QACzC,kEAAkE;QAClE,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9G,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QAC3G,mEAAmE;QACnE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3E,MAAM,IAAI,GAAG,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAChF,IAAI,MAAM,GAAG,cAAc,CAAC;QAC5B,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,MAAM,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAA4B,EAAE,EAAE;gBAC5G,+DAA+D;gBAC/D,yEAAyE;gBACzE,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;oBAAE,MAAM,KAAK,CAAC;gBACzC,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,CAAC,0CAA0C,CAAC,CAAC,CAAC;YACjG,CAAC,CAAC,CAAC;YACH,IAAI,IAAI,KAAK,IAAI;gBAAE,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,GAAG,UAAU,iBAAiB,CAAC,CAAC;YACtF,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,0EAA0E;QAC1E,wDAAwD;QACxD,MAAM,OAAO,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC;QACvF,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC;YAC7B,IAAI;YACJ,MAAM;YACN,iFAAiF;YACjF,UAAU,EAAE,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YAClE,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI,cAAc,CAAC,IAAI,CAAC;YAC1C,GAAG,EAAE,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;SACnF,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,IAAI,MAAM;QACR,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;QAC1G,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,qDAAqD;IACrD,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACxE,MAAM,OAAO,GAAG,CAAC,IAAY,EAAW,EAAE,CACxC,MAAM,CAAC,EAAE,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QACpG,MAAM,IAAI,GAAG,KAAK,EAAE,SAAiB,EAAE,KAAY,EAAoB,EAAE;YACvE,MAAM,GAAG,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;YACrC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC7B,MAAM,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;gBAClD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACtC,+FAA+F;gBAC/F,IAAI,IAAI,KAAK,IAAI;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;QACF,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;QAC/D,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,aAAa;QACX,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,+EAA+E,CAAC,CAAC;QAC1J,CAAC;IACH,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,OAAO,KAAK,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,qEAAqE;IACrE,IAAI,CAAC,SAAiB;QACpB,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,sBAAsB,SAAS,GAAG,CAAC,CAAC;QAC/F,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,IAAI,SAAS,WAAW,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,OAAO,KAAK,CAAC,CAAC,CAAU,CAAC;IAC3B,CAAC;IAED,+FAA+F;IAC/F,KAAK,CAAC,SAAS;QACb,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QAC3E,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,UAAU,GAAsB,EAAE;QAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACvF,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IAClI,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,OAAO,GAAqB,EAAE;QAC7C,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QAC1F,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IACtE,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,OAAO,GAAoB,EAAE;QAC1C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC,SAAS,EAAE,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC;QAC1F,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;IACnF,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU,CAAC,QAAkB;QACjC,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QACjC,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9G,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACjE,CAAC;IAED,iGAAiG;IACjG,KAAK;QACH,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,kDAAkD;IAClD,SAAS,CAAC,KAAY;QACpB,OAAO,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,OAAO,GAAmB,EAAE;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,IAAI,oCAAoC,CAAC,CAAC;QAClG,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,0EAA0E,CAAC,CAAC;QAC/G,CAAC;QACD,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QACrD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC;QACrE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAEnC,IAAI,MAA8B,CAAC;QACnC,IAAI,OAAiC,CAAC;QACtC,IAAI,UAAkC,CAAC;QACvC,IAAI,KAA2B,CAAC;QAChC,IAAI,EAA2B,CAAC;QAChC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC7E,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,SAAS,CAAC;gBACnD,IAAI,MAAM,KAAK,SAAS;oBAAE,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,IAAI,QAAQ,mBAAmB,CAAC,CAAC;gBAC9F,IAAI,IAAI,GAAkB,IAAI,CAAC;gBAC/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC7C,iEAAiE;oBACjE,kEAAkE;oBAClE,wBAAwB;oBACxB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;wBAClB,IAAI,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;4BACtC,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,aAAa,IAAI,sDAAsD,CAAC,CAAC;wBAC9G,CAAC;wBACD,IAAI,GAAG,IAAI,CAAC;oBACd,CAAC;gBACH,CAAC;gBACD,sEAAsE;gBACtE,oEAAoE;gBACpE,2DAA2D;gBAC3D,IAAI,IAAI,KAAK,MAAM,CAAC,GAAG;oBAAE,UAAU,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAc,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC;;oBAChG,OAAO,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YACrD,CAAC;YACD,sEAAsE;YACtE,oEAAoE;YACpE,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACjC,CAAC;QACD,IAAI,OAAO,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;YAC7B,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;QACpH,CAAC;QACD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE;YAC3C,IAAI;YACJ,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,EAAE;YACF,MAAM;YACN,OAAO;YACP,UAAU;YACV,KAAK;YACL,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,QAAQ;SACT,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC;IACtF,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,KAAK,CAAC,KAAK,CAAC,IAAU,EAAE,IAAmB,EAAE,MAAqB,EAAE,MAAe;QACzF,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACvE,MAAM,OAAO,GAAG,UAAU,CAAC;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YAC1E,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI;YACJ,MAAM;SACP,CAAC,CAAC;QACH,MAAM,OAAO,GAAyC,EAAE,CAAC;QACzD,KAAK,MAAM,MAAM,IAAI,MAAM,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;YAChD,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;gBAAE,SAAS;YACzC,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACvC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,WAAW,CAAC,WAAW,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,mBAAmB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAClI,CAAC;YACD,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACrG,CAAC;QACD,OAAO,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IAED,aAAa,CAAC,SAAiB,EAAE,OAAO,GAAqB,EAAE;QAC7D,OAAO,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;IACnE,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,KAAK,CAAC,IAAU;QACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,gBAAiB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAChI,CAAC;QACD,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO;QACtB,MAAM,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,MAAM,CAAC,OAAmB;QAC9B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,KAAK,KAAK,EAAE;YAAE,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC;QAC5E,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,EAAE,KAAK,IAAI;YAAE,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,kEAAkE,CAAC,CAAC;QACtH,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,iDAAiD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;QACvH,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;QACvH,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACxF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC7C,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,OAAO,CAAC,IAAI,0CAA0C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC7H,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,KAAK,EAAE,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,SAAS,EAAE,IAAI,IAAI,oCAAoC,CAAC,CAAC;QAClG,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjG,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACvD,MAAM,IAAI,WAAW,CAAC,WAAW,EAAE,gBAAgB,IAAI,CAAC,MAAM,CAAC,QAAQ,iBAAiB,CAAC,CAAC;QAC5F,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAC5B,IAAI,CAAC,MAAM,EACX,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,EACzF,QAAQ,CACT,CAAC;QACF,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC;QAC7D,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,EAAE,GAAG,IAAI,iBAAiB,CAAC,CAAC;QAC1F,MAAM,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF","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"]}
package/dist/git.d.ts CHANGED
@@ -31,6 +31,14 @@ export interface Git {
31
31
  files(): Promise<string[]>;
32
32
  remoteUrl(name: string): Promise<string | null>;
33
33
  }
34
+ /**
35
+ * A revision git cannot be handed, or a command of git's that failed: the
36
+ * message is the whole report, since what failed is git or what it was given,
37
+ * and no line of spec-brief's.
38
+ */
39
+ export declare class GitError extends Error {
40
+ constructor(message: string);
41
+ }
34
42
  /** Parses `--numstat -z` output with renames off: `added<TAB>deleted<TAB>path<NUL>`. */
35
43
  export declare function parseNumstat(output: string): FileChange[];
36
44
  /** Parses `status --porcelain=v1 -z`, where a rename's original path is a record of its own. */
package/dist/git.js CHANGED
@@ -11,6 +11,17 @@ import { execFile } from 'node:child_process';
11
11
  import { resolve } from 'node:path';
12
12
  import { canonicalPath } from './fs.js';
13
13
  import { relativePath } from './links.js';
14
+ /**
15
+ * A revision git cannot be handed, or a command of git's that failed: the
16
+ * message is the whole report, since what failed is git or what it was given,
17
+ * and no line of spec-brief's.
18
+ */
19
+ export class GitError extends Error {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'GitError';
23
+ }
24
+ }
14
25
  function run(cwd, args) {
15
26
  return new Promise((done) => {
16
27
  execFile('git', ['-c', 'core.quotepath=off', ...args],
@@ -21,7 +32,7 @@ function run(cwd, args) {
21
32
  /** A revision that starts with "-" would be read as an option. */
22
33
  function safeRevision(revision) {
23
34
  if (revision.startsWith('-') || revision.trim() === '')
24
- throw new Error(`"${revision}" is not a revision`);
35
+ throw new GitError(`"${revision}" is not a revision`);
25
36
  return revision;
26
37
  }
27
38
  function records(output) {
@@ -100,20 +111,20 @@ export class NodeGit {
100
111
  : await run(this.cwd, ['show', '--numstat', '-z', '--no-renames', '--relative', '--format=', target]);
101
112
  }
102
113
  if (!result.ok)
103
- throw new Error(`git could not diff ${from ?? `${to}^`}..${to}: ${result.stderr.trim()}`);
114
+ throw new GitError(`git could not diff ${from ?? `${to}^`}..${to}: ${result.stderr.trim()}`);
104
115
  return parseNumstat(result.stdout);
105
116
  }
106
117
  async dirty() {
107
118
  const result = await run(this.cwd, ['status', '--porcelain=v1', '-z', '--untracked-files=all']);
108
119
  if (!result.ok)
109
- throw new Error(`git status failed: ${result.stderr.trim()}`);
120
+ throw new GitError(`git status failed: ${result.stderr.trim()}`);
110
121
  const prefix = await this.rootPrefix();
111
122
  return parsePorcelain(result.stdout).map((path) => relativePath(prefix, path));
112
123
  }
113
124
  async files() {
114
125
  const result = await run(this.cwd, ['ls-files', '-z', '--cached', '--others', '--exclude-standard']);
115
126
  if (!result.ok)
116
- throw new Error(`git ls-files failed: ${result.stderr.trim()}`);
127
+ throw new GitError(`git ls-files failed: ${result.stderr.trim()}`);
117
128
  return records(result.stdout).sort();
118
129
  }
119
130
  async remoteUrl(name) {
package/dist/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git, read-only.\n *\n * spec-brief never stages, commits or rewrites history. It reads the commit a\n * round landed as, the files that commit touched, and whether the working tree\n * holds work the commit does not. What goes into a commit stays the decision\n * of whoever makes it, and archival is atomic over the files it writes rather\n * than over a repository state it would have to reverse.\n */\n\nimport { execFile } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nimport { canonicalPath } from './fs.js';\nimport { relativePath } from './links.js';\n\nexport interface CommitInfo {\n readonly sha: string;\n readonly author: string;\n /** ISO 8601 author date. */\n readonly date: string;\n}\n\nexport interface FileChange {\n readonly path: string;\n /** `null` for a binary file, which has no line counts. */\n readonly insertions: number | null;\n readonly deletions: number | null;\n}\n\nexport interface Git {\n /** The commit a revision names, or `null` when it names none. */\n commit(revision: string): Promise<CommitInfo | null>;\n mergeBase(a: string, b: string): Promise<string | null>;\n /** Files changed from `from` to `to`; with no `from`, the files `to` changed against its first parent. */\n changes(from: string | null, to: string): Promise<FileChange[]>;\n /** Paths with uncommitted changes, untracked files included. */\n dirty(): Promise<string[]>;\n /** Every path git sees: tracked, or untracked and not ignored. A file a round just created counts. */\n files(): Promise<string[]>;\n remoteUrl(name: string): Promise<string | null>;\n}\n\ninterface Run {\n readonly ok: boolean;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nfunction run(cwd: string, args: readonly string[]): Promise<Run> {\n return new Promise((done) => {\n execFile(\n 'git',\n ['-c', 'core.quotepath=off', ...args],\n // windowsHide keeps a console window from flashing up on Windows; no other host has one to hide.\n { cwd, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, windowsHide: true },\n (error, stdout, stderr) => done({ ok: error === null, stdout, stderr }),\n );\n });\n}\n\n/** A revision that starts with \"-\" would be read as an option. */\nfunction safeRevision(revision: string): string {\n if (revision.startsWith('-') || revision.trim() === '') throw new Error(`\"${revision}\" is not a revision`);\n return revision;\n}\n\nfunction records(output: string): string[] {\n return output.split('\\0').filter((r) => r !== '');\n}\n\n/** Parses `--numstat -z` output with renames off: `added<TAB>deleted<TAB>path<NUL>`. */\nexport function parseNumstat(output: string): FileChange[] {\n // `show --format=` leaves the empty header's newline in front of the first record.\n return records(output.replace(/^\\n+/, '')).map((record) => {\n const [added, deleted, ...path] = record.split('\\t') as [string, string, ...string[]];\n return {\n path: path.join('\\t'),\n insertions: added === '-' ? null : Number(added),\n deletions: deleted === '-' ? null : Number(deleted),\n };\n });\n}\n\n/** Parses `status --porcelain=v1 -z`, where a rename's original path is a record of its own. */\nexport function parsePorcelain(output: string): string[] {\n const fields = records(output);\n const paths: string[] = [];\n for (let i = 0; i < fields.length; i += 1) {\n const field = fields[i] as string;\n paths.push(field.slice(3));\n const x = field.charAt(0);\n if (x === 'R' || x === 'C') i += 1;\n }\n return paths;\n}\n\n/**\n * Git reports paths from the top of the work tree; spec-brief's paths start at\n * the root, which may be a directory below it. Every path that crosses this\n * boundary is made relative to the root.\n */\nexport class NodeGit implements Git {\n readonly cwd: string;\n private prefix: Promise<string> | undefined;\n\n constructor(cwd: string) {\n this.cwd = cwd;\n }\n\n /** The root's path from the top of the work tree, `sub/dir/` or empty. */\n private async rootPrefix(): Promise<string> {\n // Asked only after a status that succeeded, so the repository exists.\n this.prefix ??= run(this.cwd, ['rev-parse', '--show-prefix']).then((r) => r.stdout.trim());\n return this.prefix;\n }\n\n /** The working tree's top directory, canonical, or `null` outside a repository. */\n static async toplevel(cwd: string): Promise<string | null> {\n const result = await run(cwd, ['rev-parse', '--show-toplevel']);\n return result.ok ? canonicalPath(resolve(result.stdout.trim())) : null;\n }\n\n async commit(revision: string): Promise<CommitInfo | null> {\n const sha = await run(this.cwd, ['rev-parse', '--verify', '--quiet', `${safeRevision(revision)}^{commit}`]);\n if (!sha.ok) return null;\n const hash = sha.stdout.trim();\n const show = await run(this.cwd, ['show', '-s', '--format=%an%x00%aI', hash]);\n const [author, date] = show.stdout.trim().split('\\0') as [string, string];\n return { sha: hash, author, date };\n }\n\n async mergeBase(a: string, b: string): Promise<string | null> {\n const result = await run(this.cwd, ['merge-base', safeRevision(a), safeRevision(b)]);\n return result.ok ? result.stdout.trim() : null;\n }\n\n async changes(from: string | null, to: string): Promise<FileChange[]> {\n const target = safeRevision(to);\n let result: Run;\n if (from !== null) {\n result = await run(this.cwd, ['diff', '--numstat', '-z', '--no-renames', '--relative', safeRevision(from), target]);\n } else {\n const parent = await run(this.cwd, ['rev-parse', '--verify', '--quiet', `${target}^`]);\n result = parent.ok\n ? await run(this.cwd, ['diff', '--numstat', '-z', '--no-renames', '--relative', `${target}^`, target])\n : await run(this.cwd, ['show', '--numstat', '-z', '--no-renames', '--relative', '--format=', target]);\n }\n if (!result.ok) throw new Error(`git could not diff ${from ?? `${to}^`}..${to}: ${result.stderr.trim()}`);\n return parseNumstat(result.stdout);\n }\n\n async dirty(): Promise<string[]> {\n const result = await run(this.cwd, ['status', '--porcelain=v1', '-z', '--untracked-files=all']);\n if (!result.ok) throw new Error(`git status failed: ${result.stderr.trim()}`);\n const prefix = await this.rootPrefix();\n return parsePorcelain(result.stdout).map((path) => relativePath(prefix, path));\n }\n\n async files(): Promise<string[]> {\n const result = await run(this.cwd, ['ls-files', '-z', '--cached', '--others', '--exclude-standard']);\n if (!result.ok) throw new Error(`git ls-files failed: ${result.stderr.trim()}`);\n return records(result.stdout).sort();\n }\n\n async remoteUrl(name: string): Promise<string | null> {\n const result = await run(this.cwd, ['remote', 'get-url', safeRevision(name)]);\n return result.ok ? result.stdout.trim() : null;\n }\n}\n\n/** The web address of a pull request, for remotes on github.com. */\nexport function pullRequestUrl(remote: string | null, number: number): string | null {\n // Said for the reader and the compiler: no remote reads as the text \"null\", which is no address either.\n if (remote === null) return null;\n const match = /github\\.com[:/]([^/\\s]+)\\/([^/\\s]+?)(?:\\.git)?\\/?$/.exec(remote);\n if (!match) return null;\n return `https://github.com/${match[1] as string}/${match[2] as string}/pull/${number}`;\n}\n"]}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git, read-only.\n *\n * spec-brief never stages, commits or rewrites history. It reads the commit a\n * round landed as, the files that commit touched, and whether the working tree\n * holds work the commit does not. What goes into a commit stays the decision\n * of whoever makes it, and archival is atomic over the files it writes rather\n * than over a repository state it would have to reverse.\n */\n\nimport { execFile } from 'node:child_process';\nimport { resolve } from 'node:path';\n\nimport { canonicalPath } from './fs.js';\nimport { relativePath } from './links.js';\n\nexport interface CommitInfo {\n readonly sha: string;\n readonly author: string;\n /** ISO 8601 author date. */\n readonly date: string;\n}\n\nexport interface FileChange {\n readonly path: string;\n /** `null` for a binary file, which has no line counts. */\n readonly insertions: number | null;\n readonly deletions: number | null;\n}\n\nexport interface Git {\n /** The commit a revision names, or `null` when it names none. */\n commit(revision: string): Promise<CommitInfo | null>;\n mergeBase(a: string, b: string): Promise<string | null>;\n /** Files changed from `from` to `to`; with no `from`, the files `to` changed against its first parent. */\n changes(from: string | null, to: string): Promise<FileChange[]>;\n /** Paths with uncommitted changes, untracked files included. */\n dirty(): Promise<string[]>;\n /** Every path git sees: tracked, or untracked and not ignored. A file a round just created counts. */\n files(): Promise<string[]>;\n remoteUrl(name: string): Promise<string | null>;\n}\n\n/**\n * A revision git cannot be handed, or a command of git's that failed: the\n * message is the whole report, since what failed is git or what it was given,\n * and no line of spec-brief's.\n */\nexport class GitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'GitError';\n }\n}\n\ninterface Run {\n readonly ok: boolean;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nfunction run(cwd: string, args: readonly string[]): Promise<Run> {\n return new Promise((done) => {\n execFile(\n 'git',\n ['-c', 'core.quotepath=off', ...args],\n // windowsHide keeps a console window from flashing up on Windows; no other host has one to hide.\n { cwd, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024, windowsHide: true },\n (error, stdout, stderr) => done({ ok: error === null, stdout, stderr }),\n );\n });\n}\n\n/** A revision that starts with \"-\" would be read as an option. */\nfunction safeRevision(revision: string): string {\n if (revision.startsWith('-') || revision.trim() === '') throw new GitError(`\"${revision}\" is not a revision`);\n return revision;\n}\n\nfunction records(output: string): string[] {\n return output.split('\\0').filter((r) => r !== '');\n}\n\n/** Parses `--numstat -z` output with renames off: `added<TAB>deleted<TAB>path<NUL>`. */\nexport function parseNumstat(output: string): FileChange[] {\n // `show --format=` leaves the empty header's newline in front of the first record.\n return records(output.replace(/^\\n+/, '')).map((record) => {\n const [added, deleted, ...path] = record.split('\\t') as [string, string, ...string[]];\n return {\n path: path.join('\\t'),\n insertions: added === '-' ? null : Number(added),\n deletions: deleted === '-' ? null : Number(deleted),\n };\n });\n}\n\n/** Parses `status --porcelain=v1 -z`, where a rename's original path is a record of its own. */\nexport function parsePorcelain(output: string): string[] {\n const fields = records(output);\n const paths: string[] = [];\n for (let i = 0; i < fields.length; i += 1) {\n const field = fields[i] as string;\n paths.push(field.slice(3));\n const x = field.charAt(0);\n if (x === 'R' || x === 'C') i += 1;\n }\n return paths;\n}\n\n/**\n * Git reports paths from the top of the work tree; spec-brief's paths start at\n * the root, which may be a directory below it. Every path that crosses this\n * boundary is made relative to the root.\n */\nexport class NodeGit implements Git {\n readonly cwd: string;\n private prefix: Promise<string> | undefined;\n\n constructor(cwd: string) {\n this.cwd = cwd;\n }\n\n /** The root's path from the top of the work tree, `sub/dir/` or empty. */\n private async rootPrefix(): Promise<string> {\n // Asked only after a status that succeeded, so the repository exists.\n this.prefix ??= run(this.cwd, ['rev-parse', '--show-prefix']).then((r) => r.stdout.trim());\n return this.prefix;\n }\n\n /** The working tree's top directory, canonical, or `null` outside a repository. */\n static async toplevel(cwd: string): Promise<string | null> {\n const result = await run(cwd, ['rev-parse', '--show-toplevel']);\n return result.ok ? canonicalPath(resolve(result.stdout.trim())) : null;\n }\n\n async commit(revision: string): Promise<CommitInfo | null> {\n const sha = await run(this.cwd, ['rev-parse', '--verify', '--quiet', `${safeRevision(revision)}^{commit}`]);\n if (!sha.ok) return null;\n const hash = sha.stdout.trim();\n const show = await run(this.cwd, ['show', '-s', '--format=%an%x00%aI', hash]);\n const [author, date] = show.stdout.trim().split('\\0') as [string, string];\n return { sha: hash, author, date };\n }\n\n async mergeBase(a: string, b: string): Promise<string | null> {\n const result = await run(this.cwd, ['merge-base', safeRevision(a), safeRevision(b)]);\n return result.ok ? result.stdout.trim() : null;\n }\n\n async changes(from: string | null, to: string): Promise<FileChange[]> {\n const target = safeRevision(to);\n let result: Run;\n if (from !== null) {\n result = await run(this.cwd, ['diff', '--numstat', '-z', '--no-renames', '--relative', safeRevision(from), target]);\n } else {\n const parent = await run(this.cwd, ['rev-parse', '--verify', '--quiet', `${target}^`]);\n result = parent.ok\n ? await run(this.cwd, ['diff', '--numstat', '-z', '--no-renames', '--relative', `${target}^`, target])\n : await run(this.cwd, ['show', '--numstat', '-z', '--no-renames', '--relative', '--format=', target]);\n }\n if (!result.ok) throw new GitError(`git could not diff ${from ?? `${to}^`}..${to}: ${result.stderr.trim()}`);\n return parseNumstat(result.stdout);\n }\n\n async dirty(): Promise<string[]> {\n const result = await run(this.cwd, ['status', '--porcelain=v1', '-z', '--untracked-files=all']);\n if (!result.ok) throw new GitError(`git status failed: ${result.stderr.trim()}`);\n const prefix = await this.rootPrefix();\n return parsePorcelain(result.stdout).map((path) => relativePath(prefix, path));\n }\n\n async files(): Promise<string[]> {\n const result = await run(this.cwd, ['ls-files', '-z', '--cached', '--others', '--exclude-standard']);\n if (!result.ok) throw new GitError(`git ls-files failed: ${result.stderr.trim()}`);\n return records(result.stdout).sort();\n }\n\n async remoteUrl(name: string): Promise<string | null> {\n const result = await run(this.cwd, ['remote', 'get-url', safeRevision(name)]);\n return result.ok ? result.stdout.trim() : null;\n }\n}\n\n/** The web address of a pull request, for remotes on github.com. */\nexport function pullRequestUrl(remote: string | null, number: number): string | null {\n // Said for the reader and the compiler: no remote reads as the text \"null\", which is no address either.\n if (remote === null) return null;\n const match = /github\\.com[:/]([^/\\s]+)\\/([^/\\s]+?)(?:\\.git)?\\/?$/.exec(remote);\n if (!match) return null;\n return `https://github.com/${match[1] as string}/${match[2] as string}/pull/${number}`;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { BANNER_PLACEHOLDERS, type Config, CONFIG_FILES, CONFIG_SCHEMA, ConfigEr
11
11
  export { buildCorpus, type Corpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, type SourceFile, type Waiting, waitingOnDeferred, } from './corpus.js';
12
12
  export { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';
13
13
  export { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';
14
- export { type CommitInfo, type FileChange, type Git, NodeGit } from './git.js';
14
+ export { type CommitInfo, type FileChange, type Git, GitError, NodeGit } from './git.js';
15
15
  export { type Glob, globBase, globBases, globCovers, globWitness, intersectGlobs, type Literal, type LiteralReading, matchGlob, parseGlob, type ParseOptions, readingIn, WITNESS_BUDGET, type Witness, } from './glob.js';
16
16
  export { integrityOf } from './integrity.js';
17
17
  export { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise, type WaiveContext } from './lint.js';
package/dist/index.js CHANGED
@@ -11,7 +11,7 @@ export { BANNER_PLACEHOLDERS, CONFIG_FILES, CONFIG_SCHEMA, ConfigError, configJs
11
11
  export { buildCorpus, dependencyCycles, findBriefs, isReady, pendingDependencies, resolveDependency, waitingOnDeferred, } from './corpus.js';
12
12
  export { BriefEngine, EngineError, today } from './engine.js';
13
13
  export { MemoryFileSystem, NodeFileSystem } from './fs.js';
14
- export { NodeGit } from './git.js';
14
+ export { GitError, NodeGit } from './git.js';
15
15
  export { globBase, globBases, globCovers, globWitness, intersectGlobs, matchGlob, parseGlob, readingIn, WITNESS_BUDGET, } from './glob.js';
16
16
  export { integrityOf } from './integrity.js';
17
17
  export { failing, lint, ruleIds, sortFindings, summarise } from './lint.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EACL,YAAY,EAGZ,SAAS,EAET,WAAW,EACX,aAAa,EAEb,YAAY,EAGZ,QAAQ,GAET,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAc,eAAe,EAAqB,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAEL,iBAAiB,EAGjB,UAAU,GAKX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAEnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EAEX,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EAEX,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,iBAAiB,EAGjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,WAAW,EAAE,WAAW,EAAuD,KAAK,EAAE,MAAM,aAAa,CAAC;AACxI,OAAO,EAAmB,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAA8C,OAAO,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAEL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,WAAW,EACX,cAAc,EAGd,SAAS,EACT,SAAS,EAET,SAAS,EACT,cAAc,GAEf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAiC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAqB,MAAM,WAAW,CAAC;AAC9H,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAe,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1J,OAAO,EAAE,aAAa,EAAE,eAAe,EAA+D,KAAK,EAAE,MAAM,YAAY,CAAC;AAChI,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAEL,aAAa,EACb,KAAK,EACL,KAAK,EAGL,SAAS,EACT,OAAO,EAEP,QAAQ,EACR,gBAAgB,GAIjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,IAAI,EAA4B,OAAO,EAAqB,MAAM,YAAY,CAAC;AAGxG,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAuC,MAAS;IAC1E,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * The library. Everything the CLI does is reachable from here, and the\n * CLI is built from nothing else.\n */\n\nexport { applyPlan, ConflictError, TransactionError } from './apply.js';\nexport {\n applyWaivers,\n type ArchiveRequest,\n type FileOp,\n openTasks,\n type Plan,\n planArchive,\n planUnarchive,\n type PullRequest,\n renderBanner,\n type UnarchiveRequest,\n type Unmeasured,\n WAIVABLE,\n type Waiver,\n} from './archive.js';\nexport { BANNER_CLOSE, BANNER_OPEN, type Brief, BUILT_IN_FIELDS, type FieldProblem, idFromName, parseBrief } from './brief.js';\nexport { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';\nexport {\n type Collision,\n collisionFindings,\n type CollisionOptions,\n type CollisionReport,\n collisions,\n type Overlap,\n type SharedDirectory,\n type UndecidedPair,\n type WaveMatrix,\n} from './collisions.js';\nexport {\n BANNER_PLACEHOLDERS,\n type Config,\n CONFIG_FILES,\n CONFIG_SCHEMA,\n ConfigError,\n configJsonSchema,\n DEFAULT_CONFIG,\n parseConfig,\n type PluginReference,\n resolveConfig,\n type SectionRule,\n} from './config.js';\nexport {\n buildCorpus,\n type Corpus,\n dependencyCycles,\n findBriefs,\n isReady,\n pendingDependencies,\n resolveDependency,\n type SourceFile,\n type Waiting,\n waitingOnDeferred,\n} from './corpus.js';\nexport { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';\nexport { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';\nexport { type CommitInfo, type FileChange, type Git, NodeGit } from './git.js';\nexport {\n type Glob,\n globBase,\n globBases,\n globCovers,\n globWitness,\n intersectGlobs,\n type Literal,\n type LiteralReading,\n matchGlob,\n parseGlob,\n type ParseOptions,\n readingIn,\n WITNESS_BUDGET,\n type Witness,\n} from './glob.js';\nexport { integrityOf } from './integrity.js';\nexport { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise, type WaiveContext } from './lint.js';\nexport { asPlugin, asWaivers, loadPlugins } from './plugins.js';\nexport { type Format, FORMATS, githubCommands, gitlabCodeQuality, JSON_SCHEMA_VERSION, matrixJson, sarif, scheduleJson, sectionsJson } from './report.js';\nexport { ARCHIVE_RULES, COLLISION_RULES, type Rule, type RuleContext, type RuleInfo, type RuleResult, RULES } from './rules.js';\nexport { nextId, renderNewBrief } from './scaffold.js';\nexport {\n type Breach,\n breachReasons,\n holds,\n moves,\n type Passed,\n type Placement,\n planWaves,\n reasons,\n type Schedule,\n schedule,\n scheduleFindings,\n type ScheduleOptions,\n type Unplaced,\n type WavePlan,\n} from './schedule.js';\nexport { contradictions, meet, type Meeting, type Scope, scopeOf, type ScopePattern } from './scope.js';\nexport type { Finding, Phase, Severity, SeveritySetting, Status } from './types.js';\n\n/** Types a plugin author writes against, with inference for the rule list. */\nexport function definePlugin<T extends import('./lint.js').Plugin>(plugin: T): T {\n return plugin;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxE,OAAO,EACL,YAAY,EAGZ,SAAS,EAET,WAAW,EACX,aAAa,EAEb,YAAY,EAGZ,QAAQ,GAET,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAc,eAAe,EAAqB,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/H,OAAO,EAAE,IAAI,EAAc,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAEL,iBAAiB,EAGjB,UAAU,GAKX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EAEnB,YAAY,EACZ,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,WAAW,EAEX,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,WAAW,EAEX,gBAAgB,EAChB,UAAU,EACV,OAAO,EACP,mBAAmB,EACnB,iBAAiB,EAGjB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,WAAW,EAAE,WAAW,EAAuD,KAAK,EAAE,MAAM,aAAa,CAAC;AACxI,OAAO,EAAmB,gBAAgB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC5E,OAAO,EAA8C,QAAQ,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACzF,OAAO,EAEL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,WAAW,EACX,cAAc,EAGd,SAAS,EACT,SAAS,EAET,SAAS,EACT,cAAc,GAEf,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAiC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAqB,MAAM,WAAW,CAAC;AAC9H,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAe,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,UAAU,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1J,OAAO,EAAE,aAAa,EAAE,eAAe,EAA+D,KAAK,EAAE,MAAM,YAAY,CAAC;AAChI,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAEL,aAAa,EACb,KAAK,EACL,KAAK,EAGL,SAAS,EACT,OAAO,EAEP,QAAQ,EACR,gBAAgB,GAIjB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,IAAI,EAA4B,OAAO,EAAqB,MAAM,YAAY,CAAC;AAGxG,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAuC,MAAS;IAC1E,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["/**\n * The library. Everything the CLI does is reachable from here, and the\n * CLI is built from nothing else.\n */\n\nexport { applyPlan, ConflictError, TransactionError } from './apply.js';\nexport {\n applyWaivers,\n type ArchiveRequest,\n type FileOp,\n openTasks,\n type Plan,\n planArchive,\n planUnarchive,\n type PullRequest,\n renderBanner,\n type UnarchiveRequest,\n type Unmeasured,\n WAIVABLE,\n type Waiver,\n} from './archive.js';\nexport { BANNER_CLOSE, BANNER_OPEN, type Brief, BUILT_IN_FIELDS, type FieldProblem, idFromName, parseBrief } from './brief.js';\nexport { main, type CliIO, EXIT_ERROR, EXIT_FAILED, EXIT_OK, HELP, UsageError } from './cli.js';\nexport {\n type Collision,\n collisionFindings,\n type CollisionOptions,\n type CollisionReport,\n collisions,\n type Overlap,\n type SharedDirectory,\n type UndecidedPair,\n type WaveMatrix,\n} from './collisions.js';\nexport {\n BANNER_PLACEHOLDERS,\n type Config,\n CONFIG_FILES,\n CONFIG_SCHEMA,\n ConfigError,\n configJsonSchema,\n DEFAULT_CONFIG,\n parseConfig,\n type PluginReference,\n resolveConfig,\n type SectionRule,\n} from './config.js';\nexport {\n buildCorpus,\n type Corpus,\n dependencyCycles,\n findBriefs,\n isReady,\n pendingDependencies,\n resolveDependency,\n type SourceFile,\n type Waiting,\n waitingOnDeferred,\n} from './corpus.js';\nexport { type ArchiveOptions, BriefEngine, EngineError, type EngineSetup, type NewOptions, type OpenOptions, today } from './engine.js';\nexport { type FileSystem, MemoryFileSystem, NodeFileSystem } from './fs.js';\nexport { type CommitInfo, type FileChange, type Git, GitError, NodeGit } from './git.js';\nexport {\n type Glob,\n globBase,\n globBases,\n globCovers,\n globWitness,\n intersectGlobs,\n type Literal,\n type LiteralReading,\n matchGlob,\n parseGlob,\n type ParseOptions,\n readingIn,\n WITNESS_BUDGET,\n type Witness,\n} from './glob.js';\nexport { integrityOf } from './integrity.js';\nexport { failing, lint, type LintOptions, type Plugin, ruleIds, sortFindings, summarise, type WaiveContext } from './lint.js';\nexport { asPlugin, asWaivers, loadPlugins } from './plugins.js';\nexport { type Format, FORMATS, githubCommands, gitlabCodeQuality, JSON_SCHEMA_VERSION, matrixJson, sarif, scheduleJson, sectionsJson } from './report.js';\nexport { ARCHIVE_RULES, COLLISION_RULES, type Rule, type RuleContext, type RuleInfo, type RuleResult, RULES } from './rules.js';\nexport { nextId, renderNewBrief } from './scaffold.js';\nexport {\n type Breach,\n breachReasons,\n holds,\n moves,\n type Passed,\n type Placement,\n planWaves,\n reasons,\n type Schedule,\n schedule,\n scheduleFindings,\n type ScheduleOptions,\n type Unplaced,\n type WavePlan,\n} from './schedule.js';\nexport { contradictions, meet, type Meeting, type Scope, scopeOf, type ScopePattern } from './scope.js';\nexport type { Finding, Phase, Severity, SeveritySetting, Status } from './types.js';\n\n/** Types a plugin author writes against, with inference for the rule list. */\nexport function definePlugin<T extends import('./lint.js').Plugin>(plugin: T): T {\n return plugin;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descent-vtt/spec-brief",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Lifecycle management for task briefs: lint the contract, map parallel collisions, and archive a round in one atomic command.",
5
5
  "keywords": [
6
6
  "agents",