Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .changeset/9641-registry-namespace-by-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@object-ui/cli": patch
---

Stop `objectui check` reporting five real page types as unknown.

`ui:page`, `ui:app`, `ui:utility`, `ui:home` and `ui:record` are registry keys the platform stores and the renderer paints, and `objectui check` called every document that spelled one of them an unknown schema type. The list the check judges against is generated from this repository's registration calls, and the generator could not see a `namespace` that arrives through a reference: the page kinds declare their options once and register from that object — one call passing it whole, four spreading it to vary a label — so there is no `namespace:` inside any of those call spans. The derivation read the bare half of each registration, produced no finding, and shipped a list short by exactly the namespaced half.

The derivation now reads an options object passed by identifier or by top-level spread. It does so from an allowlist of ARGUMENT shapes: an object literal whose top-level entries are all key-value pairs or plain-identifier spreads, or an identifier resolving to one such literal. Every other options ARGUMENT — a cast, a member expression, a call, a spread of any of those, a conditional spread, a computed `namespace` or `skipFallback` — is now reported instead of read as namespace-free, and so is an identifier the derivation declines to follow: one it counts as bound more than once or with `let`, an imported name, or one whose `namespace` or `skipFallback` the file assigns, deletes or `Object.assign`-es in a spelling the guard matches. A validator that refuses what the platform renders is the expensive direction — it teaches authors to stop reading the validator, which costs the opposite direction (a type the check blesses and the runtime rejects) its only reader.

Within a literal the derivation reads, entries are applied in source order and an entry that sets `namespace` or `skipFallback` replaces what an earlier one set — including a base spread twice around an intervening entry, and including an explicit `skipFallback: false` arriving by spread after an explicit `true`. Both of those were read wrongly, in silence, by the first draft of this change.

Refusing a name whose options this file is SEEN to write is the sharper half of that, because its failure direction is a phantom rather than a miss. A `let` may hold a different object by the time the call runs, and a `const` cannot be rebound but its `namespace` can be deleted after declaration — either way the derivation would publish a key the runtime never stores, and the check would bless a spelling that renders nothing. A miss refuses something that renders; a phantom green-lights a spelling that renders nothing.

⚠️ What the instrument does NOT see is stated rather than implied, because a comment claiming more than the code does is the defect this card was filed about. Two rules bound it, and both are narrower than "the object is not rebound or written":

- A name counts as a binding only where it IMMEDIATELY follows `const` / `let` / `var`, or sits in an import clause. A function parameter, a destructuring pattern, a later declarator of the same statement and a `catch` binding are invisible to the count, so a module-level object answers while the call passes a different one.
- A write counts only in three spellings: an assignment or a `delete` whose target is the name spelled exactly followed by `namespace` / `skipFallback` written out, dotted or in a quoted bracket; or an `Object.assign` whose first argument is that name. A write through an alias, inside a callee, with a computed key, as a destructuring-assignment target, or through `Reflect.set` / `Reflect.deleteProperty` / `Object.defineProperty` / `Object.setPrototypeOf` is invisible.

Closing either needs scope and aliasing analysis this regex-level derivation does not do, and a regex approximation of those semantics has no finishing line — so the end state chosen for this card is an accurate declaration instead. Every shape above is pinned as a KNOWN GAP reading that asserts today's silent answer, so closing one later fails a test instead of passing unnoticed.

⛔ Nothing in this repository is KNOWN to hit one of them, and that is a weaker statement than "none is hit" — the whole point of a silent reading is that a clean run does not rule it out. What is re-derived every run is the size of the population a gap could reach: `counters.metaViaReference`, the number of call sites whose options arrive by reference at all. Today that is five sites in one file, all reading the same declaration, the derivation reports zero findings, and the generated list moved by exactly the five namespaced halves and nothing else.

The registration calls themselves are unchanged; so is every key that was already derived. Five keys are added to the generated list and none is removed or renamed.

⚠️ Read against a census, not a guess: every `ComponentRegistry.register` / `registerLazy` call in the tree was classified by how its options argument arrives, because "are these five all of them?" was explicitly unmeasured when this was filed. ⚠️ That census is about CALL SITES and their arguments; it says nothing about the 132 keys whose namespace comes from a hand-kept indirect table rather than from a call, which is a different population with its own failure mode. The five page kinds were the only registrations losing a namespace this way; the one other site reaching its options by reference registers a third-party plugin's own key and is already declared unresolvable-by-design. `deriveRegistryKeys`' `metaViaReference` counter re-derives that population on every run — the number is not written down anywhere, here included.
5 changes: 5 additions & 0 deletions packages/cli/src/utils/known-schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
'ui:address',
'ui:alert',
'ui:alert-dialog',
'ui:app',
'ui:article',
'ui:aside',
'ui:aspect-ratio',
Expand Down Expand Up @@ -561,6 +562,7 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
'ui:h6',
'ui:header',
'ui:header-bar',
'ui:home',
'ui:hover-card',
'ui:hr',
'ui:html',
Expand All @@ -583,13 +585,15 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
'ui:navigation-menu',
'ui:ol',
'ui:p',
'ui:page',
'ui:pagination',
'ui:password',
'ui:popover',
'ui:pre',
'ui:progress',
'ui:q',
'ui:radio-group',
'ui:record',
'ui:resizable',
'ui:scroll-area',
'ui:section',
Expand Down Expand Up @@ -632,6 +636,7 @@ export const KNOWN_SCHEMA_TYPES: readonly string[] = [
'ui:tree-view',
'ui:u',
'ui:ul',
'ui:utility',
'ul',
'url',
'user',
Expand Down
Loading
Loading