import type { BlockDef, FormDef, TypeDef } from './types.js'; export interface AnnotateRequest { block: BlockDef; action: string; /** edit mode: value of the row's FIRST column identifying the target row */ row?: string; fields: Record; /** ISO date for the {date} template variable */ date: string; /** the HOST-RESOLVED actor's handle for the {actor} template variable * (ruling P14, doctrine 0020): the portable spelling documents store — * never a platform user id. Absent (no resolved actor) resolves {actor} * to '', so a template using it degrades to an empty cell, not a leaked * literal. */ actor?: string; } /** * Mint the next id for the block's id column over a SET OF EXISTING VALUES: * "q" + existing q1,q4 → q5. The container-free allocator (E40 PR1) — * `nextId` feeds it table cells, `annotateRecords` feeds it the sidecar's id * values, and a host whose records live in its own store feeds it whatever * that store holds (live AND archived, P13 — an archived id is still * addressable and its number is never reclaimed). One rule, `maxIdNumber`. */ export declare function nextAutoId(block: BlockDef, existing: Iterable): string; export declare function annotate(content: string, req: AnnotateRequest): string; /** * The sidecar-shaped counterpart of `annotate()`: same request, same form * grammar, but the block's rows live in `.records.yaml`. Pure — sidecar * text in, sidecar text out — so the caller wraps it in the same referee gate. * The caller routes here iff `sidecarHasBlock` says so, which is why `src` is * a string, never null. * * Two deliberate divergences from the table path, both because the container * differs and the RECORD must not: * · the identity key (t53) is NEVER settable through a form — an identity a * form can write is an identity nobody can trust — where the table path * has no identity field at all; * · `{auto}` allocates over the records already in the sidecar, same * max+1 rule as `nextId` over table rows: one allocator, two containers. * * Returns the minted uuid on an append into a block that declares identity, * '' otherwise. */ export declare function annotateRecords(src: string, req: AnnotateRequest): { text: string; uuid: string; }; /** `compileRecordWrite`'s product: the resolved form and the composed cells — * everything about a record write that is true whichever container holds the * rows. The CONTAINER (sidecar YAML, a host's op store) still owns applying * it: addressing the row, minting identity, the transaction. */ export interface RecordWrite { /** the resolved form — callers deciding capability read its declarations */ form: FormDef; mode: 'edit' | 'append'; /** edit mode: the target row address as the caller sent it, trimmed — * resolving it against the store is the container's business */ row?: string; /** lowercased column key → composed value: `form.set` (edit) or `form.row` * (append) with every template variable filled */ cells: Record; } /** * Compile one record write — THE ONE COMPILER (E40 PR1, ruling P2). Pure: * request in, cells out; no container is read or written here. * * This is the container-free half of `annotateRecords`, extracted so that a * host whose records never touch a sidecar (an operational store) compiles a * declared form through EXACTLY the code the sidecar path runs — one funnel, * one vocabulary of refusals, no second write discipline to keep in * agreement. `annotateRecords` calls it and stays byte-identical. * * What it enforces, in `annotateRecords`' own order and words: * · the form must be declared, its required fields present (`prepareForm`); * · the identity key is NEVER settable through a form (t53); * · every target column must be declared — a definition bug, not drift; * · an edit must name its row; an append fills `{auto}` from the caller's * `existingIds` — every id value the container already holds, LIVE AND * ARCHIVED (P13: an archived record's number is never reclaimed). */ export declare function compileRecordWrite(req: AnnotateRequest, ctx: { existingIds: Iterable; }): RecordWrite; /** * The FRONTMATTER-shaped counterpart of `annotate()` and `annotateRecords()`: * the same form grammar over a third container — the `---` region of the * document itself. Pure, string in → string out, so the caller wraps it in the * exact referee gate the other two containers get. * * BYTE-PRESERVATION CONTRACT (the 0.9.x sidecar-write discipline applied to * frontmatter): only the edited keys' lines change. Ordering, comments and * unrelated keys survive byte-for-byte, because the editor is a line splice — * find the key's own `key: value` line, rewrite that line, touch nothing else. * A declared-but-absent key appends one line before the closing fence (the * `ensureColumns` analog: the gap is the definition's business, the document * merely predates the field). Shapes the splice cannot re-emit — a block * scalar, a key whose CARRY WINDOW (every line before the next column-0 key, * the reader's own attachment rule) holds `- item` lines, continuation * scalars or nested maps — are REFUSED loudly rather than rewritten into * orphaned bytes, and the fence rule is `parseFrontmatter`'s own (`\n---` * after the opening fence), so the editor can never disagree with the reader * about where the region ends. * * Two divergences from the table path, both because the container differs: * · identity is never settable — `type`/`uuid` are refused here as well as * at load (the annotateRecords rule, third container); * · a field the form declares as `kind: date` must RECEIVE a date, and the * value that LANDS in a declared date frontmatter field must be one too * — both checked with `parseDateCell`'s calendar grammar (2026-02-30 is * refused, not just misshapen strings), because that is the grammar the * timeline reads back. No frontmatter parse rule is error-level for a * malformed date, so the declared kind is enforced at the write itself — * engine mechanics over a declared shape, no domain meaning. */ export interface FrontmatterAnnotateRequest { type: TypeDef; action: string; fields: Record; /** ISO date for the {date} template variable */ date: string; } export declare function annotateFrontmatter(content: string, req: FrontmatterAnnotateRequest): string; /** Rewrite every ![…](src) image ref (plain or ) to point at url. */ export declare function rewriteMediaRef(content: string, src: string, url: string): string; /** One intent in a change set: the same shape a form submit posts. * `block` absent (or empty) IS the frontmatter spelling — the op then names * no row either, and `action` resolves against the CONCEPT's TYPE's declared * frontmatter forms (the sidecarHasBlock routing rule's sibling: the op * names no block → the write targets the document's own `---` region). */ export interface ChangeOp { concept: string; block?: string; action: string; row?: string; fields: Record; } /** Apply a whole change set in memory. * * Every op is an INTENT — "set these cells on this row" — replayed against * current content rather than a diff against content that has since moved on. * Ops touching the same document stack in order, so the last write to a cell * wins, deterministically. Nothing here touches disk: the caller supplies the * reader and decides what to do with the result, which is what lets one host * write files and another open a pull request from the same set. * * Returns the new content per document, or the first op that could not apply. */ export declare function applyChangeSet(ops: ChangeOp[], read: (rel: string) => string | null, blockOf: (name: string) => BlockDef | undefined, date?: string, /** sidecar text beside a CONCEPT path, or null — what routes an op to the * records file (t55). Defaults to "no vault has sidecars" so every existing * caller keeps its exact behaviour. */ readSidecar?: (rel: string) => string | null, /** resolve a document type for a BLOCK-LESS op (a frontmatter write, E18 * t9). Defaults to "this host knows no types", so a frontmatter op on an * existing caller fails loudly rather than silently no-opping. */ typeOf?: (name: string) => TypeDef | undefined): { ok: true; files: Map; } | { ok: false; error: string; };