@ai-matrx/content-ir-react 0.8.0 → 0.9.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 +51 -0
- package/dist/{component-resolution.types-BS1ySIg-.d.cts → component-resolution.types-C1MzCf2s.d.cts} +13 -0
- package/dist/{component-resolution.types-BS1ySIg-.d.ts → component-resolution.types-C1MzCf2s.d.ts} +13 -0
- package/dist/{component-resolution.types-fvR92hW7.d.cts → component-resolution.types-DLO2w-0n.d.cts} +13 -0
- package/dist/{component-resolution.types-fvR92hW7.d.ts → component-resolution.types-DLO2w-0n.d.ts} +13 -0
- package/dist/db-component.d.cts +1 -1
- package/dist/db-component.d.ts +1 -1
- package/dist/{host-types-DvGv8s9L.d.cts → host-types-CDcLW0TD.d.cts} +7 -2
- package/dist/{host-types-CZ_Bkp4R.d.ts → host-types-CpItjcYh.d.ts} +7 -2
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts +3 -3
- package/dist/host.d.ts +3 -3
- package/dist/host.js.map +1 -1
- package/dist/index.cjs +16 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js.map +1 -1
- package/dist/resolver.cjs +14 -0
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.d.cts +11 -2
- package/dist/resolver.d.ts +11 -2
- package/dist/resolver.js +14 -0
- package/dist/resolver.js.map +1 -1
- package/dist/{route-C1XNcubB.d.ts → route-PUuEuEug.d.ts} +7 -2
- package/dist/{route-C55jsF4B.d.cts → route-QL2Vqf2e.d.cts} +7 -2
- package/dist/route.cjs +2 -0
- package/dist/route.cjs.map +1 -1
- package/dist/route.d.cts +2 -2
- package/dist/route.d.ts +2 -2
- package/dist/route.js +2 -0
- package/dist/route.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 — 2026-08-31
|
|
4
|
+
|
|
5
|
+
- **THE COLD-VERDICT RULE — a registry that has not looked says nothing.**
|
|
6
|
+
`ComponentResolutionSource` gains optional `hasSettled()`, `ComponentResolver`
|
|
7
|
+
implements it, and `applyIrKindRoute` holds a block (exactly as
|
|
8
|
+
`pending_schema` does) instead of publishing `unregistered` / `no-component`
|
|
9
|
+
while the resolver's list load has not completed and nothing has been
|
|
10
|
+
ingested.
|
|
11
|
+
|
|
12
|
+
WHY: every generic-fallback reason is a durable, user-visible claim written
|
|
13
|
+
into the block's `metadata.__ir_route`. On the FIRST PAINT of a fresh tab the
|
|
14
|
+
resolver has fetched nothing, so `resolve()` returning null meant both "no
|
|
15
|
+
such component" and "I haven't looked" — and the route could not tell them
|
|
16
|
+
apart. Live report (2026-08-31): `agent_mandate_specification`, registered +
|
|
17
|
+
active with an active `source='db'` component, rendered on /chat as a
|
|
18
|
+
key/value dump captioned "this shape isn't registered on this platform".
|
|
19
|
+
The same conversation rendered correctly in a window panel whose registries
|
|
20
|
+
were already warm.
|
|
21
|
+
|
|
22
|
+
A POSITIVE answer is never suppressed (compiled floor and already-ingested
|
|
23
|
+
rows route immediately), a FAILED list load still settles (blocks are never
|
|
24
|
+
held forever), and a resolver with no `loadAll` is settled from birth. Once
|
|
25
|
+
settled, a genuinely unknown kind still reaches the generic floor with
|
|
26
|
+
`unregistered` exactly as before.
|
|
27
|
+
|
|
28
|
+
Pinned by `__tests__/cold-verdict-rule.test.ts` (5 tests, built from the
|
|
29
|
+
live row that failed).
|
|
30
|
+
|
|
31
|
+
## 0.8.1 — 2026-08-30
|
|
32
|
+
|
|
33
|
+
- **Republished against `@ai-matrx/content-ir` 0.10.0.** The 0.8.0 tarball
|
|
34
|
+
declares `"@ai-matrx/content-ir": "^0.9.0"`, and on a `0.x` line that caret
|
|
35
|
+
range EXCLUDES 0.10.0 — so a host installing both packages at `"latest"`
|
|
36
|
+
resolved content-ir 0.10.0 at the top level and a NESTED 0.9.0 underneath
|
|
37
|
+
this one: two copies of the kernel in one graph. This release carries the
|
|
38
|
+
0.10.0 spec so one version serves both.
|
|
39
|
+
|
|
40
|
+
No source change. This package's imports stay on the content-ir ROOT
|
|
41
|
+
export, which 0.10.0 keeps as the full surface; the new area subpaths
|
|
42
|
+
(`/core`, `/session`, `/registry`, `/convert`, `/wire`) would only split
|
|
43
|
+
this package's mixed-area import statements, since it reaches four of the
|
|
44
|
+
five areas anyway.
|
|
45
|
+
|
|
46
|
+
In-monorepo the spec is `workspace:*` (THE LATEST LAW's monorepo form); pnpm
|
|
47
|
+
substitutes the local content-ir version at pack time. Do NOT hand-write a
|
|
48
|
+
published range here — `^0.10.0` written into the source manifest is an
|
|
49
|
+
unresolvable spec until that version exists, and it breaks
|
|
50
|
+
`pnpm install --frozen-lockfile` for ALL 17 workspace projects, which is the
|
|
51
|
+
first step of every package's publish workflow (it briefly broke the release
|
|
52
|
+
lane for print, content-ir, and this package on 2026-08-30).
|
|
53
|
+
|
|
3
54
|
## 0.8.0 — 2026-08-30
|
|
4
55
|
|
|
5
56
|
The CONTENT-IR-REACT row of the C23 all-inclusive retrofit register: the
|
package/dist/{component-resolution.types-BS1ySIg-.d.cts → component-resolution.types-C1MzCf2s.d.cts}
RENAMED
|
@@ -91,6 +91,19 @@ interface ComponentResolutionSource {
|
|
|
91
91
|
requestComponent(kind: string, platform: string, role: ComponentRole): void;
|
|
92
92
|
/** One list fetch per session. */
|
|
93
93
|
ensureWarm(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Has the session's list load COMPLETED (successfully or not)?
|
|
96
|
+
*
|
|
97
|
+
* 🚨 THE COLD-VERDICT RULE. Before this existed, `resolve()` answering null
|
|
98
|
+
* was ambiguous: it meant BOTH "this kind genuinely has no component" and
|
|
99
|
+
* "I have not looked yet". The render route could not tell them apart, so a
|
|
100
|
+
* block routed during the first paint of a fresh tab got stamped
|
|
101
|
+
* `unregistered` — a durable, user-visible claim that the shape does not
|
|
102
|
+
* exist — produced by a registry that had loaded nothing. Optional so an
|
|
103
|
+
* existing host satisfies the interface unchanged; absent reads as settled,
|
|
104
|
+
* which is the pre-existing behavior.
|
|
105
|
+
*/
|
|
106
|
+
hasSettled?(): boolean;
|
|
94
107
|
/** Refresh-on-view; rate-limited and deduped by the implementation. */
|
|
95
108
|
refresh(maxAgeMs?: number): Promise<void>;
|
|
96
109
|
/** Monotonic tier version — has anything at all changed? */
|
package/dist/{component-resolution.types-BS1ySIg-.d.ts → component-resolution.types-C1MzCf2s.d.ts}
RENAMED
|
@@ -91,6 +91,19 @@ interface ComponentResolutionSource {
|
|
|
91
91
|
requestComponent(kind: string, platform: string, role: ComponentRole): void;
|
|
92
92
|
/** One list fetch per session. */
|
|
93
93
|
ensureWarm(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Has the session's list load COMPLETED (successfully or not)?
|
|
96
|
+
*
|
|
97
|
+
* 🚨 THE COLD-VERDICT RULE. Before this existed, `resolve()` answering null
|
|
98
|
+
* was ambiguous: it meant BOTH "this kind genuinely has no component" and
|
|
99
|
+
* "I have not looked yet". The render route could not tell them apart, so a
|
|
100
|
+
* block routed during the first paint of a fresh tab got stamped
|
|
101
|
+
* `unregistered` — a durable, user-visible claim that the shape does not
|
|
102
|
+
* exist — produced by a registry that had loaded nothing. Optional so an
|
|
103
|
+
* existing host satisfies the interface unchanged; absent reads as settled,
|
|
104
|
+
* which is the pre-existing behavior.
|
|
105
|
+
*/
|
|
106
|
+
hasSettled?(): boolean;
|
|
94
107
|
/** Refresh-on-view; rate-limited and deduped by the implementation. */
|
|
95
108
|
refresh(maxAgeMs?: number): Promise<void>;
|
|
96
109
|
/** Monotonic tier version — has anything at all changed? */
|
package/dist/{component-resolution.types-fvR92hW7.d.cts → component-resolution.types-DLO2w-0n.d.cts}
RENAMED
|
@@ -129,6 +129,19 @@ interface ComponentResolutionSource {
|
|
|
129
129
|
requestComponent(kind: string, platform: string, role: ComponentRole): void;
|
|
130
130
|
/** One list fetch per session. */
|
|
131
131
|
ensureWarm(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Has the session's list load COMPLETED (successfully or not)?
|
|
134
|
+
*
|
|
135
|
+
* 🚨 THE COLD-VERDICT RULE. Before this existed, `resolve()` answering null
|
|
136
|
+
* was ambiguous: it meant BOTH "this kind genuinely has no component" and
|
|
137
|
+
* "I have not looked yet". The render route could not tell them apart, so a
|
|
138
|
+
* block routed during the first paint of a fresh tab got stamped
|
|
139
|
+
* `unregistered` — a durable, user-visible claim that the shape does not
|
|
140
|
+
* exist — produced by a registry that had loaded nothing. Optional so an
|
|
141
|
+
* existing host satisfies the interface unchanged; absent reads as settled,
|
|
142
|
+
* which is the pre-existing behavior.
|
|
143
|
+
*/
|
|
144
|
+
hasSettled?(): boolean;
|
|
132
145
|
/** Refresh-on-view; rate-limited and deduped by the implementation. */
|
|
133
146
|
refresh(maxAgeMs?: number): Promise<void>;
|
|
134
147
|
/** Monotonic tier version — has anything at all changed? */
|
package/dist/{component-resolution.types-fvR92hW7.d.ts → component-resolution.types-DLO2w-0n.d.ts}
RENAMED
|
@@ -129,6 +129,19 @@ interface ComponentResolutionSource {
|
|
|
129
129
|
requestComponent(kind: string, platform: string, role: ComponentRole): void;
|
|
130
130
|
/** One list fetch per session. */
|
|
131
131
|
ensureWarm(): Promise<void>;
|
|
132
|
+
/**
|
|
133
|
+
* Has the session's list load COMPLETED (successfully or not)?
|
|
134
|
+
*
|
|
135
|
+
* 🚨 THE COLD-VERDICT RULE. Before this existed, `resolve()` answering null
|
|
136
|
+
* was ambiguous: it meant BOTH "this kind genuinely has no component" and
|
|
137
|
+
* "I have not looked yet". The render route could not tell them apart, so a
|
|
138
|
+
* block routed during the first paint of a fresh tab got stamped
|
|
139
|
+
* `unregistered` — a durable, user-visible claim that the shape does not
|
|
140
|
+
* exist — produced by a registry that had loaded nothing. Optional so an
|
|
141
|
+
* existing host satisfies the interface unchanged; absent reads as settled,
|
|
142
|
+
* which is the pre-existing behavior.
|
|
143
|
+
*/
|
|
144
|
+
hasSettled?(): boolean;
|
|
132
145
|
/** Refresh-on-view; rate-limited and deduped by the implementation. */
|
|
133
146
|
refresh(maxAgeMs?: number): Promise<void>;
|
|
134
147
|
/** Monotonic tier version — has anything at all changed? */
|
package/dist/db-component.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-
|
|
2
|
+
import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The DB-component COMPILE CACHE — session-scoped, keyed per winning row
|
package/dist/db-component.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-
|
|
2
|
+
import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* The DB-component COMPILE CACHE — session-scoped, keyed per winning row
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { KindDefinition } from '@ai-matrx/content-ir';
|
|
3
|
-
import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-
|
|
3
|
+
import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* THE HOST CONTRACT — everything this package refuses to decide.
|
|
@@ -121,7 +121,12 @@ interface ContentIrHost extends ContentIrRenderSeams {
|
|
|
121
121
|
*/
|
|
122
122
|
interface KindRouteEnv {
|
|
123
123
|
kinds: Pick<KindDefinitionSource, "getDefinition">;
|
|
124
|
-
|
|
124
|
+
/**
|
|
125
|
+
* `hasSettled` is optional and read ONLY to suppress a negative verdict from
|
|
126
|
+
* a registry that has not loaded yet — see THE COLD-VERDICT RULE on
|
|
127
|
+
* `ComponentResolutionSource`.
|
|
128
|
+
*/
|
|
129
|
+
components: Pick<ComponentResolutionSource, "resolve"> & Partial<Pick<ComponentResolutionSource, "hasSettled">>;
|
|
125
130
|
reportError: ContentIrErrorReporter;
|
|
126
131
|
platform: string;
|
|
127
132
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { KindDefinition } from '@ai-matrx/content-ir';
|
|
3
|
-
import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-
|
|
3
|
+
import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* THE HOST CONTRACT — everything this package refuses to decide.
|
|
@@ -121,7 +121,12 @@ interface ContentIrHost extends ContentIrRenderSeams {
|
|
|
121
121
|
*/
|
|
122
122
|
interface KindRouteEnv {
|
|
123
123
|
kinds: Pick<KindDefinitionSource, "getDefinition">;
|
|
124
|
-
|
|
124
|
+
/**
|
|
125
|
+
* `hasSettled` is optional and read ONLY to suppress a negative verdict from
|
|
126
|
+
* a registry that has not loaded yet — see THE COLD-VERDICT RULE on
|
|
127
|
+
* `ComponentResolutionSource`.
|
|
128
|
+
*/
|
|
129
|
+
components: Pick<ComponentResolutionSource, "resolve"> & Partial<Pick<ComponentResolutionSource, "hasSettled">>;
|
|
125
130
|
reportError: ContentIrErrorReporter;
|
|
126
131
|
platform: string;
|
|
127
132
|
}
|
package/dist/host.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../host.ts","../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/host` — the contract every consumer wires once:\n * the scream seam, the host/seam types, and the React provider.\n *\n * NOTE: this entry is stamped \"use client\" (it carries the provider). The\n * TYPES here are importable from anywhere; a server-side module that needs\n * only pure route logic imports `/route` instead.\n */\nexport * from \"./host/error-report\";\nexport * from \"./host/host-types\";\nexport * from \"./host/ContentIrProvider\";\n","/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n components: Pick<ComponentResolutionSource, \"resolve\">;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n {children}\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;AC2GO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;AC3HA,mBAAmE;AAgB/D;AAZJ,IAAM,2BAAuB,4BAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,4CAAC,qBAAqB,UAArB,EAA8B,OAAO,MACnC,UACH;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,aAAO,yBAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,aAAO,sBAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../host.ts","../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/host` — the contract every consumer wires once:\n * the scream seam, the host/seam types, and the React provider.\n *\n * NOTE: this entry is stamped \"use client\" (it carries the provider). The\n * TYPES here are importable from anywhere; a server-side module that needs\n * only pure route logic imports `/route` instead.\n */\nexport * from \"./host/error-report\";\nexport * from \"./host/host-types\";\nexport * from \"./host/ContentIrProvider\";\n","/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n /**\n * `hasSettled` is optional and read ONLY to suppress a negative verdict from\n * a registry that has not loaded yet — see THE COLD-VERDICT RULE on\n * `ComponentResolutionSource`.\n */\n components: Pick<ComponentResolutionSource, \"resolve\"> &\n Partial<Pick<ComponentResolutionSource, \"hasSettled\">>;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n {children}\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;ACiHO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;ACjIA,mBAAmE;AAgB/D;AAZJ,IAAM,2BAAuB,4BAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,4CAAC,qBAAqB,UAArB,EAA8B,OAAO,MACnC,UACH;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,aAAO,yBAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,aAAO,sBAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":[]}
|
package/dist/host.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { b as ContentIrErrorReport, a as ContentIrErrorReporter, c as consoleErrorReporter } from './component-resolution.types-
|
|
2
|
-
import { C as ContentIrHost, K as KindRouteEnv } from './host-types-
|
|
3
|
-
export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-
|
|
1
|
+
export { b as ContentIrErrorReport, a as ContentIrErrorReporter, c as consoleErrorReporter } from './component-resolution.types-C1MzCf2s.cjs';
|
|
2
|
+
import { C as ContentIrHost, K as KindRouteEnv } from './host-types-CDcLW0TD.cjs';
|
|
3
|
+
export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-CDcLW0TD.cjs';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
import '@ai-matrx/content-ir';
|
package/dist/host.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { b as ContentIrErrorReport, a as ContentIrErrorReporter, c as consoleErrorReporter } from './component-resolution.types-
|
|
2
|
-
import { C as ContentIrHost, K as KindRouteEnv } from './host-types-
|
|
3
|
-
export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-
|
|
1
|
+
export { b as ContentIrErrorReport, a as ContentIrErrorReporter, c as consoleErrorReporter } from './component-resolution.types-C1MzCf2s.js';
|
|
2
|
+
import { C as ContentIrHost, K as KindRouteEnv } from './host-types-CpItjcYh.js';
|
|
3
|
+
export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-CpItjcYh.js';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
6
|
import '@ai-matrx/content-ir';
|
package/dist/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx"],"sourcesContent":["/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n components: Pick<ComponentResolutionSource, \"resolve\">;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n {children}\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n"],"mappings":";;;AA2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;AC2GO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;AC3HA,SAAS,eAAe,YAAY,eAA+B;AAgB/D;AAZJ,IAAM,uBAAuB,cAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,MACnC,UACH;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,SAAO,WAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,SAAO,QAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx"],"sourcesContent":["/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n /**\n * `hasSettled` is optional and read ONLY to suppress a negative verdict from\n * a registry that has not loaded yet — see THE COLD-VERDICT RULE on\n * `ComponentResolutionSource`.\n */\n components: Pick<ComponentResolutionSource, \"resolve\"> &\n Partial<Pick<ComponentResolutionSource, \"hasSettled\">>;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n {children}\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n"],"mappings":";;;AA2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;ACiHO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;ACjIA,SAAS,eAAe,YAAY,eAA+B;AAgB/D;AAZJ,IAAM,uBAAuB,cAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,MACnC,UACH;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,SAAO,WAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,SAAO,QAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -131,6 +131,13 @@ var ComponentResolver = class {
|
|
|
131
131
|
db = /* @__PURE__ */ new Map();
|
|
132
132
|
warmPromise = null;
|
|
133
133
|
warmFailureLogged = false;
|
|
134
|
+
/**
|
|
135
|
+
* True once a list load has RUN TO COMPLETION (success or failure), or once
|
|
136
|
+
* any rows have been ingested by any tier. Until then `resolve()` answering
|
|
137
|
+
* null means "I have not looked yet" — never "no such component". See THE
|
|
138
|
+
* COLD-VERDICT RULE on `ComponentResolutionSource.hasSettled`.
|
|
139
|
+
*/
|
|
140
|
+
settled = false;
|
|
134
141
|
/**
|
|
135
142
|
* When the last successful wholesale refresh landed, or null for "never".
|
|
136
143
|
* Explicitly nullable rather than 0: with a host-supplied clock that starts
|
|
@@ -388,6 +395,7 @@ var ComponentResolver = class {
|
|
|
388
395
|
if (!this.warmPromise) {
|
|
389
396
|
this.warmPromise = loadAll().then((rows) => {
|
|
390
397
|
this.ingestDbRows(rows);
|
|
398
|
+
this.settled = true;
|
|
391
399
|
}).catch((error) => {
|
|
392
400
|
const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;
|
|
393
401
|
if (!this.warmFailureLogged) {
|
|
@@ -400,11 +408,17 @@ var ComponentResolver = class {
|
|
|
400
408
|
...errorFields(error),
|
|
401
409
|
raw: error
|
|
402
410
|
});
|
|
411
|
+
this.settled = true;
|
|
403
412
|
this.warmPromise = null;
|
|
404
413
|
});
|
|
405
414
|
}
|
|
406
415
|
return this.warmPromise;
|
|
407
416
|
}
|
|
417
|
+
/** @see ComponentResolutionSource.hasSettled */
|
|
418
|
+
hasSettled() {
|
|
419
|
+
if (!this.options.loadAll) return true;
|
|
420
|
+
return this.settled || this.db.size > 0;
|
|
421
|
+
}
|
|
408
422
|
};
|
|
409
423
|
|
|
410
424
|
// route/kind-route.ts
|
|
@@ -491,6 +505,8 @@ function applyIrKindRoute(block, env, options) {
|
|
|
491
505
|
if (envelope.root.kindState === "pending_schema") return block;
|
|
492
506
|
const def = env.kinds.getDefinition(kind);
|
|
493
507
|
const resolution = env.components.resolve(kind, env.platform, "output");
|
|
508
|
+
const componentsSettled = env.components.hasSettled?.() ?? true;
|
|
509
|
+
if (!componentsSettled && !resolution && !def) return block;
|
|
494
510
|
if (envelope.root.kindState === "raw") {
|
|
495
511
|
return routeToGeneric(
|
|
496
512
|
block,
|