/** * THE SCREAM SEAM. * * Every recovery path in this package is loud: a defective component row, a * failed resolver load, a kind whose component throws on a provisional value. * The package refuses to own where those screams land — matrx-frontend routes * them to the Error Inspector, the dashboard to its own log, the extension to * its background page. Each host binds one function. * * A host that binds a no-op has chosen silence, which is a defect in itself; * {@link consoleErrorReporter} is the honest default. */ interface ContentIrErrorReport { /** Always `"content-ir"` — hosts key their capture stores on it. */ source: "content-ir"; message: string; name?: string; stack?: string; /** Free-form grouping key (the host's `relation`), e.g. `"partial-kind"`. */ relation?: string; raw?: unknown; } type ContentIrErrorReporter = (report: ContentIrErrorReport) => void; /** The default when a host binds nothing: never silent. */ declare const consoleErrorReporter: ContentIrErrorReporter; /** * The (kind, platform, role) → component vocabulary — the Shape System's * resolver contract (rulings R1 + R6), stated once for every UI. * * Cross-repo system-of-record: * `common-docs/systems/architecture/content-ir/FEATURE.md` (package boundary) and * `matrx-frontend/features/content-ir/docs/SHAPE_SYSTEM.md` (the semantics). * * Nothing here knows where a row comes from. A host supplies rows from * `content_ir.kind_component` (Supabase, a REST call, a fixture); the resolver * only knows the shape. */ /** JSON object, restated locally so this package depends on no host type. */ type JsonObject = Record; /** * Which face of the shape a component draws. * * - `output` — the shape's finished value, the component a reader sees. * - `input` — the authoring form for a value of this shape. * - `loading` — the shape's LOADING face: what renders from the instant the * kind is identified until its output component has a renderable frame. * Authored per kind like any other component and fed the region's partial * value each frame, so it can PERFORM the arrival (surface real text as it * lands) instead of drawing a shapeless skeleton. Optional: a kind with no * `loading` component falls back to the host's loading library. */ type ComponentRole = "output" | "input" | "loading"; /** Whose view a layered row is: the viewer's own, or one of their organizations'. */ type ComponentLayerScope = "user" | "organization"; /** Which layered row produced a resolution — present only for overlay answers. */ interface ComponentLayerRef { scope: ComponentLayerScope; /** `kind_component_layer.id` — the compile-cache and incident identity. */ rowId: string; } /** The resolver's answer for one (kind, platform, role). */ interface ComponentResolution { /** The component key the renderer routes on (e.g. "flashcards"). */ componentKey: string; source: string; config: JsonObject; /** * Render-trust verdict (R6). Compiled entries are always active (trusted at * bootstrap); DB rows carry their own `is_active`. */ isActive: boolean; /** Which tier produced this answer — the verification hook's `by`. */ resolvedBy: "compiled" | "db"; componentSource: string | null; propsTransform: string | null; /** * TRUE when the row owns a real component body — even if `componentSource` * is null because the host's warm list is body-less and the cold fetch has * not landed it yet. THE routing gate: `routeToDbComponent` re-types on * this flag, never on body presence, so a body-less row still routes and * the renderer fetches/compiles the body lazily. */ hasComponentSource: boolean; pinnedKindVersion: number | null; /** The db row's `updated_at` — the compile-cache staleness key. */ updatedAt: string | null; createdBy: string | null; /** * The winning row's id when known (db tier: host-supplied; overlay: always). * Part of the compile-cache key — a body is never served for another row. */ rowId?: string | null; /** * Set ONLY when the answer came from the viewer's overlay (a personal or * organization layered row). Hosts MUST render a layered `source='db'` body * sandboxed regardless of any organization sandbox setting, and must never * file its failures against the shared kind. */ layer?: ComponentLayerRef | null; } /** * The read side of a component resolver, as the render layer consumes it. * The package ships {@link ComponentResolver}; a host with its own resolver * only has to satisfy this. */ interface ComponentResolutionSource { /** Synchronous — the render seam's per-block call. */ resolve(kind: string, platform: string, role: ComponentRole): ComponentResolution | null; /** * The SHARED tiers only (db → compiled), ignoring the viewer's overlay. * Optional so an existing host resolver satisfies the interface unchanged. */ resolveShared?(kind: string, platform: string, role: ComponentRole): ComponentResolution | null; /** Eager targeted fetch for one kind; fire-and-forget, never awaited on the hot path. */ requestComponent(kind: string, platform: string, role: ComponentRole): void; /** One list fetch per session. */ ensureWarm(): Promise; /** * Has the session's list load COMPLETED (successfully or not)? * * 🚨 THE COLD-VERDICT RULE. Before this existed, `resolve()` answering null * was ambiguous: it meant BOTH "this kind genuinely has no component" and * "I have not looked yet". The render route could not tell them apart, so a * block routed during the first paint of a fresh tab got stamped * `unregistered` — a durable, user-visible claim that the shape does not * exist — produced by a registry that had loaded nothing. Optional so an * existing host satisfies the interface unchanged; absent reads as settled, * which is the pre-existing behavior. */ hasSettled?(): boolean; /** * Has the lookup FOR THIS KIND settled — the session's list landed, or this kind's own read * answered? `hasSettled()` alone answers for the whole tier, and a host whose tier "settles" the * moment ANY row lands (one kind's per-kind read) closed the window for every other kind on the * page: the kind-registry Preview said brand_basics "isn't registered on this platform" on 7/7 * cold loads (2026-10-17). Optional; absent falls back to `hasSettled()`. */ hasSettledFor?(kind: string, platform: string): boolean; /** Refresh-on-view; rate-limited and deduped by the implementation. */ refresh(maxAgeMs?: number): Promise; /** Monotonic tier version — has anything at all changed? */ getVersion(): number; /** Monotonic per-kind version (+ wholesale epoch) — the granular repaint key. */ getKindVersion(kind: string): number; subscribe(listener: () => void): () => void; subscribeKind(kind: string, listener: () => void): () => void; } export { consoleErrorReporter as d }; export type { ComponentResolution as C, ContentIrErrorReporter as a, ComponentResolutionSource as b, ContentIrErrorReport as c };