@ai-matrx/content-ir-react 0.8.1 → 0.9.1

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/{component-resolution.types-BS1ySIg-.d.cts → component-resolution.types-C1MzCf2s.d.cts} +13 -0
  3. package/dist/{component-resolution.types-BS1ySIg-.d.ts → component-resolution.types-C1MzCf2s.d.ts} +13 -0
  4. package/dist/{component-resolution.types-fvR92hW7.d.cts → component-resolution.types-DLO2w-0n.d.cts} +13 -0
  5. package/dist/{component-resolution.types-fvR92hW7.d.ts → component-resolution.types-DLO2w-0n.d.ts} +13 -0
  6. package/dist/db-component.d.cts +1 -1
  7. package/dist/db-component.d.ts +1 -1
  8. package/dist/{host-types-DvGv8s9L.d.cts → host-types-CDcLW0TD.d.cts} +7 -2
  9. package/dist/{host-types-CZ_Bkp4R.d.ts → host-types-CpItjcYh.d.ts} +7 -2
  10. package/dist/host.cjs.map +1 -1
  11. package/dist/host.d.cts +3 -3
  12. package/dist/host.d.ts +3 -3
  13. package/dist/host.js.map +1 -1
  14. package/dist/index.cjs +17 -0
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +4 -4
  17. package/dist/index.d.ts +4 -4
  18. package/dist/index.js +17 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/react.cjs.map +1 -1
  21. package/dist/react.d.cts +2 -2
  22. package/dist/react.d.ts +2 -2
  23. package/dist/react.js.map +1 -1
  24. package/dist/resolver.cjs +15 -0
  25. package/dist/resolver.cjs.map +1 -1
  26. package/dist/resolver.d.cts +11 -2
  27. package/dist/resolver.d.ts +11 -2
  28. package/dist/resolver.js +15 -0
  29. package/dist/resolver.js.map +1 -1
  30. package/dist/{route-C1XNcubB.d.ts → route-PUuEuEug.d.ts} +7 -2
  31. package/dist/{route-C55jsF4B.d.cts → route-QL2Vqf2e.d.cts} +7 -2
  32. package/dist/route.cjs +2 -0
  33. package/dist/route.cjs.map +1 -1
  34. package/dist/route.d.cts +2 -2
  35. package/dist/route.d.ts +2 -2
  36. package/dist/route.js +2 -0
  37. package/dist/route.js.map +1 -1
  38. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.1 — 2026-08-31
4
+
5
+ - `replaceDbRows` now settles the resolver. It is the refresh landing point —
6
+ a completed wholesale list load — and the seam a host or test uses to
7
+ declare "the list loaded". Without it, a host that warms through
8
+ `refresh()` instead of `ensureWarm()` would have every block held by THE
9
+ COLD-VERDICT RULE indefinitely.
10
+
11
+ ## 0.9.0 — 2026-08-31
12
+
13
+ - **THE COLD-VERDICT RULE — a registry that has not looked says nothing.**
14
+ `ComponentResolutionSource` gains optional `hasSettled()`, `ComponentResolver`
15
+ implements it, and `applyIrKindRoute` holds a block (exactly as
16
+ `pending_schema` does) instead of publishing `unregistered` / `no-component`
17
+ while the resolver's list load has not completed and nothing has been
18
+ ingested.
19
+
20
+ WHY: every generic-fallback reason is a durable, user-visible claim written
21
+ into the block's `metadata.__ir_route`. On the FIRST PAINT of a fresh tab the
22
+ resolver has fetched nothing, so `resolve()` returning null meant both "no
23
+ such component" and "I haven't looked" — and the route could not tell them
24
+ apart. Live report (2026-08-31): `agent_mandate_specification`, registered +
25
+ active with an active `source='db'` component, rendered on /chat as a
26
+ key/value dump captioned "this shape isn't registered on this platform".
27
+ The same conversation rendered correctly in a window panel whose registries
28
+ were already warm.
29
+
30
+ A POSITIVE answer is never suppressed (compiled floor and already-ingested
31
+ rows route immediately), a FAILED list load still settles (blocks are never
32
+ held forever), and a resolver with no `loadAll` is settled from birth. Once
33
+ settled, a genuinely unknown kind still reaches the generic floor with
34
+ `unregistered` exactly as before.
35
+
36
+ Pinned by `__tests__/cold-verdict-rule.test.ts` (5 tests, built from the
37
+ live row that failed).
38
+
3
39
  ## 0.8.1 — 2026-08-30
4
40
 
5
41
  - **Republished against `@ai-matrx/content-ir` 0.10.0.** The 0.8.0 tarball
@@ -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? */
@@ -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? */
@@ -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? */
@@ -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? */
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-BS1ySIg-.cjs';
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
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { C as ComponentResolution, a as ContentIrErrorReporter } from './component-resolution.types-BS1ySIg-.js';
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-BS1ySIg-.cjs';
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
- components: Pick<ComponentResolutionSource, "resolve">;
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-BS1ySIg-.js';
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
- components: Pick<ComponentResolutionSource, "resolve">;
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-BS1ySIg-.cjs';
2
- import { C as ContentIrHost, K as KindRouteEnv } from './host-types-DvGv8s9L.cjs';
3
- export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-DvGv8s9L.cjs';
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-BS1ySIg-.js';
2
- import { C as ContentIrHost, K as KindRouteEnv } from './host-types-CZ_Bkp4R.js';
3
- export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-CZ_Bkp4R.js';
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
@@ -244,6 +251,7 @@ var ComponentResolver = class {
244
251
  * and is_active flips all take effect. Always notifies.
245
252
  */
246
253
  replaceDbRows(rows) {
254
+ this.settled = true;
247
255
  this.db.clear();
248
256
  this.coldMisses.clear();
249
257
  for (const row of rows) {
@@ -388,6 +396,7 @@ var ComponentResolver = class {
388
396
  if (!this.warmPromise) {
389
397
  this.warmPromise = loadAll().then((rows) => {
390
398
  this.ingestDbRows(rows);
399
+ this.settled = true;
391
400
  }).catch((error) => {
392
401
  const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;
393
402
  if (!this.warmFailureLogged) {
@@ -400,11 +409,17 @@ var ComponentResolver = class {
400
409
  ...errorFields(error),
401
410
  raw: error
402
411
  });
412
+ this.settled = true;
403
413
  this.warmPromise = null;
404
414
  });
405
415
  }
406
416
  return this.warmPromise;
407
417
  }
418
+ /** @see ComponentResolutionSource.hasSettled */
419
+ hasSettled() {
420
+ if (!this.options.loadAll) return true;
421
+ return this.settled || this.db.size > 0;
422
+ }
408
423
  };
409
424
 
410
425
  // route/kind-route.ts
@@ -491,6 +506,8 @@ function applyIrKindRoute(block, env, options) {
491
506
  if (envelope.root.kindState === "pending_schema") return block;
492
507
  const def = env.kinds.getDefinition(kind);
493
508
  const resolution = env.components.resolve(kind, env.platform, "output");
509
+ const componentsSettled = env.components.hasSettled?.() ?? true;
510
+ if (!componentsSettled && !resolution && !def) return block;
494
511
  if (envelope.root.kindState === "raw") {
495
512
  return routeToGeneric(
496
513
  block,