@ai-matrx/content-ir-react 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,769 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var contentIr = require('@ai-matrx/content-ir');
6
+
7
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
+
9
+ var React__default = /*#__PURE__*/_interopDefault(React);
10
+
11
+ // host/error-report.ts
12
+ var consoleErrorReporter = (report) => {
13
+ console.error(`[content-ir] ${report.message}`, report.raw ?? "");
14
+ };
15
+
16
+ // host/host-types.ts
17
+ function routeEnvOf(host) {
18
+ return {
19
+ kinds: host.kinds,
20
+ components: host.components,
21
+ reportError: host.reportError,
22
+ platform: host.platform
23
+ };
24
+ }
25
+ var ContentIrHostContext = React.createContext(null);
26
+ function ContentIrRenderProvider({
27
+ host,
28
+ children
29
+ }) {
30
+ return /* @__PURE__ */ jsxRuntime.jsx(ContentIrHostContext.Provider, { value: host, children });
31
+ }
32
+ function useContentIrHostOrNull() {
33
+ return React.useContext(ContentIrHostContext);
34
+ }
35
+ function useContentIrHost() {
36
+ const host = useContentIrHostOrNull();
37
+ if (!host) {
38
+ throw new Error(
39
+ "[content-ir-react] No ContentIrRenderProvider above this component. Mount one at the app root with your host adapter (kind definitions, component resolver, renderBlock, renderValue, reportError)."
40
+ );
41
+ }
42
+ return host;
43
+ }
44
+ function useKindRouteEnv() {
45
+ const host = useContentIrHost();
46
+ return React.useMemo(() => routeEnvOf(host), [host]);
47
+ }
48
+
49
+ // resolver/component-resolver.ts
50
+ var KEY_SEPARATOR = String.fromCharCode(1);
51
+ function keyOf(kind, platform, role) {
52
+ return `${kind}${KEY_SEPARATOR}${platform}${KEY_SEPARATOR}${role}`;
53
+ }
54
+ function describe(error) {
55
+ return error instanceof Error ? error.message : String(error);
56
+ }
57
+ function errorFields(error) {
58
+ if (!(error instanceof Error)) return {};
59
+ return {
60
+ name: error.name,
61
+ ...error.stack === void 0 ? {} : { stack: error.stack }
62
+ };
63
+ }
64
+ var ComponentResolver = class {
65
+ compiled = null;
66
+ db = /* @__PURE__ */ new Map();
67
+ warmPromise = null;
68
+ warmFailureLogged = false;
69
+ /**
70
+ * When the last successful wholesale refresh landed, or null for "never".
71
+ * Explicitly nullable rather than 0: with a host-supplied clock that starts
72
+ * near zero, a `0` sentinel makes the FIRST refresh look rate-limited and
73
+ * silently skip.
74
+ */
75
+ lastRefreshAt = null;
76
+ refreshPromise = null;
77
+ listeners = /* @__PURE__ */ new Set();
78
+ /**
79
+ * Cold single-kind fetch dedupe (streaming eager path). In-flight is keyed
80
+ * by (kind, platform) — the fetch unit; misses are keyed by (kind, platform,
81
+ * role) so a miss on web/output never suppresses other roles, and CLEARED on
82
+ * every wholesale refresh (a component created mid-session becomes eagerly
83
+ * fetchable again — misses are cheap to re-verify).
84
+ */
85
+ coldInFlight = /* @__PURE__ */ new Set();
86
+ coldMisses = /* @__PURE__ */ new Set();
87
+ /** Monotonic db-tier version — the repaint hook's snapshot key. */
88
+ version = 0;
89
+ /** Per-kind versions + listeners (granular repaint) + wholesale epoch. */
90
+ kindVersions = /* @__PURE__ */ new Map();
91
+ kindListeners = /* @__PURE__ */ new Map();
92
+ epoch = 0;
93
+ reportError;
94
+ now;
95
+ options;
96
+ // Explicit field, not a parameter property: consumers compile this source
97
+ // directly, and a strict host (the dashboard) sets `erasableSyntaxOnly`,
98
+ // under which parameter properties are a hard error.
99
+ constructor(options = {}) {
100
+ this.options = options;
101
+ this.reportError = options.reportError ?? consoleErrorReporter;
102
+ this.now = options.now ?? (() => Date.now());
103
+ }
104
+ compiledMap() {
105
+ if (!this.compiled) {
106
+ this.compiled = /* @__PURE__ */ new Map();
107
+ for (const entry of this.options.compiledEntries?.() ?? []) {
108
+ this.compiled.set(keyOf(entry.kind, entry.platform, entry.role), entry);
109
+ }
110
+ }
111
+ return this.compiled;
112
+ }
113
+ /**
114
+ * Synchronous resolve — the render seam's per-block call. DB override first
115
+ * (once warm), compiled floor second, null for unknown.
116
+ */
117
+ resolve(kind, platform, role) {
118
+ const key = keyOf(kind, platform, role);
119
+ const dbRow = this.db.get(key);
120
+ if (dbRow) {
121
+ return {
122
+ componentKey: dbRow.componentKey,
123
+ source: dbRow.source,
124
+ config: dbRow.config,
125
+ isActive: dbRow.isActive,
126
+ resolvedBy: "db",
127
+ componentSource: dbRow.componentSource,
128
+ propsTransform: dbRow.propsTransform,
129
+ pinnedKindVersion: dbRow.pinnedKindVersion,
130
+ updatedAt: dbRow.updatedAt,
131
+ createdBy: dbRow.createdBy
132
+ };
133
+ }
134
+ const compiledEntry = this.compiledMap().get(key);
135
+ if (compiledEntry) {
136
+ return {
137
+ componentKey: compiledEntry.componentKey,
138
+ source: compiledEntry.source,
139
+ config: compiledEntry.config,
140
+ isActive: true,
141
+ // trusted at bootstrap (R6)
142
+ resolvedBy: "compiled",
143
+ componentSource: null,
144
+ propsTransform: null,
145
+ pinnedKindVersion: null,
146
+ updatedAt: null,
147
+ createdBy: null
148
+ };
149
+ }
150
+ return null;
151
+ }
152
+ /** R6 floor check: compiled-bootstrap membership = always render-trusted. */
153
+ hasCompiled(kind, platform, role) {
154
+ return this.compiledMap().has(keyOf(kind, platform, role));
155
+ }
156
+ /**
157
+ * Pure ingest — the warm landing point and the unit-test seam. First row per
158
+ * key wins: rows arrive is_default-first / sort_order-asc from the source.
159
+ */
160
+ ingestDbRows(rows) {
161
+ const changedKinds = /* @__PURE__ */ new Set();
162
+ for (const row of rows) {
163
+ const key = keyOf(row.kind, row.platform, row.role);
164
+ if (!this.db.has(key)) {
165
+ this.db.set(key, row);
166
+ changedKinds.add(row.kind);
167
+ }
168
+ }
169
+ if (changedKinds.size > 0) {
170
+ for (const kind of changedKinds) this.bumpKind(kind);
171
+ this.notifyChanged();
172
+ }
173
+ }
174
+ /**
175
+ * Refresh landing point: REPLACE the db tier wholesale (same
176
+ * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
177
+ * and is_active flips all take effect. Always notifies.
178
+ */
179
+ replaceDbRows(rows) {
180
+ this.db.clear();
181
+ this.coldMisses.clear();
182
+ for (const row of rows) {
183
+ const key = keyOf(row.kind, row.platform, row.role);
184
+ if (!this.db.has(key)) this.db.set(key, row);
185
+ }
186
+ this.epoch += 1;
187
+ for (const set of this.kindListeners.values()) {
188
+ for (const listener of set) listener();
189
+ }
190
+ this.notifyChanged();
191
+ }
192
+ getVersion() {
193
+ return this.version;
194
+ }
195
+ getKindVersion(kind) {
196
+ return this.epoch + (this.kindVersions.get(kind) ?? 0);
197
+ }
198
+ subscribeKind(kind, listener) {
199
+ const set = this.kindListeners.get(kind) ?? /* @__PURE__ */ new Set();
200
+ set.add(listener);
201
+ this.kindListeners.set(kind, set);
202
+ return () => {
203
+ set.delete(listener);
204
+ if (set.size === 0) this.kindListeners.delete(kind);
205
+ };
206
+ }
207
+ subscribe(listener) {
208
+ this.listeners.add(listener);
209
+ return () => {
210
+ this.listeners.delete(listener);
211
+ };
212
+ }
213
+ bumpKind(kind) {
214
+ this.kindVersions.set(kind, (this.kindVersions.get(kind) ?? 0) + 1);
215
+ const listeners = this.kindListeners.get(kind);
216
+ if (listeners) for (const listener of listeners) listener();
217
+ }
218
+ notifyChanged() {
219
+ this.version += 1;
220
+ for (const listener of this.listeners) listener();
221
+ }
222
+ /**
223
+ * The eager lightweight single-kind fetch (streaming path): the moment a
224
+ * cloud kind is identified mid-stream, pull ONLY that kind's resolver rows
225
+ * and ingest them so {@link resolve} can answer before — or shortly after —
226
+ * the region completes. Deduped in-flight and by known-miss. Fire-and-forget;
227
+ * failures are loud (the warm list remains the backstop).
228
+ */
229
+ requestComponent(kind, platform, role) {
230
+ const loadForKind = this.options.loadForKind;
231
+ if (!loadForKind) return;
232
+ if (this.resolve(kind, platform, role)) return;
233
+ const missKey = keyOf(kind, platform, role);
234
+ const flightKey = `${kind}${KEY_SEPARATOR}${platform}`;
235
+ if (this.coldInFlight.has(flightKey) || this.coldMisses.has(missKey)) {
236
+ return;
237
+ }
238
+ this.coldInFlight.add(flightKey);
239
+ void (async () => {
240
+ try {
241
+ this.ingestDbRows(await loadForKind(kind, platform));
242
+ if (!this.resolve(kind, platform, role)) this.coldMisses.add(missKey);
243
+ } catch (error) {
244
+ this.reportError({
245
+ source: "content-ir",
246
+ message: `component-resolver cold fetch failed for "${kind}": ${describe(error)}`,
247
+ ...errorFields(error),
248
+ raw: error
249
+ });
250
+ } finally {
251
+ this.coldInFlight.delete(flightKey);
252
+ }
253
+ })();
254
+ }
255
+ /**
256
+ * Refresh-on-view: re-fetch the warm list and REPLACE the db tier, so an
257
+ * edited `source='db'` component (its `updated_at` bump re-keys the host's
258
+ * compile cache) renders fresh on the next view. Deduped in-flight and
259
+ * rate-limited by `maxAgeMs` (default 10s) — mounting several previews costs
260
+ * one fetch. Server-side edits do NOT push to open clients; the contract is
261
+ * refresh-on-view via this call.
262
+ */
263
+ refresh(maxAgeMs = 1e4) {
264
+ const loadAll = this.options.loadAll;
265
+ if (!loadAll) return Promise.resolve();
266
+ if (this.refreshPromise) return this.refreshPromise;
267
+ if (this.lastRefreshAt !== null && this.now() - this.lastRefreshAt < maxAgeMs) {
268
+ return Promise.resolve();
269
+ }
270
+ this.coldMisses.clear();
271
+ this.refreshPromise = loadAll().then((rows) => {
272
+ this.lastRefreshAt = this.now();
273
+ this.replaceDbRows(rows);
274
+ }).catch((error) => {
275
+ this.reportError({
276
+ source: "content-ir",
277
+ message: `component-resolver refresh failed (current resolver tier still serving): ${describe(error)}`,
278
+ ...errorFields(error),
279
+ raw: error
280
+ });
281
+ }).finally(() => {
282
+ this.refreshPromise = null;
283
+ });
284
+ return this.refreshPromise;
285
+ }
286
+ /** One list fetch per app session; failed loads retry on the next call. */
287
+ ensureWarm() {
288
+ const loadAll = this.options.loadAll;
289
+ if (!loadAll) return Promise.resolve();
290
+ if (!this.warmPromise) {
291
+ this.warmPromise = loadAll().then((rows) => {
292
+ this.ingestDbRows(rows);
293
+ }).catch((error) => {
294
+ const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;
295
+ if (!this.warmFailureLogged) {
296
+ this.warmFailureLogged = true;
297
+ console.error(`[content-ir] ${message}`);
298
+ }
299
+ this.reportError({
300
+ source: "content-ir",
301
+ message,
302
+ ...errorFields(error),
303
+ raw: error
304
+ });
305
+ this.warmPromise = null;
306
+ });
307
+ }
308
+ return this.warmPromise;
309
+ }
310
+ };
311
+ var IR_ROUTE_KEY = "__ir_route";
312
+ var GENERIC_STRUCTURED_COMPONENT_KEY = "generic_structured";
313
+ var DB_KIND_COMPONENT_KEY = "db_kind_component";
314
+ function isDbSourceResolution(resolution) {
315
+ return resolution !== null && resolution.resolvedBy === "db" && resolution.source === "db" && resolution.isActive;
316
+ }
317
+ var reportedSourcelessDbRows = /* @__PURE__ */ new Set();
318
+ function reportDbRowWithoutSource(kind, env) {
319
+ const message = `[content-ir] kind_component for "${kind}" declares source='db' + is_active but has NO component_source \u2014 data defect; falling through to bundled rendering.`;
320
+ if (!reportedSourcelessDbRows.has(kind)) {
321
+ reportedSourcelessDbRows.add(kind);
322
+ console.error(message);
323
+ }
324
+ env.reportError({ source: "content-ir", message, raw: { kind } });
325
+ }
326
+ function resetSourcelessDbRowReports() {
327
+ reportedSourcelessDbRows.clear();
328
+ }
329
+ function routeToDbComponent(block, kind, resolution, env) {
330
+ if (!isDbSourceResolution(resolution)) return null;
331
+ if (!resolution.componentSource || !resolution.componentSource.trim()) {
332
+ reportDbRowWithoutSource(kind, env);
333
+ return null;
334
+ }
335
+ if (block.type === DB_KIND_COMPONENT_KEY) return block;
336
+ return {
337
+ ...block,
338
+ type: DB_KIND_COMPONENT_KEY,
339
+ // The compiled/sandboxed component reads the envelope, never the raw
340
+ // region's annotation serverData (same poison rule as bridged kinds).
341
+ serverData: void 0,
342
+ metadata: withRouteMarker(block.metadata, resolution)
343
+ };
344
+ }
345
+ function isRecord(value) {
346
+ return typeof value === "object" && value !== null && !Array.isArray(value);
347
+ }
348
+ function isRouteMarker(value) {
349
+ return isRecord(value) && typeof value.by === "string" && typeof value.key === "string";
350
+ }
351
+ function readIrRouteMarker(metadata) {
352
+ const candidate = metadata?.[IR_ROUTE_KEY];
353
+ return isRouteMarker(candidate) ? candidate : null;
354
+ }
355
+ function withRouteMarker(metadata, resolution) {
356
+ return {
357
+ ...metadata,
358
+ [IR_ROUTE_KEY]: {
359
+ by: resolution.resolvedBy,
360
+ key: resolution.componentKey
361
+ }
362
+ };
363
+ }
364
+ function routeToGeneric(block, reason) {
365
+ if (block.type === GENERIC_STRUCTURED_COMPONENT_KEY) return block;
366
+ return {
367
+ ...block,
368
+ type: GENERIC_STRUCTURED_COMPONENT_KEY,
369
+ serverData: void 0,
370
+ metadata: {
371
+ ...block.metadata,
372
+ [IR_ROUTE_KEY]: {
373
+ by: "generic",
374
+ key: GENERIC_STRUCTURED_COMPONENT_KEY,
375
+ unverified: true,
376
+ reason
377
+ }
378
+ }
379
+ };
380
+ }
381
+ function applyIrKindRoute(block, env, options) {
382
+ if (options?.ownedTypes?.includes(block.type)) return block;
383
+ const envelope = contentIr.readEnvelope(block.metadata);
384
+ if (!envelope) return block;
385
+ const kind = envelope.root.kind;
386
+ if (!kind) return block;
387
+ if (envelope.root.kindState === "pending_schema") return block;
388
+ const def = env.kinds.getDefinition(kind);
389
+ const resolution = env.components.resolve(kind, env.platform, "output");
390
+ const dbRouted = routeToDbComponent(block, kind, resolution, env);
391
+ if (dbRouted) return dbRouted;
392
+ if (def?.legacyBlockType) {
393
+ if (block.type === def.legacyBlockType && block.serverData) return block;
394
+ const serverData = def.toLegacyServerData?.(envelope);
395
+ if (block.type === def.legacyBlockType && serverData === void 0) {
396
+ return block;
397
+ }
398
+ return {
399
+ ...block,
400
+ type: def.legacyBlockType,
401
+ serverData,
402
+ ...resolution ? { metadata: withRouteMarker(block.metadata, resolution) } : null
403
+ };
404
+ }
405
+ if (resolution?.isActive) {
406
+ if (block.type === resolution.componentKey) return block;
407
+ return {
408
+ ...block,
409
+ type: resolution.componentKey,
410
+ // No compiled bridge exists — the routed component parses `content`
411
+ // itself; the raw region's annotation serverData is CLEARED.
412
+ serverData: void 0,
413
+ metadata: withRouteMarker(block.metadata, resolution)
414
+ };
415
+ }
416
+ if (def) {
417
+ return routeToGeneric(block, resolution ? "inactive" : "no-component");
418
+ }
419
+ return block;
420
+ }
421
+ function kindServerDataFromStoredValue(value, env) {
422
+ if (!isRecord(value)) return null;
423
+ const kind = contentIr.readObjectKind(value);
424
+ if (!kind) return null;
425
+ const def = env.kinds.getDefinition(kind);
426
+ if (!def?.toLegacyServerData) return null;
427
+ return def.toLegacyServerData(contentIr.envelopeFromCompleteValue(value, kind)) ?? null;
428
+ }
429
+ var IR_PROVISIONAL_KEY = "__ir_provisional";
430
+ function isProvisionalBlock(metadata) {
431
+ return metadata?.[IR_PROVISIONAL_KEY] === true;
432
+ }
433
+ function envelopeFromPartialKind(event) {
434
+ return {
435
+ v: contentIr.IR_VERSION,
436
+ // The partial channel is only ever produced by the Python detector; the
437
+ // envelope's `engine` union has no third member and inventing one would
438
+ // break every existing reader.
439
+ engine: "py-block-detector",
440
+ fingerprint: event.fingerprint,
441
+ root: event.root
442
+ };
443
+ }
444
+ var partialUnsafeKinds = /* @__PURE__ */ new Set();
445
+ function markKindPartialUnsafe(kind) {
446
+ partialUnsafeKinds.add(kind);
447
+ }
448
+ function resetPartialUnsafeKinds() {
449
+ partialUnsafeKinds.clear();
450
+ }
451
+ function isPartialReadyKind(kind, env) {
452
+ if (!kind || partialUnsafeKinds.has(kind)) return false;
453
+ return env.kinds.getDefinition(kind)?.partialReady === true;
454
+ }
455
+ function resolveAnnouncedKindLoading(block, options) {
456
+ if (options?.streamActive === false) return null;
457
+ const event = contentIr.readPartialKindEvent(block.metadata);
458
+ if (!contentIr.isProvisionalKind(event)) return null;
459
+ const kind = event.root.kind;
460
+ if (!kind) return null;
461
+ const verified = contentIr.readEnvelope(block.metadata);
462
+ if (verified && verified.root.status === "complete") return null;
463
+ return { kind, envelope: envelopeFromPartialKind(event) };
464
+ }
465
+ function resolveProvisionalKindRender(block, env, options) {
466
+ if (options?.streamActive === false) return null;
467
+ const event = contentIr.readPartialKindEvent(block.metadata);
468
+ if (!contentIr.isProvisionalKind(event)) return null;
469
+ const kind = event.root.kind;
470
+ if (!isPartialReadyKind(kind, env)) return null;
471
+ const verified = contentIr.readEnvelope(block.metadata);
472
+ if (verified && verified.root.status === "complete") return null;
473
+ const { [contentIr.IR_PARTIAL_KEY]: _partial, ...rest } = block.metadata ?? {};
474
+ const envelope = envelopeFromPartialKind(event);
475
+ const provisionalBlock = {
476
+ ...block,
477
+ // The raw region annotation (`{ language: "json" }`) is not kind data —
478
+ // same poison rule the verified route follows.
479
+ serverData: void 0,
480
+ metadata: {
481
+ ...rest,
482
+ [contentIr.IR_ENVELOPE_KEY]: envelope,
483
+ [IR_PROVISIONAL_KEY]: true
484
+ }
485
+ };
486
+ const routed = applyIrKindRoute(provisionalBlock, env, options);
487
+ if (routed === provisionalBlock) return null;
488
+ if (env.kinds.getDefinition(kind)?.toLegacyServerData && !routed.serverData) {
489
+ return null;
490
+ }
491
+ return { block: routed, kind, seq: event.seq, envelope };
492
+ }
493
+ var noopSubscribe = () => () => {
494
+ };
495
+ var zero = () => 0;
496
+ function useContentIrKindVersion(kind, sources) {
497
+ const host = useContentIrHostOrNull();
498
+ const resolved = sources ?? host;
499
+ if (kind && !resolved) {
500
+ throw new Error(
501
+ "[content-ir-react] useContentIrKindVersion needs either a ContentIrRenderProvider above it or explicit `sources`."
502
+ );
503
+ }
504
+ const subscribe = React.useCallback(
505
+ (onStoreChange) => {
506
+ if (!kind || !resolved) return noopSubscribe();
507
+ const unsubKinds = resolved.kinds.subscribeKind(kind, onStoreChange);
508
+ const unsubComponents = resolved.components.subscribeKind(
509
+ kind,
510
+ onStoreChange
511
+ );
512
+ return () => {
513
+ unsubKinds();
514
+ unsubComponents();
515
+ };
516
+ },
517
+ [resolved, kind]
518
+ );
519
+ const getSnapshot = React.useCallback(
520
+ () => kind && resolved ? (
521
+ // Both counters are monotonic, so the sum is monotonic — a change in
522
+ // either registry produces a new snapshot value for this kind.
523
+ resolved.kinds.getKindVersion(kind) + resolved.components.getKindVersion(kind)
524
+ ) : 0,
525
+ [resolved, kind]
526
+ );
527
+ return React.useSyncExternalStore(subscribe, getSnapshot, zero);
528
+ }
529
+ function isRecordValue(value) {
530
+ return typeof value === "object" && value !== null && !Array.isArray(value);
531
+ }
532
+ function kindIsRoutable(kind, host) {
533
+ if (host.kinds.getDefinition(kind)?.legacyBlockType) return true;
534
+ return Boolean(
535
+ host.components.resolve(kind, host.platform, "output")?.isActive
536
+ );
537
+ }
538
+ function KindInstanceRender({
539
+ kind,
540
+ value,
541
+ showRoutingNote = true,
542
+ unroutableFallback,
543
+ variant = "card",
544
+ className
545
+ }) {
546
+ const host = useContentIrHost();
547
+ const [routingStatus, setRoutingStatus] = React.useState(
548
+ () => kindIsRoutable(kind, host) ? "routable" : "checking"
549
+ );
550
+ React.useEffect(() => {
551
+ let cancelled = false;
552
+ let warmed = false;
553
+ const syncRoutingStatus = () => {
554
+ if (cancelled) return;
555
+ setRoutingStatus(
556
+ kindIsRoutable(kind, host) ? "routable" : warmed ? "unroutable" : "checking"
557
+ );
558
+ };
559
+ const unsubscribe = host.components.subscribe(syncRoutingStatus);
560
+ syncRoutingStatus();
561
+ host.components.requestComponent(kind, host.platform, "output");
562
+ const wasAlreadyWarm = host.components.getVersion() > 0;
563
+ void Promise.allSettled([
564
+ host.kinds.ensureWarm(),
565
+ host.components.ensureWarm()
566
+ ]).then(async () => {
567
+ warmed = true;
568
+ syncRoutingStatus();
569
+ if (wasAlreadyWarm) await host.components.refresh();
570
+ syncRoutingStatus();
571
+ });
572
+ return () => {
573
+ cancelled = true;
574
+ unsubscribe();
575
+ };
576
+ }, [host, kind]);
577
+ const block = isRecordValue(value) ? {
578
+ type: "code",
579
+ content: JSON.stringify(value, null, 2),
580
+ language: "json",
581
+ metadata: {
582
+ [contentIr.IR_ENVELOPE_KEY]: contentIr.envelopeFromCompleteValue(value, kind)
583
+ }
584
+ } : null;
585
+ const frameClass = variant === "bare" ? void 0 : "rounded-md border border-border bg-card p-3";
586
+ if (routingStatus === "unroutable" && unroutableFallback !== void 0) {
587
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: unroutableFallback });
588
+ }
589
+ const onTheFloor = block === null || routingStatus === "unroutable";
590
+ const notice = "This shape has no custom component yet, so it renders through the universal viewer \u2014 exactly what production shows today.";
591
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: className ? `space-y-3 ${className}` : "space-y-3", children: [
592
+ showRoutingNote && routingStatus === "unroutable" ? host.renderNotice?.(notice) ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-amber-500/30 bg-amber-500/5 px-3 py-2 text-xs text-amber-800 dark:text-amber-200", children: notice }) : null,
593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: frameClass, children: onTheFloor ? host.renderValue({ value, kind }) : host.renderBlock(block) })
594
+ ] });
595
+ }
596
+ var ProvisionalKindBoundaryInner = class extends React__default.default.Component {
597
+ state = { failed: false };
598
+ static getDerivedStateFromError() {
599
+ return { failed: true };
600
+ }
601
+ componentDidCatch(error, info) {
602
+ const { kind, reportError } = this.props;
603
+ markKindPartialUnsafe(kind);
604
+ reportError({
605
+ source: "content-ir",
606
+ message: `kind "${kind}" declares partialReady but its component threw on a provisional value \u2014 provisional rendering disabled for this kind (falling back to its loading skeleton). Fix the component or drop partialReady.`,
607
+ name: error.name,
608
+ ...error.stack === void 0 ? {} : { stack: error.stack },
609
+ relation: "partial-kind",
610
+ raw: { kind, componentStack: info.componentStack }
611
+ });
612
+ }
613
+ render() {
614
+ if (this.state.failed) return this.props.fallback;
615
+ return this.props.children;
616
+ }
617
+ };
618
+ function ProvisionalKindBoundary(props) {
619
+ const host = useContentIrHost();
620
+ return /* @__PURE__ */ jsxRuntime.jsx(ProvisionalKindBoundaryInner, { ...props, reportError: host.reportError });
621
+ }
622
+ function ProvisionalKindFrame({ children }) {
623
+ const host = useContentIrHost();
624
+ const label = "Still arriving";
625
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", "aria-busy": "true", children: [
626
+ children,
627
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute -top-2 right-4 z-10 select-none rounded-full bg-background px-1.5 leading-4", children: host.renderShimmer?.(label) ?? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: label }) })
628
+ ] });
629
+ }
630
+ function readStructuredValue(content, metadata) {
631
+ const envelope = contentIr.readEnvelope(metadata);
632
+ if (envelope) {
633
+ return { value: contentIr.reconstructRegionValue(envelope), recovered: true };
634
+ }
635
+ try {
636
+ return { value: JSON.parse(content), recovered: true };
637
+ } catch {
638
+ return { value: null, recovered: false };
639
+ }
640
+ }
641
+ function GenericStructuredView({
642
+ content,
643
+ metadata,
644
+ streamingIndicator,
645
+ className
646
+ }) {
647
+ const host = useContentIrHost();
648
+ const envelope = contentIr.readEnvelope(metadata);
649
+ const status = envelope?.root.status ?? "complete";
650
+ const { value, recovered } = readStructuredValue(content, metadata);
651
+ const kind = envelope?.root.kind ?? (typeof value === "object" && value !== null && !Array.isArray(value) ? contentIr.readObjectKind(value) : null) ?? "";
652
+ const marker = readIrRouteMarker(metadata);
653
+ const note = marker?.reason === "inactive" ? "a custom view is registered but held inactive" : "no custom view yet";
654
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: className ? `my-2 min-w-0 ${className}` : "my-2 min-w-0", children: [
655
+ status === "streaming" ? streamingIndicator ?? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 text-xs text-muted-foreground", children: "Still arriving\u2026" }) : null,
656
+ recovered ? host.renderValue({ value, ...kind ? { kind } : {}, note }) : (
657
+ // Zero-data-loss backstop: the region never parsed, so show the source
658
+ // verbatim rather than swallowing it.
659
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-96 overflow-auto font-mono text-xs leading-relaxed text-muted-foreground", children: content })
660
+ )
661
+ ] });
662
+ }
663
+ function isRecord2(value) {
664
+ return typeof value === "object" && value !== null && !Array.isArray(value);
665
+ }
666
+ function readWrapperFrom(serverData, read) {
667
+ if (!isRecord2(serverData)) return null;
668
+ return read(serverData.wrapper);
669
+ }
670
+ function DelegatedOutput({
671
+ output,
672
+ declaredKind,
673
+ fallback,
674
+ emptyLabel = "This step ran, and handed its result to the next one."
675
+ }) {
676
+ const host = useContentIrHost();
677
+ if (output === null || output === void 0) {
678
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: emptyLabel });
679
+ }
680
+ if (!isRecord2(output)) {
681
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: host.renderValue({ value: output }) });
682
+ }
683
+ const kind = contentIr.readObjectKind(output) ?? declaredKind;
684
+ if (!kind) {
685
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback?.(output) ?? host.renderValue({ value: output }) });
686
+ }
687
+ return /* @__PURE__ */ jsxRuntime.jsx(
688
+ KindInstanceRender,
689
+ {
690
+ kind,
691
+ value: output,
692
+ showRoutingNote: false,
693
+ variant: "bare",
694
+ ...fallback === void 0 ? {} : { unroutableFallback: fallback(output) }
695
+ }
696
+ );
697
+ }
698
+ function NodeOutcomeView({ serverData, fallback }) {
699
+ const wrapper = readWrapperFrom(
700
+ serverData,
701
+ contentIr.readNodeOutcomeValue
702
+ );
703
+ if (!wrapper) return null;
704
+ return /* @__PURE__ */ jsxRuntime.jsx(
705
+ DelegatedOutput,
706
+ {
707
+ output: wrapper.output,
708
+ declaredKind: wrapper.output_kind,
709
+ ...fallback === void 0 ? {} : { fallback }
710
+ }
711
+ );
712
+ }
713
+ function RunResultView({ serverData, fallback }) {
714
+ const wrapper = readWrapperFrom(
715
+ serverData,
716
+ contentIr.readRunResultValue
717
+ );
718
+ if (!wrapper) return null;
719
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: wrapper.outputs.length > 0 ? wrapper.outputs.map((outcome) => /* @__PURE__ */ jsxRuntime.jsx(
720
+ NodeOutcomeView,
721
+ {
722
+ serverData: { wrapper: outcome },
723
+ ...fallback === void 0 ? {} : { fallback }
724
+ },
725
+ `${outcome.node_id}:${outcome.attempt}`
726
+ )) : /* @__PURE__ */ jsxRuntime.jsx(
727
+ DelegatedOutput,
728
+ {
729
+ output: wrapper.output,
730
+ declaredKind: wrapper.output_kind,
731
+ ...fallback === void 0 ? {} : { fallback }
732
+ }
733
+ ) });
734
+ }
735
+
736
+ exports.ComponentResolver = ComponentResolver;
737
+ exports.ContentIrRenderProvider = ContentIrRenderProvider;
738
+ exports.DB_KIND_COMPONENT_KEY = DB_KIND_COMPONENT_KEY;
739
+ exports.DelegatedOutput = DelegatedOutput;
740
+ exports.GENERIC_STRUCTURED_COMPONENT_KEY = GENERIC_STRUCTURED_COMPONENT_KEY;
741
+ exports.GenericStructuredView = GenericStructuredView;
742
+ exports.IR_PROVISIONAL_KEY = IR_PROVISIONAL_KEY;
743
+ exports.IR_ROUTE_KEY = IR_ROUTE_KEY;
744
+ exports.KindInstanceRender = KindInstanceRender;
745
+ exports.NodeOutcomeView = NodeOutcomeView;
746
+ exports.ProvisionalKindBoundary = ProvisionalKindBoundary;
747
+ exports.ProvisionalKindFrame = ProvisionalKindFrame;
748
+ exports.RunResultView = RunResultView;
749
+ exports.applyIrKindRoute = applyIrKindRoute;
750
+ exports.consoleErrorReporter = consoleErrorReporter;
751
+ exports.envelopeFromPartialKind = envelopeFromPartialKind;
752
+ exports.isPartialReadyKind = isPartialReadyKind;
753
+ exports.isProvisionalBlock = isProvisionalBlock;
754
+ exports.isRecordValue = isRecordValue;
755
+ exports.kindIsRoutable = kindIsRoutable;
756
+ exports.kindServerDataFromStoredValue = kindServerDataFromStoredValue;
757
+ exports.markKindPartialUnsafe = markKindPartialUnsafe;
758
+ exports.readIrRouteMarker = readIrRouteMarker;
759
+ exports.resetPartialUnsafeKinds = resetPartialUnsafeKinds;
760
+ exports.resetSourcelessDbRowReports = resetSourcelessDbRowReports;
761
+ exports.resolveAnnouncedKindLoading = resolveAnnouncedKindLoading;
762
+ exports.resolveProvisionalKindRender = resolveProvisionalKindRender;
763
+ exports.routeEnvOf = routeEnvOf;
764
+ exports.useContentIrHost = useContentIrHost;
765
+ exports.useContentIrHostOrNull = useContentIrHostOrNull;
766
+ exports.useContentIrKindVersion = useContentIrKindVersion;
767
+ exports.useKindRouteEnv = useKindRouteEnv;
768
+ //# sourceMappingURL=index.cjs.map
769
+ //# sourceMappingURL=index.cjs.map