/**
 * TML v0 — Trellis Markup Language runtime.
 *
 * Typed source; transpiled to JS on the fly by the dashboard server for the
 * browser (`/tml-runtime.js`), and imported directly by vitest under node.
 *
 * Spec: docs/specs/tml-v0.md. Core (Store / parse / evaluate) is DOM-free so it
 * unit-tests under node; only `mount` / `applyBindings` touch the DOM.
 *
 * @module trellis/ui
 */
import { EAVStore } from '../core/store/eav-store.js';
import type { VcsOp } from '../vcs/types.js';
export type ResultRow = Record<string, unknown>;
export interface RefHandle {
    id: string;
    read(): unknown;
    write(value: unknown): void;
}
export interface TmlDriver {
    query(q: string): Promise<ResultRow[]>;
    op(action: string, args: Record<string, unknown>): Promise<void>;
    live(q: string, cb: (rows?: ResultRow[]) => void): () => void;
    ref(id: string): RefHandle;
}
export interface TmlBinding {
    query?: string;
    each?: {
        var: string;
        collection: string;
    } | null;
    live?: boolean;
    ref?: string;
    op?: {
        action: string;
        args: {
            name: string;
            expr: string;
        }[];
    } | null;
    if?: string;
    text?: string;
    attrs: Record<string, string>;
}
/** Outcome of walking a dotted field path. See `resolvePath`. */
export interface PathResult {
    /** false = a key along the path was absent from its container. */
    ok: boolean;
    /** Identical to `getPath`'s return value. */
    value: unknown;
}
export interface TmlDiagnostic {
    code: 'unresolved-path' | 'malformed-each' | 'malformed-op' | 'each-without-query';
    /** The attribute the diagnostic came from, e.g. `tml-text`. */
    attr: string;
    /** The offending expression, verbatim. */
    expr: string;
    /** Context, e.g. the scope's top-level keys. Never contains row values. */
    detail?: string;
}
export interface MountOptions {
    onDiagnostic?: (d: TmlDiagnostic) => void;
    /** Throw on the first diagnostic instead of reporting and continuing. */
    strict?: boolean;
}
/**
 * Resolve a dotted field path, distinguishing "key absent" from "key present
 * and undefined". `value` matches `getPath` exactly; `ok` is the new signal.
 */
export declare function resolvePath(obj: unknown, path: string): PathResult;
/** Resolve a dotted field path against an object, e.g. `lane.id`. */
export declare function getPath(obj: unknown, path: string): unknown;
/**
 * Resolve a TML expression against a scope.
 * Single field path / literal -> raw value (preserves booleans/numbers).
 * Multiple parts joined by `+` -> concatenated string.
 */
export declare function resolveExpr(expr: string, scope: Record<string, unknown>): unknown;
/**
 * The field paths an expression reads, ignoring quoted literals. Shares
 * `splitPlus` with `resolveExpr` so both agree on what a literal is.
 */
export declare function expressionPaths(expr: string): string[];
/**
 * Truthiness for `tml-if` only. Empty arrays and empty strings are falsy
 * (unlike bare JS), so `tml-if="issue.laneIds"` hides when `laneIds === []`.
 * Does not affect `tml-text` / `tml-attr-*` via `resolveExpr`.
 */
export declare function isTmlTruthy(v: unknown): boolean;
/** Parse a flat attribute map into a structured TML binding. */
export declare function parseTmlAttrs(attrs: Record<string, string | null>): TmlBinding;
/**
 * Evaluate a minimal TQL `find ?e where type = 'X' [and …]` against a
 * LanesSnapshot. Supports parenthesized OR groups and negated OR groups:
 * `and (status = 'backlog' or status = 'queue')`
 * `and not (status = 'in_progress' or status = 'paused' or status = 'closed')`
 */
export declare function evaluateQuery(query: string, snapshot: unknown): ResultRow[];
export declare class Store {
    snapshot: unknown;
    private _subs;
    seed(s: unknown): void;
    mutate(fn: (s: unknown) => void): void;
    subscribe(cb: (rows?: ResultRow[]) => void): () => void;
    private _notify;
}
export declare class WebDriver implements TmlDriver {
    base: string;
    store: Store;
    private _refs;
    constructor(opts?: {
        baseUrl?: string;
    });
    seed(snapshot: unknown): void;
    query(q: string): Promise<ResultRow[]>;
    live(_q: string, cb: (rows?: ResultRow[]) => void): () => void;
    op(action: string, args: Record<string, unknown>): Promise<void>;
    ref(id: string): RefHandle;
    /** Seed from snapshot, then subscribe to the SSE op stream. Returns the EventSource. */
    connect(opts: {
        snapshotUrl: string;
        streamUrl: string;
    }): Promise<unknown>;
}
/**
 * A TML driver that IS a peer.
 *
 * `WebDriver` renders from server-built snapshots, which means the server has to
 * anticipate every projection the page wants — the page cannot ask a question
 * nobody planned for. That is not local-first (TRL-108 Finding 2), and it forced
 * `evaluateQuery`: a regex approximation of TQL over two hardcoded collections.
 * Two implementations of one query language.
 *
 * This applies the op stream to a real `EAVStore` via `decompose` and answers
 * with the real `QueryEngine`. So TML gets whatever TQL can express — traversal,
 * links, arbitrary types — instead of `Lane` and `Issue`.
 *
 * Cost of the whole thing (store + decompose + QueryEngine + hash verify) is
 * ~5.7 KB gzipped, measured. `decompose` only became bundleable once its
 * accidental `fs`/`path` imports were removed.
 *
 * Not the default yet: SPEC-v1.1 (TRL-110) owns whether peers materialize, and
 * `WebDriver` remains for consumers that genuinely want a thin client.
 */
export declare class PeerDriver implements TmlDriver {
    base: string;
    store: EAVStore;
    private _engine;
    private _subs;
    private _seen;
    private _refs;
    constructor(opts?: {
        baseUrl?: string;
    });
    /**
     * Apply one op. Idempotent by hash: the stream replays on reconnect, and a
     * peer that double-applied would double-project.
     */
    applyOp(op: VcsOp): void;
    /** Apply a batch, then notify once — a per-op notify would re-render per op. */
    applyOps(ops: VcsOp[]): void;
    query(q: string): Promise<ResultRow[]>;
    live(_q: string, cb: () => void): () => void;
    op(action: string, args: Record<string, unknown>): Promise<void>;
    ref(id: string): RefHandle;
    /** Subscribe to the op stream and materialize it. Returns the EventSource. */
    connect(opts: {
        streamUrl: string;
    }): unknown;
}
/**
 * Resolve text/attr/if/op bindings on `el` and its children against `scope`.
 * Returns false if the element was removed by a falsy `tml-if` (caller skips it).
 */
export declare function applyBindings(el: any, scope: Record<string, unknown>, driver: TmlDriver): boolean;
/**
 * Static tier: malformed attributes, detectable without data. Runs at mount on
 * `el` and its descendants.
 */
export declare function checkStatic(el: any, report: (d: TmlDiagnostic) => void): void;
/**
 * Shape tier: field paths that do not resolve against a real result row. Runs
 * once, against the first row — a binding well-formed for row 0 is well-formed
 * for row N.
 */
export declare function checkShape(el: any, scope: Record<string, unknown>, report: (d: TmlDiagnostic) => void): void;
/** Mount all `tml-query` subtrees under `root` against `driver`. */
export declare function mount(root: any, driver: TmlDriver, opts?: MountOptions): void;
//# sourceMappingURL=tml-runtime.d.ts.map