@ai-matrx/content-ir-react 0.12.7 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/resolver.cjs CHANGED
@@ -153,6 +153,7 @@ var ComponentResolver = class {
153
153
  * key wins: rows arrive is_default-first / sort_order-asc from the source.
154
154
  */
155
155
  ingestDbRows(rows) {
156
+ const wasSettled = this.hasSettled();
156
157
  const changedKinds = /* @__PURE__ */ new Set();
157
158
  for (const row of rows) {
158
159
  const key = keyOf(row.kind, row.platform, row.role);
@@ -165,6 +166,27 @@ var ComponentResolver = class {
165
166
  for (const kind of changedKinds) this.bumpKind(kind);
166
167
  this.notifyChanged();
167
168
  }
169
+ if (!wasSettled && this.hasSettled()) this.notifySettled();
170
+ }
171
+ /**
172
+ * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind
173
+ * the tier could not answer; settling changes that verdict for ALL of them,
174
+ * so the flip bumps the epoch and calls every per-kind listener (as a
175
+ * wholesale replace does). Before 0.13.0 a settle notified only kinds that
176
+ * gained rows, and a waiting block for any other kind kept its pre-settle
177
+ * rendering until something unrelated re-rendered it.
178
+ */
179
+ markSettled() {
180
+ const wasSettled = this.hasSettled();
181
+ this.settled = true;
182
+ if (!wasSettled) this.notifySettled();
183
+ }
184
+ notifySettled() {
185
+ this.epoch += 1;
186
+ for (const set of this.kindListeners.values()) {
187
+ for (const listener of set) listener();
188
+ }
189
+ this.notifyChanged();
168
190
  }
169
191
  /**
170
192
  * Refresh landing point: REPLACE the db tier wholesale (same
@@ -261,6 +283,7 @@ var ComponentResolver = class {
261
283
  return;
262
284
  }
263
285
  this.coldInFlight.add(flightKey);
286
+ const versionBefore = this.kindVersions.get(kind) ?? 0;
264
287
  void (async () => {
265
288
  try {
266
289
  this.ingestColdRows(await loadForKind(kind, platform));
@@ -276,6 +299,9 @@ var ComponentResolver = class {
276
299
  });
277
300
  } finally {
278
301
  this.coldInFlight.delete(flightKey);
302
+ if ((this.kindVersions.get(kind) ?? 0) === versionBefore) {
303
+ this.bumpKind(kind);
304
+ }
279
305
  }
280
306
  })();
281
307
  }
@@ -317,7 +343,7 @@ var ComponentResolver = class {
317
343
  if (!this.warmPromise) {
318
344
  this.warmPromise = loadAll().then((rows) => {
319
345
  this.ingestDbRows(rows);
320
- this.settled = true;
346
+ this.markSettled();
321
347
  this.warmFailures = 0;
322
348
  if (this.warmRetryTimer) {
323
349
  clearTimeout(this.warmRetryTimer);
@@ -335,7 +361,7 @@ var ComponentResolver = class {
335
361
  this.warmFailures += 1;
336
362
  const delay = WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;
337
363
  if (delay === null) {
338
- this.settled = true;
364
+ this.markSettled();
339
365
  } else if (!this.warmRetryTimer) {
340
366
  this.warmRetryTimer = setTimeout(() => {
341
367
  this.warmRetryTimer = null;
@@ -1 +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\n/**\n * Warm-load retry backoff. Sized for the auth-hydration race: a fresh tab's\n * first list load can run before the Supabase session restores and fail with\n * a permission error; the session is typically live within a second or two,\n * so the first retry usually completes the load. After the last delay the\n * resolver settles on whatever it has — see the failure branch in ensureWarm.\n */\nconst WARM_RETRY_DELAYS_MS: readonly number[] = [1_000, 5_000, 15_000];\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 /** Consecutive warm-load failures — indexes WARM_RETRY_DELAYS_MS. */\n private warmFailures = 0;\n private warmRetryTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * True once a list load has RUN TO COMPLETION (success or failure), or once\n * any rows have been ingested by any tier. Until then `resolve()` answering\n * null means \"I have not looked yet\" — never \"no such component\". See THE\n * COLD-VERDICT RULE on `ComponentResolutionSource.hasSettled`.\n */\n private settled = 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 // A wholesale replace IS a completed list load — the refresh landing\n // point, and the seam tests use to declare \"the list loaded\". Settling\n // here keeps THE COLD-VERDICT RULE from holding blocks forever in any\n // host that warms through refresh() rather than ensureWarm().\n this.settled = true;\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 this.settled = true;\n this.warmFailures = 0;\n if (this.warmRetryTimer) {\n clearTimeout(this.warmRetryTimer);\n this.warmRetryTimer = null;\n }\n })\n .catch((error: unknown) => {\n // The host owns presentation and severity through reportError. A\n // direct console.error here duplicates that report and prevents a\n // host from classifying a still-serving compiled fallback as a\n // recoverable warning.\n const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;\n this.reportError({\n source: \"content-ir\",\n message,\n ...errorFields(error),\n raw: error,\n });\n this.warmPromise = null;\n // 🚨 A FAILED load is NOT truth (2026-08-31). The 0.9.0 posture —\n // \"a failed load still settles\" — turned the auth-hydration race\n // into a lie: a fresh tab fires this load BEFORE the Supabase\n // session hydrates, the read runs as `anon`, RLS answers\n // 42501 permission denied, and settling on that failure declared an\n // EMPTY resolver to be the loaded state of the world. Every kind on\n // the page then routed to the generic floor as \"unregistered\" —\n // while the same rows were one authenticated retry away. So: retry\n // with backoff, and settle only when retries are exhausted (the\n // backend really is down; the generic floor + the per-attempt\n // scream above are then the honest, announced stand-in).\n this.warmFailures += 1;\n const delay =\n WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;\n if (delay === null) {\n this.settled = true;\n } else if (!this.warmRetryTimer) {\n this.warmRetryTimer = setTimeout(() => {\n this.warmRetryTimer = null;\n void this.ensureWarm();\n }, delay);\n }\n });\n }\n return this.warmPromise;\n }\n\n /** @see ComponentResolutionSource.hasSettled */\n hasSettled(): boolean {\n // No list loader configured = nothing to wait for: the compiled floor IS\n // the complete truth for this resolver, so it is settled from birth. (A\n // host that never wired a loader must not have every block held forever.)\n if (!this.options.loadAll) return true;\n return this.settled || this.db.size > 0;\n }\n}\n"],"mappings":"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","names":[]}
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\n/**\n * Warm-load retry backoff. Sized for the auth-hydration race: a fresh tab's\n * first list load can run before the Supabase session restores and fail with\n * a permission error; the session is typically live within a second or two,\n * so the first retry usually completes the load. After the last delay the\n * resolver settles on whatever it has — see the failure branch in ensureWarm.\n */\nconst WARM_RETRY_DELAYS_MS: readonly number[] = [1_000, 5_000, 15_000];\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 /** Consecutive warm-load failures — indexes WARM_RETRY_DELAYS_MS. */\n private warmFailures = 0;\n private warmRetryTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * True once a list load has RUN TO COMPLETION (success or failure), or once\n * any rows have been ingested by any tier. Until then `resolve()` answering\n * null means \"I have not looked yet\" — never \"no such component\". See THE\n * COLD-VERDICT RULE on `ComponentResolutionSource.hasSettled`.\n */\n private settled = 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 wasSettled = this.hasSettled();\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 // The first rows settle the tier (hasSettled) — every waiting kind hears it.\n if (!wasSettled && this.hasSettled()) this.notifySettled();\n }\n\n /**\n * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind\n * the tier could not answer; settling changes that verdict for ALL of them,\n * so the flip bumps the epoch and calls every per-kind listener (as a\n * wholesale replace does). Before 0.13.0 a settle notified only kinds that\n * gained rows, and a waiting block for any other kind kept its pre-settle\n * rendering until something unrelated re-rendered it.\n */\n private markSettled(): void {\n const wasSettled = this.hasSettled();\n this.settled = true;\n if (!wasSettled) this.notifySettled();\n }\n\n private notifySettled(): void {\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 /**\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 // A wholesale replace IS a completed list load — the refresh landing\n // point, and the seam tests use to declare \"the list loaded\". Settling\n // here keeps THE COLD-VERDICT RULE from holding blocks forever in any\n // host that warms through refresh() rather than ensureWarm().\n this.settled = true;\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 const versionBefore = this.kindVersions.get(kind) ?? 0;\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 // The kind's single-kind fetch answered (rows, a miss, or a failure):\n // a block waiting on THIS kind re-routes now. A landing already bumped\n // inside ingestColdRows; a miss or failure changed nothing in the\n // tier, but the waiting block must still be told to re-ask.\n if ((this.kindVersions.get(kind) ?? 0) === versionBefore) {\n this.bumpKind(kind);\n }\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 this.markSettled();\n this.warmFailures = 0;\n if (this.warmRetryTimer) {\n clearTimeout(this.warmRetryTimer);\n this.warmRetryTimer = null;\n }\n })\n .catch((error: unknown) => {\n // The host owns presentation and severity through reportError. A\n // direct console.error here duplicates that report and prevents a\n // host from classifying a still-serving compiled fallback as a\n // recoverable warning.\n const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;\n this.reportError({\n source: \"content-ir\",\n message,\n ...errorFields(error),\n raw: error,\n });\n this.warmPromise = null;\n // 🚨 A FAILED load is NOT truth (2026-08-31). The 0.9.0 posture —\n // \"a failed load still settles\" — turned the auth-hydration race\n // into a lie: a fresh tab fires this load BEFORE the Supabase\n // session hydrates, the read runs as `anon`, RLS answers\n // 42501 permission denied, and settling on that failure declared an\n // EMPTY resolver to be the loaded state of the world. Every kind on\n // the page then routed to the generic floor as \"unregistered\" —\n // while the same rows were one authenticated retry away. So: retry\n // with backoff, and settle only when retries are exhausted (the\n // backend really is down; the generic floor + the per-attempt\n // scream above are then the honest, announced stand-in).\n this.warmFailures += 1;\n const delay =\n WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;\n if (delay === null) {\n this.markSettled();\n } else if (!this.warmRetryTimer) {\n this.warmRetryTimer = setTimeout(() => {\n this.warmRetryTimer = null;\n void this.ensureWarm();\n }, delay);\n }\n });\n }\n return this.warmPromise;\n }\n\n /** @see ComponentResolutionSource.hasSettled */\n hasSettled(): boolean {\n // No list loader configured = nothing to wait for: the compiled floor IS\n // the complete truth for this resolver, so it is settled from birth. (A\n // host that never wired a loader must not have every block held forever.)\n if (!this.options.loadAll) return true;\n return this.settled || this.db.size > 0;\n }\n}\n"],"mappings":"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","names":[]}
@@ -94,6 +94,16 @@ declare class ComponentResolver implements ComponentResolutionSource {
94
94
  * key wins: rows arrive is_default-first / sort_order-asc from the source.
95
95
  */
96
96
  ingestDbRows(rows: readonly KindComponentRow[]): void;
97
+ /**
98
+ * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind
99
+ * the tier could not answer; settling changes that verdict for ALL of them,
100
+ * so the flip bumps the epoch and calls every per-kind listener (as a
101
+ * wholesale replace does). Before 0.13.0 a settle notified only kinds that
102
+ * gained rows, and a waiting block for any other kind kept its pre-settle
103
+ * rendering until something unrelated re-rendered it.
104
+ */
105
+ private markSettled;
106
+ private notifySettled;
97
107
  /**
98
108
  * Refresh landing point: REPLACE the db tier wholesale (same
99
109
  * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
@@ -94,6 +94,16 @@ declare class ComponentResolver implements ComponentResolutionSource {
94
94
  * key wins: rows arrive is_default-first / sort_order-asc from the source.
95
95
  */
96
96
  ingestDbRows(rows: readonly KindComponentRow[]): void;
97
+ /**
98
+ * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind
99
+ * the tier could not answer; settling changes that verdict for ALL of them,
100
+ * so the flip bumps the epoch and calls every per-kind listener (as a
101
+ * wholesale replace does). Before 0.13.0 a settle notified only kinds that
102
+ * gained rows, and a waiting block for any other kind kept its pre-settle
103
+ * rendering until something unrelated re-rendered it.
104
+ */
105
+ private markSettled;
106
+ private notifySettled;
97
107
  /**
98
108
  * Refresh landing point: REPLACE the db tier wholesale (same
99
109
  * first-row-per-key contract as {@link ingestDbRows}) so edits, deletions,
package/dist/resolver.js CHANGED
@@ -127,6 +127,7 @@ var ComponentResolver = class {
127
127
  * key wins: rows arrive is_default-first / sort_order-asc from the source.
128
128
  */
129
129
  ingestDbRows(rows) {
130
+ const wasSettled = this.hasSettled();
130
131
  const changedKinds = /* @__PURE__ */ new Set();
131
132
  for (const row of rows) {
132
133
  const key = keyOf(row.kind, row.platform, row.role);
@@ -139,6 +140,27 @@ var ComponentResolver = class {
139
140
  for (const kind of changedKinds) this.bumpKind(kind);
140
141
  this.notifyChanged();
141
142
  }
143
+ if (!wasSettled && this.hasSettled()) this.notifySettled();
144
+ }
145
+ /**
146
+ * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind
147
+ * the tier could not answer; settling changes that verdict for ALL of them,
148
+ * so the flip bumps the epoch and calls every per-kind listener (as a
149
+ * wholesale replace does). Before 0.13.0 a settle notified only kinds that
150
+ * gained rows, and a waiting block for any other kind kept its pre-settle
151
+ * rendering until something unrelated re-rendered it.
152
+ */
153
+ markSettled() {
154
+ const wasSettled = this.hasSettled();
155
+ this.settled = true;
156
+ if (!wasSettled) this.notifySettled();
157
+ }
158
+ notifySettled() {
159
+ this.epoch += 1;
160
+ for (const set of this.kindListeners.values()) {
161
+ for (const listener of set) listener();
162
+ }
163
+ this.notifyChanged();
142
164
  }
143
165
  /**
144
166
  * Refresh landing point: REPLACE the db tier wholesale (same
@@ -235,6 +257,7 @@ var ComponentResolver = class {
235
257
  return;
236
258
  }
237
259
  this.coldInFlight.add(flightKey);
260
+ const versionBefore = this.kindVersions.get(kind) ?? 0;
238
261
  void (async () => {
239
262
  try {
240
263
  this.ingestColdRows(await loadForKind(kind, platform));
@@ -250,6 +273,9 @@ var ComponentResolver = class {
250
273
  });
251
274
  } finally {
252
275
  this.coldInFlight.delete(flightKey);
276
+ if ((this.kindVersions.get(kind) ?? 0) === versionBefore) {
277
+ this.bumpKind(kind);
278
+ }
253
279
  }
254
280
  })();
255
281
  }
@@ -291,7 +317,7 @@ var ComponentResolver = class {
291
317
  if (!this.warmPromise) {
292
318
  this.warmPromise = loadAll().then((rows) => {
293
319
  this.ingestDbRows(rows);
294
- this.settled = true;
320
+ this.markSettled();
295
321
  this.warmFailures = 0;
296
322
  if (this.warmRetryTimer) {
297
323
  clearTimeout(this.warmRetryTimer);
@@ -309,7 +335,7 @@ var ComponentResolver = class {
309
335
  this.warmFailures += 1;
310
336
  const delay = WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;
311
337
  if (delay === null) {
312
- this.settled = true;
338
+ this.markSettled();
313
339
  } else if (!this.warmRetryTimer) {
314
340
  this.warmRetryTimer = setTimeout(() => {
315
341
  this.warmRetryTimer = null;
@@ -1 +1 @@
1
- {"version":3,"sources":["../host/error-report.ts","../resolver/component-resolver.ts"],"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 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\n/**\n * Warm-load retry backoff. Sized for the auth-hydration race: a fresh tab's\n * first list load can run before the Supabase session restores and fail with\n * a permission error; the session is typically live within a second or two,\n * so the first retry usually completes the load. After the last delay the\n * resolver settles on whatever it has — see the failure branch in ensureWarm.\n */\nconst WARM_RETRY_DELAYS_MS: readonly number[] = [1_000, 5_000, 15_000];\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 /** Consecutive warm-load failures — indexes WARM_RETRY_DELAYS_MS. */\n private warmFailures = 0;\n private warmRetryTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * True once a list load has RUN TO COMPLETION (success or failure), or once\n * any rows have been ingested by any tier. Until then `resolve()` answering\n * null means \"I have not looked yet\" — never \"no such component\". See THE\n * COLD-VERDICT RULE on `ComponentResolutionSource.hasSettled`.\n */\n private settled = 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 // A wholesale replace IS a completed list load — the refresh landing\n // point, and the seam tests use to declare \"the list loaded\". Settling\n // here keeps THE COLD-VERDICT RULE from holding blocks forever in any\n // host that warms through refresh() rather than ensureWarm().\n this.settled = true;\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 this.settled = true;\n this.warmFailures = 0;\n if (this.warmRetryTimer) {\n clearTimeout(this.warmRetryTimer);\n this.warmRetryTimer = null;\n }\n })\n .catch((error: unknown) => {\n // The host owns presentation and severity through reportError. A\n // direct console.error here duplicates that report and prevents a\n // host from classifying a still-serving compiled fallback as a\n // recoverable warning.\n const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;\n this.reportError({\n source: \"content-ir\",\n message,\n ...errorFields(error),\n raw: error,\n });\n this.warmPromise = null;\n // 🚨 A FAILED load is NOT truth (2026-08-31). The 0.9.0 posture —\n // \"a failed load still settles\" — turned the auth-hydration race\n // into a lie: a fresh tab fires this load BEFORE the Supabase\n // session hydrates, the read runs as `anon`, RLS answers\n // 42501 permission denied, and settling on that failure declared an\n // EMPTY resolver to be the loaded state of the world. Every kind on\n // the page then routed to the generic floor as \"unregistered\" —\n // while the same rows were one authenticated retry away. So: retry\n // with backoff, and settle only when retries are exhausted (the\n // backend really is down; the generic floor + the per-attempt\n // scream above are then the honest, announced stand-in).\n this.warmFailures += 1;\n const delay =\n WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;\n if (delay === null) {\n this.settled = true;\n } else if (!this.warmRetryTimer) {\n this.warmRetryTimer = setTimeout(() => {\n this.warmRetryTimer = null;\n void this.ensureWarm();\n }, delay);\n }\n });\n }\n return this.warmPromise;\n }\n\n /** @see ComponentResolutionSource.hasSettled */\n hasSettled(): boolean {\n // No list loader configured = nothing to wait for: the compiled floor IS\n // the complete truth for this resolver, so it is settled from birth. (A\n // host that never wired a loader must not have every block held forever.)\n if (!this.options.loadAll) return true;\n return this.settled || this.db.size > 0;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../host/error-report.ts","../resolver/component-resolver.ts"],"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 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\n/**\n * Warm-load retry backoff. Sized for the auth-hydration race: a fresh tab's\n * first list load can run before the Supabase session restores and fail with\n * a permission error; the session is typically live within a second or two,\n * so the first retry usually completes the load. After the last delay the\n * resolver settles on whatever it has — see the failure branch in ensureWarm.\n */\nconst WARM_RETRY_DELAYS_MS: readonly number[] = [1_000, 5_000, 15_000];\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 /** Consecutive warm-load failures — indexes WARM_RETRY_DELAYS_MS. */\n private warmFailures = 0;\n private warmRetryTimer: ReturnType<typeof setTimeout> | null = null;\n /**\n * True once a list load has RUN TO COMPLETION (success or failure), or once\n * any rows have been ingested by any tier. Until then `resolve()` answering\n * null means \"I have not looked yet\" — never \"no such component\". See THE\n * COLD-VERDICT RULE on `ComponentResolutionSource.hasSettled`.\n */\n private settled = 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 wasSettled = this.hasSettled();\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 // The first rows settle the tier (hasSettled) — every waiting kind hears it.\n if (!wasSettled && this.hasSettled()) this.notifySettled();\n }\n\n /**\n * Mark the tier settled. THE COLD-VERDICT RULE held every block whose kind\n * the tier could not answer; settling changes that verdict for ALL of them,\n * so the flip bumps the epoch and calls every per-kind listener (as a\n * wholesale replace does). Before 0.13.0 a settle notified only kinds that\n * gained rows, and a waiting block for any other kind kept its pre-settle\n * rendering until something unrelated re-rendered it.\n */\n private markSettled(): void {\n const wasSettled = this.hasSettled();\n this.settled = true;\n if (!wasSettled) this.notifySettled();\n }\n\n private notifySettled(): void {\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 /**\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 // A wholesale replace IS a completed list load — the refresh landing\n // point, and the seam tests use to declare \"the list loaded\". Settling\n // here keeps THE COLD-VERDICT RULE from holding blocks forever in any\n // host that warms through refresh() rather than ensureWarm().\n this.settled = true;\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 const versionBefore = this.kindVersions.get(kind) ?? 0;\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 // The kind's single-kind fetch answered (rows, a miss, or a failure):\n // a block waiting on THIS kind re-routes now. A landing already bumped\n // inside ingestColdRows; a miss or failure changed nothing in the\n // tier, but the waiting block must still be told to re-ask.\n if ((this.kindVersions.get(kind) ?? 0) === versionBefore) {\n this.bumpKind(kind);\n }\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 this.markSettled();\n this.warmFailures = 0;\n if (this.warmRetryTimer) {\n clearTimeout(this.warmRetryTimer);\n this.warmRetryTimer = null;\n }\n })\n .catch((error: unknown) => {\n // The host owns presentation and severity through reportError. A\n // direct console.error here duplicates that report and prevents a\n // host from classifying a still-serving compiled fallback as a\n // recoverable warning.\n const message = `component-resolver warm load failed (compiled bootstrap still serving): ${describe(error)}`;\n this.reportError({\n source: \"content-ir\",\n message,\n ...errorFields(error),\n raw: error,\n });\n this.warmPromise = null;\n // 🚨 A FAILED load is NOT truth (2026-08-31). The 0.9.0 posture —\n // \"a failed load still settles\" — turned the auth-hydration race\n // into a lie: a fresh tab fires this load BEFORE the Supabase\n // session hydrates, the read runs as `anon`, RLS answers\n // 42501 permission denied, and settling on that failure declared an\n // EMPTY resolver to be the loaded state of the world. Every kind on\n // the page then routed to the generic floor as \"unregistered\" —\n // while the same rows were one authenticated retry away. So: retry\n // with backoff, and settle only when retries are exhausted (the\n // backend really is down; the generic floor + the per-attempt\n // scream above are then the honest, announced stand-in).\n this.warmFailures += 1;\n const delay =\n WARM_RETRY_DELAYS_MS[this.warmFailures - 1] ?? null;\n if (delay === null) {\n this.markSettled();\n } else if (!this.warmRetryTimer) {\n this.warmRetryTimer = setTimeout(() => {\n this.warmRetryTimer = null;\n void this.ensureWarm();\n }, delay);\n }\n });\n }\n return this.warmPromise;\n }\n\n /** @see ComponentResolutionSource.hasSettled */\n hasSettled(): boolean {\n // No list loader configured = nothing to wait for: the compiled floor IS\n // the complete truth for this resolver, so it is settled from birth. (A\n // host that never wired a loader must not have every block held forever.)\n if (!this.options.loadAll) return true;\n return this.settled || this.db.size > 0;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-matrx/content-ir-react",
3
- "version": "0.12.7",
3
+ "version": "0.13.0",
4
4
  "description": "The shared AI Matrx Content IR RENDER layer: kind routing, the component resolver, provisional (streaming) rendering, the runtime-wrapper chrome, and the generic structured floor — as portable React.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",