@ai-matrx/content-ir-react 0.7.1 → 0.8.0
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 +61 -0
- package/dist/component-resolution.types-BS1ySIg-.d.cts +104 -0
- package/dist/component-resolution.types-BS1ySIg-.d.ts +104 -0
- package/dist/component-resolution.types-fvR92hW7.d.cts +142 -0
- package/dist/component-resolution.types-fvR92hW7.d.ts +142 -0
- package/dist/db-component.cjs +226 -0
- package/dist/db-component.cjs.map +1 -0
- package/dist/db-component.d.cts +267 -0
- package/dist/db-component.d.ts +267 -0
- package/dist/db-component.js +194 -0
- package/dist/db-component.js.map +1 -0
- package/dist/host-types-CZ_Bkp4R.d.ts +131 -0
- package/dist/host-types-DvGv8s9L.d.cts +131 -0
- package/dist/host.cjs +74 -0
- package/dist/host.cjs.map +1 -0
- package/dist/host.d.cts +24 -0
- package/dist/host.d.ts +24 -0
- package/dist/host.js +52 -0
- package/dist/host.js.map +1 -0
- package/dist/index.cjs +356 -90
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +280 -672
- package/dist/index.d.ts +280 -672
- package/dist/index.js +316 -31
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +361 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +195 -0
- package/dist/react.d.ts +195 -0
- package/dist/react.js +344 -0
- package/dist/react.js.map +1 -0
- package/dist/resolver.cjs +327 -0
- package/dist/resolver.cjs.map +1 -0
- package/dist/resolver.d.cts +133 -0
- package/dist/resolver.d.ts +133 -0
- package/dist/resolver.js +304 -0
- package/dist/resolver.js.map +1 -0
- package/dist/route-C1XNcubB.d.ts +433 -0
- package/dist/route-C55jsF4B.d.cts +433 -0
- package/dist/route.cjs +259 -0
- package/dist/route.cjs.map +1 -0
- package/dist/route.d.cts +4 -0
- package/dist/route.d.ts +4 -0
- package/dist/route.js +247 -0
- package/dist/route.js.map +1 -0
- package/package.json +75 -2
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-BS1ySIg-.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The DB-component COMPILE CACHE — session-scoped, keyed per winning row
|
|
6
|
+
* VERSION, with the loud-recovery policy built in.
|
|
7
|
+
*
|
|
8
|
+
* This was matrx-frontend's `dbKindComponentCache.ts` (316 lines of compile
|
|
9
|
+
* caching + invalidation living in one host), absorbed per C22: cache
|
|
10
|
+
* mechanics, key derivation, the scream-once latch, transform recovery, and
|
|
11
|
+
* config narrowing are PACKAGE policy — every host was going to need exactly
|
|
12
|
+
* these lines, bug for bug. What stays host-injected is exactly what is
|
|
13
|
+
* host-shaped:
|
|
14
|
+
*
|
|
15
|
+
* - THE COMPILER ({@link DbKindComponentCompiler}). Compiling or sandboxing
|
|
16
|
+
* user-authored component source is a security-shaped host decision — the
|
|
17
|
+
* package never bundles Babel or an allowlist scope. matrx-frontend
|
|
18
|
+
* injects its shared allowlist compiler (`compileSlotComponent`); a host
|
|
19
|
+
* with no sandbox simply never constructs a cache.
|
|
20
|
+
* - The default allowed-imports list (host scope registry).
|
|
21
|
+
* - Where screams land ({@link ContentIrErrorReporter}) and where durable
|
|
22
|
+
* incidents go ({@link DbKindComponentIncidentSink}).
|
|
23
|
+
* - Invalidation TRIGGERS ({@link DbKindComponentCachePorts.registerInvalidation}):
|
|
24
|
+
* the host wires its invalidation-registry keys (e.g.
|
|
25
|
+
* `@ai-matrx/kit/invalidation` fired by name when an agent edits a
|
|
26
|
+
* component row); the cache owns what invalidation MEANS.
|
|
27
|
+
*
|
|
28
|
+
* Contract (the db-row component contract, SHAPE_SYSTEM.md):
|
|
29
|
+
* - `component_source` compiles to a React component receiving
|
|
30
|
+
* `{ data, kind, config, runAction, onResolve?, uiOptions? }` where `data`
|
|
31
|
+
* is the kind instance value.
|
|
32
|
+
* - `props_transform` (optional) compiles to a `(data) => data` function
|
|
33
|
+
* applied to the value BEFORE it reaches the component. A throwing or
|
|
34
|
+
* non-compiling transform screams and the untransformed value is used
|
|
35
|
+
* (never a blank hole).
|
|
36
|
+
* - `config.allowed_imports` (string[]) narrows the compile scope; absent,
|
|
37
|
+
* the row gets the host's FULL registered allowlist.
|
|
38
|
+
*
|
|
39
|
+
* Cache key: (kind, platform, role, row.updated_at) — one compile per winning
|
|
40
|
+
* row VERSION. A resolver refresh delivering an edited row (bumped
|
|
41
|
+
* `updated_at`) re-keys and recompiles naturally; `invalidate` /
|
|
42
|
+
* `invalidateAll` force-drop keys. Server edits never push to open clients —
|
|
43
|
+
* the contract is refresh-on-view.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Settings the MOUNTING UI dictates to a kind component — the standard,
|
|
48
|
+
* growing arg list every component honors (Arman, 2026-07-23). `selectionMode`
|
|
49
|
+
* is the first: it tells a component whether the surface wants the user to
|
|
50
|
+
* pick one value, many, or none (pure display). A component reads what it
|
|
51
|
+
* understands and ignores the rest; absent = its own default (usually display).
|
|
52
|
+
*/
|
|
53
|
+
interface KindComponentUiOptions {
|
|
54
|
+
selectionMode?: "single" | "multiple" | "none";
|
|
55
|
+
[key: string]: unknown;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The return channel: how a kind component hands a chosen value back to the
|
|
59
|
+
* surface that mounted it (a Kind Request). The mirror of `runAction`
|
|
60
|
+
* (surface → agent); this is component → surface. Undefined in normal render
|
|
61
|
+
* (chat/notes) — the component simply doesn't offer selection there.
|
|
62
|
+
*/
|
|
63
|
+
type ResolveKindValue = (value: unknown) => void;
|
|
64
|
+
/** The envelope every kind action resolves to — a skip/failure is never a silent pass. */
|
|
65
|
+
type DbKindActionResult = {
|
|
66
|
+
ok: true;
|
|
67
|
+
result: unknown;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
error: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* The action seam a compiled component receives: `runAction(key, input)`
|
|
74
|
+
* triggers a registered platform capability (e.g. "trigger_agent"). Always
|
|
75
|
+
* resolves to an `{ ok }` envelope, never throws — safe to call from imperfect
|
|
76
|
+
* component code. This is the ONLY side-effect channel a sandboxed component
|
|
77
|
+
* gets; it reaches no data directly.
|
|
78
|
+
*/
|
|
79
|
+
type RunDbKindAction = (key: string, input: unknown) => Promise<DbKindActionResult>;
|
|
80
|
+
/** Props every compiled DB kind component receives. */
|
|
81
|
+
interface DbKindComponentRenderProps {
|
|
82
|
+
/** The kind instance value — post-`props_transform` when one exists. */
|
|
83
|
+
data: unknown;
|
|
84
|
+
/** The kind slug the value belongs to. */
|
|
85
|
+
kind: string;
|
|
86
|
+
/** The resolver row's `config` object, verbatim (narrowed — see {@link DbKindComponentCache.config}). */
|
|
87
|
+
config: Record<string, unknown>;
|
|
88
|
+
/** The action seam — see {@link RunDbKindAction}. */
|
|
89
|
+
runAction: RunDbKindAction;
|
|
90
|
+
/**
|
|
91
|
+
* Present only when the component is mounted inside a Kind Request. Call it
|
|
92
|
+
* with the user's chosen value to hand that value back to the requesting
|
|
93
|
+
* surface (which resolves its promise). Undefined in normal render.
|
|
94
|
+
*/
|
|
95
|
+
onResolve?: ResolveKindValue;
|
|
96
|
+
/** Settings the mounting surface dictates (e.g. `{ selectionMode: "single" }`). */
|
|
97
|
+
uiOptions?: KindComponentUiOptions;
|
|
98
|
+
}
|
|
99
|
+
interface CompiledDbKindComponent {
|
|
100
|
+
Component: React.ComponentType<DbKindComponentRenderProps>;
|
|
101
|
+
/** Compiled `props_transform`, or null when the row declares none. */
|
|
102
|
+
transform: ((data: unknown) => unknown) | null;
|
|
103
|
+
}
|
|
104
|
+
type DbKindCompileResult = {
|
|
105
|
+
ok: true;
|
|
106
|
+
compiled: CompiledDbKindComponent;
|
|
107
|
+
} | {
|
|
108
|
+
ok: false;
|
|
109
|
+
error: string;
|
|
110
|
+
};
|
|
111
|
+
/** The failure modes a browser can observe on a DB kind component. */
|
|
112
|
+
type DbKindComponentIncidentType =
|
|
113
|
+
/** `component_source` did not compile (or produced no component). */
|
|
114
|
+
"compile_error"
|
|
115
|
+
/** The compiled component threw during render (error boundary caught it). */
|
|
116
|
+
| "render_throw"
|
|
117
|
+
/** `props_transform` failed to compile or threw. */
|
|
118
|
+
| "transform_error";
|
|
119
|
+
/**
|
|
120
|
+
* One durable render incident — the payload for whoever can actually FIX the
|
|
121
|
+
* component. A host sink typically files it on the kind's own incident queue
|
|
122
|
+
* (matrx-frontend: `content_ir.kind_component_incident` via RPC); without a
|
|
123
|
+
* sink, a broken component seen only by ordinary users is invisible to its
|
|
124
|
+
* author forever. `data` is the kind instance value — a privacy-conscious sink
|
|
125
|
+
* reduces it to keys + types before it leaves the browser.
|
|
126
|
+
*/
|
|
127
|
+
interface DbKindComponentIncident {
|
|
128
|
+
kind: string;
|
|
129
|
+
errorType: DbKindComponentIncidentType;
|
|
130
|
+
message: string;
|
|
131
|
+
platform?: string;
|
|
132
|
+
role?: string;
|
|
133
|
+
componentKey?: string | null;
|
|
134
|
+
componentUpdatedAt?: string | null;
|
|
135
|
+
stack?: string | null;
|
|
136
|
+
data?: unknown;
|
|
137
|
+
}
|
|
138
|
+
type DbKindComponentIncidentSink = (incident: DbKindComponentIncident) => void;
|
|
139
|
+
/**
|
|
140
|
+
* THE COMPILER PORT. Matches matrx-frontend's shared allowlist compiler
|
|
141
|
+
* (`compileSlotComponent`) structurally: source in, `{ Component | error }`
|
|
142
|
+
* out, never throws. The compiled component is generically typed — the cache
|
|
143
|
+
* performs the single deliberate narrowing where the generic compiler meets
|
|
144
|
+
* the kind prop contract.
|
|
145
|
+
*/
|
|
146
|
+
type DbKindComponentCompiler = (args: {
|
|
147
|
+
code: string;
|
|
148
|
+
allowedImports: string[];
|
|
149
|
+
}) => {
|
|
150
|
+
Component: React.ComponentType<Record<string, unknown>> | null;
|
|
151
|
+
error: string | null;
|
|
152
|
+
};
|
|
153
|
+
interface DbKindComponentCachePorts {
|
|
154
|
+
/** The host's sandbox compiler — see {@link DbKindComponentCompiler}. */
|
|
155
|
+
compile: DbKindComponentCompiler;
|
|
156
|
+
/** The FULL registered allowlist a row without `config.allowed_imports` gets. */
|
|
157
|
+
defaultAllowedImports: () => string[];
|
|
158
|
+
/** Where screams land (every recovery path is loud). */
|
|
159
|
+
reportError: ContentIrErrorReporter;
|
|
160
|
+
/** Durable incident sink — optional, but a host without one blinds authors. */
|
|
161
|
+
reportIncident?: DbKindComponentIncidentSink;
|
|
162
|
+
/** Default `kind_component.platform` for this host (default "web"). */
|
|
163
|
+
platform?: string;
|
|
164
|
+
/**
|
|
165
|
+
* THE INVALIDATION REGISTRATION PORT. Called once at construction with the
|
|
166
|
+
* cache's `invalidateAll`; the host registers it under its own invalidation
|
|
167
|
+
* keys (e.g. `@ai-matrx/kit/invalidation`'s name-keyed registry, fired when
|
|
168
|
+
* an agent's component write completes). The cache owns the mechanics —
|
|
169
|
+
* what gets dropped, the scream-latch reset; the host owns only WHEN.
|
|
170
|
+
*/
|
|
171
|
+
registerInvalidation?: (invalidateAll: () => void) => void;
|
|
172
|
+
}
|
|
173
|
+
interface DbKindComponentCache {
|
|
174
|
+
/**
|
|
175
|
+
* Compile (once per resolver key per row version) a db-source resolution's
|
|
176
|
+
* `component_source` + optional `props_transform`. Synchronous — the caller
|
|
177
|
+
* (a lazily-loaded Impl) already paid the compiler chunk.
|
|
178
|
+
*/
|
|
179
|
+
getOrCompile(kind: string, resolution: ComponentResolution, platform?: string, role?: string): DbKindCompileResult;
|
|
180
|
+
/**
|
|
181
|
+
* Apply the row's transform to the kind value. Loud on throw, never fatal —
|
|
182
|
+
* the untransformed value comes back so content is never hidden.
|
|
183
|
+
*/
|
|
184
|
+
applyPropsTransform(kind: string, compiled: CompiledDbKindComponent, value: unknown): unknown;
|
|
185
|
+
/**
|
|
186
|
+
* THE CONFIG BOUNDARY. `ComponentResolution.config` is typed
|
|
187
|
+
* `Record<string, unknown>` — deliberately agnostic about a host's JSONB
|
|
188
|
+
* interior. This narrows at runtime (plain object → itself; anything else →
|
|
189
|
+
* scream + empty config) rather than asserting: a row whose `config` is not
|
|
190
|
+
* an object is a malformed `kind_component` row, and the component still
|
|
191
|
+
* renders rather than crashing.
|
|
192
|
+
*/
|
|
193
|
+
config(config: unknown, kind: string): Record<string, unknown>;
|
|
194
|
+
/** Drop a (kind, platform, role) key family (authoring surfaces call after editing a row). */
|
|
195
|
+
invalidate(kind: string, platform?: string, role?: string): void;
|
|
196
|
+
/** Drop everything — the invalidation-port payload. */
|
|
197
|
+
invalidateAll(): void;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* A routed lazy warm row owns a real body, but the per-kind cold fetch has not
|
|
201
|
+
* landed it yet. This is loading, not a compile failure.
|
|
202
|
+
*/
|
|
203
|
+
declare function isDbKindComponentBodyPending(resolution: ComponentResolution): boolean;
|
|
204
|
+
/** Build the session cache. One per host app — construct it in the wiring module. */
|
|
205
|
+
declare function createDbKindComponentCache(ports: DbKindComponentCachePorts): DbKindComponentCache;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* DbKindComponentErrorBoundary — the DEFAULT error boundary around a compiled
|
|
209
|
+
* DB kind component (absorbed from matrx-frontend per the C23 retrofit; the
|
|
210
|
+
* package previously shipped "no error capture by design", which left every
|
|
211
|
+
* host to rebuild this class).
|
|
212
|
+
*
|
|
213
|
+
* A user-authored component that throws during render must never crash the
|
|
214
|
+
* surface (chat, notes, admin preview). The boundary catches, renders the
|
|
215
|
+
* fallback (the kind's generic structured viewer — the R6 disposition, never
|
|
216
|
+
* a blank hole), and SCREAMS: a recovery firing means a real bug got past
|
|
217
|
+
* authoring.
|
|
218
|
+
*
|
|
219
|
+
* `resetSignal` (the component's `updated_at`) un-latches the boundary when a
|
|
220
|
+
* NEW component version arrives: an author who fixes a throwing component must
|
|
221
|
+
* see the fix on the next re-render, not stay stuck on the fallback until a
|
|
222
|
+
* full unmount. Without this the error state is sticky for the whole session —
|
|
223
|
+
* the "broke, then magically fixed itself minutes later" class. React error
|
|
224
|
+
* boundaries never self-clear, so a version change is the only safe signal
|
|
225
|
+
* (`hasError` alone can't tell a fixed version from the same broken one).
|
|
226
|
+
*
|
|
227
|
+
* `onCaught` is the scream seam: a host binds its structured render-error
|
|
228
|
+
* capture + durable incident filing (matrx-frontend files a `render_throw`
|
|
229
|
+
* incident so the component's author learns). Bound to nothing, the default is
|
|
230
|
+
* an honest console scream — never silence.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
interface DbKindComponentErrorBoundaryProps {
|
|
234
|
+
kind: string;
|
|
235
|
+
/** The resolved component's `updated_at`; a change means a new version. */
|
|
236
|
+
resetSignal: string | null;
|
|
237
|
+
fallback: React.ReactNode;
|
|
238
|
+
children: React.ReactNode;
|
|
239
|
+
/**
|
|
240
|
+
* Where the catch lands. A host binds structured capture + incident filing
|
|
241
|
+
* here; the default screams to the console. NOTE for hosts whose console
|
|
242
|
+
* adapter persists `console.error`: bind this (which replaces the default)
|
|
243
|
+
* rather than mirroring the capture through the console twice.
|
|
244
|
+
*/
|
|
245
|
+
onCaught?: (error: Error, info: {
|
|
246
|
+
componentStack: string | null;
|
|
247
|
+
}) => void;
|
|
248
|
+
}
|
|
249
|
+
interface State {
|
|
250
|
+
hasError: boolean;
|
|
251
|
+
/**
|
|
252
|
+
* The `resetSignal` seen on the LAST render — tracked every render (not just
|
|
253
|
+
* on error) so that at throw time it already holds the version that threw.
|
|
254
|
+
* `getDerivedStateFromError` cannot read props, so this is how the boundary
|
|
255
|
+
* knows which version to un-latch away from.
|
|
256
|
+
*/
|
|
257
|
+
signalAtLastRender: string | null;
|
|
258
|
+
}
|
|
259
|
+
declare class DbKindComponentErrorBoundary extends React.Component<DbKindComponentErrorBoundaryProps, State> {
|
|
260
|
+
constructor(props: DbKindComponentErrorBoundaryProps);
|
|
261
|
+
static getDerivedStateFromError(): Partial<State>;
|
|
262
|
+
static getDerivedStateFromProps(props: DbKindComponentErrorBoundaryProps, state: State): State | null;
|
|
263
|
+
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void;
|
|
264
|
+
render(): React.ReactNode;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export { type CompiledDbKindComponent, type DbKindActionResult, type DbKindCompileResult, type DbKindComponentCache, type DbKindComponentCachePorts, type DbKindComponentCompiler, DbKindComponentErrorBoundary, type DbKindComponentErrorBoundaryProps, type DbKindComponentIncident, type DbKindComponentIncidentSink, type DbKindComponentIncidentType, type DbKindComponentRenderProps, type KindComponentUiOptions, type ResolveKindValue, type RunDbKindAction, createDbKindComponentCache, isDbKindComponentBodyPending };
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// db-component/db-kind-component-cache.ts
|
|
4
|
+
function isDbKindComponentBodyPending(resolution) {
|
|
5
|
+
return resolution.hasComponentSource && resolution.componentSource === null;
|
|
6
|
+
}
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function createDbKindComponentCache(ports) {
|
|
11
|
+
const cache = /* @__PURE__ */ new Map();
|
|
12
|
+
const screamedKeys = /* @__PURE__ */ new Set();
|
|
13
|
+
const defaultPlatform = ports.platform ?? "web";
|
|
14
|
+
function cacheKey(kind, platform, role, updatedAt) {
|
|
15
|
+
return `${kind}${platform}${role}${updatedAt ?? ""}`;
|
|
16
|
+
}
|
|
17
|
+
function screamCompileFailure(key, kind, error, resolution, platform, role) {
|
|
18
|
+
const message = `[content-ir] DB kind component for "${kind}" failed to compile \u2014 rendering the generic structured viewer instead: ${error}`;
|
|
19
|
+
if (!screamedKeys.has(key)) {
|
|
20
|
+
screamedKeys.add(key);
|
|
21
|
+
console.error(message);
|
|
22
|
+
}
|
|
23
|
+
ports.reportError({ source: "content-ir", message, raw: { kind, error } });
|
|
24
|
+
ports.reportIncident?.({
|
|
25
|
+
kind,
|
|
26
|
+
errorType: "compile_error",
|
|
27
|
+
message,
|
|
28
|
+
platform,
|
|
29
|
+
role,
|
|
30
|
+
componentKey: resolution.componentKey,
|
|
31
|
+
componentUpdatedAt: resolution.updatedAt
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function config(value, kind) {
|
|
35
|
+
if (isPlainObject(value)) return value;
|
|
36
|
+
if (value === null || value === void 0) return {};
|
|
37
|
+
const message = `[content-ir] kind_component.config for "${kind}" is not a JSON object (${typeof value}) \u2014 rendering with an empty config.`;
|
|
38
|
+
console.error(message);
|
|
39
|
+
ports.reportError({ source: "content-ir", message, raw: { kind } });
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
function resolveAllowedImports(rowConfig) {
|
|
43
|
+
const declared = rowConfig.allowed_imports;
|
|
44
|
+
if (Array.isArray(declared) && declared.every((entry) => typeof entry === "string")) {
|
|
45
|
+
return declared;
|
|
46
|
+
}
|
|
47
|
+
return ports.defaultAllowedImports();
|
|
48
|
+
}
|
|
49
|
+
function getOrCompile(kind, resolution, platform = defaultPlatform, role = "output") {
|
|
50
|
+
const key = cacheKey(kind, platform, role, resolution.updatedAt);
|
|
51
|
+
const cached = cache.get(key);
|
|
52
|
+
if (cached) return cached;
|
|
53
|
+
const source = resolution.componentSource;
|
|
54
|
+
if (!source || !source.trim()) {
|
|
55
|
+
const result2 = {
|
|
56
|
+
ok: false,
|
|
57
|
+
error: "db-source resolution has no component_source"
|
|
58
|
+
};
|
|
59
|
+
screamCompileFailure(key, kind, result2.error, resolution, platform, role);
|
|
60
|
+
cache.set(key, result2);
|
|
61
|
+
return result2;
|
|
62
|
+
}
|
|
63
|
+
const allowedImports = resolveAllowedImports(
|
|
64
|
+
config(resolution.config, kind)
|
|
65
|
+
);
|
|
66
|
+
const { Component: Component2, error } = ports.compile({ code: source, allowedImports });
|
|
67
|
+
if (!Component2 || error) {
|
|
68
|
+
const result2 = {
|
|
69
|
+
ok: false,
|
|
70
|
+
error: error ?? "compile produced no component"
|
|
71
|
+
};
|
|
72
|
+
screamCompileFailure(key, kind, result2.error, resolution, platform, role);
|
|
73
|
+
cache.set(key, result2);
|
|
74
|
+
return result2;
|
|
75
|
+
}
|
|
76
|
+
let transform = null;
|
|
77
|
+
if (resolution.propsTransform && resolution.propsTransform.trim()) {
|
|
78
|
+
const { Component: transformFn, error: transformError } = ports.compile({
|
|
79
|
+
code: resolution.propsTransform,
|
|
80
|
+
allowedImports: []
|
|
81
|
+
});
|
|
82
|
+
if (typeof transformFn === "function" && !transformError) {
|
|
83
|
+
transform = transformFn;
|
|
84
|
+
} else {
|
|
85
|
+
const message = `[content-ir] props_transform for kind "${kind}" failed to compile \u2014 value passes through untransformed: ${transformError ?? "no function produced"}`;
|
|
86
|
+
console.error(message);
|
|
87
|
+
ports.reportError({
|
|
88
|
+
source: "content-ir",
|
|
89
|
+
message,
|
|
90
|
+
raw: { kind, error: transformError }
|
|
91
|
+
});
|
|
92
|
+
ports.reportIncident?.({
|
|
93
|
+
kind,
|
|
94
|
+
errorType: "transform_error",
|
|
95
|
+
message,
|
|
96
|
+
platform,
|
|
97
|
+
role,
|
|
98
|
+
componentKey: resolution.componentKey,
|
|
99
|
+
componentUpdatedAt: resolution.updatedAt
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const result = {
|
|
104
|
+
ok: true,
|
|
105
|
+
compiled: {
|
|
106
|
+
// The injected compiler types components as
|
|
107
|
+
// ComponentType<Record<string, unknown>>; the callsite always passes
|
|
108
|
+
// DbKindComponentRenderProps — the single deliberate narrowing where
|
|
109
|
+
// the generic compiler meets the kind contract.
|
|
110
|
+
Component: Component2,
|
|
111
|
+
transform
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
cache.set(key, result);
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
function applyPropsTransform(kind, compiled, value) {
|
|
118
|
+
if (!compiled.transform) return value;
|
|
119
|
+
try {
|
|
120
|
+
return compiled.transform(value);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const message = `[content-ir] props_transform for kind "${kind}" threw \u2014 using the untransformed value: ${error instanceof Error ? error.message : String(error)}`;
|
|
123
|
+
console.error(message);
|
|
124
|
+
ports.reportError({ source: "content-ir", message, raw: { kind } });
|
|
125
|
+
ports.reportIncident?.({
|
|
126
|
+
kind,
|
|
127
|
+
errorType: "transform_error",
|
|
128
|
+
message,
|
|
129
|
+
stack: error instanceof Error ? error.stack ?? null : null,
|
|
130
|
+
data: value
|
|
131
|
+
});
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function invalidate(kind, platform = defaultPlatform, role = "output") {
|
|
136
|
+
const prefix = `${kind}${platform}${role}`;
|
|
137
|
+
for (const key of [...cache.keys()]) {
|
|
138
|
+
if (key.startsWith(prefix)) cache.delete(key);
|
|
139
|
+
}
|
|
140
|
+
for (const key of [...screamedKeys]) {
|
|
141
|
+
if (key.startsWith(prefix)) screamedKeys.delete(key);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function invalidateAll() {
|
|
145
|
+
cache.clear();
|
|
146
|
+
screamedKeys.clear();
|
|
147
|
+
}
|
|
148
|
+
const api = {
|
|
149
|
+
getOrCompile,
|
|
150
|
+
applyPropsTransform,
|
|
151
|
+
config,
|
|
152
|
+
invalidate,
|
|
153
|
+
invalidateAll
|
|
154
|
+
};
|
|
155
|
+
ports.registerInvalidation?.(() => invalidateAll());
|
|
156
|
+
return api;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// db-component/DbKindComponentErrorBoundary.tsx
|
|
160
|
+
import * as React from "react";
|
|
161
|
+
var DbKindComponentErrorBoundary = class extends React.Component {
|
|
162
|
+
constructor(props) {
|
|
163
|
+
super(props);
|
|
164
|
+
this.state = { hasError: false, signalAtLastRender: props.resetSignal };
|
|
165
|
+
}
|
|
166
|
+
static getDerivedStateFromError() {
|
|
167
|
+
return { hasError: true };
|
|
168
|
+
}
|
|
169
|
+
static getDerivedStateFromProps(props, state) {
|
|
170
|
+
if (props.resetSignal === state.signalAtLastRender) return null;
|
|
171
|
+
return { hasError: false, signalAtLastRender: props.resetSignal };
|
|
172
|
+
}
|
|
173
|
+
componentDidCatch(error, errorInfo) {
|
|
174
|
+
const info = { componentStack: errorInfo.componentStack ?? null };
|
|
175
|
+
if (this.props.onCaught) {
|
|
176
|
+
this.props.onCaught(error, info);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
console.error(
|
|
180
|
+
`[content-ir] DB kind component for "${this.props.kind}" threw during render \u2014 showing the fallback:`,
|
|
181
|
+
error
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
render() {
|
|
185
|
+
if (this.state.hasError) return this.props.fallback;
|
|
186
|
+
return this.props.children;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
export {
|
|
190
|
+
DbKindComponentErrorBoundary,
|
|
191
|
+
createDbKindComponentCache,
|
|
192
|
+
isDbKindComponentBodyPending
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=db-component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../db-component/db-kind-component-cache.ts","../db-component/DbKindComponentErrorBoundary.tsx"],"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"],"mappings":"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","names":["result","Component"]}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { KindDefinition } from '@ai-matrx/content-ir';
|
|
3
|
+
import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-BS1ySIg-.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* THE HOST CONTRACT — everything this package refuses to decide.
|
|
7
|
+
*
|
|
8
|
+
* The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable
|
|
9
|
+
* part, let host capability enter through explicit named seams. The render
|
|
10
|
+
* layer needs four things it must never own:
|
|
11
|
+
*
|
|
12
|
+
* 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST
|
|
13
|
+
* call). The route only asks "is this kind registered, and does it carry a
|
|
14
|
+
* legacy bridge / a partial-ready opt-in".
|
|
15
|
+
* 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The
|
|
16
|
+
* package ships `ComponentResolver`, which any host can construct with its
|
|
17
|
+
* own loaders; a host with its own resolver only has to satisfy the type.
|
|
18
|
+
* 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's
|
|
19
|
+
* TYPE; turning that type into pixels is the host's dispatch table
|
|
20
|
+
* (matrx-frontend's `BlockRenderer`, the dashboard's small map). The
|
|
21
|
+
* package never imports a component library.
|
|
22
|
+
* 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.
|
|
23
|
+
*
|
|
24
|
+
* No Next.js, no Redux, no Supabase, no router, no host error capture. If a
|
|
25
|
+
* seam is missing, ADD A SEAM — never an import.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The minimum a render block must expose for the kind route to work. Hosts
|
|
30
|
+
* pass their own richer block type through; the route is generic over `T` and
|
|
31
|
+
* preserves every extra field.
|
|
32
|
+
*/
|
|
33
|
+
interface IrRoutableBlock {
|
|
34
|
+
type: string;
|
|
35
|
+
serverData?: Record<string, unknown>;
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** A routable block that also carries its source text (the raw region). */
|
|
39
|
+
interface IrRenderBlock extends IrRoutableBlock {
|
|
40
|
+
content: string;
|
|
41
|
+
language?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Where registered kinds come from. `getDefinition` is the only method the
|
|
45
|
+
* pure route needs; the rest drive the granular repaint and the warm-up tick
|
|
46
|
+
* that keeps a cold registry from being mistaken for "this kind has no
|
|
47
|
+
* component".
|
|
48
|
+
*/
|
|
49
|
+
interface KindDefinitionSource {
|
|
50
|
+
getDefinition(kind: string): KindDefinition | undefined;
|
|
51
|
+
/** Monotonic per-kind version — the repaint snapshot key. */
|
|
52
|
+
getKindVersion(kind: string): number;
|
|
53
|
+
subscribeKind(kind: string, listener: () => void): () => void;
|
|
54
|
+
/** One definition load per app session. */
|
|
55
|
+
ensureWarm(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The rendering seams. Everything here returns host UI; the package supplies
|
|
59
|
+
* the DECISIONS and the chrome-free structure around them.
|
|
60
|
+
*/
|
|
61
|
+
interface ContentIrRenderSeams {
|
|
62
|
+
/**
|
|
63
|
+
* Draw a block the kind route has already typed. This is the host's dispatch
|
|
64
|
+
* table — the single place a component key becomes a component.
|
|
65
|
+
*/
|
|
66
|
+
renderBlock(block: IrRenderBlock): ReactNode;
|
|
67
|
+
/**
|
|
68
|
+
* THE FLOOR: render ANY JSON value as a human document. Reached when a kind
|
|
69
|
+
* has no render-trusted component, when the value is not an object at all,
|
|
70
|
+
* and by the generic structured view.
|
|
71
|
+
*
|
|
72
|
+
* This is deliberately a seam and not a bundled component. Rendering a value
|
|
73
|
+
* WELL means prose through the host's markdown renderer, media through the
|
|
74
|
+
* host's file handler, and uniform arrays through the host's data table —
|
|
75
|
+
* all of which are host property. A host that has none of those can pass a
|
|
76
|
+
* `<pre>`; it will be honest, just plain.
|
|
77
|
+
*/
|
|
78
|
+
renderValue(props: StructuredValueRenderProps): ReactNode;
|
|
79
|
+
/**
|
|
80
|
+
* The "still arriving" indicator used by the provisional frame. Optional —
|
|
81
|
+
* hosts without a shimmer get plain text.
|
|
82
|
+
*/
|
|
83
|
+
renderShimmer?(text: string): ReactNode;
|
|
84
|
+
/**
|
|
85
|
+
* The honest "this shape has no custom component yet" notice. Optional: the
|
|
86
|
+
* package draws a plain amber line when a host supplies nothing, and a host
|
|
87
|
+
* with an icon set or its own callout component supplies that instead. It is
|
|
88
|
+
* a seam and not a bundled component because this package depends on no icon
|
|
89
|
+
* library.
|
|
90
|
+
*/
|
|
91
|
+
renderNotice?(text: string): ReactNode;
|
|
92
|
+
}
|
|
93
|
+
interface StructuredValueRenderProps {
|
|
94
|
+
value: unknown;
|
|
95
|
+
/** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */
|
|
96
|
+
kind?: string;
|
|
97
|
+
/** Why this shape has no custom view, in human words. */
|
|
98
|
+
note?: string;
|
|
99
|
+
/** Show the "what this is / raw data" footer. Default true. */
|
|
100
|
+
footer?: boolean;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* One host, wired once, read by every component in this package through
|
|
104
|
+
* {@link ContentIrRenderProvider}.
|
|
105
|
+
*/
|
|
106
|
+
interface ContentIrHost extends ContentIrRenderSeams {
|
|
107
|
+
kinds: KindDefinitionSource;
|
|
108
|
+
components: ComponentResolutionSource;
|
|
109
|
+
reportError: ContentIrErrorReporter;
|
|
110
|
+
/**
|
|
111
|
+
* The `kind_component.platform` this host resolves as. "web" for every
|
|
112
|
+
* browser UI; the column already models "react-native" and friends, and a
|
|
113
|
+
* host that lies here renders the wrong component everywhere.
|
|
114
|
+
*/
|
|
115
|
+
platform: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The pure route's dependencies — a strict subset of {@link ContentIrHost},
|
|
119
|
+
* because `applyIrKindRoute` is called OUTSIDE React (reducers, stream
|
|
120
|
+
* accumulators, tests) where there is no provider to read.
|
|
121
|
+
*/
|
|
122
|
+
interface KindRouteEnv {
|
|
123
|
+
kinds: Pick<KindDefinitionSource, "getDefinition">;
|
|
124
|
+
components: Pick<ComponentResolutionSource, "resolve">;
|
|
125
|
+
reportError: ContentIrErrorReporter;
|
|
126
|
+
platform: string;
|
|
127
|
+
}
|
|
128
|
+
/** Narrow a host (or anything host-shaped) to what the pure route needs. */
|
|
129
|
+
declare function routeEnvOf(host: ContentIrHost): KindRouteEnv;
|
|
130
|
+
|
|
131
|
+
export { type ContentIrHost as C, type IrRenderBlock as I, type KindRouteEnv as K, type StructuredValueRenderProps as S, type ContentIrRenderSeams as a, type IrRoutableBlock as b, type KindDefinitionSource as c, routeEnvOf as r };
|