|
| 1 | +--- |
| 2 | +'@object-ui/plugin-dashboard': minor |
| 3 | +'@object-ui/cli': minor |
| 4 | +--- |
| 5 | + |
| 6 | +Converge the bare `dashboard` key on `plugin-dashboard`, and retire |
| 7 | +`view:dashboard` with a by-name tombstone (objectui#9533). |
| 8 | + |
| 9 | +**Breaking for authored metadata.** `{ "type": "view:dashboard" }` no longer |
| 10 | +renders a dashboard. It now resolves to a refusal that names the spelling and |
| 11 | +names its replacement, and `objectui check` reports it as an unknown schema |
| 12 | +type. `{ "type": "dashboard" }` and `{ "type": "plugin-dashboard:dashboard" }` |
| 13 | +both render `DashboardRenderer`, unchanged and newly-working respectively. |
| 14 | + |
| 15 | +**What was wrong.** `apps/console` declares the lazy stub for bare `dashboard` |
| 16 | +under `plugin-dashboard` — twice, in `preview-gallery.tsx` and |
| 17 | +`register-plugins.ts` — while this package registered the renderer as |
| 18 | +`view:dashboard`. Neither site passed `skipFallback`, so both also claimed the |
| 19 | +bare key (`Registry.register` and `Registry.registerLazy` share the |
| 20 | +`meta?.namespace && !meta?.skipFallback` branch, and `registerLazy` has no |
| 21 | +collision check at all, so nothing warned). Two consequences followed, and both |
| 22 | +were reproduced against a real `Registry` before anything here was written: |
| 23 | + |
| 24 | +1. **The bare key was double-claimed, phase-dependently.** After the stub step |
| 25 | + bare `dashboard` declared namespace `plugin-dashboard`; after the chunk |
| 26 | + loaded the same key declared `view`. Which answer a host got depended on when |
| 27 | + it asked — the objectui#6416 shape, which converged `plugin-report` off it. |
| 28 | +2. **The namespaced stub was never cleared.** `register()` clears the lazy stub |
| 29 | + of the type IT registers, and that type was `view:dashboard`. So |
| 30 | + `hasLazy('dashboard', 'plugin-dashboard')` read `true` after the package had |
| 31 | + fully loaded and `get('dashboard', 'plugin-dashboard')` read `undefined`, |
| 32 | + while the generated CLI whitelist listed that spelling as renderable. A node |
| 33 | + authored with it could only ever paint `Loading plugin-dashboard:dashboard…` |
| 34 | + forever: `Registry.loadLazy` resolves whether or not the loaded module |
| 35 | + registered the expected type, and `SchemaRenderer` re-checks `hasLazy` on |
| 36 | + every pass. That is the objectui#8760 shape — a key that passes every |
| 37 | + authoring check and fails only in front of a user. |
| 38 | + |
| 39 | +**What changed.** The renderer registers as `plugin-dashboard:dashboard`, the |
| 40 | +namespace every sibling plugin, both console stubs and the CLI whitelist already |
| 41 | +use, so the console stubs are cleared on load and all three claimants of the bare |
| 42 | +key name ONE full type. The retired `view:dashboard` key answers |
| 43 | +`RetiredDashboardNodeTombstone` — an inline refusal, plus a `console.error` |
| 44 | +carrying the same text — registered with `skipFallback: true` so it claims no |
| 45 | +bare key. Its spelling is withheld from the derived key universe by declaration |
| 46 | +in `scripts/check-doc-component-types.mjs`, the same disposition the |
| 47 | +`RETIRED_FIELD_TYPES` tombstones take, so `objectui check` names it rather than |
| 48 | +blessing it; `packages/cli/src/utils/known-schema-types.ts` regenerates and loses |
| 49 | +that one entry. |
| 50 | + |
| 51 | +**Also published: four new symbols on the package entry.** `src/index.tsx` |
| 52 | +re-exports `RETIRED_DASHBOARD_NODE_TYPES`, `RetiredDashboardNodeTombstone`, |
| 53 | +`reportRetiredDashboardNodeType` and `resetRetiredDashboardNodeTypeReports` from |
| 54 | +`./retired-node-types`, and `exports["."]` is what publishes them — so these are |
| 55 | +**published** surface, not an internal one, and removing or renaming one later is |
| 56 | +a breaking change like any other export. They exist for the same reason |
| 57 | +`@object-ui/fields` publishes its tombstone: the pin has to import the refusal |
| 58 | +text it asserts rather than restate it. |
| 59 | + |
| 60 | +**Also visible in the DOM: the `data-obj-type` value on a dashboard grid.** That |
| 61 | +attribute carries the type the node was AUTHORED with, so the spelling that puts |
| 62 | +a dashboard grid on the page moves with the registration. |
| 63 | +`[data-obj-type="view:dashboard"]` no longer selects one — that node now renders |
| 64 | +the tombstone, which publishes `data-retired-node-type` and no `data-obj-type` at |
| 65 | +all — while `[data-obj-type="plugin-dashboard:dashboard"]` now does. |
| 66 | +`{ "type": "dashboard" }` is unchanged and still reads `dashboard`. A stylesheet, |
| 67 | +a selector or a DOM assertion keyed on the retired value stops matching, and |
| 68 | +stops matching silently, so it is declared here rather than left to be found. |
| 69 | + |
| 70 | +**Ruled, not chosen.** Director seat summon #24, batch #152 item 5 letter 1, |
| 71 | +maintainer 「其他同意」 2026-09-18. The two rejected routes are on the record: |
| 72 | +converging on `view` would leave the whitelist advertising a dead |
| 73 | +`plugin-dashboard:dashboard`, and standing one side down resolves the naming |
| 74 | +contest but not the unsatisfiable stub. |
| 75 | + |
| 76 | +**`Clause-②: yes`**, declared by that ruling: the published full name moves. Note |
| 77 | +the shape, because it is unusual — the accept set **narrows** (an authored |
| 78 | +`view:dashboard` stops resolving) and the clause is `yes` anyway, on the |
| 79 | +published-name move rather than on a widened surface. `minor` per AGENTS.md's |
| 80 | +版本号策略: objectui's own breaking changes take `minor`, with the breaking |
| 81 | +semantics written out here. |
| 82 | + |
| 83 | +`packages/plugin-dashboard/src/__tests__/dashboardBareKeyOwnership.test.tsx` is |
| 84 | +the half that outlives the fix. It reads the package's real declared metadata |
| 85 | +back out of the registry and replays it into a fresh `Registry` in **both** |
| 86 | +orders — eager-then-lazy and lazy-then-eager — checking the bare key's declared |
| 87 | +namespace after every step, so order- and phase-independence are properties under |
| 88 | +test rather than properties of the file it imports. It also asserts `hasLazy` is |
| 89 | +false after the load, and asserts the `view:dashboard` refusal **together with** |
| 90 | +the tombstone's own migration text, because a pin that asserts only "no dashboard |
| 91 | +rendered" passes identically against a registration that was simply deleted — |
| 92 | +the one outcome the ruling refuses. `objectui#9264`'s shrink-only bare-name |
| 93 | +collision ledger loses its row: it is now empty. |
0 commit comments