@ai-matrx/content-ir-react 0.24.1 → 0.24.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.24.2 — 2026-10-06
4
+
5
+ - Compiled kind components receive `itemState` (the item's durable state; image refs carry `src`).
6
+ `DbKindComponentRenderProps.itemState?: Record<string, unknown>`.
7
+ - Consumer action: none. (0.24.0 / 0.24.1 carry `KindActionButton`; 0.24.2 is the first with `itemState` typed.)
8
+
3
9
  ## 0.24.1 — 2026-10-06
4
10
 
5
11
  - `KindActionButton` takes `run?: () => Promise<KindActionEnvelope>` as an alternative to `action` + `input`
@@ -1 +1 @@
1
- {"version":3,"sources":["../db-component.ts","../db-component/db-kind-component-cache.ts","../db-component/DbKindComponentErrorBoundary.tsx","../db-component/kind-actions-through-surface.ts"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/db-component` — rendering DB-authored kind\n * components (`content_ir.kind_component`, `source='db'`): the session compile\n * cache with its invalidation port, the kind prop contract, and the default\n * error boundary. The COMPILER itself stays a host injection (security-shaped\n * — see `db-component/db-kind-component-cache.ts`). Client-only; the entry is\n * stamped \"use client\".\n */\nexport * from \"./db-component/db-kind-component-cache\";\nexport * from \"./db-component/DbKindComponentErrorBoundary\";\nexport * from \"./db-component/kind-actions-through-surface\";\n","/**\n * The DB-component COMPILE CACHE — session-scoped, keyed per winning row\n * VERSION, with the loud-recovery policy built in.\n *\n * This was matrx-frontend's `dbKindComponentCache.ts` (316 lines of compile\n * caching + invalidation living in one host), absorbed per C22: cache\n * mechanics, key derivation, the scream-once latch, transform recovery, and\n * config narrowing are PACKAGE policy — every host was going to need exactly\n * these lines, bug for bug. What stays host-injected is exactly what is\n * host-shaped:\n *\n * - THE COMPILER ({@link DbKindComponentCompiler}). Compiling or sandboxing\n * user-authored component source is a security-shaped host decision — the\n * package never bundles Babel or an allowlist scope. matrx-frontend\n * injects its shared allowlist compiler (`compileSlotComponent`); a host\n * with no sandbox simply never constructs a cache.\n * - The default allowed-imports list (host scope registry).\n * - Where screams land ({@link ContentIrErrorReporter}) and where durable\n * incidents go ({@link DbKindComponentIncidentSink}).\n * - Invalidation TRIGGERS ({@link DbKindComponentCachePorts.registerInvalidation}):\n * the host wires its invalidation-registry keys (e.g.\n * `@ai-matrx/kit/invalidation` fired by name when an agent edits a\n * component row); the cache owns what invalidation MEANS.\n *\n * Contract (the db-row component contract, SHAPE_SYSTEM.md):\n * - `component_source` compiles to a React component receiving\n * `{ data, kind, config, runAction, onResolve?, uiOptions? }` where `data`\n * is the kind instance value.\n * - `props_transform` (optional) compiles to a `(data) => data` function\n * applied to the value BEFORE it reaches the component. A throwing or\n * non-compiling transform screams and the untransformed value is used\n * (never a blank hole).\n * - `config.allowed_imports` (string[]) narrows the compile scope; absent,\n * the row gets the host's FULL registered allowlist.\n *\n * Cache key: (kind, platform, role, row.updated_at) — one compile per winning\n * row VERSION. A resolver refresh delivering an edited row (bumped\n * `updated_at`) re-keys and recompiles naturally; `invalidate` /\n * `invalidateAll` force-drop keys. Server edits never push to open clients —\n * the contract is refresh-on-view.\n */\n\nimport type * as React from \"react\";\n\nimport type { ContentIrErrorReporter } from \"../host/error-report\";\nimport type { ComponentResolution } from \"../resolver/component-resolution.types\";\n\n/**\n * Settings the MOUNTING UI dictates to a kind component — the standard,\n * growing arg list every component honors (Arman, 2026-07-23). `selectionMode`\n * is the first: it tells a component whether the surface wants the user to\n * pick one value, many, or none (pure display). A component reads what it\n * understands and ignores the rest; absent = its own default (usually display).\n */\nexport interface KindComponentUiOptions {\n selectionMode?: \"single\" | \"multiple\" | \"none\";\n [key: string]: unknown;\n}\n\n/**\n * The return channel: how a kind component hands a chosen value back to the\n * surface that mounted it (a Kind Request). The mirror of `runAction`\n * (surface → agent); this is component → surface. Undefined in normal render\n * (chat/notes) — the component simply doesn't offer selection there.\n */\nexport type ResolveKindValue = (value: unknown) => void;\n\n/** The envelope every kind action resolves to — a skip/failure is never a silent pass. */\nexport type DbKindActionResult =\n | { ok: true; result: unknown }\n | { ok: false; error: string };\n\n/**\n * The action seam a compiled component receives: `runAction(key, input)`\n * triggers a registered platform capability (e.g. \"trigger_agent\"). Always\n * resolves to an `{ ok }` envelope, never throws — safe to call from imperfect\n * component code. This is the ONLY side-effect channel a sandboxed component\n * gets; it reaches no data directly.\n */\nexport type RunDbKindAction = (\n key: string,\n input: unknown,\n) => Promise<DbKindActionResult>;\n\n/** Props every compiled DB kind component receives. */\nexport interface DbKindComponentRenderProps {\n /** The kind instance value — post-`props_transform` when one exists. */\n data: unknown;\n /** The kind slug the value belongs to. */\n kind: string;\n /** The resolver row's `config` object, verbatim (narrowed — see {@link DbKindComponentCache.config}). */\n config: Record<string, unknown>;\n /** The action seam — see {@link RunDbKindAction}. */\n runAction: RunDbKindAction;\n /**\n * Present only when the component is mounted inside a Kind Request. Call it\n * with the user's chosen value to hand that value back to the requesting\n * surface (which resolves its promise). Undefined in normal render.\n */\n onResolve?: ResolveKindValue;\n /** Settings the mounting surface dictates (e.g. `{ selectionMode: \"single\" }`). */\n uiOptions?: KindComponentUiOptions;\n}\n\nexport interface CompiledDbKindComponent {\n Component: React.ComponentType<DbKindComponentRenderProps>;\n /** Compiled `props_transform`, or null when the row declares none. */\n transform: ((data: unknown) => unknown) | null;\n}\n\nexport type DbKindCompileResult =\n | { ok: true; compiled: CompiledDbKindComponent }\n | { ok: false; error: string };\n\n/** The failure modes a browser can observe on a DB kind component. */\nexport type DbKindComponentIncidentType =\n /** `component_source` did not compile (or produced no component). */\n | \"compile_error\"\n /** The compiled component threw during render (error boundary caught it). */\n | \"render_throw\"\n /** `props_transform` failed to compile or threw. */\n | \"transform_error\";\n\n/**\n * One durable render incident — the payload for whoever can actually FIX the\n * component. A host sink typically files it on the kind's own incident queue\n * (matrx-frontend: `content_ir.kind_component_incident` via RPC); without a\n * sink, a broken component seen only by ordinary users is invisible to its\n * author forever. `data` is the kind instance value — a privacy-conscious sink\n * reduces it to keys + types before it leaves the browser.\n */\nexport interface DbKindComponentIncident {\n kind: string;\n errorType: DbKindComponentIncidentType;\n message: string;\n platform?: string;\n role?: string;\n componentKey?: string | null;\n componentUpdatedAt?: string | null;\n stack?: string | null;\n data?: unknown;\n}\n\nexport type DbKindComponentIncidentSink = (\n incident: DbKindComponentIncident,\n) => void;\n\n/**\n * THE COMPILER PORT. Matches matrx-frontend's shared allowlist compiler\n * (`compileSlotComponent`) structurally: source in, `{ Component | error }`\n * out, never throws. The compiled component is generically typed — the cache\n * performs the single deliberate narrowing where the generic compiler meets\n * the kind prop contract.\n */\nexport type DbKindComponentCompiler = (args: {\n code: string;\n allowedImports: string[];\n}) => {\n Component: React.ComponentType<Record<string, unknown>> | null;\n error: string | null;\n};\n\nexport interface DbKindComponentCachePorts {\n /** The host's sandbox compiler — see {@link DbKindComponentCompiler}. */\n compile: DbKindComponentCompiler;\n /** The FULL registered allowlist a row without `config.allowed_imports` gets. */\n defaultAllowedImports: () => string[];\n /** Where screams land (every recovery path is loud). */\n reportError: ContentIrErrorReporter;\n /** Durable incident sink — optional, but a host without one blinds authors. */\n reportIncident?: DbKindComponentIncidentSink;\n /** Default `kind_component.platform` for this host (default \"web\"). */\n platform?: string;\n /**\n * THE INVALIDATION REGISTRATION PORT. Called once at construction with the\n * cache's `invalidateAll`; the host registers it under its own invalidation\n * keys (e.g. `@ai-matrx/kit/invalidation`'s name-keyed registry, fired when\n * an agent's component write completes). The cache owns the mechanics —\n * what gets dropped, the scream-latch reset; the host owns only WHEN.\n */\n registerInvalidation?: (invalidateAll: () => void) => void;\n}\n\nexport interface DbKindComponentCache {\n /**\n * Compile (once per resolver key per row version) a db-source resolution's\n * `component_source` + optional `props_transform`. Synchronous — the caller\n * (a lazily-loaded Impl) already paid the compiler chunk.\n */\n getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform?: string,\n role?: string,\n ): DbKindCompileResult;\n /**\n * Apply the row's transform to the kind value. Loud on throw, never fatal —\n * the untransformed value comes back so content is never hidden.\n */\n applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown;\n /**\n * THE CONFIG BOUNDARY. `ComponentResolution.config` is typed\n * `Record<string, unknown>` — deliberately agnostic about a host's JSONB\n * interior. This narrows at runtime (plain object → itself; anything else →\n * scream + empty config) rather than asserting: a row whose `config` is not\n * an object is a malformed `kind_component` row, and the component still\n * renders rather than crashing.\n */\n config(config: unknown, kind: string): Record<string, unknown>;\n /** Drop a (kind, platform, role) key family (authoring surfaces call after editing a row). */\n invalidate(kind: string, platform?: string, role?: string): void;\n /** Drop everything — the invalidation-port payload. */\n invalidateAll(): void;\n}\n\n/**\n * A routed lazy warm row owns a real body, but the per-kind cold fetch has not\n * landed it yet. This is loading, not a compile failure.\n */\nexport function isDbKindComponentBodyPending(\n resolution: ComponentResolution,\n): boolean {\n return resolution.hasComponentSource && resolution.componentSource === null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Build the session cache. One per host app — construct it in the wiring module. */\nexport function createDbKindComponentCache(\n ports: DbKindComponentCachePorts,\n): DbKindComponentCache {\n const cache = new Map<string, DbKindCompileResult>();\n const screamedKeys = new Set<string>();\n const defaultPlatform = ports.platform ?? \"web\";\n\n function cacheKey(\n kind: string,\n platform: string,\n role: string,\n updatedAt: string | null,\n ): string {\n // `updatedAt` (the winning row's freshness) is part of the key: a refresh\n // that delivers an edited row (bumped updated_at) recompiles automatically\n // instead of serving the stale compile for the rest of the session.\n return `${kind}${platform}${role}${updatedAt ?? \"\"}`;\n }\n\n /**\n * Loud recovery: one console scream per key + a structured report on every\n * attempt + a durable incident for whoever can fix the component.\n */\n function screamCompileFailure(\n key: string,\n kind: string,\n error: string,\n resolution: ComponentResolution,\n platform: string,\n role: string,\n ): void {\n const message = `[content-ir] DB kind component for \"${kind}\" failed to compile — rendering the generic structured viewer instead: ${error}`;\n if (!screamedKeys.has(key)) {\n screamedKeys.add(key);\n console.error(message);\n }\n ports.reportError({ source: \"content-ir\", message, raw: { kind, error } });\n ports.reportIncident?.({\n kind,\n errorType: \"compile_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n\n function config(value: unknown, kind: string): Record<string, unknown> {\n if (isPlainObject(value)) return value;\n if (value === null || value === undefined) return {};\n const message = `[content-ir] kind_component.config for \"${kind}\" is not a JSON object (${typeof value}) — rendering with an empty config.`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n return {};\n }\n\n function resolveAllowedImports(rowConfig: Record<string, unknown>): string[] {\n const declared = rowConfig.allowed_imports;\n if (\n Array.isArray(declared) &&\n declared.every((entry): entry is string => typeof entry === \"string\")\n ) {\n return declared;\n }\n return ports.defaultAllowedImports();\n }\n\n function getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform: string = defaultPlatform,\n role = \"output\",\n ): DbKindCompileResult {\n const key = cacheKey(kind, platform, role, resolution.updatedAt);\n const cached = cache.get(key);\n if (cached) return cached;\n\n const source = resolution.componentSource;\n if (!source || !source.trim()) {\n const result: DbKindCompileResult = {\n ok: false,\n error: \"db-source resolution has no component_source\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n const allowedImports = resolveAllowedImports(\n config(resolution.config, kind),\n );\n\n const { Component, error } = ports.compile({ code: source, allowedImports });\n if (!Component || error) {\n const result: DbKindCompileResult = {\n ok: false,\n error: error ?? \"compile produced no component\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n // The optional props transform is its own tiny compile — a `(data) => data`\n // function (mirroring tool_ui's header_subtitle_code pattern). A broken\n // transform is LOUD but never fatal: the component still renders, fed the\n // untransformed value.\n let transform: ((data: unknown) => unknown) | null = null;\n if (resolution.propsTransform && resolution.propsTransform.trim()) {\n const { Component: transformFn, error: transformError } = ports.compile({\n code: resolution.propsTransform,\n allowedImports: [],\n });\n if (typeof transformFn === \"function\" && !transformError) {\n transform = transformFn as unknown as (data: unknown) => unknown;\n } else {\n const message = `[content-ir] props_transform for kind \"${kind}\" failed to compile — value passes through untransformed: ${transformError ?? \"no function produced\"}`;\n console.error(message);\n ports.reportError({\n source: \"content-ir\",\n message,\n raw: { kind, error: transformError },\n });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n }\n\n const result: DbKindCompileResult = {\n ok: true,\n compiled: {\n // The injected compiler types components as\n // ComponentType<Record<string, unknown>>; the callsite always passes\n // DbKindComponentRenderProps — the single deliberate narrowing where\n // the generic compiler meets the kind contract.\n Component:\n Component as unknown as React.ComponentType<DbKindComponentRenderProps>,\n transform,\n },\n };\n cache.set(key, result);\n return result;\n }\n\n function applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown {\n if (!compiled.transform) return value;\n try {\n return compiled.transform(value);\n } catch (error) {\n const message = `[content-ir] props_transform for kind \"${kind}\" threw — using the untransformed value: ${\n error instanceof Error ? error.message : String(error)\n }`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n stack: error instanceof Error ? (error.stack ?? null) : null,\n data: value,\n });\n return value;\n }\n }\n\n function invalidate(\n kind: string,\n platform: string = defaultPlatform,\n role = \"output\",\n ): void {\n const prefix = `${kind}${platform}${role}`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n for (const key of [...screamedKeys]) {\n if (key.startsWith(prefix)) screamedKeys.delete(key);\n }\n }\n\n function invalidateAll(): void {\n cache.clear();\n screamedKeys.clear();\n }\n\n const api: DbKindComponentCache = {\n getOrCompile,\n applyPropsTransform,\n config,\n invalidate,\n invalidateAll,\n };\n\n ports.registerInvalidation?.(() => invalidateAll());\n\n return api;\n}\n","\"use client\";\n\n/**\n * DbKindComponentErrorBoundary — the DEFAULT error boundary around a compiled\n * DB kind component (absorbed from matrx-frontend per the C23 retrofit; the\n * package previously shipped \"no error capture by design\", which left every\n * host to rebuild this class).\n *\n * A user-authored component that throws during render must never crash the\n * surface (chat, notes, admin preview). The boundary catches, renders the\n * fallback (the kind's generic structured viewer — the R6 disposition, never\n * a blank hole), and SCREAMS: a recovery firing means a real bug got past\n * authoring.\n *\n * `resetSignal` (the component's `updated_at`) un-latches the boundary when a\n * NEW component version arrives: an author who fixes a throwing component must\n * see the fix on the next re-render, not stay stuck on the fallback until a\n * full unmount. Without this the error state is sticky for the whole session —\n * the \"broke, then magically fixed itself minutes later\" class. React error\n * boundaries never self-clear, so a version change is the only safe signal\n * (`hasError` alone can't tell a fixed version from the same broken one).\n *\n * `onCaught` is the scream seam: a host binds its structured render-error\n * capture + durable incident filing (matrx-frontend files a `render_throw`\n * incident so the component's author learns). Bound to nothing, the default is\n * an honest console scream — never silence.\n */\nimport * as React from \"react\";\n\nexport interface DbKindComponentErrorBoundaryProps {\n kind: string;\n /** The resolved component's `updated_at`; a change means a new version. */\n resetSignal: string | null;\n fallback: React.ReactNode;\n children: React.ReactNode;\n /**\n * Where the catch lands. A host binds structured capture + incident filing\n * here; the default screams to the console. NOTE for hosts whose console\n * adapter persists `console.error`: bind this (which replaces the default)\n * rather than mirroring the capture through the console twice.\n */\n onCaught?: (\n error: Error,\n info: { componentStack: string | null },\n ) => void;\n}\n\ninterface State {\n hasError: boolean;\n /**\n * The `resetSignal` seen on the LAST render — tracked every render (not just\n * on error) so that at throw time it already holds the version that threw.\n * `getDerivedStateFromError` cannot read props, so this is how the boundary\n * knows which version to un-latch away from.\n */\n signalAtLastRender: string | null;\n}\n\nexport class DbKindComponentErrorBoundary extends React.Component<\n DbKindComponentErrorBoundaryProps,\n State\n> {\n constructor(props: DbKindComponentErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n static getDerivedStateFromError(): Partial<State> {\n return { hasError: true };\n }\n\n static getDerivedStateFromProps(\n props: DbKindComponentErrorBoundaryProps,\n state: State,\n ): State | null {\n if (props.resetSignal === state.signalAtLastRender) return null;\n // The version changed. If we were latched on the old version, un-latch so\n // the new (hopefully fixed) component gets a fresh render attempt; either\n // way, record the new signal as the one now rendering.\n return { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n const info = { componentStack: errorInfo.componentStack ?? null };\n if (this.props.onCaught) {\n this.props.onCaught(error, info);\n return;\n }\n // The honest default when a host binds nothing: never silent.\n console.error(\n `[content-ir] DB kind component for \"${this.props.kind}\" threw during render — showing the fallback:`,\n error,\n );\n }\n\n override render(): React.ReactNode {\n if (this.state.hasError) return this.props.fallback;\n return this.props.children;\n }\n}\n","/**\n * Kind components call Actions (Applets CONTRACTS §6/§7).\n *\n * A compiled kind component's ONE side-effect channel is `runAction(key,\n * input)` (see {@link RunDbKindAction}). This binds that channel to a surface's\n * Actions — the one registry (`@ai-matrx/alchemy`'s `SurfacePort.runAction`,\n * whose runner applies each Action's policy and `triggeredBy`). A host mounts a\n * surface (an Applet frame mounts `applets/<id>`), then hands every kind it\n * renders `runKindActionsThroughSurface(port)`.\n *\n * Package policy, so every host gets it bug for bug:\n * - NEVER throws: a refusal, an unknown Action, a port that rejects — all\n * resolve `{ ok: false, error }` with the system's own sentence (and its\n * remedy, when the refusal names one), so imperfect component code keeps\n * rendering.\n * - ONE run in flight per Action and target: a double-click or a re-render\n * storm cannot run an agent, a write or a server action twice. The key is\n * the Action name plus `input.target` / `input.id` when the input names one,\n * so one slow Action never locks every other.\n * - The caller is a PERSON by default: a kind component acts on a click in a\n * person's page. A host that runs kinds on an agent's behalf passes `by`.\n *\n * Typed structurally over the port so this package does not depend on alchemy.\n */\nimport type { DbKindActionResult, RunDbKindAction } from \"./db-kind-component-cache\";\n\nexport type KindActionCaller = \"person\" | \"agent\" | \"applet\" | \"automation\";\n\n/** The one method of alchemy's `SurfacePort` this needs. */\nexport interface KindActionSurface {\n runAction(\n name: string,\n input: unknown,\n by: KindActionCaller,\n ): Promise<{ ok: true; data: unknown } | { ok: false; error: { message: string; remedy?: string } }>;\n}\n\nexport interface KindActionsThroughSurfaceOptions {\n /** Who is acting (default \"person\"). */\n by?: KindActionCaller;\n}\n\nfunction inFlightKey(name: string, input: unknown): string {\n if (input && typeof input === \"object\") {\n const rec = input as { target?: unknown; id?: unknown };\n if (typeof rec.target === \"string\" && rec.target) return `${name}::${rec.target}`;\n if (typeof rec.id === \"string\" && rec.id) return `${name}::${rec.id}`;\n }\n return name;\n}\n\nexport function runKindActionsThroughSurface(\n surface: KindActionSurface,\n options: KindActionsThroughSurfaceOptions = {},\n): RunDbKindAction {\n const by = options.by ?? \"person\";\n const inFlight = new Set<string>();\n return async (key: string, input: unknown): Promise<DbKindActionResult> => {\n if (typeof key !== \"string\" || !key.trim()) {\n return { ok: false, error: \"This component asked to run an Action without naming one.\" };\n }\n const flight = inFlightKey(key, input);\n if (inFlight.has(flight)) {\n return { ok: false, error: `\"${key}\" is already running. Wait for it to finish.` };\n }\n inFlight.add(flight);\n try {\n const result = await surface.runAction(key, input, by);\n if (result.ok) return { ok: true, result: result.data };\n const { message, remedy } = result.error;\n return { ok: false, error: remedy ? `${message} ${remedy}` : message };\n } catch (err) {\n return {\n ok: false,\n error: `\"${key}\" could not run: ${err instanceof Error ? err.message : String(err)}`,\n };\n } finally {\n inFlight.delete(flight);\n }\n };\n}\n"],"mappings":"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","names":["result","Component"]}
1
+ {"version":3,"sources":["../db-component.ts","../db-component/db-kind-component-cache.ts","../db-component/DbKindComponentErrorBoundary.tsx","../db-component/kind-actions-through-surface.ts"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/db-component` — rendering DB-authored kind\n * components (`content_ir.kind_component`, `source='db'`): the session compile\n * cache with its invalidation port, the kind prop contract, and the default\n * error boundary. The COMPILER itself stays a host injection (security-shaped\n * — see `db-component/db-kind-component-cache.ts`). Client-only; the entry is\n * stamped \"use client\".\n */\nexport * from \"./db-component/db-kind-component-cache\";\nexport * from \"./db-component/DbKindComponentErrorBoundary\";\nexport * from \"./db-component/kind-actions-through-surface\";\n","/**\n * The DB-component COMPILE CACHE — session-scoped, keyed per winning row\n * VERSION, with the loud-recovery policy built in.\n *\n * This was matrx-frontend's `dbKindComponentCache.ts` (316 lines of compile\n * caching + invalidation living in one host), absorbed per C22: cache\n * mechanics, key derivation, the scream-once latch, transform recovery, and\n * config narrowing are PACKAGE policy — every host was going to need exactly\n * these lines, bug for bug. What stays host-injected is exactly what is\n * host-shaped:\n *\n * - THE COMPILER ({@link DbKindComponentCompiler}). Compiling or sandboxing\n * user-authored component source is a security-shaped host decision — the\n * package never bundles Babel or an allowlist scope. matrx-frontend\n * injects its shared allowlist compiler (`compileSlotComponent`); a host\n * with no sandbox simply never constructs a cache.\n * - The default allowed-imports list (host scope registry).\n * - Where screams land ({@link ContentIrErrorReporter}) and where durable\n * incidents go ({@link DbKindComponentIncidentSink}).\n * - Invalidation TRIGGERS ({@link DbKindComponentCachePorts.registerInvalidation}):\n * the host wires its invalidation-registry keys (e.g.\n * `@ai-matrx/kit/invalidation` fired by name when an agent edits a\n * component row); the cache owns what invalidation MEANS.\n *\n * Contract (the db-row component contract, SHAPE_SYSTEM.md):\n * - `component_source` compiles to a React component receiving\n * `{ data, kind, config, runAction, itemState?, onResolve?, uiOptions? }`\n * where `data` is the kind instance value and `itemState` what actions\n * saved onto this item.\n * - `props_transform` (optional) compiles to a `(data) => data` function\n * applied to the value BEFORE it reaches the component. A throwing or\n * non-compiling transform screams and the untransformed value is used\n * (never a blank hole).\n * - `config.allowed_imports` (string[]) narrows the compile scope; absent,\n * the row gets the host's FULL registered allowlist.\n *\n * Cache key: (kind, platform, role, row.updated_at) — one compile per winning\n * row VERSION. A resolver refresh delivering an edited row (bumped\n * `updated_at`) re-keys and recompiles naturally; `invalidate` /\n * `invalidateAll` force-drop keys. Server edits never push to open clients —\n * the contract is refresh-on-view.\n */\n\nimport type * as React from \"react\";\n\nimport type { ContentIrErrorReporter } from \"../host/error-report\";\nimport type { ComponentResolution } from \"../resolver/component-resolution.types\";\n\n/**\n * Settings the MOUNTING UI dictates to a kind component — the standard,\n * growing arg list every component honors (Arman, 2026-07-23). `selectionMode`\n * is the first: it tells a component whether the surface wants the user to\n * pick one value, many, or none (pure display). A component reads what it\n * understands and ignores the rest; absent = its own default (usually display).\n */\nexport interface KindComponentUiOptions {\n selectionMode?: \"single\" | \"multiple\" | \"none\";\n [key: string]: unknown;\n}\n\n/**\n * The return channel: how a kind component hands a chosen value back to the\n * surface that mounted it (a Kind Request). The mirror of `runAction`\n * (surface → agent); this is component → surface. Undefined in normal render\n * (chat/notes) — the component simply doesn't offer selection there.\n */\nexport type ResolveKindValue = (value: unknown) => void;\n\n/** The envelope every kind action resolves to — a skip/failure is never a silent pass. */\nexport type DbKindActionResult =\n | { ok: true; result: unknown }\n | { ok: false; error: string };\n\n/**\n * The action seam a compiled component receives: `runAction(key, input)`\n * triggers a registered platform capability (e.g. \"trigger_agent\"). Always\n * resolves to an `{ ok }` envelope, never throws — safe to call from imperfect\n * component code. This is the ONLY side-effect channel a sandboxed component\n * gets; it reaches no data directly.\n */\nexport type RunDbKindAction = (\n key: string,\n input: unknown,\n) => Promise<DbKindActionResult>;\n\n/** Props every compiled DB kind component receives. */\nexport interface DbKindComponentRenderProps {\n /** The kind instance value — post-`props_transform` when one exists. */\n data: unknown;\n /** The kind slug the value belongs to. */\n kind: string;\n /** The resolver row's `config` object, verbatim (narrowed — see {@link DbKindComponentCache.config}). */\n config: Record<string, unknown>;\n /** The action seam — see {@link RunDbKindAction}. */\n runAction: RunDbKindAction;\n /**\n * Present only when the component is mounted inside a Kind Request. Call it\n * with the user's chosen value to hand that value back to the requesting\n * surface (which resolves its promise). Undefined in normal render.\n */\n onResolve?: ResolveKindValue;\n /** Settings the mounting surface dictates (e.g. `{ selectionMode: \"single\" }`). */\n uiOptions?: KindComponentUiOptions;\n /**\n * This item's durable state: what `save_item_state` / `run_shortcut` with\n * `saveAs` wrote onto it (`{}` when nothing yet; survives reload). The host\n * adds `src` to every saved image ref (`{ file_id, mime_type, width, height }`)\n * so the component renders `<img src={itemState.key.src}>`.\n */\n itemState?: Record<string, unknown>;\n}\n\nexport interface CompiledDbKindComponent {\n Component: React.ComponentType<DbKindComponentRenderProps>;\n /** Compiled `props_transform`, or null when the row declares none. */\n transform: ((data: unknown) => unknown) | null;\n}\n\nexport type DbKindCompileResult =\n | { ok: true; compiled: CompiledDbKindComponent }\n | { ok: false; error: string };\n\n/** The failure modes a browser can observe on a DB kind component. */\nexport type DbKindComponentIncidentType =\n /** `component_source` did not compile (or produced no component). */\n | \"compile_error\"\n /** The compiled component threw during render (error boundary caught it). */\n | \"render_throw\"\n /** `props_transform` failed to compile or threw. */\n | \"transform_error\";\n\n/**\n * One durable render incident — the payload for whoever can actually FIX the\n * component. A host sink typically files it on the kind's own incident queue\n * (matrx-frontend: `content_ir.kind_component_incident` via RPC); without a\n * sink, a broken component seen only by ordinary users is invisible to its\n * author forever. `data` is the kind instance value — a privacy-conscious sink\n * reduces it to keys + types before it leaves the browser.\n */\nexport interface DbKindComponentIncident {\n kind: string;\n errorType: DbKindComponentIncidentType;\n message: string;\n platform?: string;\n role?: string;\n componentKey?: string | null;\n componentUpdatedAt?: string | null;\n stack?: string | null;\n data?: unknown;\n}\n\nexport type DbKindComponentIncidentSink = (\n incident: DbKindComponentIncident,\n) => void;\n\n/**\n * THE COMPILER PORT. Matches matrx-frontend's shared allowlist compiler\n * (`compileSlotComponent`) structurally: source in, `{ Component | error }`\n * out, never throws. The compiled component is generically typed — the cache\n * performs the single deliberate narrowing where the generic compiler meets\n * the kind prop contract.\n */\nexport type DbKindComponentCompiler = (args: {\n code: string;\n allowedImports: string[];\n}) => {\n Component: React.ComponentType<Record<string, unknown>> | null;\n error: string | null;\n};\n\nexport interface DbKindComponentCachePorts {\n /** The host's sandbox compiler — see {@link DbKindComponentCompiler}. */\n compile: DbKindComponentCompiler;\n /** The FULL registered allowlist a row without `config.allowed_imports` gets. */\n defaultAllowedImports: () => string[];\n /** Where screams land (every recovery path is loud). */\n reportError: ContentIrErrorReporter;\n /** Durable incident sink — optional, but a host without one blinds authors. */\n reportIncident?: DbKindComponentIncidentSink;\n /** Default `kind_component.platform` for this host (default \"web\"). */\n platform?: string;\n /**\n * THE INVALIDATION REGISTRATION PORT. Called once at construction with the\n * cache's `invalidateAll`; the host registers it under its own invalidation\n * keys (e.g. `@ai-matrx/kit/invalidation`'s name-keyed registry, fired when\n * an agent's component write completes). The cache owns the mechanics —\n * what gets dropped, the scream-latch reset; the host owns only WHEN.\n */\n registerInvalidation?: (invalidateAll: () => void) => void;\n}\n\nexport interface DbKindComponentCache {\n /**\n * Compile (once per resolver key per row version) a db-source resolution's\n * `component_source` + optional `props_transform`. Synchronous — the caller\n * (a lazily-loaded Impl) already paid the compiler chunk.\n */\n getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform?: string,\n role?: string,\n ): DbKindCompileResult;\n /**\n * Apply the row's transform to the kind value. Loud on throw, never fatal —\n * the untransformed value comes back so content is never hidden.\n */\n applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown;\n /**\n * THE CONFIG BOUNDARY. `ComponentResolution.config` is typed\n * `Record<string, unknown>` — deliberately agnostic about a host's JSONB\n * interior. This narrows at runtime (plain object → itself; anything else →\n * scream + empty config) rather than asserting: a row whose `config` is not\n * an object is a malformed `kind_component` row, and the component still\n * renders rather than crashing.\n */\n config(config: unknown, kind: string): Record<string, unknown>;\n /** Drop a (kind, platform, role) key family (authoring surfaces call after editing a row). */\n invalidate(kind: string, platform?: string, role?: string): void;\n /** Drop everything — the invalidation-port payload. */\n invalidateAll(): void;\n}\n\n/**\n * A routed lazy warm row owns a real body, but the per-kind cold fetch has not\n * landed it yet. This is loading, not a compile failure.\n */\nexport function isDbKindComponentBodyPending(\n resolution: ComponentResolution,\n): boolean {\n return resolution.hasComponentSource && resolution.componentSource === null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Build the session cache. One per host app — construct it in the wiring module. */\nexport function createDbKindComponentCache(\n ports: DbKindComponentCachePorts,\n): DbKindComponentCache {\n const cache = new Map<string, DbKindCompileResult>();\n const screamedKeys = new Set<string>();\n const defaultPlatform = ports.platform ?? \"web\";\n\n function cacheKey(\n kind: string,\n platform: string,\n role: string,\n updatedAt: string | null,\n ): string {\n // `updatedAt` (the winning row's freshness) is part of the key: a refresh\n // that delivers an edited row (bumped updated_at) recompiles automatically\n // instead of serving the stale compile for the rest of the session.\n return `${kind}${platform}${role}${updatedAt ?? \"\"}`;\n }\n\n /**\n * Loud recovery: one console scream per key + a structured report on every\n * attempt + a durable incident for whoever can fix the component.\n */\n function screamCompileFailure(\n key: string,\n kind: string,\n error: string,\n resolution: ComponentResolution,\n platform: string,\n role: string,\n ): void {\n const message = `[content-ir] DB kind component for \"${kind}\" failed to compile — rendering the generic structured viewer instead: ${error}`;\n if (!screamedKeys.has(key)) {\n screamedKeys.add(key);\n console.error(message);\n }\n ports.reportError({ source: \"content-ir\", message, raw: { kind, error } });\n ports.reportIncident?.({\n kind,\n errorType: \"compile_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n\n function config(value: unknown, kind: string): Record<string, unknown> {\n if (isPlainObject(value)) return value;\n if (value === null || value === undefined) return {};\n const message = `[content-ir] kind_component.config for \"${kind}\" is not a JSON object (${typeof value}) — rendering with an empty config.`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n return {};\n }\n\n function resolveAllowedImports(rowConfig: Record<string, unknown>): string[] {\n const declared = rowConfig.allowed_imports;\n if (\n Array.isArray(declared) &&\n declared.every((entry): entry is string => typeof entry === \"string\")\n ) {\n return declared;\n }\n return ports.defaultAllowedImports();\n }\n\n function getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform: string = defaultPlatform,\n role = \"output\",\n ): DbKindCompileResult {\n const key = cacheKey(kind, platform, role, resolution.updatedAt);\n const cached = cache.get(key);\n if (cached) return cached;\n\n const source = resolution.componentSource;\n if (!source || !source.trim()) {\n const result: DbKindCompileResult = {\n ok: false,\n error: \"db-source resolution has no component_source\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n const allowedImports = resolveAllowedImports(\n config(resolution.config, kind),\n );\n\n const { Component, error } = ports.compile({ code: source, allowedImports });\n if (!Component || error) {\n const result: DbKindCompileResult = {\n ok: false,\n error: error ?? \"compile produced no component\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n // The optional props transform is its own tiny compile — a `(data) => data`\n // function (mirroring tool_ui's header_subtitle_code pattern). A broken\n // transform is LOUD but never fatal: the component still renders, fed the\n // untransformed value.\n let transform: ((data: unknown) => unknown) | null = null;\n if (resolution.propsTransform && resolution.propsTransform.trim()) {\n const { Component: transformFn, error: transformError } = ports.compile({\n code: resolution.propsTransform,\n allowedImports: [],\n });\n if (typeof transformFn === \"function\" && !transformError) {\n transform = transformFn as unknown as (data: unknown) => unknown;\n } else {\n const message = `[content-ir] props_transform for kind \"${kind}\" failed to compile — value passes through untransformed: ${transformError ?? \"no function produced\"}`;\n console.error(message);\n ports.reportError({\n source: \"content-ir\",\n message,\n raw: { kind, error: transformError },\n });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n }\n\n const result: DbKindCompileResult = {\n ok: true,\n compiled: {\n // The injected compiler types components as\n // ComponentType<Record<string, unknown>>; the callsite always passes\n // DbKindComponentRenderProps — the single deliberate narrowing where\n // the generic compiler meets the kind contract.\n Component:\n Component as unknown as React.ComponentType<DbKindComponentRenderProps>,\n transform,\n },\n };\n cache.set(key, result);\n return result;\n }\n\n function applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown {\n if (!compiled.transform) return value;\n try {\n return compiled.transform(value);\n } catch (error) {\n const message = `[content-ir] props_transform for kind \"${kind}\" threw — using the untransformed value: ${\n error instanceof Error ? error.message : String(error)\n }`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n stack: error instanceof Error ? (error.stack ?? null) : null,\n data: value,\n });\n return value;\n }\n }\n\n function invalidate(\n kind: string,\n platform: string = defaultPlatform,\n role = \"output\",\n ): void {\n const prefix = `${kind}${platform}${role}`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n for (const key of [...screamedKeys]) {\n if (key.startsWith(prefix)) screamedKeys.delete(key);\n }\n }\n\n function invalidateAll(): void {\n cache.clear();\n screamedKeys.clear();\n }\n\n const api: DbKindComponentCache = {\n getOrCompile,\n applyPropsTransform,\n config,\n invalidate,\n invalidateAll,\n };\n\n ports.registerInvalidation?.(() => invalidateAll());\n\n return api;\n}\n","\"use client\";\n\n/**\n * DbKindComponentErrorBoundary — the DEFAULT error boundary around a compiled\n * DB kind component (absorbed from matrx-frontend per the C23 retrofit; the\n * package previously shipped \"no error capture by design\", which left every\n * host to rebuild this class).\n *\n * A user-authored component that throws during render must never crash the\n * surface (chat, notes, admin preview). The boundary catches, renders the\n * fallback (the kind's generic structured viewer — the R6 disposition, never\n * a blank hole), and SCREAMS: a recovery firing means a real bug got past\n * authoring.\n *\n * `resetSignal` (the component's `updated_at`) un-latches the boundary when a\n * NEW component version arrives: an author who fixes a throwing component must\n * see the fix on the next re-render, not stay stuck on the fallback until a\n * full unmount. Without this the error state is sticky for the whole session —\n * the \"broke, then magically fixed itself minutes later\" class. React error\n * boundaries never self-clear, so a version change is the only safe signal\n * (`hasError` alone can't tell a fixed version from the same broken one).\n *\n * `onCaught` is the scream seam: a host binds its structured render-error\n * capture + durable incident filing (matrx-frontend files a `render_throw`\n * incident so the component's author learns). Bound to nothing, the default is\n * an honest console scream — never silence.\n */\nimport * as React from \"react\";\n\nexport interface DbKindComponentErrorBoundaryProps {\n kind: string;\n /** The resolved component's `updated_at`; a change means a new version. */\n resetSignal: string | null;\n fallback: React.ReactNode;\n children: React.ReactNode;\n /**\n * Where the catch lands. A host binds structured capture + incident filing\n * here; the default screams to the console. NOTE for hosts whose console\n * adapter persists `console.error`: bind this (which replaces the default)\n * rather than mirroring the capture through the console twice.\n */\n onCaught?: (\n error: Error,\n info: { componentStack: string | null },\n ) => void;\n}\n\ninterface State {\n hasError: boolean;\n /**\n * The `resetSignal` seen on the LAST render — tracked every render (not just\n * on error) so that at throw time it already holds the version that threw.\n * `getDerivedStateFromError` cannot read props, so this is how the boundary\n * knows which version to un-latch away from.\n */\n signalAtLastRender: string | null;\n}\n\nexport class DbKindComponentErrorBoundary extends React.Component<\n DbKindComponentErrorBoundaryProps,\n State\n> {\n constructor(props: DbKindComponentErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n static getDerivedStateFromError(): Partial<State> {\n return { hasError: true };\n }\n\n static getDerivedStateFromProps(\n props: DbKindComponentErrorBoundaryProps,\n state: State,\n ): State | null {\n if (props.resetSignal === state.signalAtLastRender) return null;\n // The version changed. If we were latched on the old version, un-latch so\n // the new (hopefully fixed) component gets a fresh render attempt; either\n // way, record the new signal as the one now rendering.\n return { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n const info = { componentStack: errorInfo.componentStack ?? null };\n if (this.props.onCaught) {\n this.props.onCaught(error, info);\n return;\n }\n // The honest default when a host binds nothing: never silent.\n console.error(\n `[content-ir] DB kind component for \"${this.props.kind}\" threw during render — showing the fallback:`,\n error,\n );\n }\n\n override render(): React.ReactNode {\n if (this.state.hasError) return this.props.fallback;\n return this.props.children;\n }\n}\n","/**\n * Kind components call Actions (Applets CONTRACTS §6/§7).\n *\n * A compiled kind component's ONE side-effect channel is `runAction(key,\n * input)` (see {@link RunDbKindAction}). This binds that channel to a surface's\n * Actions — the one registry (`@ai-matrx/alchemy`'s `SurfacePort.runAction`,\n * whose runner applies each Action's policy and `triggeredBy`). A host mounts a\n * surface (an Applet frame mounts `applets/<id>`), then hands every kind it\n * renders `runKindActionsThroughSurface(port)`.\n *\n * Package policy, so every host gets it bug for bug:\n * - NEVER throws: a refusal, an unknown Action, a port that rejects — all\n * resolve `{ ok: false, error }` with the system's own sentence (and its\n * remedy, when the refusal names one), so imperfect component code keeps\n * rendering.\n * - ONE run in flight per Action and target: a double-click or a re-render\n * storm cannot run an agent, a write or a server action twice. The key is\n * the Action name plus `input.target` / `input.id` when the input names one,\n * so one slow Action never locks every other.\n * - The caller is a PERSON by default: a kind component acts on a click in a\n * person's page. A host that runs kinds on an agent's behalf passes `by`.\n *\n * Typed structurally over the port so this package does not depend on alchemy.\n */\nimport type { DbKindActionResult, RunDbKindAction } from \"./db-kind-component-cache\";\n\nexport type KindActionCaller = \"person\" | \"agent\" | \"applet\" | \"automation\";\n\n/** The one method of alchemy's `SurfacePort` this needs. */\nexport interface KindActionSurface {\n runAction(\n name: string,\n input: unknown,\n by: KindActionCaller,\n ): Promise<{ ok: true; data: unknown } | { ok: false; error: { message: string; remedy?: string } }>;\n}\n\nexport interface KindActionsThroughSurfaceOptions {\n /** Who is acting (default \"person\"). */\n by?: KindActionCaller;\n}\n\nfunction inFlightKey(name: string, input: unknown): string {\n if (input && typeof input === \"object\") {\n const rec = input as { target?: unknown; id?: unknown };\n if (typeof rec.target === \"string\" && rec.target) return `${name}::${rec.target}`;\n if (typeof rec.id === \"string\" && rec.id) return `${name}::${rec.id}`;\n }\n return name;\n}\n\nexport function runKindActionsThroughSurface(\n surface: KindActionSurface,\n options: KindActionsThroughSurfaceOptions = {},\n): RunDbKindAction {\n const by = options.by ?? \"person\";\n const inFlight = new Set<string>();\n return async (key: string, input: unknown): Promise<DbKindActionResult> => {\n if (typeof key !== \"string\" || !key.trim()) {\n return { ok: false, error: \"This component asked to run an Action without naming one.\" };\n }\n const flight = inFlightKey(key, input);\n if (inFlight.has(flight)) {\n return { ok: false, error: `\"${key}\" is already running. Wait for it to finish.` };\n }\n inFlight.add(flight);\n try {\n const result = await surface.runAction(key, input, by);\n if (result.ok) return { ok: true, result: result.data };\n const { message, remedy } = result.error;\n return { ok: false, error: remedy ? `${message} ${remedy}` : message };\n } catch (err) {\n return {\n ok: false,\n error: `\"${key}\" could not run: ${err instanceof Error ? err.message : String(err)}`,\n };\n } finally {\n inFlight.delete(flight);\n }\n };\n}\n"],"mappings":"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","names":["result","Component"]}
@@ -27,8 +27,9 @@ import { C as ComponentResolution, a as ContentIrErrorReporter } from './compone
27
27
  *
28
28
  * Contract (the db-row component contract, SHAPE_SYSTEM.md):
29
29
  * - `component_source` compiles to a React component receiving
30
- * `{ data, kind, config, runAction, onResolve?, uiOptions? }` where `data`
31
- * is the kind instance value.
30
+ * `{ data, kind, config, runAction, itemState?, onResolve?, uiOptions? }`
31
+ * where `data` is the kind instance value and `itemState` what actions
32
+ * saved onto this item.
32
33
  * - `props_transform` (optional) compiles to a `(data) => data` function
33
34
  * applied to the value BEFORE it reaches the component. A throwing or
34
35
  * non-compiling transform screams and the untransformed value is used
@@ -95,6 +96,13 @@ interface DbKindComponentRenderProps {
95
96
  onResolve?: ResolveKindValue;
96
97
  /** Settings the mounting surface dictates (e.g. `{ selectionMode: "single" }`). */
97
98
  uiOptions?: KindComponentUiOptions;
99
+ /**
100
+ * This item's durable state: what `save_item_state` / `run_shortcut` with
101
+ * `saveAs` wrote onto it (`{}` when nothing yet; survives reload). The host
102
+ * adds `src` to every saved image ref (`{ file_id, mime_type, width, height }`)
103
+ * so the component renders `<img src={itemState.key.src}>`.
104
+ */
105
+ itemState?: Record<string, unknown>;
98
106
  }
99
107
  interface CompiledDbKindComponent {
100
108
  Component: React.ComponentType<DbKindComponentRenderProps>;
@@ -27,8 +27,9 @@ import { C as ComponentResolution, a as ContentIrErrorReporter } from './compone
27
27
  *
28
28
  * Contract (the db-row component contract, SHAPE_SYSTEM.md):
29
29
  * - `component_source` compiles to a React component receiving
30
- * `{ data, kind, config, runAction, onResolve?, uiOptions? }` where `data`
31
- * is the kind instance value.
30
+ * `{ data, kind, config, runAction, itemState?, onResolve?, uiOptions? }`
31
+ * where `data` is the kind instance value and `itemState` what actions
32
+ * saved onto this item.
32
33
  * - `props_transform` (optional) compiles to a `(data) => data` function
33
34
  * applied to the value BEFORE it reaches the component. A throwing or
34
35
  * non-compiling transform screams and the untransformed value is used
@@ -95,6 +96,13 @@ interface DbKindComponentRenderProps {
95
96
  onResolve?: ResolveKindValue;
96
97
  /** Settings the mounting surface dictates (e.g. `{ selectionMode: "single" }`). */
97
98
  uiOptions?: KindComponentUiOptions;
99
+ /**
100
+ * This item's durable state: what `save_item_state` / `run_shortcut` with
101
+ * `saveAs` wrote onto it (`{}` when nothing yet; survives reload). The host
102
+ * adds `src` to every saved image ref (`{ file_id, mime_type, width, height }`)
103
+ * so the component renders `<img src={itemState.key.src}>`.
104
+ */
105
+ itemState?: Record<string, unknown>;
98
106
  }
99
107
  interface CompiledDbKindComponent {
100
108
  Component: React.ComponentType<DbKindComponentRenderProps>;
@@ -1 +1 @@
1
- {"version":3,"sources":["../db-component/db-kind-component-cache.ts","../db-component/DbKindComponentErrorBoundary.tsx","../db-component/kind-actions-through-surface.ts"],"sourcesContent":["/**\n * The DB-component COMPILE CACHE — session-scoped, keyed per winning row\n * VERSION, with the loud-recovery policy built in.\n *\n * This was matrx-frontend's `dbKindComponentCache.ts` (316 lines of compile\n * caching + invalidation living in one host), absorbed per C22: cache\n * mechanics, key derivation, the scream-once latch, transform recovery, and\n * config narrowing are PACKAGE policy — every host was going to need exactly\n * these lines, bug for bug. What stays host-injected is exactly what is\n * host-shaped:\n *\n * - THE COMPILER ({@link DbKindComponentCompiler}). Compiling or sandboxing\n * user-authored component source is a security-shaped host decision — the\n * package never bundles Babel or an allowlist scope. matrx-frontend\n * injects its shared allowlist compiler (`compileSlotComponent`); a host\n * with no sandbox simply never constructs a cache.\n * - The default allowed-imports list (host scope registry).\n * - Where screams land ({@link ContentIrErrorReporter}) and where durable\n * incidents go ({@link DbKindComponentIncidentSink}).\n * - Invalidation TRIGGERS ({@link DbKindComponentCachePorts.registerInvalidation}):\n * the host wires its invalidation-registry keys (e.g.\n * `@ai-matrx/kit/invalidation` fired by name when an agent edits a\n * component row); the cache owns what invalidation MEANS.\n *\n * Contract (the db-row component contract, SHAPE_SYSTEM.md):\n * - `component_source` compiles to a React component receiving\n * `{ data, kind, config, runAction, onResolve?, uiOptions? }` where `data`\n * is the kind instance value.\n * - `props_transform` (optional) compiles to a `(data) => data` function\n * applied to the value BEFORE it reaches the component. A throwing or\n * non-compiling transform screams and the untransformed value is used\n * (never a blank hole).\n * - `config.allowed_imports` (string[]) narrows the compile scope; absent,\n * the row gets the host's FULL registered allowlist.\n *\n * Cache key: (kind, platform, role, row.updated_at) — one compile per winning\n * row VERSION. A resolver refresh delivering an edited row (bumped\n * `updated_at`) re-keys and recompiles naturally; `invalidate` /\n * `invalidateAll` force-drop keys. Server edits never push to open clients —\n * the contract is refresh-on-view.\n */\n\nimport type * as React from \"react\";\n\nimport type { ContentIrErrorReporter } from \"../host/error-report\";\nimport type { ComponentResolution } from \"../resolver/component-resolution.types\";\n\n/**\n * Settings the MOUNTING UI dictates to a kind component — the standard,\n * growing arg list every component honors (Arman, 2026-07-23). `selectionMode`\n * is the first: it tells a component whether the surface wants the user to\n * pick one value, many, or none (pure display). A component reads what it\n * understands and ignores the rest; absent = its own default (usually display).\n */\nexport interface KindComponentUiOptions {\n selectionMode?: \"single\" | \"multiple\" | \"none\";\n [key: string]: unknown;\n}\n\n/**\n * The return channel: how a kind component hands a chosen value back to the\n * surface that mounted it (a Kind Request). The mirror of `runAction`\n * (surface → agent); this is component → surface. Undefined in normal render\n * (chat/notes) — the component simply doesn't offer selection there.\n */\nexport type ResolveKindValue = (value: unknown) => void;\n\n/** The envelope every kind action resolves to — a skip/failure is never a silent pass. */\nexport type DbKindActionResult =\n | { ok: true; result: unknown }\n | { ok: false; error: string };\n\n/**\n * The action seam a compiled component receives: `runAction(key, input)`\n * triggers a registered platform capability (e.g. \"trigger_agent\"). Always\n * resolves to an `{ ok }` envelope, never throws — safe to call from imperfect\n * component code. This is the ONLY side-effect channel a sandboxed component\n * gets; it reaches no data directly.\n */\nexport type RunDbKindAction = (\n key: string,\n input: unknown,\n) => Promise<DbKindActionResult>;\n\n/** Props every compiled DB kind component receives. */\nexport interface DbKindComponentRenderProps {\n /** The kind instance value — post-`props_transform` when one exists. */\n data: unknown;\n /** The kind slug the value belongs to. */\n kind: string;\n /** The resolver row's `config` object, verbatim (narrowed — see {@link DbKindComponentCache.config}). */\n config: Record<string, unknown>;\n /** The action seam — see {@link RunDbKindAction}. */\n runAction: RunDbKindAction;\n /**\n * Present only when the component is mounted inside a Kind Request. Call it\n * with the user's chosen value to hand that value back to the requesting\n * surface (which resolves its promise). Undefined in normal render.\n */\n onResolve?: ResolveKindValue;\n /** Settings the mounting surface dictates (e.g. `{ selectionMode: \"single\" }`). */\n uiOptions?: KindComponentUiOptions;\n}\n\nexport interface CompiledDbKindComponent {\n Component: React.ComponentType<DbKindComponentRenderProps>;\n /** Compiled `props_transform`, or null when the row declares none. */\n transform: ((data: unknown) => unknown) | null;\n}\n\nexport type DbKindCompileResult =\n | { ok: true; compiled: CompiledDbKindComponent }\n | { ok: false; error: string };\n\n/** The failure modes a browser can observe on a DB kind component. */\nexport type DbKindComponentIncidentType =\n /** `component_source` did not compile (or produced no component). */\n | \"compile_error\"\n /** The compiled component threw during render (error boundary caught it). */\n | \"render_throw\"\n /** `props_transform` failed to compile or threw. */\n | \"transform_error\";\n\n/**\n * One durable render incident — the payload for whoever can actually FIX the\n * component. A host sink typically files it on the kind's own incident queue\n * (matrx-frontend: `content_ir.kind_component_incident` via RPC); without a\n * sink, a broken component seen only by ordinary users is invisible to its\n * author forever. `data` is the kind instance value — a privacy-conscious sink\n * reduces it to keys + types before it leaves the browser.\n */\nexport interface DbKindComponentIncident {\n kind: string;\n errorType: DbKindComponentIncidentType;\n message: string;\n platform?: string;\n role?: string;\n componentKey?: string | null;\n componentUpdatedAt?: string | null;\n stack?: string | null;\n data?: unknown;\n}\n\nexport type DbKindComponentIncidentSink = (\n incident: DbKindComponentIncident,\n) => void;\n\n/**\n * THE COMPILER PORT. Matches matrx-frontend's shared allowlist compiler\n * (`compileSlotComponent`) structurally: source in, `{ Component | error }`\n * out, never throws. The compiled component is generically typed — the cache\n * performs the single deliberate narrowing where the generic compiler meets\n * the kind prop contract.\n */\nexport type DbKindComponentCompiler = (args: {\n code: string;\n allowedImports: string[];\n}) => {\n Component: React.ComponentType<Record<string, unknown>> | null;\n error: string | null;\n};\n\nexport interface DbKindComponentCachePorts {\n /** The host's sandbox compiler — see {@link DbKindComponentCompiler}. */\n compile: DbKindComponentCompiler;\n /** The FULL registered allowlist a row without `config.allowed_imports` gets. */\n defaultAllowedImports: () => string[];\n /** Where screams land (every recovery path is loud). */\n reportError: ContentIrErrorReporter;\n /** Durable incident sink — optional, but a host without one blinds authors. */\n reportIncident?: DbKindComponentIncidentSink;\n /** Default `kind_component.platform` for this host (default \"web\"). */\n platform?: string;\n /**\n * THE INVALIDATION REGISTRATION PORT. Called once at construction with the\n * cache's `invalidateAll`; the host registers it under its own invalidation\n * keys (e.g. `@ai-matrx/kit/invalidation`'s name-keyed registry, fired when\n * an agent's component write completes). The cache owns the mechanics —\n * what gets dropped, the scream-latch reset; the host owns only WHEN.\n */\n registerInvalidation?: (invalidateAll: () => void) => void;\n}\n\nexport interface DbKindComponentCache {\n /**\n * Compile (once per resolver key per row version) a db-source resolution's\n * `component_source` + optional `props_transform`. Synchronous — the caller\n * (a lazily-loaded Impl) already paid the compiler chunk.\n */\n getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform?: string,\n role?: string,\n ): DbKindCompileResult;\n /**\n * Apply the row's transform to the kind value. Loud on throw, never fatal —\n * the untransformed value comes back so content is never hidden.\n */\n applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown;\n /**\n * THE CONFIG BOUNDARY. `ComponentResolution.config` is typed\n * `Record<string, unknown>` — deliberately agnostic about a host's JSONB\n * interior. This narrows at runtime (plain object → itself; anything else →\n * scream + empty config) rather than asserting: a row whose `config` is not\n * an object is a malformed `kind_component` row, and the component still\n * renders rather than crashing.\n */\n config(config: unknown, kind: string): Record<string, unknown>;\n /** Drop a (kind, platform, role) key family (authoring surfaces call after editing a row). */\n invalidate(kind: string, platform?: string, role?: string): void;\n /** Drop everything — the invalidation-port payload. */\n invalidateAll(): void;\n}\n\n/**\n * A routed lazy warm row owns a real body, but the per-kind cold fetch has not\n * landed it yet. This is loading, not a compile failure.\n */\nexport function isDbKindComponentBodyPending(\n resolution: ComponentResolution,\n): boolean {\n return resolution.hasComponentSource && resolution.componentSource === null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Build the session cache. One per host app — construct it in the wiring module. */\nexport function createDbKindComponentCache(\n ports: DbKindComponentCachePorts,\n): DbKindComponentCache {\n const cache = new Map<string, DbKindCompileResult>();\n const screamedKeys = new Set<string>();\n const defaultPlatform = ports.platform ?? \"web\";\n\n function cacheKey(\n kind: string,\n platform: string,\n role: string,\n updatedAt: string | null,\n ): string {\n // `updatedAt` (the winning row's freshness) is part of the key: a refresh\n // that delivers an edited row (bumped updated_at) recompiles automatically\n // instead of serving the stale compile for the rest of the session.\n return `${kind}${platform}${role}${updatedAt ?? \"\"}`;\n }\n\n /**\n * Loud recovery: one console scream per key + a structured report on every\n * attempt + a durable incident for whoever can fix the component.\n */\n function screamCompileFailure(\n key: string,\n kind: string,\n error: string,\n resolution: ComponentResolution,\n platform: string,\n role: string,\n ): void {\n const message = `[content-ir] DB kind component for \"${kind}\" failed to compile — rendering the generic structured viewer instead: ${error}`;\n if (!screamedKeys.has(key)) {\n screamedKeys.add(key);\n console.error(message);\n }\n ports.reportError({ source: \"content-ir\", message, raw: { kind, error } });\n ports.reportIncident?.({\n kind,\n errorType: \"compile_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n\n function config(value: unknown, kind: string): Record<string, unknown> {\n if (isPlainObject(value)) return value;\n if (value === null || value === undefined) return {};\n const message = `[content-ir] kind_component.config for \"${kind}\" is not a JSON object (${typeof value}) — rendering with an empty config.`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n return {};\n }\n\n function resolveAllowedImports(rowConfig: Record<string, unknown>): string[] {\n const declared = rowConfig.allowed_imports;\n if (\n Array.isArray(declared) &&\n declared.every((entry): entry is string => typeof entry === \"string\")\n ) {\n return declared;\n }\n return ports.defaultAllowedImports();\n }\n\n function getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform: string = defaultPlatform,\n role = \"output\",\n ): DbKindCompileResult {\n const key = cacheKey(kind, platform, role, resolution.updatedAt);\n const cached = cache.get(key);\n if (cached) return cached;\n\n const source = resolution.componentSource;\n if (!source || !source.trim()) {\n const result: DbKindCompileResult = {\n ok: false,\n error: \"db-source resolution has no component_source\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n const allowedImports = resolveAllowedImports(\n config(resolution.config, kind),\n );\n\n const { Component, error } = ports.compile({ code: source, allowedImports });\n if (!Component || error) {\n const result: DbKindCompileResult = {\n ok: false,\n error: error ?? \"compile produced no component\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n // The optional props transform is its own tiny compile — a `(data) => data`\n // function (mirroring tool_ui's header_subtitle_code pattern). A broken\n // transform is LOUD but never fatal: the component still renders, fed the\n // untransformed value.\n let transform: ((data: unknown) => unknown) | null = null;\n if (resolution.propsTransform && resolution.propsTransform.trim()) {\n const { Component: transformFn, error: transformError } = ports.compile({\n code: resolution.propsTransform,\n allowedImports: [],\n });\n if (typeof transformFn === \"function\" && !transformError) {\n transform = transformFn as unknown as (data: unknown) => unknown;\n } else {\n const message = `[content-ir] props_transform for kind \"${kind}\" failed to compile — value passes through untransformed: ${transformError ?? \"no function produced\"}`;\n console.error(message);\n ports.reportError({\n source: \"content-ir\",\n message,\n raw: { kind, error: transformError },\n });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n }\n\n const result: DbKindCompileResult = {\n ok: true,\n compiled: {\n // The injected compiler types components as\n // ComponentType<Record<string, unknown>>; the callsite always passes\n // DbKindComponentRenderProps — the single deliberate narrowing where\n // the generic compiler meets the kind contract.\n Component:\n Component as unknown as React.ComponentType<DbKindComponentRenderProps>,\n transform,\n },\n };\n cache.set(key, result);\n return result;\n }\n\n function applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown {\n if (!compiled.transform) return value;\n try {\n return compiled.transform(value);\n } catch (error) {\n const message = `[content-ir] props_transform for kind \"${kind}\" threw — using the untransformed value: ${\n error instanceof Error ? error.message : String(error)\n }`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n stack: error instanceof Error ? (error.stack ?? null) : null,\n data: value,\n });\n return value;\n }\n }\n\n function invalidate(\n kind: string,\n platform: string = defaultPlatform,\n role = \"output\",\n ): void {\n const prefix = `${kind}${platform}${role}`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n for (const key of [...screamedKeys]) {\n if (key.startsWith(prefix)) screamedKeys.delete(key);\n }\n }\n\n function invalidateAll(): void {\n cache.clear();\n screamedKeys.clear();\n }\n\n const api: DbKindComponentCache = {\n getOrCompile,\n applyPropsTransform,\n config,\n invalidate,\n invalidateAll,\n };\n\n ports.registerInvalidation?.(() => invalidateAll());\n\n return api;\n}\n","\"use client\";\n\n/**\n * DbKindComponentErrorBoundary — the DEFAULT error boundary around a compiled\n * DB kind component (absorbed from matrx-frontend per the C23 retrofit; the\n * package previously shipped \"no error capture by design\", which left every\n * host to rebuild this class).\n *\n * A user-authored component that throws during render must never crash the\n * surface (chat, notes, admin preview). The boundary catches, renders the\n * fallback (the kind's generic structured viewer — the R6 disposition, never\n * a blank hole), and SCREAMS: a recovery firing means a real bug got past\n * authoring.\n *\n * `resetSignal` (the component's `updated_at`) un-latches the boundary when a\n * NEW component version arrives: an author who fixes a throwing component must\n * see the fix on the next re-render, not stay stuck on the fallback until a\n * full unmount. Without this the error state is sticky for the whole session —\n * the \"broke, then magically fixed itself minutes later\" class. React error\n * boundaries never self-clear, so a version change is the only safe signal\n * (`hasError` alone can't tell a fixed version from the same broken one).\n *\n * `onCaught` is the scream seam: a host binds its structured render-error\n * capture + durable incident filing (matrx-frontend files a `render_throw`\n * incident so the component's author learns). Bound to nothing, the default is\n * an honest console scream — never silence.\n */\nimport * as React from \"react\";\n\nexport interface DbKindComponentErrorBoundaryProps {\n kind: string;\n /** The resolved component's `updated_at`; a change means a new version. */\n resetSignal: string | null;\n fallback: React.ReactNode;\n children: React.ReactNode;\n /**\n * Where the catch lands. A host binds structured capture + incident filing\n * here; the default screams to the console. NOTE for hosts whose console\n * adapter persists `console.error`: bind this (which replaces the default)\n * rather than mirroring the capture through the console twice.\n */\n onCaught?: (\n error: Error,\n info: { componentStack: string | null },\n ) => void;\n}\n\ninterface State {\n hasError: boolean;\n /**\n * The `resetSignal` seen on the LAST render — tracked every render (not just\n * on error) so that at throw time it already holds the version that threw.\n * `getDerivedStateFromError` cannot read props, so this is how the boundary\n * knows which version to un-latch away from.\n */\n signalAtLastRender: string | null;\n}\n\nexport class DbKindComponentErrorBoundary extends React.Component<\n DbKindComponentErrorBoundaryProps,\n State\n> {\n constructor(props: DbKindComponentErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n static getDerivedStateFromError(): Partial<State> {\n return { hasError: true };\n }\n\n static getDerivedStateFromProps(\n props: DbKindComponentErrorBoundaryProps,\n state: State,\n ): State | null {\n if (props.resetSignal === state.signalAtLastRender) return null;\n // The version changed. If we were latched on the old version, un-latch so\n // the new (hopefully fixed) component gets a fresh render attempt; either\n // way, record the new signal as the one now rendering.\n return { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n const info = { componentStack: errorInfo.componentStack ?? null };\n if (this.props.onCaught) {\n this.props.onCaught(error, info);\n return;\n }\n // The honest default when a host binds nothing: never silent.\n console.error(\n `[content-ir] DB kind component for \"${this.props.kind}\" threw during render — showing the fallback:`,\n error,\n );\n }\n\n override render(): React.ReactNode {\n if (this.state.hasError) return this.props.fallback;\n return this.props.children;\n }\n}\n","/**\n * Kind components call Actions (Applets CONTRACTS §6/§7).\n *\n * A compiled kind component's ONE side-effect channel is `runAction(key,\n * input)` (see {@link RunDbKindAction}). This binds that channel to a surface's\n * Actions — the one registry (`@ai-matrx/alchemy`'s `SurfacePort.runAction`,\n * whose runner applies each Action's policy and `triggeredBy`). A host mounts a\n * surface (an Applet frame mounts `applets/<id>`), then hands every kind it\n * renders `runKindActionsThroughSurface(port)`.\n *\n * Package policy, so every host gets it bug for bug:\n * - NEVER throws: a refusal, an unknown Action, a port that rejects — all\n * resolve `{ ok: false, error }` with the system's own sentence (and its\n * remedy, when the refusal names one), so imperfect component code keeps\n * rendering.\n * - ONE run in flight per Action and target: a double-click or a re-render\n * storm cannot run an agent, a write or a server action twice. The key is\n * the Action name plus `input.target` / `input.id` when the input names one,\n * so one slow Action never locks every other.\n * - The caller is a PERSON by default: a kind component acts on a click in a\n * person's page. A host that runs kinds on an agent's behalf passes `by`.\n *\n * Typed structurally over the port so this package does not depend on alchemy.\n */\nimport type { DbKindActionResult, RunDbKindAction } from \"./db-kind-component-cache\";\n\nexport type KindActionCaller = \"person\" | \"agent\" | \"applet\" | \"automation\";\n\n/** The one method of alchemy's `SurfacePort` this needs. */\nexport interface KindActionSurface {\n runAction(\n name: string,\n input: unknown,\n by: KindActionCaller,\n ): Promise<{ ok: true; data: unknown } | { ok: false; error: { message: string; remedy?: string } }>;\n}\n\nexport interface KindActionsThroughSurfaceOptions {\n /** Who is acting (default \"person\"). */\n by?: KindActionCaller;\n}\n\nfunction inFlightKey(name: string, input: unknown): string {\n if (input && typeof input === \"object\") {\n const rec = input as { target?: unknown; id?: unknown };\n if (typeof rec.target === \"string\" && rec.target) return `${name}::${rec.target}`;\n if (typeof rec.id === \"string\" && rec.id) return `${name}::${rec.id}`;\n }\n return name;\n}\n\nexport function runKindActionsThroughSurface(\n surface: KindActionSurface,\n options: KindActionsThroughSurfaceOptions = {},\n): RunDbKindAction {\n const by = options.by ?? \"person\";\n const inFlight = new Set<string>();\n return async (key: string, input: unknown): Promise<DbKindActionResult> => {\n if (typeof key !== \"string\" || !key.trim()) {\n return { ok: false, error: \"This component asked to run an Action without naming one.\" };\n }\n const flight = inFlightKey(key, input);\n if (inFlight.has(flight)) {\n return { ok: false, error: `\"${key}\" is already running. Wait for it to finish.` };\n }\n inFlight.add(flight);\n try {\n const result = await surface.runAction(key, input, by);\n if (result.ok) return { ok: true, result: result.data };\n const { message, remedy } = result.error;\n return { ok: false, error: remedy ? `${message} ${remedy}` : message };\n } catch (err) {\n return {\n ok: false,\n error: `\"${key}\" could not run: ${err instanceof Error ? err.message : String(err)}`,\n };\n } finally {\n inFlight.delete(flight);\n }\n };\n}\n"],"mappings":"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","names":["result","Component"]}
1
+ {"version":3,"sources":["../db-component/db-kind-component-cache.ts","../db-component/DbKindComponentErrorBoundary.tsx","../db-component/kind-actions-through-surface.ts"],"sourcesContent":["/**\n * The DB-component COMPILE CACHE — session-scoped, keyed per winning row\n * VERSION, with the loud-recovery policy built in.\n *\n * This was matrx-frontend's `dbKindComponentCache.ts` (316 lines of compile\n * caching + invalidation living in one host), absorbed per C22: cache\n * mechanics, key derivation, the scream-once latch, transform recovery, and\n * config narrowing are PACKAGE policy — every host was going to need exactly\n * these lines, bug for bug. What stays host-injected is exactly what is\n * host-shaped:\n *\n * - THE COMPILER ({@link DbKindComponentCompiler}). Compiling or sandboxing\n * user-authored component source is a security-shaped host decision — the\n * package never bundles Babel or an allowlist scope. matrx-frontend\n * injects its shared allowlist compiler (`compileSlotComponent`); a host\n * with no sandbox simply never constructs a cache.\n * - The default allowed-imports list (host scope registry).\n * - Where screams land ({@link ContentIrErrorReporter}) and where durable\n * incidents go ({@link DbKindComponentIncidentSink}).\n * - Invalidation TRIGGERS ({@link DbKindComponentCachePorts.registerInvalidation}):\n * the host wires its invalidation-registry keys (e.g.\n * `@ai-matrx/kit/invalidation` fired by name when an agent edits a\n * component row); the cache owns what invalidation MEANS.\n *\n * Contract (the db-row component contract, SHAPE_SYSTEM.md):\n * - `component_source` compiles to a React component receiving\n * `{ data, kind, config, runAction, itemState?, onResolve?, uiOptions? }`\n * where `data` is the kind instance value and `itemState` what actions\n * saved onto this item.\n * - `props_transform` (optional) compiles to a `(data) => data` function\n * applied to the value BEFORE it reaches the component. A throwing or\n * non-compiling transform screams and the untransformed value is used\n * (never a blank hole).\n * - `config.allowed_imports` (string[]) narrows the compile scope; absent,\n * the row gets the host's FULL registered allowlist.\n *\n * Cache key: (kind, platform, role, row.updated_at) — one compile per winning\n * row VERSION. A resolver refresh delivering an edited row (bumped\n * `updated_at`) re-keys and recompiles naturally; `invalidate` /\n * `invalidateAll` force-drop keys. Server edits never push to open clients —\n * the contract is refresh-on-view.\n */\n\nimport type * as React from \"react\";\n\nimport type { ContentIrErrorReporter } from \"../host/error-report\";\nimport type { ComponentResolution } from \"../resolver/component-resolution.types\";\n\n/**\n * Settings the MOUNTING UI dictates to a kind component — the standard,\n * growing arg list every component honors (Arman, 2026-07-23). `selectionMode`\n * is the first: it tells a component whether the surface wants the user to\n * pick one value, many, or none (pure display). A component reads what it\n * understands and ignores the rest; absent = its own default (usually display).\n */\nexport interface KindComponentUiOptions {\n selectionMode?: \"single\" | \"multiple\" | \"none\";\n [key: string]: unknown;\n}\n\n/**\n * The return channel: how a kind component hands a chosen value back to the\n * surface that mounted it (a Kind Request). The mirror of `runAction`\n * (surface → agent); this is component → surface. Undefined in normal render\n * (chat/notes) — the component simply doesn't offer selection there.\n */\nexport type ResolveKindValue = (value: unknown) => void;\n\n/** The envelope every kind action resolves to — a skip/failure is never a silent pass. */\nexport type DbKindActionResult =\n | { ok: true; result: unknown }\n | { ok: false; error: string };\n\n/**\n * The action seam a compiled component receives: `runAction(key, input)`\n * triggers a registered platform capability (e.g. \"trigger_agent\"). Always\n * resolves to an `{ ok }` envelope, never throws — safe to call from imperfect\n * component code. This is the ONLY side-effect channel a sandboxed component\n * gets; it reaches no data directly.\n */\nexport type RunDbKindAction = (\n key: string,\n input: unknown,\n) => Promise<DbKindActionResult>;\n\n/** Props every compiled DB kind component receives. */\nexport interface DbKindComponentRenderProps {\n /** The kind instance value — post-`props_transform` when one exists. */\n data: unknown;\n /** The kind slug the value belongs to. */\n kind: string;\n /** The resolver row's `config` object, verbatim (narrowed — see {@link DbKindComponentCache.config}). */\n config: Record<string, unknown>;\n /** The action seam — see {@link RunDbKindAction}. */\n runAction: RunDbKindAction;\n /**\n * Present only when the component is mounted inside a Kind Request. Call it\n * with the user's chosen value to hand that value back to the requesting\n * surface (which resolves its promise). Undefined in normal render.\n */\n onResolve?: ResolveKindValue;\n /** Settings the mounting surface dictates (e.g. `{ selectionMode: \"single\" }`). */\n uiOptions?: KindComponentUiOptions;\n /**\n * This item's durable state: what `save_item_state` / `run_shortcut` with\n * `saveAs` wrote onto it (`{}` when nothing yet; survives reload). The host\n * adds `src` to every saved image ref (`{ file_id, mime_type, width, height }`)\n * so the component renders `<img src={itemState.key.src}>`.\n */\n itemState?: Record<string, unknown>;\n}\n\nexport interface CompiledDbKindComponent {\n Component: React.ComponentType<DbKindComponentRenderProps>;\n /** Compiled `props_transform`, or null when the row declares none. */\n transform: ((data: unknown) => unknown) | null;\n}\n\nexport type DbKindCompileResult =\n | { ok: true; compiled: CompiledDbKindComponent }\n | { ok: false; error: string };\n\n/** The failure modes a browser can observe on a DB kind component. */\nexport type DbKindComponentIncidentType =\n /** `component_source` did not compile (or produced no component). */\n | \"compile_error\"\n /** The compiled component threw during render (error boundary caught it). */\n | \"render_throw\"\n /** `props_transform` failed to compile or threw. */\n | \"transform_error\";\n\n/**\n * One durable render incident — the payload for whoever can actually FIX the\n * component. A host sink typically files it on the kind's own incident queue\n * (matrx-frontend: `content_ir.kind_component_incident` via RPC); without a\n * sink, a broken component seen only by ordinary users is invisible to its\n * author forever. `data` is the kind instance value — a privacy-conscious sink\n * reduces it to keys + types before it leaves the browser.\n */\nexport interface DbKindComponentIncident {\n kind: string;\n errorType: DbKindComponentIncidentType;\n message: string;\n platform?: string;\n role?: string;\n componentKey?: string | null;\n componentUpdatedAt?: string | null;\n stack?: string | null;\n data?: unknown;\n}\n\nexport type DbKindComponentIncidentSink = (\n incident: DbKindComponentIncident,\n) => void;\n\n/**\n * THE COMPILER PORT. Matches matrx-frontend's shared allowlist compiler\n * (`compileSlotComponent`) structurally: source in, `{ Component | error }`\n * out, never throws. The compiled component is generically typed — the cache\n * performs the single deliberate narrowing where the generic compiler meets\n * the kind prop contract.\n */\nexport type DbKindComponentCompiler = (args: {\n code: string;\n allowedImports: string[];\n}) => {\n Component: React.ComponentType<Record<string, unknown>> | null;\n error: string | null;\n};\n\nexport interface DbKindComponentCachePorts {\n /** The host's sandbox compiler — see {@link DbKindComponentCompiler}. */\n compile: DbKindComponentCompiler;\n /** The FULL registered allowlist a row without `config.allowed_imports` gets. */\n defaultAllowedImports: () => string[];\n /** Where screams land (every recovery path is loud). */\n reportError: ContentIrErrorReporter;\n /** Durable incident sink — optional, but a host without one blinds authors. */\n reportIncident?: DbKindComponentIncidentSink;\n /** Default `kind_component.platform` for this host (default \"web\"). */\n platform?: string;\n /**\n * THE INVALIDATION REGISTRATION PORT. Called once at construction with the\n * cache's `invalidateAll`; the host registers it under its own invalidation\n * keys (e.g. `@ai-matrx/kit/invalidation`'s name-keyed registry, fired when\n * an agent's component write completes). The cache owns the mechanics —\n * what gets dropped, the scream-latch reset; the host owns only WHEN.\n */\n registerInvalidation?: (invalidateAll: () => void) => void;\n}\n\nexport interface DbKindComponentCache {\n /**\n * Compile (once per resolver key per row version) a db-source resolution's\n * `component_source` + optional `props_transform`. Synchronous — the caller\n * (a lazily-loaded Impl) already paid the compiler chunk.\n */\n getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform?: string,\n role?: string,\n ): DbKindCompileResult;\n /**\n * Apply the row's transform to the kind value. Loud on throw, never fatal —\n * the untransformed value comes back so content is never hidden.\n */\n applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown;\n /**\n * THE CONFIG BOUNDARY. `ComponentResolution.config` is typed\n * `Record<string, unknown>` — deliberately agnostic about a host's JSONB\n * interior. This narrows at runtime (plain object → itself; anything else →\n * scream + empty config) rather than asserting: a row whose `config` is not\n * an object is a malformed `kind_component` row, and the component still\n * renders rather than crashing.\n */\n config(config: unknown, kind: string): Record<string, unknown>;\n /** Drop a (kind, platform, role) key family (authoring surfaces call after editing a row). */\n invalidate(kind: string, platform?: string, role?: string): void;\n /** Drop everything — the invalidation-port payload. */\n invalidateAll(): void;\n}\n\n/**\n * A routed lazy warm row owns a real body, but the per-kind cold fetch has not\n * landed it yet. This is loading, not a compile failure.\n */\nexport function isDbKindComponentBodyPending(\n resolution: ComponentResolution,\n): boolean {\n return resolution.hasComponentSource && resolution.componentSource === null;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Build the session cache. One per host app — construct it in the wiring module. */\nexport function createDbKindComponentCache(\n ports: DbKindComponentCachePorts,\n): DbKindComponentCache {\n const cache = new Map<string, DbKindCompileResult>();\n const screamedKeys = new Set<string>();\n const defaultPlatform = ports.platform ?? \"web\";\n\n function cacheKey(\n kind: string,\n platform: string,\n role: string,\n updatedAt: string | null,\n ): string {\n // `updatedAt` (the winning row's freshness) is part of the key: a refresh\n // that delivers an edited row (bumped updated_at) recompiles automatically\n // instead of serving the stale compile for the rest of the session.\n return `${kind}${platform}${role}${updatedAt ?? \"\"}`;\n }\n\n /**\n * Loud recovery: one console scream per key + a structured report on every\n * attempt + a durable incident for whoever can fix the component.\n */\n function screamCompileFailure(\n key: string,\n kind: string,\n error: string,\n resolution: ComponentResolution,\n platform: string,\n role: string,\n ): void {\n const message = `[content-ir] DB kind component for \"${kind}\" failed to compile — rendering the generic structured viewer instead: ${error}`;\n if (!screamedKeys.has(key)) {\n screamedKeys.add(key);\n console.error(message);\n }\n ports.reportError({ source: \"content-ir\", message, raw: { kind, error } });\n ports.reportIncident?.({\n kind,\n errorType: \"compile_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n\n function config(value: unknown, kind: string): Record<string, unknown> {\n if (isPlainObject(value)) return value;\n if (value === null || value === undefined) return {};\n const message = `[content-ir] kind_component.config for \"${kind}\" is not a JSON object (${typeof value}) — rendering with an empty config.`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n return {};\n }\n\n function resolveAllowedImports(rowConfig: Record<string, unknown>): string[] {\n const declared = rowConfig.allowed_imports;\n if (\n Array.isArray(declared) &&\n declared.every((entry): entry is string => typeof entry === \"string\")\n ) {\n return declared;\n }\n return ports.defaultAllowedImports();\n }\n\n function getOrCompile(\n kind: string,\n resolution: ComponentResolution,\n platform: string = defaultPlatform,\n role = \"output\",\n ): DbKindCompileResult {\n const key = cacheKey(kind, platform, role, resolution.updatedAt);\n const cached = cache.get(key);\n if (cached) return cached;\n\n const source = resolution.componentSource;\n if (!source || !source.trim()) {\n const result: DbKindCompileResult = {\n ok: false,\n error: \"db-source resolution has no component_source\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n const allowedImports = resolveAllowedImports(\n config(resolution.config, kind),\n );\n\n const { Component, error } = ports.compile({ code: source, allowedImports });\n if (!Component || error) {\n const result: DbKindCompileResult = {\n ok: false,\n error: error ?? \"compile produced no component\",\n };\n screamCompileFailure(key, kind, result.error, resolution, platform, role);\n cache.set(key, result);\n return result;\n }\n\n // The optional props transform is its own tiny compile — a `(data) => data`\n // function (mirroring tool_ui's header_subtitle_code pattern). A broken\n // transform is LOUD but never fatal: the component still renders, fed the\n // untransformed value.\n let transform: ((data: unknown) => unknown) | null = null;\n if (resolution.propsTransform && resolution.propsTransform.trim()) {\n const { Component: transformFn, error: transformError } = ports.compile({\n code: resolution.propsTransform,\n allowedImports: [],\n });\n if (typeof transformFn === \"function\" && !transformError) {\n transform = transformFn as unknown as (data: unknown) => unknown;\n } else {\n const message = `[content-ir] props_transform for kind \"${kind}\" failed to compile — value passes through untransformed: ${transformError ?? \"no function produced\"}`;\n console.error(message);\n ports.reportError({\n source: \"content-ir\",\n message,\n raw: { kind, error: transformError },\n });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n platform,\n role,\n componentKey: resolution.componentKey,\n componentUpdatedAt: resolution.updatedAt,\n });\n }\n }\n\n const result: DbKindCompileResult = {\n ok: true,\n compiled: {\n // The injected compiler types components as\n // ComponentType<Record<string, unknown>>; the callsite always passes\n // DbKindComponentRenderProps — the single deliberate narrowing where\n // the generic compiler meets the kind contract.\n Component:\n Component as unknown as React.ComponentType<DbKindComponentRenderProps>,\n transform,\n },\n };\n cache.set(key, result);\n return result;\n }\n\n function applyPropsTransform(\n kind: string,\n compiled: CompiledDbKindComponent,\n value: unknown,\n ): unknown {\n if (!compiled.transform) return value;\n try {\n return compiled.transform(value);\n } catch (error) {\n const message = `[content-ir] props_transform for kind \"${kind}\" threw — using the untransformed value: ${\n error instanceof Error ? error.message : String(error)\n }`;\n console.error(message);\n ports.reportError({ source: \"content-ir\", message, raw: { kind } });\n ports.reportIncident?.({\n kind,\n errorType: \"transform_error\",\n message,\n stack: error instanceof Error ? (error.stack ?? null) : null,\n data: value,\n });\n return value;\n }\n }\n\n function invalidate(\n kind: string,\n platform: string = defaultPlatform,\n role = \"output\",\n ): void {\n const prefix = `${kind}${platform}${role}`;\n for (const key of [...cache.keys()]) {\n if (key.startsWith(prefix)) cache.delete(key);\n }\n for (const key of [...screamedKeys]) {\n if (key.startsWith(prefix)) screamedKeys.delete(key);\n }\n }\n\n function invalidateAll(): void {\n cache.clear();\n screamedKeys.clear();\n }\n\n const api: DbKindComponentCache = {\n getOrCompile,\n applyPropsTransform,\n config,\n invalidate,\n invalidateAll,\n };\n\n ports.registerInvalidation?.(() => invalidateAll());\n\n return api;\n}\n","\"use client\";\n\n/**\n * DbKindComponentErrorBoundary — the DEFAULT error boundary around a compiled\n * DB kind component (absorbed from matrx-frontend per the C23 retrofit; the\n * package previously shipped \"no error capture by design\", which left every\n * host to rebuild this class).\n *\n * A user-authored component that throws during render must never crash the\n * surface (chat, notes, admin preview). The boundary catches, renders the\n * fallback (the kind's generic structured viewer — the R6 disposition, never\n * a blank hole), and SCREAMS: a recovery firing means a real bug got past\n * authoring.\n *\n * `resetSignal` (the component's `updated_at`) un-latches the boundary when a\n * NEW component version arrives: an author who fixes a throwing component must\n * see the fix on the next re-render, not stay stuck on the fallback until a\n * full unmount. Without this the error state is sticky for the whole session —\n * the \"broke, then magically fixed itself minutes later\" class. React error\n * boundaries never self-clear, so a version change is the only safe signal\n * (`hasError` alone can't tell a fixed version from the same broken one).\n *\n * `onCaught` is the scream seam: a host binds its structured render-error\n * capture + durable incident filing (matrx-frontend files a `render_throw`\n * incident so the component's author learns). Bound to nothing, the default is\n * an honest console scream — never silence.\n */\nimport * as React from \"react\";\n\nexport interface DbKindComponentErrorBoundaryProps {\n kind: string;\n /** The resolved component's `updated_at`; a change means a new version. */\n resetSignal: string | null;\n fallback: React.ReactNode;\n children: React.ReactNode;\n /**\n * Where the catch lands. A host binds structured capture + incident filing\n * here; the default screams to the console. NOTE for hosts whose console\n * adapter persists `console.error`: bind this (which replaces the default)\n * rather than mirroring the capture through the console twice.\n */\n onCaught?: (\n error: Error,\n info: { componentStack: string | null },\n ) => void;\n}\n\ninterface State {\n hasError: boolean;\n /**\n * The `resetSignal` seen on the LAST render — tracked every render (not just\n * on error) so that at throw time it already holds the version that threw.\n * `getDerivedStateFromError` cannot read props, so this is how the boundary\n * knows which version to un-latch away from.\n */\n signalAtLastRender: string | null;\n}\n\nexport class DbKindComponentErrorBoundary extends React.Component<\n DbKindComponentErrorBoundaryProps,\n State\n> {\n constructor(props: DbKindComponentErrorBoundaryProps) {\n super(props);\n this.state = { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n static getDerivedStateFromError(): Partial<State> {\n return { hasError: true };\n }\n\n static getDerivedStateFromProps(\n props: DbKindComponentErrorBoundaryProps,\n state: State,\n ): State | null {\n if (props.resetSignal === state.signalAtLastRender) return null;\n // The version changed. If we were latched on the old version, un-latch so\n // the new (hopefully fixed) component gets a fresh render attempt; either\n // way, record the new signal as the one now rendering.\n return { hasError: false, signalAtLastRender: props.resetSignal };\n }\n\n override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {\n const info = { componentStack: errorInfo.componentStack ?? null };\n if (this.props.onCaught) {\n this.props.onCaught(error, info);\n return;\n }\n // The honest default when a host binds nothing: never silent.\n console.error(\n `[content-ir] DB kind component for \"${this.props.kind}\" threw during render — showing the fallback:`,\n error,\n );\n }\n\n override render(): React.ReactNode {\n if (this.state.hasError) return this.props.fallback;\n return this.props.children;\n }\n}\n","/**\n * Kind components call Actions (Applets CONTRACTS §6/§7).\n *\n * A compiled kind component's ONE side-effect channel is `runAction(key,\n * input)` (see {@link RunDbKindAction}). This binds that channel to a surface's\n * Actions — the one registry (`@ai-matrx/alchemy`'s `SurfacePort.runAction`,\n * whose runner applies each Action's policy and `triggeredBy`). A host mounts a\n * surface (an Applet frame mounts `applets/<id>`), then hands every kind it\n * renders `runKindActionsThroughSurface(port)`.\n *\n * Package policy, so every host gets it bug for bug:\n * - NEVER throws: a refusal, an unknown Action, a port that rejects — all\n * resolve `{ ok: false, error }` with the system's own sentence (and its\n * remedy, when the refusal names one), so imperfect component code keeps\n * rendering.\n * - ONE run in flight per Action and target: a double-click or a re-render\n * storm cannot run an agent, a write or a server action twice. The key is\n * the Action name plus `input.target` / `input.id` when the input names one,\n * so one slow Action never locks every other.\n * - The caller is a PERSON by default: a kind component acts on a click in a\n * person's page. A host that runs kinds on an agent's behalf passes `by`.\n *\n * Typed structurally over the port so this package does not depend on alchemy.\n */\nimport type { DbKindActionResult, RunDbKindAction } from \"./db-kind-component-cache\";\n\nexport type KindActionCaller = \"person\" | \"agent\" | \"applet\" | \"automation\";\n\n/** The one method of alchemy's `SurfacePort` this needs. */\nexport interface KindActionSurface {\n runAction(\n name: string,\n input: unknown,\n by: KindActionCaller,\n ): Promise<{ ok: true; data: unknown } | { ok: false; error: { message: string; remedy?: string } }>;\n}\n\nexport interface KindActionsThroughSurfaceOptions {\n /** Who is acting (default \"person\"). */\n by?: KindActionCaller;\n}\n\nfunction inFlightKey(name: string, input: unknown): string {\n if (input && typeof input === \"object\") {\n const rec = input as { target?: unknown; id?: unknown };\n if (typeof rec.target === \"string\" && rec.target) return `${name}::${rec.target}`;\n if (typeof rec.id === \"string\" && rec.id) return `${name}::${rec.id}`;\n }\n return name;\n}\n\nexport function runKindActionsThroughSurface(\n surface: KindActionSurface,\n options: KindActionsThroughSurfaceOptions = {},\n): RunDbKindAction {\n const by = options.by ?? \"person\";\n const inFlight = new Set<string>();\n return async (key: string, input: unknown): Promise<DbKindActionResult> => {\n if (typeof key !== \"string\" || !key.trim()) {\n return { ok: false, error: \"This component asked to run an Action without naming one.\" };\n }\n const flight = inFlightKey(key, input);\n if (inFlight.has(flight)) {\n return { ok: false, error: `\"${key}\" is already running. Wait for it to finish.` };\n }\n inFlight.add(flight);\n try {\n const result = await surface.runAction(key, input, by);\n if (result.ok) return { ok: true, result: result.data };\n const { message, remedy } = result.error;\n return { ok: false, error: remedy ? `${message} ${remedy}` : message };\n } catch (err) {\n return {\n ok: false,\n error: `\"${key}\" could not run: ${err instanceof Error ? err.message : String(err)}`,\n };\n } finally {\n inFlight.delete(flight);\n }\n };\n}\n"],"mappings":"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","names":["result","Component"]}