@ai-matrx/content-ir-react 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/component-resolution.types-BS1ySIg-.d.cts +104 -0
  3. package/dist/component-resolution.types-BS1ySIg-.d.ts +104 -0
  4. package/dist/component-resolution.types-fvR92hW7.d.cts +142 -0
  5. package/dist/component-resolution.types-fvR92hW7.d.ts +142 -0
  6. package/dist/db-component.cjs +226 -0
  7. package/dist/db-component.cjs.map +1 -0
  8. package/dist/db-component.d.cts +267 -0
  9. package/dist/db-component.d.ts +267 -0
  10. package/dist/db-component.js +194 -0
  11. package/dist/db-component.js.map +1 -0
  12. package/dist/host-types-CZ_Bkp4R.d.ts +131 -0
  13. package/dist/host-types-DvGv8s9L.d.cts +131 -0
  14. package/dist/host.cjs +74 -0
  15. package/dist/host.cjs.map +1 -0
  16. package/dist/host.d.cts +24 -0
  17. package/dist/host.d.ts +24 -0
  18. package/dist/host.js +52 -0
  19. package/dist/host.js.map +1 -0
  20. package/dist/index.cjs +356 -90
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +280 -672
  23. package/dist/index.d.ts +280 -672
  24. package/dist/index.js +316 -31
  25. package/dist/index.js.map +1 -1
  26. package/dist/react.cjs +361 -0
  27. package/dist/react.cjs.map +1 -0
  28. package/dist/react.d.cts +195 -0
  29. package/dist/react.d.ts +195 -0
  30. package/dist/react.js +344 -0
  31. package/dist/react.js.map +1 -0
  32. package/dist/resolver.cjs +327 -0
  33. package/dist/resolver.cjs.map +1 -0
  34. package/dist/resolver.d.cts +133 -0
  35. package/dist/resolver.d.ts +133 -0
  36. package/dist/resolver.js +304 -0
  37. package/dist/resolver.js.map +1 -0
  38. package/dist/route-C1XNcubB.d.ts +433 -0
  39. package/dist/route-C55jsF4B.d.cts +433 -0
  40. package/dist/route.cjs +259 -0
  41. package/dist/route.cjs.map +1 -0
  42. package/dist/route.d.cts +4 -0
  43. package/dist/route.d.ts +4 -0
  44. package/dist/route.js +247 -0
  45. package/dist/route.js.map +1 -0
  46. package/package.json +75 -2
