import { Buffer } from 'node:buffer'; import { InferenceProvider } from '@hawkeyexl/inference'; /** * Shared types for docmeta. * * The pipeline is: load files -> extract metadata (format-specific) -> * resolve a schema set per file -> validate against each schema -> report. * Everything after extraction operates only on `ExtractedMetadata`, so new * input formats never touch validation, resolution, or reporting. */ /** Result of pulling a metadata block out of a single document. */ interface ExtractedMetadata { /** Parsed metadata key/values. `{}` when a block is present but empty. */ data: Record; /** Whether a metadata block was found at all. */ present: boolean; /** * Whether the metadata came from a removable fenced front matter block. * A per-extraction fact, deliberately not an extractor capability: RST and * AsciiDoc read a fenced block when one exists and fall back to native * docinfo/attribute parsing when not, so the same extractor yields both * answers. Absent (element-backed, native-header) means `DELETE FROM docs` * has no block whose removal would leave the document whole, and refuses. */ fenced?: boolean; /** Name of the extractor/format that produced this (e.g. "markdown"). */ format: string; /** * Map a JSON Pointer (Ajv `instancePath`, e.g. "/tags/0") or a bare top-level * key to its 1-based source line, for precise annotations. Returns undefined * when no position is known. */ lineFor(this: void, pointer: string): number | undefined; /** * The column counterpart of {@link lineFor}, 1-based, resolving the same * pointer forms. * * **Optional on purpose.** `lineFor` is required and public, so widening it * to return a position pair would break every consumer that implements * `MetadataExtractor` outside this repository. A format that cannot cheaply * give a column simply omits this — today that is every frontmatter-based * extractor, whose `yaml` node offsets would need an offset -> line/col * conversion first. `html` and `xml` implement it. */ colFor?(this: void, pointer: string): number | undefined; } /** Fenced front matter flavors, in fence order: `---`, `+++`, `;;;`. */ type FrontmatterFlavor = "yaml" | "toml" | "json"; /** Top-level metadata keys to set. Keys with `undefined` values are ignored. */ type MetadataPatch = Record; interface ApplyOptions { /** Flavor to use when creating a block from scratch. Default "yaml". */ newBlockFlavor?: FrontmatterFlavor; /** * Top-level keys to remove entirely — deletion, where `patch` can only set * (`undefined` values there are ignored by contract). Removing a key that * is already absent is a no-op. Writers that cannot remove a key ignore * this option, so a caller that needs certainty must re-extract and check — * `runQuery`'s apply phase does exactly that and refuses the run on a * survivor. */ deletions?: readonly string[]; /** * The document's path, when the caller knows it. `extract` receives one and * `apply` did not, which left a writer unable to use the extension as a * signal — the XML writer needs it to tell a DITA topic from hand-rolled XML * that happens to have a `` root. */ filePath?: string; /** * The same `elements:` paths extraction was given. * * A writer re-reads the document to find where each key came from, and a read * without these paths would not produce a config-declared key at all — so the * writer would treat it as absent and create it somewhere else. That is the * asymmetry proposal 0018 calls a loop, arriving through the options object * rather than through the code. */ elements?: readonly string[]; } /** Per-run inputs to extraction, beyond the document itself. */ interface ExtractOptions { /** * Extra element paths to lift, from `elements:` config. Slash-separated from * the document root, optionally ending in `@attribute`. These *extend* the * per-format convention: a path producing a key the convention already filled * is a no-op, so naming one cannot retype a key a content model typed exactly. */ elements?: readonly string[]; } /** A pluggable metadata extractor for one document format. */ interface MetadataExtractor { /** Stable name, also used as `ExtractedMetadata.format`. */ name: string; /** Lowercase file extensions this extractor handles, incl. dot (e.g. ".md"). */ extensions: string[]; /** * Whether this extractor can *read* — false for a format that is registered * but not yet wired up. Every registered extractor sets it true today; it is * kept because it is a distinct capability from writability, which is the * presence of `apply`. A format can read without writing, and one being * added could be declared before it can do either. */ implemented: boolean; /** * Extract metadata from raw file content. * * `this: void` — as on `apply` below, and on `ExtractedMetadata`'s * `lineFor`/`colFor`. Every implementation in this repo is a plain function * in an object literal and none of them reads `this`, so pulling one out * (`const apply = extractor.apply`) is the ordinary thing it looks like * rather than a binding hazard. Declaring that is what lets a reader — and * `unbound-method` — know it. * * Retyping these as properties holding functions would say the same thing, * and was tried first. It is the more expensive way: a property's parameters * are contravariant where a method's are bivariant, so on an *exported* * interface that swap silently narrows what an outside implementation may * be, which is a semver-visible change bought for a lint fix. `this: void` * leaves assignability exactly as it was. */ extract(this: void, content: string, filePath: string, options?: ExtractOptions): ExtractedMetadata; /** * Return new content with every key in `patch` set at the top level. Pure: * no IO, no mutation, deterministic; returns `content` itself for a no-op. * * Absent means the format is read-only. Present but throwing `DocmetaError` * means this particular document cannot be rewritten safely. */ apply?(this: void, content: string, patch: MetadataPatch, options?: ApplyOptions): string; } /** A single schema violation for one file, attributed to one schema. */ interface FieldError { /** Schema id/ref that produced this error (e.g. "google:okf:0.1"). */ schema: string; /** Ajv instancePath, e.g. "/tags/0" or "" for the root. */ instancePath: string; /** Human-readable message. */ message: string; /** * Ajv keyword that failed (e.g. "required", "format", "pattern"), or * "parse"/"schema" for the synthetic errors the command layer raises. * * This and `subject` are the machine-stable half of a violation's identity: * `message` is generated prose that an Ajv upgrade may reword, so nothing * durable — a baseline fingerprint, a SARIF `ruleId` — may be built from it. */ keyword: string; /** * Discriminator within that keyword, when it is a stable identifier: the * missing property, the additional property, the format name, the expected * type. Deliberately unset for keywords whose parameter is a schema-authored * *value* (`pattern`'s regex, `enum`'s list, `minLength`'s number), which * changes whenever the schema is edited. */ subject?: string; /** 1-based source line, when known. */ line?: number; /** 1-based column, when known. */ col?: number; } /** Validation outcome for a single file. */ interface ValidationResult { /** Absolute or cwd-relative path of the file. */ file: string; /** Extractor/format used. */ format: string; /** Whether validation passed against every schema in the set. */ ok: boolean; /** Schema ids/refs the file was validated against. */ schemas: string[]; /** All violations, across every schema in the set. */ errors: FieldError[]; /** * Findings a baseline suppressed for this file. Absent when no baseline * governed the run, or when it forgave nothing here. */ baselined?: number; } /** How a baseline shaped the run. Present only when one governed it. */ interface BaselineSummary { /** Baseline file path, spelled the way the user would type it. */ path: string; /** Whether this run wrote the file (`--write-baseline`) or only read it. */ written: boolean; /** * Fingerprints the baseline holds. On a read this counts only the files the * run checked, so validating one file does not report the rest as prunable. */ recorded: number; /** Findings the baseline suppressed. */ suppressed: number; /** Recorded fingerprints for checked files that no longer occur. */ stale: number; /** Fingerprints this write added. Write only. */ added?: number; /** Fingerprints this write dropped. Write only. */ removed?: number; } /** Aggregate run summary. */ interface RunSummary { files: number; passed: number; failed: number; /** Violations reported. Baselined ones are excluded — see `baseline`. */ errors: number; /** * Candidate documents `.gitignore` removed from the walk. Omitted when it * removed none. Reported because silent removal is what makes the filter * dangerous — a counted removal is auditable. */ gitignoreSkipped?: number; baseline?: BaselineSummary; } /** An operational/usage failure that should map to exit code 2. */ declare class DocmetaError extends Error { constructor(message: string); } /** What a schema reference is measured against when it names a local file. */ interface FingerprintContext { /** Working directory a relative file ref was written against. */ cwd: string; /** Directory canonical paths are expressed relative to: the config's, else `cwd`. */ base: string; /** * Directory a `ValidationResult.file` label is relative to. Usually `base`, * but a run that took positional paths resolves them against `cwd` instead, * so the two genuinely differ. Defaults to `base` when omitted. */ runBase?: string; } interface SchemaOverride { /** * The glob, or globs, this override governs. A file matches when **any** of * them matches, so a list groups path shapes that brace expansion cannot * express as one pattern: a per-skill `SKILL.md` one directory down and a * flat `.claude/agents` file share a schema set but have no common stem. * * Kept as written rather than normalized to a list, because a programmatic * caller may hand `runValidate` a config object it built itself, and that * caller's bare string must keep working. Read it through `overrideGlobs` * rather than directly — that is the one place the two shapes collapse. */ files: string | string[]; schemas: string[]; /** * Optional collection name (proposal 0027): the override group becomes a * SQL view of that name over the `docs` projection, holding exactly the * files this override won schema resolution for. Any string is legal — * view names are quoted identifiers — except the refusals `parseConfig` * makes: a duplicate, `docs` (the table every query already reads), a * blank name, and a name starting `sqlite_` (SQLite reserves the prefix). * A `name:` also requires `schemas:`, because a schema-less override never * wins resolution and its view would be empty by construction. */ name?: string; /** * Extra element paths for files matching `files`. Unlike `schemas`, which the * first matching override *replaces* because a schema set is a complete * statement, these accumulate: every matching override contributes, on top of * the top-level `elements:`. A list of extra places to look is additive by * nature, and an override that silently dropped the repo-wide ones would be a * trap. */ elements?: string[]; } /** * A `schemas:` entry in its long form: a reference plus where it came from and * what it must hash to. * * Written by `docmeta schemas vendor`, which downloads a remote schema into the * repository and records both. `source` keeps the provenance the URL used to * carry, so a re-vendor knows where to look and an error can say what to * re-download; `integrity` makes an edited or corrupted copy a loud failure * rather than a silently changed contract. */ interface SchemaRefEntry { /** What is loaded: a built-in id, a local `.json` path, or a URL. */ ref: string; /** Where `ref` was vendored from — a URL, or a path for a local copy. */ source?: string; /** `sha256-<64 hex>` over the bytes of `ref`. Local files only. */ integrity?: string; } /** * One `schemas:` entry. A bare string is the original form and is unchanged by * 0008; the mapping form adds provenance and a pin. */ type SchemaEntry = string | SchemaRefEntry; /** * What a **document** is allowed to name in its own `$schema`. * * - `any` — the default, and today's behavior: a built-in id, a file in the * repository, or a URL. `schemaTrust.hosts` narrows the URL case. * - `local` — a built-in id or a file in the repository; a URL is refused. * - `none` — the document's `$schema` is ignored and config decides, with a * notice on stderr naming the file whose key was dropped. */ type DocumentRefTrust = "any" | "local" | "none"; /** * How far a **document** is trusted to choose the contract it is judged by. * * Nothing here touches a ref an *operator* supplied: `schemas:`, * `overrides[].schemas`, and `-s/--schema` are never filtered, in any mode. A * person who can edit the config or pass a flag is not the attacker this key * has in mind — a pull request against a public docs repo is. */ interface SchemaTrustConfig { /** Defaults to `any`, which is exactly what docmeta has always done. */ documentRefs?: DocumentRefTrust; /** * Hosts a document-supplied URL may name. Consulted **only** under * `documentRefs: any`; absent means any host, as before. * * A convenience for pointing at one known publisher, not a security * boundary: `fetch` follows redirects, so an allowlisted host that answers * `302` sends the fetch anywhere it likes. A repo that genuinely distrusts * its contributors wants `documentRefs: local`. */ hosts?: string[]; } /** * One named corpus check (proposal 0026): SQL run over the `docs` projection * by `validate`, whose result rows are findings under the id `check:`. */ interface CheckConfig { /** The check's name — the durable half of its findings' identity. */ name: string; /** One SQL statement whose rows follow the finding column convention. */ query: string; } /** Defaults for the `fill` command; every key is overridable by a CLI flag. */ interface FillConfig { provider?: string; model?: string; /** Minimum self-reported confidence to write a value (0-1). */ confidenceThreshold?: number; /** Stop after this many inference calls. Counts calls, not files. */ maxTurns?: number; /** Characters of document per call. Default 12000. */ chunkChars?: number; concurrency?: number; } /** Settings for the cross-run cache of schemas fetched over `http(s)`. */ interface SchemaCacheConfig { /** * Hours a cached schema is served before it is re-fetched. `0` disables the * cache entirely, in both directions. */ ttlHours?: number; } interface DocmetaConfig { paths?: string[]; exclude?: string[]; /** * The default schema set. Each entry is either a reference string or a * `{ ref, source?, integrity? }` mapping — see `SchemaEntry`. */ schemas?: SchemaEntry[]; overrides?: SchemaOverride[]; /** * Named corpus checks `validate` runs after the per-file schemas, when the * run's file set is the config-resolved corpus. See `CheckConfig`. */ checks?: CheckConfig[]; /** * Element paths to lift in addition to each format's convention. See * `parseElementPath` for the syntax. */ elements?: string[]; fill?: FillConfig; /** * Path to a validation baseline, relative to **this config file**. Setting it * implies `--baseline` on every run; `--no-baseline` suppresses it for one. */ baseline?: string; /** * Treat an input set that resolves to zero files as success rather than an * operational error. Off by default: a glob that stops matching would * otherwise leave a permanently green gate that checks nothing. */ allowEmpty?: boolean; /** * Skip files `.gitignore` covers when expanding directories and globs. On by * default; set false to check generated or vendored documents the repo does * not track. Setting it **true** explicitly also asks to be told when git * cannot answer — see `GITIGNORE_UNAVAILABLE`. */ respectGitignore?: boolean; /** Defaults for the cross-run schema cache. See `SchemaCacheConfig`. */ schemaCache?: SchemaCacheConfig; /** * Never fetch a remote schema. A URL reference resolves from the schema * cache; an uncached one is an operational error naming the URL. Built-in and * local-file references are unaffected — neither touches the network. */ offline?: boolean; /** * How far a document's own `$schema` is trusted. Absent means `any` — every * setup that exists today, unchanged. See `SchemaTrustConfig`. */ schemaTrust?: SchemaTrustConfig; } /** Parse and validate config YAML text. */ declare function parseConfig(text: string, source: string): DocmetaConfig; interface LoadedConfig { config: DocmetaConfig; /** Absolute path to the file the config was read from. */ path: string; /** * Directory holding that file. Relative paths written *in* the config — * `paths:`, `exclude:`, local-file schema refs — are meaningful relative to * this, not to the directory the command happened to be invoked from. */ dir: string; } /** * The directory a **document-supplied** local schema path must stay inside. * * The git root rather than the config's directory, so a monorepo package whose * documents reference `../shared/x.json` keeps working — that path is still * inside the repository, which is what "a schema in this project" means. * * `source` is not decoration, and it has **three** values rather than a * boolean: with no repository the boundary falls back to the config's * directory, and with no config either it falls back to the run's `cwd`. Those * are progressively narrower and less obvious rules, and the refusal message * has to name the one it actually applied — telling someone with no config file * that "the config's own directory is the boundary" sends them looking for a * file that is not there. * * Same reasoning as `SARIF_NO_GIT_ROOT`: "there is no repository" and "the * repository root is where you are standing" must stay distinguishable. */ interface SchemaTrustRoot { /** Absolute directory the path must resolve inside. */ dir: string; /** Which rule produced `dir`, so a refusal can name it accurately. */ source: "git" | "config" | "cwd"; } /** * Load config from an explicit path (error if missing) or by discovery. * * Discovery checks cwd and then each ancestor up to and including the nearest * `.git` boundary (see `searchPath`). Within a directory the order is * `docmeta.config.yaml` then `docmeta.config.yml`. The **first file found * wins** and the walk stops there — ancestor configs are never merged, because * `schemas:` is a set a file must satisfy in full and `overrides:` is * first-match-wins ordered, so a partial merge would silently redefine what * "the contract" means. * * Returns null when no config is found via discovery. */ declare function loadConfig(explicitPath?: string, cwd?: string): Promise; /** Told to a caller once, when a run turns out to be governed by a config. */ interface ConfigNotice { /** Absolute path to the config file. */ path: string; /** Directory holding it. */ dir: string; } interface RunConfigOptions { /** Defaults to `process.cwd()`, matching `loadConfig`. */ cwd?: string; /** `-c/--config`. */ configPath?: string; /** `--no-config`: skip discovery and run on the built-in defaults. */ noConfig?: boolean; /** Positional inputs; empty means fall back to the config's `paths:`. */ inputs: string[]; onConfigLoaded?: (info: ConfigNotice) => void; } interface RunConfig { /** The config, with its local file schema refs already rebased. */ config: DocmetaConfig | null; /** What to resolve: the positional inputs, or the config's `paths:`. */ inputs: string[]; /** * Directory those inputs — and so every resolved file path, and every file * read — are relative to. * * A run uses *either* positional paths *or* config `paths:`, never both, so * there is exactly one base per run and no ambiguity about which it is. * Positional paths are typed by a person standing in a shell, so they stay * relative to the working directory; `paths:` globs were written next to the * config, so they resolve from there. */ base: string; /** * Directory holding the config that governed the run, when one did. * * Distinct from `base`: `base` follows *the inputs*, so it is the working * directory whenever positional paths were given. Anything written **in** the * config — the `baseline:` path — is relative to the config itself no matter * where the command was run from, which is the whole point of discovering an * ancestor config in the first place. */ configDir?: string; /** * Absolute path of the config file itself, when one governs the run. The * one honest way to edit the governing config: discovery accepts both * `docmeta.config.yaml` and `.yml`, `-c` accepts any name, and the file may * live in an ancestor — so re-deriving the path from a directory plus an * assumed filename names the wrong file in every one of those setups. */ configPath?: string; } /** * Settle the three things every command core needs from config before it can * touch the filesystem: which config governs the run, what to resolve, and * what those relative paths are relative to. */ declare function resolveRunConfig(opts: RunConfigOptions): Promise; interface ValidateOptions { inputs: string[]; cliSchemas?: string[]; exts?: string[]; exclude?: string[]; /** `--as` format override (extractor name). */ as?: string; configPath?: string; /** `--no-config`: skip config discovery and use the built-in defaults. */ noConfig?: boolean; cwd?: string; /** Content for the `-` (stdin) input, injected by the CLI/tests. */ stdinContent?: string; /** Permit an input set that resolves to zero files (see `assertNonEmpty`). */ allowEmpty?: boolean; /** * `--no-gitignore` (false). Absent leaves config `respectGitignore:` in * charge, which itself defaults to on. */ respectGitignore?: boolean; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; /** * `--baseline [path]`: compare findings against a recorded baseline and fail * only on new ones. A string is a path relative to `cwd`; `true` means the * default path; `false` is `--no-baseline`, which suppresses a baseline the * config supplied. Absent means "whatever the config says". */ baseline?: string | boolean; /** * `--write-baseline [path]`: record this run's findings as the baseline * instead of comparing against one. Wins over `baseline`. */ writeBaseline?: string | boolean; /** Called once when a config governs the run, so the CLI can report it. */ onConfigLoaded?: (info: ConfigNotice) => void; /** * `--offline`: never fetch a remote schema. Absent leaves config `offline:` * in charge, which itself defaults to off. */ offline?: boolean; /** * `--no-checks` (false): skip the config's named corpus checks for this * run. Absent leaves them on — they still only run when the resolved file * set is the config-resolved corpus (proposal 0026). */ checks?: boolean; } interface ValidateRun { results: ValidationResult[]; summary: RunSummary; /** * Where the run stood: the working directory, the directory canonical paths * are measured from, and the directory `results[].file` labels are relative * to. * * Returned rather than kept private because a `ValidationResult.file` is only * meaningful *with* it. A reporter that has to name a file the same way from * anywhere — SARIF, whose `artifactLocation.uri` GitHub resolves against the * repository root — cannot reconstruct this after the fact, and guessing puts * it in the same false-green trap the baseline's canonical keys exist to * avoid. */ frame: FingerprintContext; } declare function runValidate(opts: ValidateOptions): Promise; interface GetOptions { fields: string[]; inputs: string[]; as?: string; exclude?: string[]; exts?: string[]; configPath?: string; /** `--no-config`: skip config discovery and use the built-in defaults. */ noConfig?: boolean; cwd?: string; /** Content for the `-` (stdin) input, injected by the CLI/tests. */ stdinContent?: string; /** Permit an input set that resolves to zero files (see `assertNonEmpty`). */ allowEmpty?: boolean; /** * `--no-gitignore` (false). Absent leaves config `respectGitignore:` in * charge, which itself defaults to on. */ respectGitignore?: boolean; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; /** Called once when a config governs the run, so the CLI can report it. */ onConfigLoaded?: (info: ConfigNotice) => void; /** * `--offline`, accepted for surface parity with `validate` and `fill`. * * It has **no effect here**, and that is a property of the command rather * than an omission: `get` prints extracted field values and never resolves or * loads a schema, so it has no network dependency to suppress. Accepting it * keeps one flag set across the three commands, so a script can pass * `--offline` uniformly without knowing which subcommand needs it. */ offline?: boolean; } interface GetFileResult { file: string; present: boolean; values: Record; /** * Why this file yielded no values, when it yielded none for a reason. * * Set only when the document's metadata block could not be read at all — the * same throw `validate` turns into a `(parse)` finding. Absent on every file * that parsed, including one with no metadata block and one where every * requested field was unset: those are answers, and this is the absence of * one. A run carrying any `error` exits 1. */ error?: string; } declare function runGet(opts: GetOptions): Promise; interface QueryOptions { /** * One SQL statement. The table is `docs`; see the system columns below. * May be empty when `db` is set — export without querying. */ sql: string; /** * `--db`: also write the built database to this file, for any SQLite * front-end (sqlite3, Datasette, duckdb) to open afterwards. The file is a * regenerated artifact: an existing SQLite file at the path is overwritten, * anything else is refused. */ db?: string; inputs: string[]; as?: string; exclude?: string[]; exts?: string[]; configPath?: string; /** `--no-config`: skip config discovery and use the built-in defaults. */ noConfig?: boolean; cwd?: string; /** Content for the `-` (stdin) input, injected by the CLI/tests. */ stdinContent?: string; /** Permit an input set that resolves to zero files (see `assertNonEmpty`). */ allowEmpty?: boolean; /** * `--no-gitignore` (false). Absent leaves config `respectGitignore:` in * charge, which itself defaults to on. */ respectGitignore?: boolean; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; /** Called once when a config governs the run, so the CLI can report it. */ onConfigLoaded?: (info: ConfigNotice) => void; /** * `--offline`, accepted for surface parity with the other commands. DDL * statements do resolve the corpus's schema set (0024), but only from disk * and the bundled built-ins — a URL ref refuses with "vendor it first" * before anything could fetch — so there is still no network dependency to * suppress. */ offline?: boolean; /** * `--dry-run`: preview the statement's per-file changes — the diff it * would make, files untouched. Without it a mutating statement applies, * matching `fill`'s convention (proposal 0025; 0022 recorded the original * preview-by-default surface this revises). */ dryRun?: boolean; /** * Values for the statement's named parameters (`$name`, `:name`, `@name`), * keyed by bare name (a `$`/`:`/`@` prefix on a key is tolerated). Each * value passes through the same `bindValue` the projection loader uses — * booleans become 1/0, arrays and objects JSON text — so a bound parameter * compares against a stored cell under exactly the encoding the cell got * (proposal 0029). A parameter the SQL references with no entry here * refuses: unbound would silently bind NULL and match nothing. */ params?: Record; /** * `-s/--schema`, repeatable: the schema set the run's DDL evolves — CLI * precedence for the DDL planner *only* (proposal 0030). The per-file * resolution walk is skipped and the deduped refs are the set; every guard * inside the set (single local file for ADD, sole declarer for * DROP/RENAME, builtin fork, URL refusal, the trust boundary) runs * unchanged. Collection views keep following the config's resolution. A * run whose statement produces no DDL effect refuses before anything is * applied — a flag that would silently mean nothing must refuse. */ schemas?: string[]; } /** * One thing a statement changed, in file-space terms. Cell-level kinds carry * a `key` (a set, a deletion via `SET k = NULL` / `ALTER DROP COLUMN`, or a * key rename); file-level kinds carry the whole event (`cleared` — the block * stripped by DELETE; `created` — a file INSERT made; `renamed` — a `_path` * move). Exactly one kind per object. */ type QueryChange = { file: string; written: boolean; } & ({ key: string; from: unknown; to: unknown; } | { key: string; from: unknown; deleted: true; } | { key: string; renamedFrom: string; to: unknown; } | { cleared: true; from: Record; } | { created: true; to: Record; } | { renamed: string; } | { /** A DDL statement edited the schema itself (0024): `file` is the * schema written — an in-place edit, or the fork of a builtin. */ schema: true; op: "add" | "drop" | "rename"; key: string; renamedTo?: string; type?: string; /** 0028: a declared type equal to a format name carries the format. */ format?: string; /** 0028: a `CHECK (k IN (…))` on the added column carries the enum. */ enum?: (string | number)[]; required?: boolean; forkedFrom?: string; } | { /** A DDL side effect on the governing config file: a fork repoints the * `schemas:` entry (`key: "schemas"`), an in-place edit of a pinned * schema refreshes its pin (`key: "integrity"`). Disclosed as a change * because the preview must name every file `--write` will touch. */ config: true; key: string; from: unknown; to: unknown; }); interface QueryRun { /** Result column names, in SELECT order — present even for zero rows. */ columns: string[]; rows: Record[]; /** Set when `db` was written: where, and how big the table is. */ db?: { path: string; files: number; columns: number; }; /** * Present when the statement was a metadata edit: every cell it changed * (empty when a mutating statement matched nothing). Absent on reads. */ changes?: QueryChange[]; /** * Where the run stood, in the same shape `ValidateRun.frame` carries — the * path-normalization frame SARIF and JUnit need before they can name a file * the way the repository does (proposal 0026). Constructed from the run's * `cwd`, its config directory, and the directory file labels resolve * against; absent only when a caller built a `QueryRun` by hand. */ frame?: FingerprintContext; } declare function runQuery(opts: QueryOptions): Promise; interface BuiltinInfo { id: string; title: string; description: string; } declare function listBuiltins(): BuiltinInfo[]; type RefKind = "builtin" | "file" | "url"; declare function classifyRef(ref: string): { kind: RefKind; ref: string; }; /** * What a config recorded about one reference beyond the reference itself. * * Kept out of the ref string deliberately: the ref appears in every report, * every baseline fingerprint, and `Validator`'s compile cache key, so it has to * stay exactly the string the user wrote. */ interface SchemaPin { /** Where the reference was vendored from — a URL, or a local path. */ source?: string; /** `sha256-<64 hex>` the file's bytes must hash to. */ integrity?: string; } interface LoadSchemaOptions { /** * Directory a **relative local-file** ref is resolved against. * * Omitted means `process.cwd()`, which is what the CLI wants and what this * always did. A library caller passing `cwd` to a command core needs the * ref measured from *that* directory instead: `rebaseConfigSchemaRefs` * deliberately leaves refs untouched when the config already sits in the * run's `cwd`, so `./schema/house.json` arrives here exactly as written and * was then read against the wrong directory. * * Resolved at read time rather than by rewriting the ref, and that choice is * load-bearing: the ref string is what reports name, what `Validator` keys * its compile cache on, and what every baseline fingerprint is taken over. * Rewriting it to an absolute path would silently move every recorded * baseline. `canonicalSchemaRef` in `baseline.ts` already measures a relative * ref from the run's `cwd`, so this makes loading agree with fingerprinting * rather than introducing a new convention. */ fileBase?: string; /** Abort a remote fetch after this many ms (default 10_000). */ timeoutMs?: number; /** Reject a remote schema whose body exceeds this many bytes (default 5 MB). */ maxBytes?: number; /** * Directory for the cross-run schema cache. Omitted means **no disk cache**: * the registry never guesses a project root, so a library caller gets the * in-process behavior until it opts in. The command cores pass * `schemaCacheDir(configDir ?? cwd)`. */ cacheDir?: string; /** Hours a cached entry stays fresh; `0` disables the cache (default 24). */ ttlHours?: number; /** * Never touch the network. A URL ref resolves from the disk cache — ignoring * the TTL, since there is no re-fetch to fall back on — and an uncached one * fails naming the URL. Built-ins and local files are unaffected. * * This is a *durability* control, not a trust boundary, and it never was: * whether a document may name a URL at all is decided upstream by * `schemaTrust` in `resolveSchemaSet`, which is the last place that still * knows a ref came from a document rather than from an operator (proposal * 0015). `offline` used to block that case by accident and was the only thing * standing there; it is now free to mean only what it says. */ offline?: boolean; /** * Provenance and integrity pins, keyed on the reference exactly as it is * passed to `loadSchema`. Built by `collectSchemaPins` from the **rebased** * config, so both sides spell a local path the same way; a config with no * mapping-form `schemas:` entries produces an empty map and none of this * runs. */ pins?: ReadonlyMap; } /** A fetched schema, with the bytes it arrived as. */ interface FetchedSchema { /** Exactly what the server sent, undecoded. */ bytes: Buffer; /** The same payload, parsed and guarded. */ schema: Record; } /** * Fetch, size-cap, parse, and guard a remote schema. At most two requests. * * Exported for `schemas vendor`, which needs the raw bytes to write and to * hash. Sharing this path rather than fetching separately is what keeps * vendoring subject to the same size cap, retry policy, and payload guard as * validation — a vendored error envelope would otherwise be committed to the * repository and pass every document from then on. * * Deliberately **not** routed through the disk cache or `offline`: vendoring is * an explicit request to download, and the cache stores a parsed schema rather * than the bytes a pin has to be taken over. */ declare function fetchSchemaBytes(ref: string, options?: LoadSchemaOptions): Promise; /** * Settle the remote-schema options for one run, from the config and the flag. * * Lives here rather than in a command core because all three commands need the * same answer, and because "where does the cache live" is a property of schema * loading, not of `validate`. `root` is the config's directory when a config * governs the run, so a developer running from `docs/` shares the cache with * CI running from the repo root instead of quietly keeping a second one. */ declare function schemaLoadOptions(args: { root: string; /** * Where a relative local-file schema ref is measured from — the run's `cwd`, * not `root`. The two differ when a config was discovered in an ancestor, and * a `--schema ./x.json` typed on the command line belongs to the directory * the user was standing in, not to the config's. */ fileBase?: string; /** Config `schemaCache.ttlHours`. */ ttlHours?: number; /** `--offline`, else config `offline:`. */ offline?: boolean; /** From `collectSchemaPins(config)`; omitted when the config pins nothing. */ pins?: ReadonlyMap; }): LoadSchemaOptions; /** Load and return the JSON Schema object for a reference. */ declare function loadSchema(ref: string, options?: LoadSchemaOptions): Promise>; interface SchemasInfo { builtins: BuiltinInfo[]; formats: { name: string; extensions: string[]; implemented: boolean; /** Whether `docmeta fill` can write metadata back to this format. */ writable: boolean; }[]; } declare function getSchemasInfo(): SchemasInfo; /** * Where a vendored schema lands by default. * * **Not** `.docmeta/`, which is gitignored wholesale and holds the schema and * proposal caches. A vendored schema is the opposite kind of artifact: it has * to be committed, because being in the consuming repository's own history is * the entire point of vendoring. */ declare const DEFAULT_VENDOR_DIR = "./schema"; interface VendorOptions { /** The `http(s)` URL to download. */ url: string; /** Directory for the vendored copy, relative to `cwd`. Default `./schema`. */ dir?: string; /** `-c/--config`. Absent discovers a config, or creates one in `cwd`. */ configPath?: string; cwd?: string; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; /** Fetch timeout, in ms. Defaults to the registry's. */ timeoutMs?: number; /** Response size cap, in bytes. Defaults to the registry's. */ maxBytes?: number; } interface VendorResult { /** The URL that was downloaded. */ url: string; /** The vendored file, relative to `cwd`, posix-style. */ file: string; /** The pin recorded for it. */ integrity: string; /** Size of the vendored copy, in bytes. */ bytes: number; /** The config that was written, relative to `cwd`, posix-style. */ config: string; /** Whether that config had to be created. */ configCreated: boolean; /** Whether an existing `schemas:` entry was replaced rather than appended. */ replaced: boolean; /** Whether the downloaded bytes were identical to the copy already on disk. */ unchanged: boolean; } /** * The filename for a vendored schema, derived from the URL's last path segment. * * Sanitized rather than trusted: the segment reaches the filesystem, so * anything that is not an ordinary filename character is replaced, and a * leading dot is prefixed away so the copy cannot land as a hidden file that * directory walks skip. */ declare function vendorFileName(url: string): string; /** * Download a remote schema into the repository and pin it. * * The order of operations is the contract: everything that can refuse does so * *before* the network call or the write, so a refused run leaves the working * tree exactly as it found it. */ declare function runVendorSchema(opts: VendorOptions): Promise; /** Shared shapes for the `fill` command, split out to keep imports acyclic. */ /** Why a proposed value was not written. */ type SkipReason = /** Self-reported confidence was below the threshold. */ "low-confidence" /** Writing it would leave the document failing its own schema. */ | "schema-mismatch" /** The model declined to propose a value. */ | "no-proposal"; interface FilledField { /** JSON Pointer, e.g. "/title" — the same form `validate` reports. */ field: string; required: boolean; confidence: number; reasoning: string; /** Absent when the field was skipped. */ value?: unknown; written: boolean; skipReason?: SkipReason; } interface FillFileResult { file: string; format: string; schemas: string[]; fields: FilledField[]; /** Whether the file's content changed (false under --dry-run too). */ changed: boolean; /** Parse failure, read-only format, or provider error. */ error?: string; /** The filled document. Populated only when the caller asks for it. */ content?: string; } interface FillSummary { files: number; changed: number; written: number; skipped: number; /** Skipped fields that the schema lists as required — drives exit code 1. */ requiredSkipped: number; errors: number; costUsd: number; cached: number; } interface FillRun { results: FillFileResult[]; summary: FillSummary; /** Echoed so JSON consumers and CI can assert which gate actually ran. */ threshold: number; dryRun: boolean; provider: string; model: string; /** True when the call cap stopped the run before every file was seen. */ turnsSpent: boolean; } interface FillOptions { inputs: string[]; cliSchemas?: string[]; exts?: string[]; exclude?: string[]; /** `--as` format override (extractor name). */ as?: string; configPath?: string; /** `--no-config`: skip config discovery and use the built-in defaults. */ noConfig?: boolean; cwd?: string; /** Content for the `-` (stdin) input, injected by the CLI/tests. */ stdinContent?: string; /** Permit an input set that resolves to zero files (see `assertNonEmpty`). */ allowEmpty?: boolean; /** * `--no-gitignore` (false). Absent leaves config `respectGitignore:` in * charge, which itself defaults to on. */ respectGitignore?: boolean; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; /** Called once when a config governs the run, so the CLI can report it. */ onConfigLoaded?: (info: ConfigNotice) => void; /** * `--offline`: never fetch a remote **schema**. Absent leaves config * `offline:` in charge. * * Scoped to schema loading only. It says nothing about the inference * provider, which is a separate network dependency with its own controls * (`--provider mock`, `--dry-run`). */ offline?: boolean; /** Restrict proposals to these top-level fields. */ fields?: string[]; /** Minimum self-reported confidence to write (0-1). Default 0.7. */ confidence?: number; /** Report proposals without writing them. */ dryRun?: boolean; provider?: string; model?: string; /** Use the on-disk proposal cache. Default true. */ cache?: boolean; /** Refuse a hosted provider: inference must run on this machine. */ local?: boolean; /** Stop after this many inference calls. Counts calls, not files. */ maxTurns?: number; /** Characters of document per call. Default 12000. */ chunkChars?: number; /** Files inferred in parallel. Default 4. */ concurrency?: number; /** Include the filled document on each result (used for stdin and tests). */ includeContent?: boolean; /** Test seam: bypasses `makeProvider`, so no API key is needed. */ inferenceProvider?: InferenceProvider; } declare function runFill(opts: FillOptions): Promise; declare class Validator { private readonly schemaOptions; /** * How this validator's schemas are loaded: the disk cache location, its TTL, * and `--offline`. Held per instance rather than read from module state, so * two differently-configured validations in one process each get their own * settings. * * That is not full isolation, and the difference matters to a library * caller: `schema-registry` keeps a process-wide memo of fetched schemas, so * one validator's successful fetch is visible to another. `offline` is * excluded from that sharing on purpose — an offline validator will not be * served something this process pulled over the network — but the memo is * still shared, so a URL fetched once is not re-fetched per instance. */ constructor(schemaOptions?: LoadSchemaOptions); private ajvByDialect; /** * Keyed on the in-flight *promise*, not the resolved validator. Caching the * result made this a check-then-act race: `fill` walks files through a worker * pool, so every worker missed the cache while the first `loadSchema` was * still pending and they all then compiled the same schema into the one * shared per-dialect Ajv. Ajv registers a schema's `$id` on the first compile * and rejects the second with "schema with key or id ... already exists", * which took down any multi-file run against an $id-bearing schema. Storing * the promise before the first await lets the losers await the one compile. */ private cache; private ajvFor; /** * Synchronous by design: the `cache.set` has to happen in the same tick as * the miss, or a second caller can slip in before the entry exists. */ private compile; private compileUncached; /** * Validate `data` against every schema in `refs`. Returns all violations, * each tagged with the schema that produced it and a source line via * `lineFor`. * * `colFor` is optional and additive: this signature is public, so a fourth * *required* parameter — or a widened third one — would be a consumer break. * Callers with an extractor that supplies no column pass nothing and get the * previous behavior exactly. */ validate(data: Record, refs: string[], lineFor: (pointer: string) => number | undefined, colFor?: (pointer: string) => number | undefined): Promise; } /** * Applied when nothing else resolves. Seven-Action is safe to include here * because it constrains `action` — a key documents don't otherwise carry — and * does not require it, so adding it fails nothing that passed before. * Diataxis is deliberately absent: it both requires and constrains `type`, so * defaulting it would fail every repo not already on Diataxis. */ declare const DEFAULT_SCHEMAS: readonly string[]; /** * The reference a `schemas:` entry loads, in either form. * * The ref *string* is what `resolveSchemaSet` returns and therefore what every * report names, what every baseline fingerprint is taken over, and what keys * `Validator`'s compile cache. Widening those to carry a mapping would have * changed all three; the `{source, integrity}` sidecar travels separately, in * the pin map below. */ declare function schemaEntryRef(entry: SchemaEntry): string; /** * The ref → pin map for a config, for `loadSchema` to consult. * * Only entries that actually carry `source` or `integrity` are recorded, so a * config written entirely in the string form produces an empty map and nothing * about how it loads changes. * * Keyed on the ref exactly as `resolveSchemaSet` will hand it to `loadSchema`, * which means this must be built from the **rebased** config — the same * absolute spelling on both sides, or the pin silently fails to apply. */ declare function collectSchemaPins(config: DocmetaConfig | null | undefined): Map; /** * Re-point a config's **local file** schema refs at the config's own directory. * * `schemas: ["./house.schema.json"]` means the file next to the config; that is * what the person editing the config can see. But `loadSchema` reads a file ref * relative to the process's working directory, so once the config lives * somewhere other than where the command was invoked — via `-c ../x.yaml`, or * via discovery finding an ancestor — the same ref points at nothing. * * Only refs the *config* supplied are rebased. A document's own `$schema` and a * `--schema` on the command line were both written by someone standing in the * working directory, so they keep resolving from there. Built-in ids and URLs * have no base to speak of and pass through untouched. * * When the config's directory *is* the working directory — every setup that * works today — the config object is returned unchanged, so nothing about an * existing run moves, right down to the ref strings that appear in reports. * The rebased form is absolute rather than relative on purpose: relative would * only be correct while `process.cwd()` matched `cwd`, which is true of the CLI * but not of `runValidate` called as a library. */ declare function rebaseConfigSchemaRefs(config: DocmetaConfig, configDir: string, cwd: string): DocmetaConfig; interface ResolveParams { /** File path (relative is fine) used for override glob matching. */ filePath: string; /** `$schema` value pulled from the file's metadata. */ fileSchema?: unknown; /** Repeatable `--schema` values; non-empty means override. */ cliSchemas?: string[]; /** Loaded config, if any. */ config?: DocmetaConfig | null; /** * Directory a **relative document-supplied** file ref is measured from — the * run's `cwd`, matching `LoadSchemaOptions.fileBase`. Only the containment * check below reads it; the ref string itself is never rewritten. */ fileBase?: string; /** * The repository a document-supplied local path may not escape. Supplied by * the command cores via `schemaTrustRoot`. * * **Omitting it skips containment.** The resolver is synchronous and pure, * and finding a git root is a filesystem walk — so the root is settled once * per run by the caller rather than rediscovered per file. `runValidate` and * `runFill` both pass it, and `test/commands.test.ts` proves they do end to * end; a library caller that resolves refs itself opts in the same way. */ trustRoot?: SchemaTrustRoot; /** * Diagnostics for the user. Used by `documentRefs: none`, which must say * which document's `$schema` it dropped — discarding input in silence is the * failure mode this key exists to remove. */ onNotice?: (message: string) => void; } declare function resolveSchemaSet(params: ResolveParams): string[]; /** What a well-formed pin looks like, for error messages. */ declare const INTEGRITY_SHAPE = "sha256-<64 hex characters>"; /** * The pin for a byte sequence. * * Hashes the **bytes**, never a string: decoding to UTF-8 and re-encoding is * lossy for a payload that is not valid UTF-8, which would make the pin wrong * in exactly the case an integrity check exists to catch. */ declare function integrityOf(bytes: Uint8Array): string; /** Whether `value` is a pin this version can verify. */ declare function isIntegrity(value: string): boolean; /** * Where the cache lives, relative to the project root. * * `.docmeta/` is already ignored wholesale, and `fill` writes its proposal * cache to `.docmeta/cache` — a different directory, so the two never collide. */ declare const SCHEMA_CACHE_DIR = ".docmeta/schema-cache"; /** The only entry format this version understands. */ declare const SCHEMA_CACHE_VERSION = 1; /** How long a cached schema is served before it is re-fetched. */ declare const DEFAULT_TTL_HOURS = 24; /** The cache directory for a project rooted at `root`. */ declare function schemaCacheDir(root: string): string; /** What one cache file holds. */ interface SchemaCacheEntry { version: number; /** The URL this entry was fetched from; re-checked on read. */ url: string; /** * When it was fetched, ISO-8601. **Diagnostic only** — freshness is measured * on the file's mtime, which a restored cache or a clock change cannot make * disagree with the filesystem the way an embedded timestamp can. */ fetchedAt: string; schema: Record; } interface ReadOptions { /** * Serve an entry regardless of age. What `--offline` needs: there is no * re-fetch available, so a stale copy beats failing the run outright. */ ignoreTtl?: boolean; } declare class SchemaCache { private readonly dir; private readonly ttlHours; constructor(dir: string, ttlHours?: number); /** A TTL of 0 disables the cache in both directions. */ get enabled(): boolean; /** * The file an entry lives in. * * Keyed on a hex digest of the URL, never on the URL itself: a URL carries * `/`, `..`, `:`, and a query string, so `join(dir, url + ".json")` lets the * *server's* address decide where the write lands. A digest cannot escape the * directory. */ entryPath(url: string): string; /** * The cached schema for `url`, or null. * * Every malformation — an unreadable file, unparseable JSON, an unknown * version, an envelope naming a different URL, a payload that is not an * object — degrades to a **miss**. A cache is an optimization, and a corrupt * entry must cost one fetch, never a failed run that no `docmeta` command * explains how to fix. */ read(url: string, options?: ReadOptions): Promise | null>; /** * Record a freshly fetched schema. * * A failure to write is swallowed. A read-only checkout, a full disk, or a * sandbox with no write access must cost the *next* run one fetch, not this * run its result. * * Returns whether the entry actually landed on disk, which is a different * question from whether the run should continue. */ write(url: string, schema: Record): Promise; } interface SarifOptions { /** The run's path frame. Without one, `file` labels are emitted as they are. */ frame?: FingerprintContext; /** Diagnostics for the user; the CLI writes these to stderr. */ onNotice?: (message: string) => void; } declare function renderSarif(results: ValidationResult[], opts?: SarifOptions): string; /** * JUnit XML — what CI systems parse for the "Tests" tab. * * There is no authoritative JUnit schema; Jenkins, GitLab, CircleCI, and Azure * each accept a different superset. This writer sticks to the attributes all of * them honor (`name`, `tests`, `failures`, `errors`, `classname`, `type`, * `message`) and avoids the contested ones — `time`, which would be meaningless * here, `system-out`, and nested suites. That is a compatibility judgement, not * a specification. * * **One `` per file, one `` per violation.** So the tab reads * "2 tests, 1 failed" and matches `2 files checked, 1 failed`. * Violation-as-testcase would make the test count rise and fall with document * quality, which reads as a suite someone broke. * * Escaping is the one thing a hand-rolled writer gets wrong. Messages carry * schema-authored text — a `pattern` regex may hold `<`, `&`, and quotes — and * paths can hold `&`. Every attribute value goes through `xmlEscape`; nothing * is interpolated raw. */ interface JunitOptions { /** * The `classname` each `` carries: which docmeta command produced * these findings. Defaults to `docmeta.validate`, the only producer before * proposal 0026 made `query --check` a second one — whose findings must not * ship under validate's name. */ classname?: string; /** * The run's path frame, used only to canonicalize a local-file schema ref in * ``. * * Without it the attribute carries the ref exactly as the run received it — * `./my.schema.json` from the repo root, `../my.schema.json` from a * subdirectory, or a machine-absolute path once config discovery has rebased * it. SARIF's `ruleId` is already canonical, so a consumer correlating the two * for one run would find them disagreeing on the same violation. */ frame?: FingerprintContext; } declare function renderJunit(results: ValidationResult[], opts?: JunitOptions): string; /** * Reporters render validation results to a string. The command layer writes * the result to stdout; diagnostics go to stderr separately. */ /** * Every value `--format` accepts, in the order help and error messages list * them. * * The list is stated **once**. It used to live in five unlinked places — this * union, a separate `Set` in the CLI, the error message, the option * description, and the docs — and the `Set` was not tied to the union at all, * so widening the union alone type-checked and still rejected the new value at * runtime. Deriving the union from the array makes that drift impossible. */ declare const REPORT_FORMATS: readonly ["pretty", "json", "github", "sarif", "junit"]; type ReportFormat = (typeof REPORT_FORMATS)[number]; declare function isReportFormat(value: string): value is ReportFormat; /** * The formats **every** command produces. * * `get` and `schemas` accept exactly these two, and each used to spell the pair * out inline in `src/cli.ts` as `format !== "pretty" && format !== "json"` with * a hand-written "Use pretty or json." beside it — the same stringly-typed * drift this module's `REPORT_FORMATS` comment describes, reproduced three more * times. Stated once, the guard and the message cannot disagree. */ declare const COMMON_FORMATS: readonly ["pretty", "json"]; type CommonFormat = (typeof COMMON_FORMATS)[number]; declare function isCommonFormat(value: string): value is CommonFormat; /** * The formats `query` accepts, one six-value list with per-value gates: * `pretty | json | csv` render result rows unconditionally (csv is proposal * 0029), and the findings three — `github | sarif | junit` — are legal only * under `--check` (proposal 0026). * * A list of its own rather than `REPORT_FORMATS`: `validate`'s list and * `query`'s grow independently — `csv` lives here and not there — and one * shared const would make every addition to either a change to both. */ declare const QUERY_FORMATS: readonly ["pretty", "json", "csv", "github", "sarif", "junit"]; type QueryFormat = (typeof QUERY_FORMATS)[number]; declare function isQueryFormat(value: string): value is QueryFormat; interface ReportOptions extends SarifOptions, JunitOptions { color?: boolean; /** In pretty output, omit passing files. */ quiet?: boolean; } declare function render(format: ReportFormat, results: ValidationResult[], summary: RunSummary, opts?: ReportOptions): string; /** * Every value `fill -f` accepts, in the order help and error messages list * them. Derived union, one statement of the list — the same rule * `REPORT_FORMATS` documents at length in ./index.ts, which this file used to * break by spelling `"pretty" | "json"` out a second time. * * `sarif` and `junit` are deliberately absent: they describe *findings in * files*, and a skipped optional property is a proposal with a confidence * score, not a finding. See 0003 § stress test 8. */ declare const FILL_FORMATS: readonly ["pretty", "json", "github"]; type FillReportFormat = (typeof FILL_FORMATS)[number]; declare function isFillFormat(value: string): value is FillReportFormat; interface FillReportOptions { color?: boolean; /** * In pretty output, omit files with nothing to report. * * "Nothing" is narrow on purpose — see `renderFillPretty`. */ quiet?: boolean; } /** * GitHub Actions annotations for the work `fill` could **not** do. * * One `::error` per property the schema requires that was not filled — the same * set that drives `fill`'s exit code 1. Optional skips stay silent, matching * the exit-code rule: a skipped optional property is a normal outcome, and * annotating it would make every run look broken. * * No `line=`. A `FilledField` carries no location — unlike a validation error, * a proposal is about a property that is *missing* from the document, so there * is nothing to point at. GitHub anchors a file-only annotation to line 1. * * The escaping is `escapeWorkflowCommandMessage`, shared with `validate`'s * renderer rather than re-derived: the `%`-before-newline ordering is the part * that is easy to get wrong. */ declare function renderFillGithub(run: FillRun): string; declare function renderFill(format: FillReportFormat, run: FillRun, opts?: FillReportOptions): string; /** * Reporter for `get`. * * `render()` in ./index.ts is keyed to ValidationResult/RunSummary, so `get` * gets its own renderer, exactly as `fill` does. It lived inline in * `src/cli.ts` until `--quiet` arrived and made the question "who decides what * is printed?" answerable in two places at once. * * `--quiet` is a **reporter** concern, not a core one. `GetOptions` / * `GetFileResult` are public API, and a programmatic caller handed a silently * filtered array cannot tell a filtered run from an empty one — so `runGet` * returns every file it read, and the filtering happens here, where it only * affects text a person is reading. */ interface GetReportOptions { color?: boolean; /** In pretty output, omit files where every requested field is unset. */ quiet?: boolean; } /** `(unset)` for a missing field; JSON for anything that is not a string. */ declare function stringifyValue(value: unknown): string; /** * One `: =` line per requested field per file. * * `quiet` hides a file only when **every** requested field is unset. A file * where one field resolved and another did not is still printed, `(unset)` * included: the flag hides files, never values, so `--quiet` can never be the * reason a value the user asked for went missing. */ declare function renderGet(results: GetFileResult[], fields: string[], opts?: GetReportOptions): string; /** * Reporter for `query`. * * Same split as `get`: `runQuery` returns every row, and presentation — * alignment, the row count, the `--check` verdict — happens here, where it * only affects text a person is reading. `json` output never passes through * this module; the CLI prints the bare row array, mirroring `get`'s bare * array, and the exit code (not the envelope) carries the `--check` verdict. */ interface QueryReportOptions { color?: boolean; /** `--check`: append a ✓/✗ verdict line instead of the plain count. */ check?: boolean; /** `--dry-run` (or `--check`): the changes were previewed, not applied. */ dryRun?: boolean; } /** * An aligned table (header + rows) and a trailing count line. The last column * is never padded, so no line carries trailing spaces. A metadata edit * renders as its per-file diff instead of a row table (0022). */ declare function renderQuery(run: QueryRun, opts?: QueryReportOptions): string; declare function renderQueryCsv(run: Pick): string; type Flavor = FrontmatterFlavor; /** * Character offsets bracketing the leading fenced block, measured against the * *original* content — BOM included, CRLF intact. Writing metadata back is a * surgical splice of `[innerStart, innerEnd)`, so everything outside that range * (the BOM, both fences, the whole body, the file's final-newline state) is * preserved byte for byte by construction rather than by careful reassembly. */ interface FrontmatterLocation { /** Which flavor the opening fence selected. */ flavor: Flavor; /** Offset of the opening fence's first char (1 when a BOM precedes it). */ openStart: number; /** Offset just past the opening fence's terminator == start of inner text. */ innerStart: number; /** Offset of the closing fence line's first char == end of inner text. */ innerEnd: number; /** Offset just past the closing fence's terminator (or EOF). */ closeEnd: number; /** Line terminator of the opening fence line — the block's EOL on re-emission. */ eol: "\n" | "\r\n"; /** 1-based file line of the first content line (always 2). */ firstContentLine: number; } /** * Locate the leading fenced front matter block, if any. Returns null when there * is no opening fence *or* no matching closing fence — an unterminated fence is * not front matter (see the rst extractor, which relies on that distinction). */ declare function locateFrontmatter(content: string): FrontmatterLocation | null; /** * The block's inner text, LF-normalized with the final terminator removed — * byte-identical to what the parsers have always received, which is what lets * `extractFrontmatter` sit on top of the locator with no behavior change. */ declare function frontmatterInnerText(content: string, loc: FrontmatterLocation): string; /** Core front matter extraction shared by the markdown, mdx, adoc, rst formats. */ declare function extractFrontmatter(content: string, format: string): ExtractedMetadata; /** * Merge `patch` into the document's leading metadata block and return the new * content. Pure: no IO, no mutation, and the input string itself is returned * when the patch is a no-op. */ declare function applyFrontmatter(content: string, patch: MetadataPatch, options?: ApplyOptions): string; /** * Write `contents` to `path`, replacing it atomically. Falls back to a direct * write (with a warning on stderr) if the rename keeps failing because the * target is locked — on Windows that is a real and recoverable situation, and * refusing to write at all would be worse than a non-atomic write. */ declare function writeFileAtomic(path: string, /** * A `Uint8Array` writes byte-for-byte. `schemas vendor` needs that: the * integrity pin it records is taken over exactly what the server sent, and a * decode/re-encode round trip through a UTF-8 string would change the bytes * of a payload that is not valid UTF-8 — so the pin would be wrong the first * time anything checked it. */ contents: string | Uint8Array): Promise; /** * Extractor registry. Maps file extensions to extractors and resolves an * extractor by name (for the `--as` override). Every registered extractor is * implemented today; the `implemented` filters below are what would keep a * declared-but-unwired format out of directory walks, so an unfinished format * can be registered without becoming "supported". */ /** Resolve an implemented extractor for a file extension (incl. dot). */ declare function extractorForExtension(ext: string): MetadataExtractor | undefined; /** Extensions handled by implemented extractors (used for directory walks). */ declare function supportedExtensions(): string[]; export { type ApplyOptions, type BaselineSummary, COMMON_FORMATS, type CheckConfig, type CommonFormat, type ConfigNotice, DEFAULT_SCHEMAS, DEFAULT_TTL_HOURS, DEFAULT_VENDOR_DIR, type DocmetaConfig, DocmetaError, type DocumentRefTrust, type ExtractOptions, type ExtractedMetadata, FILL_FORMATS, type FetchedSchema, type FieldError, type FillConfig, type FillFileResult, type FillOptions, type FillReportFormat, type FillReportOptions, type FillRun, type FillSummary, type FilledField, type FingerprintContext, type FrontmatterFlavor, type FrontmatterLocation, type GetFileResult, type GetOptions, type GetReportOptions, INTEGRITY_SHAPE, type JunitOptions, type LoadSchemaOptions, type LoadedConfig, type MetadataExtractor, type MetadataPatch, QUERY_FORMATS, type QueryChange, type QueryFormat, type QueryOptions, type QueryReportOptions, type QueryRun, REPORT_FORMATS, type ReadOptions, type ReportFormat, type ReportOptions, type RunConfig, type RunConfigOptions, type RunSummary, SCHEMA_CACHE_DIR, SCHEMA_CACHE_VERSION, type SarifOptions, SchemaCache, type SchemaCacheConfig, type SchemaCacheEntry, type SchemaEntry, type SchemaPin, type SchemaRefEntry, type SchemaTrustConfig, type SkipReason, type ValidateOptions, type ValidateRun, type ValidationResult, Validator, type VendorOptions, type VendorResult, applyFrontmatter, classifyRef, collectSchemaPins, extractFrontmatter, extractorForExtension, fetchSchemaBytes, frontmatterInnerText, getSchemasInfo, integrityOf, isCommonFormat, isFillFormat, isIntegrity, isQueryFormat, isReportFormat, listBuiltins, loadConfig, loadSchema, locateFrontmatter, parseConfig, rebaseConfigSchemaRefs, render, renderFill, renderFillGithub, renderGet, renderJunit, renderQuery, renderQueryCsv, renderSarif, resolveRunConfig, resolveSchemaSet, runFill, runGet, runQuery, runValidate, runVendorSchema, schemaCacheDir, schemaEntryRef, schemaLoadOptions, stringifyValue, supportedExtensions, vendorFileName, writeFileAtomic };