import { ComponentType, ReactNode } from 'react'; import { b as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-TrLiqXLk.cjs'; import { D as DirectiveHost } from './directive-host-BLJvN-wO.cjs'; type ArrayItemScalarType = "string" | "number" | "boolean"; /** Value domain of a `record` field — typed scalars, or any JSON value. */ type RecordValueType = ArrayItemScalarType | "json"; type FieldBase = { required?: boolean; nullable?: boolean; /** Human guidance — JSON Schema `description` / variable `helpText`. */ description?: string; /** * Default VALUE (JSON Schema `default` / variable `defaultValue`). * Annotation-level: validators never apply it; emitters carry it verbatim. */ default?: unknown; }; type FieldSchema = (FieldBase & { type: "string" | "boolean"; }) | (FieldBase & { type: "number"; /** Inclusive lower bound — JSON Schema `minimum`. */ min?: number; /** Inclusive upper bound — JSON Schema `maximum`. */ max?: number; /** Increment — JSON Schema `multipleOf`. Annotation-level in the parser. */ step?: number; }) | (FieldBase & { type: "string[]"; /** Items-enum: each item must be one of these values (checkbox option sets). */ values?: string[]; /** With `values`: the set is advisory — any string item is also legal (`allowOther`). */ open?: boolean; }) | (FieldBase & { type: "number[]" | "boolean[]"; }) | (FieldBase & { type: "json"; }) | (FieldBase & { type: "json[]"; }) | (FieldBase & { type: "array"; itemKinds: string[]; }) | (FieldBase & { type: "object"; kind: string; }) | (FieldBase & { type: "inline_object"; fields: Record; /** additionalProperties: true — unknown keys are legal, not residue-only. */ open?: boolean; }) | (FieldBase & { type: "record"; values: RecordValueType; }) | (FieldBase & { type: "enum"; values: string[]; /** "One of these OR any string" — the option set is advisory (`allowOther`). */ open?: boolean; }) | (FieldBase & { type: "union"; scalars: Array<"string" | "number" | "boolean">; /** Object union members — kind refs (anyOf of $refs), may mix with scalars. */ kinds?: string[]; }); /** * Domain fields only — __kind is enforced by the parser via KindSchema.kind * (block slug). A kind with `root` set has NO field map (fields stays `{}`): * its value is the root field's type at the top level. See the module header. */ type KindSchema = { kind: string; fields: Record; /** Non-object root form — mutually exclusive with a non-empty `fields`. */ root?: FieldSchema; }; /** * The canonical IR contract for structured content. * * Every source (live agent stream, DB reload, Python-preprocessed results, * notes, any future surface) is normalized into these shapes exactly once. * Anything already carrying this IR passes through downstream layers by * reference — see `core/normalize.ts` for the idempotence law. * * This file is pure types + path helpers. No React, no Redux, no IO. */ /** Path of a value inside a parsed region (object keys + array indices). */ type IrPath = Array; /** * How a node's kind was (or wasn't) established. * * 🚨 `unverified` IS NOT `raw`, AND THE DIFFERENCE IS LOAD-BEARING (Arman's * ruling, 2026-08-29). "We checked it and it is wrong" and "we had nothing to * check it with" are opposite facts about a payload, and collapsing them cost * ~221 live kinds their component overnight: a kind whose schema never loaded * degraded to `raw`, the render route read `raw` as "broken instance", and * perfectly valid payloads were dumped as key/value lists instead of reaching * the component the user built. A consumer that treats `unverified` as a * failure is reintroducing that outage — the data is intact and MAY be * entirely valid; all that is missing is the schema that would prove it. */ type IrKindState = "resolved" | "speculative" | "pending_kind" | "pending_schema" | "unverified" | "raw"; /** * System-level zero-data-loss channel. Unknown keys are NEVER merged into a * node's `value` (they'd be indistinguishable from schema fields); they are * carried here verbatim — Protobuf unknown-fields discipline. Distinct from * any domain-level `additionalDetails` field, which is an ordinary schema * field inside `value`. */ interface IrResidue { /** Keys present in the source object but absent from the kind schema. */ extra: Record | null; /** Optional schema fields the source never provided. */ optionalMissing: string[] | null; /** Structured warnings attached during parsing (speculation backtracks, truncation, …). */ notices: Array<{ code: string; message: string; at?: number; }> | null; } /** Which wire syntax carried the kind discriminator for a node. */ type IrDiscriminator = { format: "json"; key: "__kind"; } | { format: "xml"; tag: string; } | { format: "fence"; language: string; }; /** A schema-shaped structured node. Children live inside `value` (and carry their own metadata via `nodeIndex`). */ interface IrStructuredNode { role: "structured"; /** Canonical kind slug. Empty string while pending. */ kind: string; kindState: IrKindState; discriminator: IrDiscriminator; /** Region-relative path ([] = region root). */ path: IrPath; status: "streaming" | "complete" | "error"; /** Compliant snapshot: schema fields + __kind ONLY. Unknown keys → residue. */ value: Record; residue: IrResidue | null; } /** Envelope version. Bump = migration point for persisted envelopes. */ declare const IR_VERSION: 1; /** * One parsed region's canonical form. Serializable (plain JSON), carried on * render blocks (`data.__ir`), persisted on artifacts and message metadata, * and — with `engine: "py-block-detector"` — accepted pre-built from Python. */ interface CanonicalBlockIR { v: typeof IR_VERSION; /** Provenance: which implementation produced this envelope. */ engine: "fe-kind-parser" | "py-block-detector"; /** Stable hash of the region source text — the idempotence / cache key. */ fingerprint: string; root: IrStructuredNode; /** * pathKey → node metadata for per-path readers (child kinds under root). * Carries each child node's residue — child snapshot values inside * `root.value` hold schema fields only, so WITHOUT this the envelope would * silently drop nested unknown keys (zero-data-loss violation). */ nodeIndex?: Record & { residue?: IrResidue | null; }>; } /** * KindDefinition — one kind, many facets. THE canonical registry entry. * * This does NOT merge BlockComponentRegistry or artifact-type-registry; the * `legacyBlockType` and `artifact` facets are FACADES pointing into them, so * migration is incremental and each registry keeps its own job. */ /** * The uniform props contract for kind-driven block components. This is what * de-special-cases flashcards: no component receives bespoke glue — every * kind component gets exactly this shape. */ interface KindBlockProps { kind: string; schema: KindSchema; /** Compliant snapshot value (schema fields + __kind). */ data: Record; status: "streaming" | "complete" | "error"; residue: IrResidue | null; path: IrPath; /** ParseSession identity for live child subscriptions; null after reload. */ identity: string | null; /** Child-kind schema lookup (replaces passing allSchemas around). */ resolve: (kind: string) => KindSchema | undefined; } type KindTier = "eager" | "warm" | "cold"; /** * A TABLE AS A KIND (KINDS-GLUE wave 3 §1.4): what the host learned from the store's * `custom.table_kind_facts` when it answered a `table:` kind. `stamp` moves on any Field or * choice change; `optionsTableIds` are the Tables whose records are this Table's choices (a change * there is a change here). `refusal` is the store's own sentence when the person may not open the * Table — the definition still exists so the card can say so instead of loading forever. */ interface TableKindFacet { tableId: string; organizationId: string | null; name: string | null; titleField: string | null; stamp: string | null; fieldIds: string[]; optionsTableIds: string[]; refusal?: string | null; } interface KindDefinition { /** Canonical slug — THE key. */ kind: string; /** null until the warm/cold fetch delivers it. */ schema: KindSchema | null; /** * Where the schema came from. `"table"` (KINDS-GLUE wave 3): the kind is `table:` — "a * record of that Table" — and its schema is derived from the Table's live Fields * (`@ai-matrx/records` `tableRenderSchema`), never a `kind_definition` row. */ schemaSource: "system" | "content_ir" | "table"; tier: KindTier; /** Present only for `schemaSource: "table"` — the Table the kind is derived from. */ table?: TableKindFacet; /** Component facet — lazy-loaded renderer for this kind. */ component?: { load: () => Promise<{ default: ComponentType; }>; }; /** Facade → BlockComponentRegistry type string (e.g. "flashcards"). */ legacyBlockType?: string; /** * Legacy-bridge facet: derive the existing component's `serverData` from a * canonical envelope. This is what lets a kind light up the REAL component * (FlashcardsBlock, …) with zero component changes during migration. */ toLegacyServerData?: (envelope: CanonicalBlockIR) => Record | undefined; /** Facade → artifact-type-registry canvasType. */ artifact?: { canvasType: string; }; /** * Markdown export facet — the FORWARD leg of artifact ⇄ markdown. Receives * the ZERO-LOSS reconstructed value object (the CALLER reconstructs — for * persisted structured artifacts `content.data` already IS that object; * this facet only renders it; `__kind` discriminators may still be * present and must be ignored). MUST produce human-readable markdown * (headings / lists / bold), never a JSON dump — a fenced json body under * a heading is acceptable only for inherently-code payloads (e.g. a * schema_proposal's JSON Schema). Unknown extra keys the renderer doesn't * understand MUST be appended under a small "Additional details" * (key: value) section so nothing silently vanishes. Kinds without this * facet fall back to `genericKindMarkdown` (kinds/kind-markdown-utils.ts). */ toMarkdown?: (value: Record) => string; persistence?: { persistStructured: boolean; }; /** Future XML tags / kind aliases resolving to this kind. */ discriminatorAliases?: string[]; /** * PARTIAL-READY opt-in — the streaming partial-kinds posture. * * While a structured region streams, the server may announce a PROVISIONAL * instance of this kind on `metadata.__ir_partial` (valid, closed JSON that * may be missing required fields). The default posture is PROGRESSIVE * (Arman, 2026-10-14): the provisional value is routed to the SAME component * that renders the final one, filling in as tokens arrive — exactly what a * chat stream already does. * * Set this to `false` only for a kind whose component is known to break on a * half value: its block keeps its loading skeleton until complete. `true` is * the default spelled out. A kind * with a `toLegacyServerData` bridge must ALSO pass `{ provisional: true }` * to `makeCompleteEnvelopeBridge` (pinned by a test). A component that * throws is caught, screams, and drops this kind back to withhold for the * session. * * Contract: common-docs/systems/architecture/content-ir/FEATURE.md */ partialReady?: boolean; /** * Loading-library slug (`kind_definition.metadata.loading_component`) — * which of the ~20 hardcoded loading components renders while this kind's * instance streams in / its schema+component fetch is in flight. Null or * unknown slugs fall back to the generic structured skeleton. */ loadingComponent?: string | null; } /** * THE HOST CONTRACT — everything this package refuses to decide. * * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable * part, let host capability enter through explicit named seams. The render * layer needs four things it must never own: * * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST * call). The route only asks "is this kind registered, and does it carry a * legacy bridge / a partial-ready opt-in". * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The * package ships `ComponentResolver`, which any host can construct with its * own loaders; a host with its own resolver only has to satisfy the type. * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's * TYPE; turning that type into pixels is the host's dispatch table * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The * package never imports a component library. * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}. * * No Next.js, no Redux, no Supabase, no router, no host error capture. If a * seam is missing, ADD A SEAM — never an import. */ /** * The minimum a render block must expose for the kind route to work. Hosts * pass their own richer block type through; the route is generic over `T` and * preserves every extra field. */ interface IrRoutableBlock { type: string; serverData?: Record; metadata?: Record; } /** A routable block that also carries its source text (the raw region). */ interface IrRenderBlock extends IrRoutableBlock { content: string; language?: string; } /** * Where registered kinds come from. `getDefinition` is the only method the * pure route needs; the rest drive the granular repaint and the warm-up tick * that keeps a cold registry from being mistaken for "this kind has no * component". */ interface KindDefinitionSource { getDefinition(kind: string): KindDefinition | undefined; /** Monotonic per-kind version — the repaint snapshot key. */ getKindVersion(kind: string): number; subscribeKind(kind: string, listener: () => void): () => void; /** One definition load per app session. */ ensureWarm(): Promise; } /** * The rendering seams. Everything here returns host UI; the package supplies * the DECISIONS and the chrome-free structure around them. */ interface ContentIrRenderSeams { /** * Draw a block the kind route has already typed. This is the host's dispatch * table — the single place a component key becomes a component. */ renderBlock(block: IrRenderBlock): ReactNode; /** * THE FLOOR: render ANY JSON value as a human document. Reached when a kind * has no render-trusted component, when the value is not an object at all, * and by the generic structured view. * * This is deliberately a seam and not a bundled component. Rendering a value * WELL means prose through the host's markdown renderer, media through the * host's file handler, and uniform arrays through the host's data table — * all of which are host property. A host that has none of those can pass a * `
`; it will be honest, just plain.
     */
    renderValue(props: StructuredValueRenderProps): ReactNode;
    /**
     * The "still arriving" indicator used by the provisional frame. Optional —
     * hosts without a shimmer get plain text.
     */
    renderShimmer?(text: string): ReactNode;
    /**
     * The honest "this shape has no custom component yet" notice. Optional: the
     * package draws a plain amber line when a host supplies nothing, and a host
     * with an icon set or its own callout component supplies that instead. It is
     * a seam and not a bundled component because this package depends on no icon
     * library.
     */
    renderNotice?(text: string): ReactNode;
}
interface StructuredValueRenderProps {
    value: unknown;
    /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */
    kind?: string;
    /** Why this shape has no custom view, in human words. */
    note?: string;
    /** Show the "what this is / raw data" footer. Default true. */
    footer?: boolean;
}
/**
 * One host, wired once, read by every component in this package through
 * {@link ContentIrRenderProvider}.
 */
interface ContentIrHost extends ContentIrRenderSeams {
    kinds: KindDefinitionSource;
    components: ComponentResolutionSource;
    reportError: ContentIrErrorReporter;
    /**
     * The `kind_component.platform` this host resolves as. "web" for every
     * browser UI; the column already models "react-native" and friends, and a
     * host that lies here renders the wrong component everywhere.
     */
    platform: string;
    /**
     * The Kind Directives seams (apply / open item / copy / noun catalog / item
     * kind). Optional: a host without them still renders every directive card,
     * apply-less and copy-less — honest chrome, never hidden content. Provided
     * to the directive tier by `ContentIrRenderProvider`; a host that has not
     * adopted the render provider mounts `DirectiveHostProvider` directly.
     */
    directives?: DirectiveHost;
}
/**
 * The pure route's dependencies — a strict subset of {@link ContentIrHost},
 * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream
 * accumulators, tests) where there is no provider to read.
 */
interface KindRouteEnv {
    /**
     * `schemaState` is optional and read ONLY to suppress a negative verdict while this kind's
     * definition read has not answered ("pending") — THE COLD-VERDICT RULE, per kind.
     */
    kinds: Pick & {
        schemaState?(kind: string): "loaded" | "pending" | "absent" | "failed";
    };
    /**
     * `hasSettled` is optional and read ONLY to suppress a negative verdict from
     * a registry that has not loaded yet — see THE COLD-VERDICT RULE on
     * `ComponentResolutionSource`.
     */
    components: Pick & Partial>;
    reportError: ContentIrErrorReporter;
    platform: string;
}
/** Narrow a host (or anything host-shaped) to what the pure route needs. */
declare function routeEnvOf(host: ContentIrHost): KindRouteEnv;

export { routeEnvOf as r };
export type { ContentIrHost as C, IrRenderBlock as I, KindDefinitionSource as K, StructuredValueRenderProps as S, KindRouteEnv as a, ContentIrRenderSeams as b, IrRoutableBlock as c };