@@ -0,0 +1,327 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // resolver.ts
21
+ var resolver_exports = {};
22
+ __export(resolver_exports, {
23
+ ComponentResolver: () => ComponentResolver
24
+ });
25
+ module.exports = __toCommonJS(resolver_exports);
26
+
27
+ // host/error-report.ts
28
+ var consoleErrorReporter = (report) => {
29
+ console.error(`[content-ir] ${report.message}`, report.raw ?? "");
30
+ };
31
+
32
+ // resolver/component-resolver.ts
33
+ var KEY_SEPARATOR = String.fromCharCode(1);
34
+ function keyOf(kind, platform, role) {
35
+ return `${kind}${KEY_SEPARATOR}${platform}${KEY_SEPARATOR}${role}`;
36
+ }
37
+ function describe(error) {
38
+ return error instanceof Error ? error.message : String(error);
39
+ }
40
+ function errorFields(error) {
41
+ if (!(error instanceof Error)) return {};
42
+ return {
43
+ name: error.name,
44
+ ...error.stack === void 0 ? {} : { stack: error.stack }
45
+ };
46
+ }
47
+ var ComponentResolver = class {
48
+ compiled = null;
49
+ db = /* @__PURE__ */ new Map();
50
+ warmPromise = null;
51
+ warmFailureLogged = false;
52
+ /**
53
+ * When the last successful wholesale refresh landed, or null for "never".
54
+ * Explicitly nullable rather than 0: with a host-supplied clock that starts
55
+ * near zero, a `0` sentinel makes the FIRST refresh look rate-limited and
56
+ * silently skip.
57
+ */
58
+ lastRefreshAt = null;
59
+ refreshPromise = null;
60
+ listeners = /* @__PURE__ */ new Set();
61
+ /**
62
+ * Cold single-kind fetch dedupe (streaming eager path). In-flight is keyed
63
+ * by (kind, platform) — the fetch unit; misses are keyed by (kind, platform,
64
+ * role) so a miss on web/output never suppresses other roles, and CLEARED on
65
+ * every wholesale refresh (a component created mid-session becomes eagerly
66
+ * fetchable again — misses are cheap to re-verify).
67
+ */
68
+ coldInFlight = /* @__PURE__ */ new Set();
69
+ coldMisses = /* @__PURE__ */ new Set();
70
+ /** Monotonic db-tier version — the repaint hook's snapshot key. */
71
+ version = 0;
72
+ /** Per-kind versions + listeners (granular repaint) + wholesale epoch. */
73
+ kindVersions = /* @__PURE__ */ new Map();
74
+ kindListeners = /* @__PURE__ */ new Map();
75
+ epoch = 0;
76
+ reportError;
77
+ now;
78
+ options;
79
+ // Explicit field, not a parameter property: consumers compile this source
80
+ // directly, and a strict host (the dashboard) sets `erasableSyntaxOnly`,
81
+ // under which parameter properties are a hard error.
82
+ constructor(options = {}) {
83
+ this.options = options;
84
+ this.reportError = options.reportError ?? consoleErrorReporter;
85
+ this.now = options.now ?? (() => Date.now());
86
+ }
87
+ compiledMap() {
88
+ if (!this.compiled) {
89
+ this.compiled = /* @__PURE__ */ new Map();
90
+ for (const entry of this.options.compiledEntries?.() ?? []) {
91
+ this.compiled.set(keyOf(entry.kind, entry.platform, entry.role), entry);
92
+ }
93
+ }
94
+ return this.compiled;
95
+ }
96
+ /**
97
+ * Synchronous resolve — the render seam's per-block call. DB override first
98
+ * (once warm), compiled floor second, null for unknown.
99
+ */
100
+ resolve(kind, platform, role) {
101
+ const key = keyOf(kind, platform, role);
102
+ const dbRow = this.db.get(key);
103
+ if (dbRow) {
104
+ return {
105
+ componentKey: dbRow.componentKey,
106
+ source: dbRow.source,
107
+ config: dbRow.config,
108
+ isActive: dbRow.isActive,
109
+ resolvedBy: "db",
110
+ componentSource: dbRow.componentSource,
111
+ propsTransform: dbRow.propsTransform,
112
+ hasComponentSource: dbRow.hasComponentSource ?? Boolean(dbRow.componentSource && dbRow.componentSource.trim()),
113
+ pinnedKindVersion: dbRow.pinnedKindVersion,
114
+ updatedAt: dbRow.updatedAt,
115
+ createdBy: dbRow.createdBy
116
+ };
117
+ }
118
+ const compiledEntry = this.compiledMap().get(key);
119
+ if (compiledEntry) {
120
+ return {
121
+ componentKey: compiledEntry.componentKey,
122
+ source: compiledEntry.source,
123
+ config: compiledEntry.config,
124
+ isActive: true,
125
+ // trusted at bootstrap (R6)
126
+ resolvedBy: "compiled",
127
+ componentSource: null,
128
+ propsTransform: null,
129
+ hasComponentSource: false,
130
+ pinnedKindVersion: null,
131
+ updatedAt: null,
132
+ createdBy: null
133
+ };
134
+ }
135
+ return null;
136
+ }
137
+ /** R6 floor check: compiled-bootstrap membership = always render-trusted. */
138
+ hasCompiled(kind, platform, role) {
139
+ return this.compiledMap().has(keyOf(kind, platform, role));
140
+ }
141
+ /**
142
+ * Pure ingest — the warm landing point and the unit-test seam. First row per
143
+ * key wins: rows arrive is_default-first / sort_order-asc from the source.
144
+ */
145
+ ingestDbRows(rows) {
146
+ const changedKinds = /* @__PURE__ */ new Set();
147
+ for (const row of rows) {
148
+ const key = keyOf(row.kind, row.platform, row.role);
149
+ if (!this.db.has(key)) {
150
+ this.db.set(key, row);
151
+ changedKinds.add(row.kind);
152
+ }
153
+ }
154
+ if (changedKinds.size > 0) {
155
+ for (const kind of changedKinds) this.bumpKind(kind);
156
+ this.notifyChanged();
157
+ }
158
+ }
159
+ /**
160
+ * Refresh landing point: REPLACE the db tier wholesale (same
161
+ * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
162
+ * and is_active flips all take effect. Always notifies.
163
+ */
164
+ replaceDbRows(rows) {
165
+ this.db.clear();
166
+ this.coldMisses.clear();
167
+ for (const row of rows) {
168
+ const key = keyOf(row.kind, row.platform, row.role);
169
+ if (!this.db.has(key)) this.db.set(key, row);
170
+ }
171
+ this.epoch += 1;
172
+ for (const set of this.kindListeners.values()) {
173
+ for (const listener of set) listener();
174
+ }
175
+ this.notifyChanged();
176
+ }
177
+ getVersion() {
178
+ return this.version;
179
+ }
180
+ getKindVersion(kind) {
181
+ return this.epoch + (this.kindVersions.get(kind) ?? 0);
182
+ }
183
+ subscribeKind(kind, listener) {
184
+ const set = this.kindListeners.get(kind) ?? /* @__PURE__ */ new Set();
185
+ set.add(listener);
186
+ this.kindListeners.set(kind, set);
187
+ return () => {
188
+ set.delete(listener);
189
+ if (set.size === 0) this.kindListeners.delete(kind);
190
+ };
191
+ }
192
+ subscribe(listener) {
193
+ this.listeners.add(listener);
194
+ return () => {
195
+ this.listeners.delete(listener);
196
+ };
197
+ }
198
+ bumpKind(kind) {
199
+ this.kindVersions.set(kind, (this.kindVersions.get(kind) ?? 0) + 1);
200
+ const listeners = this.kindListeners.get(kind);
201
+ if (listeners) for (const listener of listeners) listener();
202
+ }
203
+ notifyChanged() {
204
+ this.version += 1;
205
+ for (const listener of this.listeners) listener();
206
+ }
207
+ /**
208
+ * A warm metadata shell is resolved enough for routing, but not for the
209
+ * host's db-component renderer. Its body must still come from the cold
210
+ * single-kind loader.
211
+ */
212
+ needsColdFetch(kind, platform, role) {
213
+ const resolution = this.resolve(kind, platform, role);
214
+ return resolution === null || resolution.resolvedBy === "db" && resolution.hasComponentSource && resolution.componentSource === null;
215
+ }
216
+ /**
217
+ * Cold rows are the authoritative heavy form of warm metadata shells. Keep
218
+ * the same first-row-per-key winner contract, but replace an existing shell
219
+ * so `componentSource` / `propsTransform` can land and repaint the kind.
220
+ */
221
+ ingestColdRows(rows) {
222
+ const winners = /* @__PURE__ */ new Map();
223
+ for (const row of rows) {
224
+ const key = keyOf(row.kind, row.platform, row.role);
225
+ if (!winners.has(key)) winners.set(key, row);
226
+ }
227
+ if (winners.size === 0) return;
228
+ const changedKinds = /* @__PURE__ */ new Set();
229
+ for (const [key, row] of winners) {
230
+ this.db.set(key, row);
231
+ changedKinds.add(row.kind);
232
+ }
233
+ for (const kind of changedKinds) this.bumpKind(kind);
234
+ this.notifyChanged();
235
+ }
236
+ /**
237
+ * The eager lightweight single-kind fetch (streaming path): the moment a
238
+ * cloud kind is identified mid-stream, pull ONLY that kind's resolver rows
239
+ * and ingest them so {@link resolve} can answer before — or shortly after —
240
+ * the region completes. Deduped in-flight and by known-miss. Fire-and-forget;
241
+ * failures are loud (the warm list remains the backstop).
242
+ */
243
+ requestComponent(kind, platform, role) {
244
+ const loadForKind = this.options.loadForKind;
245
+ if (!loadForKind) return;
246
+ if (!this.needsColdFetch(kind, platform, role)) return;
247
+ const missKey = keyOf(kind, platform, role);
248
+ const flightKey = `${kind}${KEY_SEPARATOR}${platform}`;
249
+ if (this.coldInFlight.has(flightKey) || this.coldMisses.has(missKey)) {
250
+ return;
251
+ }
252
+ this.coldInFlight.add(flightKey);
253
+ void (async () => {
254
+ try {
255
+ this.ingestColdRows(await loadForKind(kind, platform));
256
+ if (this.needsColdFetch(kind, platform, role)) {
257
+ this.coldMisses.add(missKey);
258
+ }
259
+ } catch (error) {
260
+ this.reportError({
261
+ source: "content-ir",
262
+ message: `component-resolver cold fetch failed for "${kind}": ${describe(error)}`,
263
+ ...errorFields(error),
264
+ raw: error
265
+ });
266
+ } finally {
267
+ this.coldInFlight.delete(flightKey);
268
+ }
269
+ })();
270
+ }
271
+ /**
272
+ * Refresh-on-view: re-fetch the warm list and REPLACE the db tier, so an
273
+ * edited `source='db'` component (its `updated_at` bump re-keys the host's
274
+ * compile cache) renders fresh on the next view. Deduped in-flight and
275
+ * rate-limited by `maxAgeMs` (default 10s) — mounting several previews costs
276
+ * one fetch. Server-side edits do NOT push to open clients; the contract is
277
+ * refresh-on-view via this call.
278
+ */
279
+ refresh(maxAgeMs = 1e4) {
280
+ const loadAll = this.options.loadAll;
281
+ if (!loadAll) return Promise.resolve();
282
+ if (this.refreshPromise) return this.refreshPromise;
283
+ if (this.lastRefreshAt !== null && this.now() - this.lastRefreshAt < maxAgeMs) {
284
+ return Promise.resolve();
285
+ }
286
+ this.coldMisses.clear();
287
+ this.refreshPromise = loadAll().then((rows) => {
288
+ this.lastRefreshAt = this.now();
289
+ this.replaceDbRows(rows);
290
+ }).catch((error) => {
291
+ this.reportError({
292
+ source: "content-ir",
293
+ message: `component-resolver refresh failed (current resolver tier still serving): ${describe(error)}`,
294
+ ...errorFields(error),
295
+ raw: error
296
+ });
297
+ }).finally(() => {
298
+ this.refreshPromise = null;
299
+ });
300
+ return this.refreshPromise;
301
+ }
302
+ /** One list fetch per app session; failed loads retry on the next call. */
303
+ ensureWarm() {
304
+ const loadAll = this.options.loadAll;
305
+ if (!loadAll) return Promise.resolve();
306
+ if (!this.warmPromise) {
307
+ this.warmPromise = loadAll().then((rows) => {
308
+ this.ingestDbRows(rows);
309
+ }).catch((error) => {
310
+ const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;
311
+ if (!this.warmFailureLogged) {
312
+ this.warmFailureLogged = true;
313
+ console.error(`[content-ir] ${message}`);
314
+ }
315
+ this.reportError({
316
+ source: "content-ir",
317
+ message,
318
+ ...errorFields(error),
319
+ raw: error
320
+ });
321
+ this.warmPromise = null;
322
+ });
323
+ }
324
+ return this.warmPromise;
325
+ }
326
+ };
327
+ //# sourceMappingURL=resolver.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../resolver.ts","../host/error-report.ts","../resolver/component-resolver.ts"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/resolver` — (kind, platform, role) → component:\n * the `kind_component` row vocabulary and the `ComponentResolver` (compiled\n * floor, DB override, granular per-kind repaint, cold-fetch dedupe,\n * warm/refresh with loud recovery). Pure TS — no React runtime; deliberately\n * unstamped so server graphs can import it.\n */\nexport * from \"./resolver/component-resolution.types\";\nexport * from \"./resolver/component-resolver\";\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 Shape System component RESOLVER (rulings R1 + R6): (kind, platform,\n * role) → component, with two tiers:\n *\n * - eager: a COMPILED bootstrap the host ships in its bundle — the\n * trusted-at-boot floor, available at import so the render seam can gate\n * synchronously from the first streamed byte.\n * - warm: one `content_ir.kind_component` list fetch per app session\n * (`ensureWarm`). A DB row for a (kind, platform, role) OVERRIDES the\n * compiled entry once warm; the compiled floor keeps answering until then\n * (and forever, on DB failure).\n *\n * Resolution is SYNCHRONOUS (the render seam calls it per block); the only\n * async work is the loading, off the render path.\n *\n * WHAT MOVED AND WHY. This class was matrx-frontend's `ComponentRegistry`. It\n * is a large part of why a second UI could not render a kind: every tier rule,\n * every repaint counter, every dedupe latch lived in one app. Nothing about it\n * is Next.js, Redux, or Supabase — the only host-specific parts were the two\n * loaders and the error sink, which are now constructor arguments.\n */\n\nimport type { ContentIrErrorReporter } from \"../host/error-report\";\nimport { consoleErrorReporter } from \"../host/error-report\";\nimport type {\n ComponentResolution,\n ComponentResolutionSource,\n ComponentRole,\n KindComponentRow,\n SystemComponentEntry,\n} from \"./component-resolution.types\";\n\nexport interface ComponentResolverOptions {\n /**\n * The compiled bootstrap, as a THUNK resolved on first use: a host's system\n * component table is often mid-initialization when this module evaluates\n * (matrx-frontend's registry cluster has a deliberate import cycle).\n */\n compiledEntries?: () => SystemComponentEntry[];\n /** The warm/refresh list load — every `kind_component` row the user may see. */\n loadAll?: () => Promise<KindComponentRow[]>;\n /** The eager single-kind load fired the moment a kind is identified mid-stream. */\n loadForKind?: (kind: string, platform: string) => Promise<KindComponentRow[]>;\n /** Where recovery screams land. Defaults to `console.error`, never silence. */\n reportError?: ContentIrErrorReporter;\n /** Clock seam — the refresh rate limiter. Defaults to `Date.now`. */\n now?: () => number;\n}\n\n/** Unit separator — a character no kind slug, platform, or role can contain. */\nconst KEY_SEPARATOR = String.fromCharCode(1);\n\nfunction keyOf(kind: string, platform: string, role: string): string {\n return `${kind}${KEY_SEPARATOR}${platform}${KEY_SEPARATOR}${role}`;\n}\n\nfunction describe(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** Error-shaped fields, spread-safe under `exactOptionalPropertyTypes`. */\nfunction errorFields(error: unknown): { name?: string; stack?: string } {\n if (!(error instanceof Error)) return {};\n return {\n name: error.name,\n ...(error.stack === undefined ? {} : { stack: error.stack }),\n };\n}\n\nexport class ComponentResolver implements ComponentResolutionSource {\n private compiled: Map<string, SystemComponentEntry> | null = null;\n private readonly db = new Map<string, KindComponentRow>();\n private warmPromise: Promise<void> | null = null;\n private warmFailureLogged = false;\n /**\n * When the last successful wholesale refresh landed, or null for \"never\".\n * Explicitly nullable rather than 0: with a host-supplied clock that starts\n * near zero, a `0` sentinel makes the FIRST refresh look rate-limited and\n * silently skip.\n */\n private lastRefreshAt: number | null = null;\n private refreshPromise: Promise<void> | null = null;\n private readonly listeners = new Set<() => void>();\n /**\n * Cold single-kind fetch dedupe (streaming eager path). In-flight is keyed\n * by (kind, platform) — the fetch unit; misses are keyed by (kind, platform,\n * role) so a miss on web/output never suppresses other roles, and CLEARED on\n * every wholesale refresh (a component created mid-session becomes eagerly\n * fetchable again — misses are cheap to re-verify).\n */\n private readonly coldInFlight = new Set<string>();\n private readonly coldMisses = new Set<string>();\n /** Monotonic db-tier version — the repaint hook's snapshot key. */\n private version = 0;\n /** Per-kind versions + listeners (granular repaint) + wholesale epoch. */\n private readonly kindVersions = new Map<string, number>();\n private readonly kindListeners = new Map<string, Set<() => void>>();\n private epoch = 0;\n\n private readonly reportError: ContentIrErrorReporter;\n private readonly now: () => number;\n\n private readonly options: ComponentResolverOptions;\n\n // Explicit field, not a parameter property: consumers compile this source\n // directly, and a strict host (the dashboard) sets `erasableSyntaxOnly`,\n // under which parameter properties are a hard error.\n constructor(options: ComponentResolverOptions = {}) {\n this.options = options;\n this.reportError = options.reportError ?? consoleErrorReporter;\n this.now = options.now ?? (() => Date.now());\n }\n\n private compiledMap(): Map<string, SystemComponentEntry> {\n if (!this.compiled) {\n this.compiled = new Map();\n for (const entry of this.options.compiledEntries?.() ?? []) {\n this.compiled.set(keyOf(entry.kind, entry.platform, entry.role), entry);\n }\n }\n return this.compiled;\n }\n\n /**\n * Synchronous resolve — the render seam's per-block call. DB override first\n * (once warm), compiled floor second, null for unknown.\n */\n resolve(\n kind: string,\n platform: string,\n role: ComponentRole,\n ): ComponentResolution | null {\n const key = keyOf(kind, platform, role);\n\n const dbRow = this.db.get(key);\n if (dbRow) {\n return {\n componentKey: dbRow.componentKey,\n source: dbRow.source,\n config: dbRow.config,\n isActive: dbRow.isActive,\n resolvedBy: \"db\",\n componentSource: dbRow.componentSource,\n propsTransform: dbRow.propsTransform,\n hasComponentSource:\n dbRow.hasComponentSource ??\n Boolean(dbRow.componentSource && dbRow.componentSource.trim()),\n pinnedKindVersion: dbRow.pinnedKindVersion,\n updatedAt: dbRow.updatedAt,\n createdBy: dbRow.createdBy,\n };\n }\n\n const compiledEntry = this.compiledMap().get(key);\n if (compiledEntry) {\n return {\n componentKey: compiledEntry.componentKey,\n source: compiledEntry.source,\n config: compiledEntry.config,\n isActive: true, // trusted at bootstrap (R6)\n resolvedBy: \"compiled\",\n componentSource: null,\n propsTransform: null,\n hasComponentSource: false,\n pinnedKindVersion: null,\n updatedAt: null,\n createdBy: null,\n };\n }\n\n return null;\n }\n\n /** R6 floor check: compiled-bootstrap membership = always render-trusted. */\n hasCompiled(kind: string, platform: string, role: ComponentRole): boolean {\n return this.compiledMap().has(keyOf(kind, platform, role));\n }\n\n /**\n * Pure ingest — the warm landing point and the unit-test seam. First row per\n * key wins: rows arrive is_default-first / sort_order-asc from the source.\n */\n ingestDbRows(rows: readonly KindComponentRow[]): void {\n const changedKinds = new Set<string>();\n for (const row of rows) {\n const key = keyOf(row.kind, row.platform, row.role);\n if (!this.db.has(key)) {\n this.db.set(key, row);\n changedKinds.add(row.kind);\n }\n }\n // Warm/cold ingest is a resolver-tier change the render seam must see — a\n // db component landing AFTER a region finalized re-runs the route via the\n // repaint hook. Per-kind bumps keep the repaint granular.\n if (changedKinds.size > 0) {\n for (const kind of changedKinds) this.bumpKind(kind);\n this.notifyChanged();\n }\n }\n\n /**\n * Refresh landing point: REPLACE the db tier wholesale (same\n * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,\n * and is_active flips all take effect. Always notifies.\n */\n replaceDbRows(rows: readonly KindComponentRow[]): void {\n this.db.clear();\n // Wholesale invalidation: every recorded miss is stale (a component\n // created mid-session must become eagerly fetchable again).\n this.coldMisses.clear();\n for (const row of rows) {\n const key = keyOf(row.kind, row.platform, row.role);\n if (!this.db.has(key)) this.db.set(key, row);\n }\n // A wholesale replace ALWAYS notifies (deletions/flips count too), and\n // bumps the epoch so EVERY per-kind subscriber re-snapshots.\n this.epoch += 1;\n for (const set of this.kindListeners.values()) {\n for (const listener of set) listener();\n }\n this.notifyChanged();\n }\n\n getVersion(): number {\n return this.version;\n }\n\n getKindVersion(kind: string): number {\n return this.epoch + (this.kindVersions.get(kind) ?? 0);\n }\n\n subscribeKind(kind: string, listener: () => void): () => void {\n const set = this.kindListeners.get(kind) ?? new Set<() => void>();\n set.add(listener);\n this.kindListeners.set(kind, set);\n return () => {\n set.delete(listener);\n if (set.size === 0) this.kindListeners.delete(kind);\n };\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n private bumpKind(kind: string): void {\n this.kindVersions.set(kind, (this.kindVersions.get(kind) ?? 0) + 1);\n const listeners = this.kindListeners.get(kind);\n if (listeners) for (const listener of listeners) listener();\n }\n\n private notifyChanged(): void {\n this.version += 1;\n for (const listener of this.listeners) listener();\n }\n\n /**\n * A warm metadata shell is resolved enough for routing, but not for the\n * host's db-component renderer. Its body must still come from the cold\n * single-kind loader.\n */\n private needsColdFetch(\n kind: string,\n platform: string,\n role: ComponentRole,\n ): boolean {\n const resolution = this.resolve(kind, platform, role);\n return (\n resolution === null ||\n (resolution.resolvedBy === \"db\" &&\n resolution.hasComponentSource &&\n resolution.componentSource === null)\n );\n }\n\n /**\n * Cold rows are the authoritative heavy form of warm metadata shells. Keep\n * the same first-row-per-key winner contract, but replace an existing shell\n * so `componentSource` / `propsTransform` can land and repaint the kind.\n */\n private ingestColdRows(rows: readonly KindComponentRow[]): void {\n const winners = new Map<string, KindComponentRow>();\n for (const row of rows) {\n const key = keyOf(row.kind, row.platform, row.role);\n if (!winners.has(key)) winners.set(key, row);\n }\n if (winners.size === 0) return;\n\n const changedKinds = new Set<string>();\n for (const [key, row] of winners) {\n this.db.set(key, row);\n changedKinds.add(row.kind);\n }\n for (const kind of changedKinds) this.bumpKind(kind);\n this.notifyChanged();\n }\n\n /**\n * The eager lightweight single-kind fetch (streaming path): the moment a\n * cloud kind is identified mid-stream, pull ONLY that kind's resolver rows\n * and ingest them so {@link resolve} can answer before — or shortly after —\n * the region completes. Deduped in-flight and by known-miss. Fire-and-forget;\n * failures are loud (the warm list remains the backstop).\n */\n requestComponent(kind: string, platform: string, role: ComponentRole): void {\n const loadForKind = this.options.loadForKind;\n if (!loadForKind) return;\n if (!this.needsColdFetch(kind, platform, role)) return;\n const missKey = keyOf(kind, platform, role);\n const flightKey = `${kind}${KEY_SEPARATOR}${platform}`;\n if (this.coldInFlight.has(flightKey) || this.coldMisses.has(missKey)) {\n return;\n }\n this.coldInFlight.add(flightKey);\n\n void (async () => {\n try {\n this.ingestColdRows(await loadForKind(kind, platform));\n // Record the miss for exactly the requested (kind, platform, role) —\n // rows may exist for OTHER roles on this platform; those must not be\n // suppressed, and this one must not be re-fetched until a refresh.\n if (this.needsColdFetch(kind, platform, role)) {\n this.coldMisses.add(missKey);\n }\n } catch (error) {\n this.reportError({\n source: \"content-ir\",\n message: `component-resolver cold fetch failed for \"${kind}\": ${describe(error)}`,\n ...errorFields(error),\n raw: error,\n });\n } finally {\n this.coldInFlight.delete(flightKey);\n }\n })();\n }\n\n /**\n * Refresh-on-view: re-fetch the warm list and REPLACE the db tier, so an\n * edited `source='db'` component (its `updated_at` bump re-keys the host's\n * compile cache) renders fresh on the next view. Deduped in-flight and\n * rate-limited by `maxAgeMs` (default 10s) — mounting several previews costs\n * one fetch. Server-side edits do NOT push to open clients; the contract is\n * refresh-on-view via this call.\n */\n refresh(maxAgeMs = 10_000): Promise<void> {\n const loadAll = this.options.loadAll;\n if (!loadAll) return Promise.resolve();\n if (this.refreshPromise) return this.refreshPromise;\n if (\n this.lastRefreshAt !== null &&\n this.now() - this.lastRefreshAt < maxAgeMs\n ) {\n return Promise.resolve();\n }\n // Any refresh intent invalidates recorded misses immediately (also cleared\n // in replaceDbRows when the fetch lands) — cheap to re-verify.\n this.coldMisses.clear();\n\n this.refreshPromise = loadAll()\n .then((rows) => {\n this.lastRefreshAt = this.now();\n this.replaceDbRows(rows);\n })\n .catch((error: unknown) => {\n // Loud recovery: the current tier keeps answering; a refresh failure is\n // a real defect (same posture as the warm loader).\n this.reportError({\n source: \"content-ir\",\n message: `component-resolver refresh failed (current resolver tier still serving): ${describe(error)}`,\n ...errorFields(error),\n raw: error,\n });\n })\n .finally(() => {\n this.refreshPromise = null;\n });\n return this.refreshPromise;\n }\n\n /** One list fetch per app session; failed loads retry on the next call. */\n ensureWarm(): Promise<void> {\n const loadAll = this.options.loadAll;\n if (!loadAll) return Promise.resolve();\n if (!this.warmPromise) {\n this.warmPromise = loadAll()\n .then((rows) => {\n this.ingestDbRows(rows);\n })\n .catch((error: unknown) => {\n // Loud recovery: the compiled floor keeps rendering, but a warm\n // failure is a real defect — one console scream (first failure) plus\n // a structured report per attempt, then retryable.\n const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;\n if (!this.warmFailureLogged) {\n this.warmFailureLogged = true;\n console.error(`[content-ir] ${message}`);\n }\n this.reportError({\n source: \"content-ir\",\n message,\n ...errorFields(error),\n raw: error,\n });\n this.warmPromise = null;\n });\n }\n return this.warmPromise;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,133 @@
1
+ import { C as ComponentResolutionSource, S as SystemComponentEntry, K as KindComponentRow, b as ContentIrErrorReporter, c as ComponentRole, a as ComponentResolution } from './component-resolution.types-fvR92hW7.cjs';
2
+ export { J as JsonObject } from './component-resolution.types-fvR92hW7.cjs';
3
+
4
+ /**
5
+ * The Shape System component RESOLVER (rulings R1 + R6): (kind, platform,
6
+ * role) → component, with two tiers:
7
+ *
8
+ * - eager: a COMPILED bootstrap the host ships in its bundle — the
9
+ * trusted-at-boot floor, available at import so the render seam can gate
10
+ * synchronously from the first streamed byte.
11
+ * - warm: one `content_ir.kind_component` list fetch per app session
12
+ * (`ensureWarm`). A DB row for a (kind, platform, role) OVERRIDES the
13
+ * compiled entry once warm; the compiled floor keeps answering until then
14
+ * (and forever, on DB failure).
15
+ *
16
+ * Resolution is SYNCHRONOUS (the render seam calls it per block); the only
17
+ * async work is the loading, off the render path.
18
+ *
19
+ * WHAT MOVED AND WHY. This class was matrx-frontend's `ComponentRegistry`. It
20
+ * is a large part of why a second UI could not render a kind: every tier rule,
21
+ * every repaint counter, every dedupe latch lived in one app. Nothing about it
22
+ * is Next.js, Redux, or Supabase — the only host-specific parts were the two
23
+ * loaders and the error sink, which are now constructor arguments.
24
+ */
25
+
26
+ interface ComponentResolverOptions {
27
+ /**
28
+ * The compiled bootstrap, as a THUNK resolved on first use: a host's system
29
+ * component table is often mid-initialization when this module evaluates
30
+ * (matrx-frontend's registry cluster has a deliberate import cycle).
31
+ */
32
+ compiledEntries?: () => SystemComponentEntry[];
33
+ /** The warm/refresh list load — every `kind_component` row the user may see. */
34
+ loadAll?: () => Promise<KindComponentRow[]>;
35
+ /** The eager single-kind load fired the moment a kind is identified mid-stream. */
36
+ loadForKind?: (kind: string, platform: string) => Promise<KindComponentRow[]>;
37
+ /** Where recovery screams land. Defaults to `console.error`, never silence. */
38
+ reportError?: ContentIrErrorReporter;
39
+ /** Clock seam — the refresh rate limiter. Defaults to `Date.now`. */
40
+ now?: () => number;
41
+ }
42
+ declare class ComponentResolver implements ComponentResolutionSource {
43
+ private compiled;
44
+ private readonly db;
45
+ private warmPromise;
46
+ private warmFailureLogged;
47
+ /**
48
+ * When the last successful wholesale refresh landed, or null for "never".
49
+ * Explicitly nullable rather than 0: with a host-supplied clock that starts
50
+ * near zero, a `0` sentinel makes the FIRST refresh look rate-limited and
51
+ * silently skip.
52
+ */
53
+ private lastRefreshAt;
54
+ private refreshPromise;
55
+ private readonly listeners;
56
+ /**
57
+ * Cold single-kind fetch dedupe (streaming eager path). In-flight is keyed
58
+ * by (kind, platform) — the fetch unit; misses are keyed by (kind, platform,
59
+ * role) so a miss on web/output never suppresses other roles, and CLEARED on
60
+ * every wholesale refresh (a component created mid-session becomes eagerly
61
+ * fetchable again — misses are cheap to re-verify).
62
+ */
63
+ private readonly coldInFlight;
64
+ private readonly coldMisses;
65
+ /** Monotonic db-tier version — the repaint hook's snapshot key. */
66
+ private version;
67
+ /** Per-kind versions + listeners (granular repaint) + wholesale epoch. */
68
+ private readonly kindVersions;
69
+ private readonly kindListeners;
70
+ private epoch;
71
+ private readonly reportError;
72
+ private readonly now;
73
+ private readonly options;
74
+ constructor(options?: ComponentResolverOptions);
75
+ private compiledMap;
76
+ /**
77
+ * Synchronous resolve — the render seam's per-block call. DB override first
78
+ * (once warm), compiled floor second, null for unknown.
79
+ */
80
+ resolve(kind: string, platform: string, role: ComponentRole): ComponentResolution | null;
81
+ /** R6 floor check: compiled-bootstrap membership = always render-trusted. */
82
+ hasCompiled(kind: string, platform: string, role: ComponentRole): boolean;
83
+ /**
84
+ * Pure ingest — the warm landing point and the unit-test seam. First row per
85
+ * key wins: rows arrive is_default-first / sort_order-asc from the source.
86
+ */
87
+ ingestDbRows(rows: readonly KindComponentRow[]): void;
88
+ /**
89
+ * Refresh landing point: REPLACE the db tier wholesale (same
90
+ * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
91
+ * and is_active flips all take effect. Always notifies.
92
+ */
93
+ replaceDbRows(rows: readonly KindComponentRow[]): void;
94
+ getVersion(): number;
95
+ getKindVersion(kind: string): number;
96
+ subscribeKind(kind: string, listener: () => void): () => void;
97
+ subscribe(listener: () => void): () => void;
98
+ private bumpKind;
99
+ private notifyChanged;
100
+ /**
101
+ * A warm metadata shell is resolved enough for routing, but not for the
102
+ * host's db-component renderer. Its body must still come from the cold
103
+ * single-kind loader.
104
+ */
105
+ private needsColdFetch;
106
+ /**
107
+ * Cold rows are the authoritative heavy form of warm metadata shells. Keep
108
+ * the same first-row-per-key winner contract, but replace an existing shell
109
+ * so `componentSource` / `propsTransform` can land and repaint the kind.
110
+ */
111
+ private ingestColdRows;
112
+ /**
113
+ * The eager lightweight single-kind fetch (streaming path): the moment a
114
+ * cloud kind is identified mid-stream, pull ONLY that kind's resolver rows
115
+ * and ingest them so {@link resolve} can answer before — or shortly after —
116
+ * the region completes. Deduped in-flight and by known-miss. Fire-and-forget;
117
+ * failures are loud (the warm list remains the backstop).
118
+ */
119
+ requestComponent(kind: string, platform: string, role: ComponentRole): void;
120
+ /**
121
+ * Refresh-on-view: re-fetch the warm list and REPLACE the db tier, so an
122
+ * edited `source='db'` component (its `updated_at` bump re-keys the host's
123
+ * compile cache) renders fresh on the next view. Deduped in-flight and
124
+ * rate-limited by `maxAgeMs` (default 10s) — mounting several previews costs
125
+ * one fetch. Server-side edits do NOT push to open clients; the contract is
126
+ * refresh-on-view via this call.
127
+ */
128
+ refresh(maxAgeMs?: number): Promise<void>;
129
+ /** One list fetch per app session; failed loads retry on the next call. */
130
+ ensureWarm(): Promise<void>;
131
+ }
132
+
133
+ export { ComponentResolution, ComponentResolutionSource, ComponentResolver, type ComponentResolverOptions, ComponentRole, KindComponentRow, SystemComponentEntry };
@@ -0,0 +1,133 @@
1
+ import { C as ComponentResolutionSource, S as SystemComponentEntry, K as KindComponentRow, b as ContentIrErrorReporter, c as ComponentRole, a as ComponentResolution } from './component-resolution.types-fvR92hW7.js';
2
+ export { J as JsonObject } from './component-resolution.types-fvR92hW7.js';
3
+
4
+ /**
5
+ * The Shape System component RESOLVER (rulings R1 + R6): (kind, platform,
6
+ * role) → component, with two tiers:
7
+ *
8
+ * - eager: a COMPILED bootstrap the host ships in its bundle — the
9
+ * trusted-at-boot floor, available at import so the render seam can gate
10
+ * synchronously from the first streamed byte.
11
+ * - warm: one `content_ir.kind_component` list fetch per app session
12
+ * (`ensureWarm`). A DB row for a (kind, platform, role) OVERRIDES the
13
+ * compiled entry once warm; the compiled floor keeps answering until then
14
+ * (and forever, on DB failure).
15
+ *
16
+ * Resolution is SYNCHRONOUS (the render seam calls it per block); the only
17
+ * async work is the loading, off the render path.
18
+ *
19
+ * WHAT MOVED AND WHY. This class was matrx-frontend's `ComponentRegistry`. It
20
+ * is a large part of why a second UI could not render a kind: every tier rule,
21
+ * every repaint counter, every dedupe latch lived in one app. Nothing about it
22
+ * is Next.js, Redux, or Supabase — the only host-specific parts were the two
23
+ * loaders and the error sink, which are now constructor arguments.
24
+ */
25
+
26
+ interface ComponentResolverOptions {
27
+ /**
28
+ * The compiled bootstrap, as a THUNK resolved on first use: a host's system
29
+ * component table is often mid-initialization when this module evaluates
30
+ * (matrx-frontend's registry cluster has a deliberate import cycle).
31
+ */
32
+ compiledEntries?: () => SystemComponentEntry[];
33
+ /** The warm/refresh list load — every `kind_component` row the user may see. */
34
+ loadAll?: () => Promise<KindComponentRow[]>;
35
+ /** The eager single-kind load fired the moment a kind is identified mid-stream. */
36
+ loadForKind?: (kind: string, platform: string) => Promise<KindComponentRow[]>;
37
+ /** Where recovery screams land. Defaults to `console.error`, never silence. */
38
+ reportError?: ContentIrErrorReporter;
39
+ /** Clock seam — the refresh rate limiter. Defaults to `Date.now`. */
40
+ now?: () => number;
41
+ }
42
+ declare class ComponentResolver implements ComponentResolutionSource {
43
+ private compiled;
44
+ private readonly db;
45
+ private warmPromise;
46
+ private warmFailureLogged;
47
+ /**
48
+ * When the last successful wholesale refresh landed, or null for "never".
49
+ * Explicitly nullable rather than 0: with a host-supplied clock that starts
50
+ * near zero, a `0` sentinel makes the FIRST refresh look rate-limited and
51
+ * silently skip.
52
+ */
53
+ private lastRefreshAt;
54
+ private refreshPromise;
55
+ private readonly listeners;
56
+ /**
57
+ * Cold single-kind fetch dedupe (streaming eager path). In-flight is keyed
58
+ * by (kind, platform) — the fetch unit; misses are keyed by (kind, platform,
59
+ * role) so a miss on web/output never suppresses other roles, and CLEARED on
60
+ * every wholesale refresh (a component created mid-session becomes eagerly
61
+ * fetchable again — misses are cheap to re-verify).
62
+ */
63
+ private readonly coldInFlight;
64
+ private readonly coldMisses;
65
+ /** Monotonic db-tier version — the repaint hook's snapshot key. */
66
+ private version;
67
+ /** Per-kind versions + listeners (granular repaint) + wholesale epoch. */
68
+ private readonly kindVersions;
69
+ private readonly kindListeners;
70
+ private epoch;
71
+ private readonly reportError;
72
+ private readonly now;
73
+ private readonly options;
74
+ constructor(options?: ComponentResolverOptions);
75
+ private compiledMap;
76
+ /**
77
+ * Synchronous resolve — the render seam's per-block call. DB override first
78
+ * (once warm), compiled floor second, null for unknown.
79
+ */
80
+ resolve(kind: string, platform: string, role: ComponentRole): ComponentResolution | null;
81
+ /** R6 floor check: compiled-bootstrap membership = always render-trusted. */
82
+ hasCompiled(kind: string, platform: string, role: ComponentRole): boolean;
83
+ /**
84
+ * Pure ingest — the warm landing point and the unit-test seam. First row per
85
+ * key wins: rows arrive is_default-first / sort_order-asc from the source.
86
+ */
87
+ ingestDbRows(rows: readonly KindComponentRow[]): void;
88
+ /**
89
+ * Refresh landing point: REPLACE the db tier wholesale (same
90
+ * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
91
+ * and is_active flips all take effect. Always notifies.
92
+ */
93
+ replaceDbRows(rows: readonly KindComponentRow[]): void;
94
+ getVersion(): number;
95
+ getKindVersion(kind: string): number;
96
+ subscribeKind(kind: string, listener: () => void): () => void;
97
+ subscribe(listener: () => void): () => void;
98
+ private bumpKind;
99
+ private notifyChanged;
100
+ /**
101
+ * A warm metadata shell is resolved enough for routing, but not for the
102
+ * host's db-component renderer. Its body must still come from the cold
103
+ * single-kind loader.
104
+ */
105
+ private needsColdFetch;
106
+ /**
107
+ * Cold rows are the authoritative heavy form of warm metadata shells. Keep
108
+ * the same first-row-per-key winner contract, but replace an existing shell
109
+ * so `componentSource` / `propsTransform` can land and repaint the kind.
110
+ */
111
+ private ingestColdRows;
112
+ /**
113
+ * The eager lightweight single-kind fetch (streaming path): the moment a
114
+ * cloud kind is identified mid-stream, pull ONLY that kind's resolver rows
115
+ * and ingest them so {@link resolve} can answer before — or shortly after —
116
+ * the region completes. Deduped in-flight and by known-miss. Fire-and-forget;
117
+ * failures are loud (the warm list remains the backstop).
118
+ */
119
+ requestComponent(kind: string, platform: string, role: ComponentRole): void;
120
+ /**
121
+ * Refresh-on-view: re-fetch the warm list and REPLACE the db tier, so an
122
+ * edited `source='db'` component (its `updated_at` bump re-keys the host's
123
+ * compile cache) renders fresh on the next view. Deduped in-flight and
124
+ * rate-limited by `maxAgeMs` (default 10s) — mounting several previews costs
125
+ * one fetch. Server-side edits do NOT push to open clients; the contract is
126
+ * refresh-on-view via this call.
127
+ */
128
+ refresh(maxAgeMs?: number): Promise<void>;
129
+ /** One list fetch per app session; failed loads retry on the next call. */
130
+ ensureWarm(): Promise<void>;
131
+ }
132
+
133
+ export { ComponentResolution, ComponentResolutionSource, ComponentResolver, type ComponentResolverOptions, ComponentRole, KindComponentRow, SystemComponentEntry };