/** * document.ts, Markdown text in, projection out. * * Parsing is LENIENT by construction: there is no path in this file that * discards, rewrites or normalises a line it did not understand. An unknown * heading, an unknown `key:` line, a table, a code fence, a nested list, an HTML * comment, all end up in `rawLines` exactly as written, and the ones this * module cannot type are served as prose. * * Two hazards drive the shape of the scanner: * * - FENCES. A fenced code block may contain a line that looks like a heading * or a field. Reading those as real would let a later write edit a line * inside the owner's code block, which is silent corruption of their content * and the worst failure this design can have. Fence state is tracked and * nothing inside a fence is ever typed. * - EM DASHES. The provenance suffix is em-dash delimited and the owner * writes em dashes in prose. The suffix is therefore recognised only when * the WHOLE * shape matches at end of line, and it is matched from the RIGHT so a line * carrying two suffix-shaped tails resolves to the newest one instead of * swallowing the tail into the quote. * * Failure is reserved for two conditions the caller detects before calling here: * the file cannot be read, and its bytes are not valid UTF-8. Nothing in this * file throws. */ import { type ProfileSectionName } from './fields.js'; import { type ProfileProjection, type ProfileProvenance, type ProfileSection } from './types.js'; /** The em-dash marker that opens a provenance suffix. */ export declare const PROVENANCE_MARKER = " \u2014 "; /** The fence a line opens or closes with, or `null` when it is not a fence line. */ export interface FenceMarker { readonly char: '`' | '~'; readonly length: number; } export declare function fenceMarkerOf(line: string): FenceMarker | null; /** * Whether `marker` closes a block opened by `open`. * * CommonMark: a closing fence uses the SAME character and is AT LEAST as long * as the opening one. Treating any fence marker as a toggle breaks two ordinary * documents. A four-backtick block containing a three-backtick sample is the * standard way to show fenced markdown inside markdown, and a `~~~` line is * ordinary content inside a backtick block. Getting this wrong does not merely * mis-parse: the scanner desynchronises, so real content after the block is read * as fenced and sample content inside it is read as real, which is how a line * in the owner's code block became a live field and a later write landed inside it. */ export declare function fenceCloses(open: FenceMarker, marker: FenceMarker): boolean; /** True when this line is a fence marker of any kind. */ export declare function isFenceToggle(line: string): boolean; export interface ProvenanceSplit { /** The line with its provenance suffix removed. */ readonly text: string; readonly provenance: ProfileProvenance | null; } /** * Split a line into its text and its provenance suffix, matching from the RIGHT. * * Rightmost wins because a line that somehow carries two suffixes should resolve * to the newest one with the older left visible as ordinary text. Matching from * the left instead swallows everything after the first suffix into the quote, * which produces a provenance record that is quietly wrong. * * Anything that is not a complete, well-formed suffix is text: an em dash in * the owner's own prose, a malformed date, a surface name outside the set, a bare trailing * quote. Such a line is preserved whole and reports no provenance. */ export declare function splitProvenanceSuffix(line: string): ProvenanceSplit; /** Render a provenance suffix. The inverse of {@link splitProvenanceSuffix}. */ export declare function renderProvenanceSuffix(provenance: ProfileProvenance): string; export interface ParsedFieldLine { readonly label: string; readonly value: string; } /** * A `key: value` line at column 0, or `null`. Bullets are excluded explicitly as * well as by the pattern, so the intent survives a later edit to either. */ export declare function parseFieldLine(text: string): ParsedFieldLine | null; export interface ParsedWasComment { /** The superseded line, exactly as it read. */ readonly previousLine: string; readonly supersededOn: string; } export declare function parseWasComment(text: string): ParsedWasComment | null; /** Render a `` history comment for a line being superseded. */ export declare function renderWasComment(previousLine: string, supersededOn: string): string; export interface ParseProfileInput { readonly path: string; readonly text: string; /** False when the file is not there yet, so `status` can say so honestly. */ readonly exists: boolean; } /** * Project Markdown text into the in-memory model. * * Split on `'\n'` alone, keeping any `'\r'` on the end of the line: joining with * `'\n'` then reproduces a CRLF file byte-for-byte, and a trailing newline * survives as a final empty element. */ export declare function parseProfileDocument(input: ParseProfileInput): ProfileProjection; /** * The section a write to `canonical` should target. * * A heading the owner renamed still matches when it normalises to a known * section name; anything else means the section is absent and the caller * creates the canonical one rather than guessing which of their headings was * meant. */ export declare function findProfileSection(projection: ProfileProjection, canonical: ProfileSectionName): ProfileSection | undefined; /** The section a heading names, matched the same way, for read verbs. */ export declare function findProfileSectionByHeading(projection: ProfileProjection, heading: string): ProfileSection | undefined; /** Join a raw line array back into document text, byte-for-byte. */ export declare function joinProfileLines(lines: readonly string[]): string; //# sourceMappingURL=document.d.ts.map