This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
Commit 610d9ef
feat(components,types)!: retire the raw-HTML html node type (objectui#13449) (#13475)
Fixes #13449
Clause-②: no (narrowing)
Metadata-protocol: narrowed
## What this does
Retires the raw-HTML `html` node type, as the triage grade routed it
(comment 6385669346: "Route: retire, as recommended"). Its renderer in
`@object-ui/components` wrote the authored `html` string into a `div`
through `dangerouslySetInnerHTML` with no sanitiser, and
`@objectstack/spec` declares no `html` component. It is removed, not
sanitised; no deprecation alias. Breaking for a page that still names
it, so the changeset is `minor` with a FROM / TO migration (this
repository's version policy forbids `major`).
- `@object-ui/components`: the `html.tsx` registration (`ui:html` plus
the bare key) and its import in `renderers/basic/index.ts` are gone.
- `@object-ui/core`: `html` leaves `PUBLIC_BLOCKS` (it sat there as the
curated tier's "escape hatch"), so `getPublicConfigs()` and the
published `sdui.manifest.json` no longer carry it.
- `@object-ui/types`: `HtmlSchema` leaves the package entry,
`DataDisplaySchema` and the `SchemaRegistry` map (so `ComponentType` no
longer offers `'html'`). The zod arm becomes `RetiredHtmlNodeSchema`, a
`retiredNodeType('html', …)` refusal inside `DataDisplaySchema`, not
exported: a `type: "html"` node is refused BY NAME at `type`, with the
remedy in the message. This is the shape the maintainer ruled for the
bare `kanban` key (objectui#8802) and that objectui#12954 reused for
`cloud:plan-status`.
- `@object-ui/cli`: `known-schema-types.ts` regenerated (`node
scripts/regenerate-known-schema-types.mjs`; its `--check` answers OK),
so `objectui check` no longer counts `html` / `ui:html`.
- Docs: `content/docs/components/basic/html.mdx` (it said "Render raw
HTML content safely", which was false), its `meta.json` row and its
index link are gone; `guide/react-pages.md` says there is no raw-HTML
block on either page tier. The schema-catalog example
`components-basic-html/basic-html.json` is gone and the catalog index
regenerated (`scripts/regenerate-catalog-index.py`; `--check` OK).
- `@object-ui/fields`: `html` stays in `FIELD_TYPES_SKIP_FALLBACK`,
moved from the "bare-name collision" group to a retired-key note. No
behaviour change: the field editor still stands down from the bare key,
so a stale node draws the unknown-component panel, never a rich-text
editor.
The `html` FIELD type (`HtmlFieldMetadata`, drawn by `field:html`, value
displayed through the rehype-sanitize path objectui#13431 landed) is a
different layer and is untouched.
## Reach, measured before and after (not a source-grep claim)
A temporary probe rendered both doors through the real `SchemaRenderer`
and the full console registration graph (`@object-ui/components` plus
`apps/console/src/register-plugins`), with a payload of an `img` element
carrying an `onerror` attribute plus a bold element:
| door | base `a3c0da9` | this branch |
|---|---|---|
| JSON node `{ "type": "html", "html": PAYLOAD }` | injected: the `img`
with its `onerror` attribute is in the DOM | unknown-component panel
(OBJUI-001), payload shown escaped as text |
| `kind: 'html'` page whose source writes an `html` tag carrying an
`html` attribute | compile reports ZERO diagnostics; the `img` with its
`onerror` attribute is in the DOM | compile refuses the tag
(`forbidden-tag`, `unknown-component`); the page draws its compile
report |
| `getPublicConfigs()` names `html` | yes | no |
The second row is worse than the card stated: the tier
`guide/react-pages.md` documents as "Untrusted OK", parsed and never
executed, could inject raw markup, because the page compiler whitelists
every registered type. The probe was replaced by a committed pin (below)
and deleted.
## Dispatch hypotheses, answered
- H1 holds: sink, import, `HtmlSchema` and the zod literal sat where the
claim read them at `a3c0da9`.
- H2: `content/docs/fields/rich-text.mdx` names `type: 'html'` twice,
and both are the FIELD type (`HtmlFieldMetadata` in the `ts` block;
"HTML Mode" of the rich-text widget in prose). Both stay.
`check:doc-types` had passed the code-block one only because the retired
node registered the same bare key, so it now carries a field-vocabulary
exemption in `scripts/check-doc-component-types.mjs`, worded like the
`richtext` row beside it.
- H3: `apps/console/dist` is not tracked, so nothing in-tree to
regenerate for the manifest; the two tracked generated files that named
the type are regenerated by their own generators (above). objectstack's
tracked `sdui.manifest.json` still names `html`: it is generated from
objectui's pinned build and its staleness gate forces a regenerate on
the next `.objectui-sha` bump (see Acceptance notes).
- H4 holds: no product producer. `git grep` for a quoted `html` over
every tracked file, read hit by hit: apart from what this PR removes
(the docs page, the catalog example, the `data-display-examples.json`
entry, tests), every hit is the field type, a `kind: 'html'` page, a
report export format, a code-editor language, or an HTML tag name.
## File surface beyond the claim's list
The claim named `renderers/basic/**`, the two types files, the docs and
tests naming the component, and the changeset. The dispatch's suggested
route asked for every other list naming `html` as a component type;
those are: `packages/core/src/registry/public-blocks.ts`,
`packages/types/src/{registry.ts,index.ts,zod/index.zod.ts,zod/README.md}`,
`packages/types/examples/data-display-examples.json`,
`packages/cli/src/utils/known-schema-types.ts` (generated),
`packages/fields/src/index.tsx` (regroup and comment only), the catalog
example and its generated index, `scripts/check-doc-component-types.mjs`
(one exemption), `content/docs/guide/react-pages.md`, and count / list
pins in `apps/console`, `packages/app-shell`, `packages/cli`,
`packages/fields`, `packages/types`. No governed surface: `node
scripts/check-governed-queue-guard.mjs --test` over all 31 paths answers
"NOT GOVERNED — 31 path(s) checked against 6 governed surface(s); none
matched."
## Pins (each red on the pre-fix implementation, see Ablation)
- `apps/console/src/__tests__/html-node-retired-13449.test.tsx` — the
`kind: 'html'` page refuses the tag and renders no authored markup; the
JSON node renders no authored markup; neither registry key survives
while `field:html` does; CONTROL: the same page without the tag renders
its `p` text.
- `packages/types/src/__tests__/html-node-retired-13449.test.ts` —
compile-time: no `html` member in `DataDisplaySchema`, no `'html'` in
`ComponentType`, with non-vacuity siblings; runtime: a `custom` issue at
`type` naming the retired type and both replacements; CONTROLS: a
sibling node still parses, an unknown literal takes the generic path
without the retirement text, and a form with an `html` FIELD still
validates.
-
`packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts`
— the strict face (`objectui validate`) refuses `html` by name; the
namespaced refused-at-type pin falls 307 to 306 (`ui:html` left the
registry).
- `packages/components/src/__tests__/basic-renderers.test.tsx`,
`packages/fields/src/__tests__/registry-collision.test.ts` — the old
"renders HTML" / "bare html is the display widget" assertions became
absence pins with lit controls.
- Shifted counts and lists: `zod-mirror-parity` (pair moved to
`EXCLUSIONS`, 205 to 204), `content-channel-family-d-9256` (row left, 67
to 66), `arm-named-export-8784` (`ABSENT_BY_DECISION` row),
`public-contract` and `html-tier-manifest` in `apps/console`,
`widget-dom-leak-sweep` in `app-shell` (target and ledger counts
re-derived without `ui:html`).
## Ablation (one-shot, not committed)
With the fix committed at `bfd3722`, the nine implementation files were
put back from `a3c0da9` (`git checkout a3c0da9 -- …`, which restores
`html.tsx` too), landing proved on disk by count (`register('html'` 1,
the `import './html';` line 1, the `PUBLIC_BLOCKS` `'html',` row 1,
`export const HtmlSchema = BaseSchema.extend` 1, `RetiredHtmlNodeSchema`
0). Tests resolve `@object-ui/*` through the root vitest `resolve.alias`
to `src`, so no rebuild is in the path. The five pin files: `Test Files
5 failed (5)`, `Tests 11 failed | 65 passed (76)`: every new assertion
red, every control green. Restore leg from `HEAD` inside a trap: `git
diff HEAD` 0 bytes, `git status --porcelain` 0 lines, `html.tsx` absent,
every restored path's hash equals its `HEAD` blob.
## Local verification
Union run at `3c6faea` (the head before the last commit), each exit
captured to a file before reading, under the shared verify lock:
- `vitest run packages/types/ packages/core/ packages/sdui-parser/
packages/cli/ examples/schema-catalog/`: `Test Files 697 passed (697)`,
`Tests 17792 passed | 27 skipped (17819)`.
- `vitest run packages/components/ packages/fields/`: `Test Files 705
passed | 2 skipped (707)`, `Tests 8517 passed | 31 skipped (8548)`.
- `vitest run apps/console/`: `Test Files 243 passed (243)`, `Tests 2460
passed (2460)`.
- 48 reader files outside those packages (every test that names
`PUBLIC_BLOCKS`, `getPublicConfigs`, `KNOWN_SCHEMA_TYPES`,
`FIELD_TYPES_SKIP_FALLBACK`, the examples file, the catalog,
`react-pages.md`, `ui:html`, or the edited doc-types gate, plus
`unit-registry-absence-collision`): `Test Files 48 passed (48)`, `Tests
1543 passed | 16 skipped (1559)`.
- type-check: `core`, `components`, `fields`, `cli` exit 0; `types` exit
2 on one vacuous compile-time pin in the new test file (`'kbd'` is not a
`SchemaRegistry` key), fixed in `bfd3722`.
Re-run at the final head `bfd3722` (it changes only that one test file):
`type-check` of `types`, `core`, `components`, `fields`, `cli` all exit
0; the changed file `Test Files 1 passed (1)`, `Tests 6 passed (6)`.
Gates at `bfd3722`, all exit 0: `check:doc-types`,
`check:registry-bare-names`, `check:handler-key-reads`,
`check:doc-example-ids`, `check:docs-route-closure`,
`check:prompt-keys`, `check:entry-guard`, `check:action-forward-parity`,
`check:spec-symbols`, `check:new-line-citations` (VERDICT 0 new
citations), `check:control-bytes`, `check:doc-fences`,
`check:unreferenced-sources`, `check:side-effects-array`,
`check:doc-example-readers`, `check:icon-record-names`,
`check:changeset-claims`, `check:pending-changeset-literals`,
`check:test-path-roots`, `check:doc-json`, `check-changeset-presence`
("22 source file(s) of 7 released package(s) changed, and this change
declares 1 changeset(s)"), `check-changeset-no-major`,
`regenerate-catalog-index.py --check`,
`regenerate-known-schema-types.mjs --check`.
NOT MEASURED locally, left to CI: `check:sdui-registration-pins` (exit
2, needs a console build), `check:readme-exports`, `check:doc-snippets`,
`check:doc-examples` (exit 2 / collapsed population, need the wider
package build), and the `apps/console` and `packages/app-shell`
type-checks (need the whole build closure).
## Acceptance notes
- objectstack's repo-root `sdui.manifest.json` names `html`
(`"description": "Raw HTML content"`). It is generated from objectui's
pinned build by `scripts/gen-sdui-manifest-node.mjs`, and
`check-sdui-manifest`'s staleness check forces the regenerate on the
next `.objectui-sha` bump, which drops the entry. Carrier: that pin
bump. Until then objectstack's server-side `validateJsxPages` still
admits the tag, while the console's own compile already refuses it.
- objectstack's ADR-0080 table names `renderers/basic/html.tsx` as the
"today unsafe" escape hatch the constrained JSX tier "replaces"; this PR
completes that replacement. The ADR is a dated decision record and is
left as written.
- `docs/audits/2026-08-zod-to-json-schema-fidelity.md` still lists
`HtmlSchema`: a dated audit, left as written.
Session: `https://claude.ai/code/session_01432GBvPzfZ6CsLanp9xzkZ`
---
_Generated by [Claude
Code](https://claude.ai/code/session_01432GBvPzfZ6CsLanp9xzkZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent ee4f1ef commit 610d9ef
31 files changed
Lines changed: 487 additions & 227 deletions
File tree
- .changeset
- apps/console/src/__tests__
- content/docs
- components
- basic
- guide
- examples/schema-catalog/src
- schemas/components-basic-html
- packages
- app-shell/src/__tests__
- cli/src
- __tests__
- utils
- components/src
- __tests__
- renderers/basic
- core/src/registry
- fields/src
- __tests__
- types
- examples
- src
- __tests__
- zod
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
202 | 202 | | |
203 | 203 | | |
204 | 204 | | |
205 | | - | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
206 | 211 | | |
207 | 212 | | |
208 | 213 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
112 | 112 | | |
113 | 113 | | |
114 | 114 | | |
115 | | - | |
| 115 | + | |
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
| |||
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
9 | 8 | | |
10 | 9 | | |
11 | 10 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
49 | 48 | | |
50 | 49 | | |
51 | 50 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
350 | 350 | | |
351 | 351 | | |
352 | 352 | | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
353 | 359 | | |
354 | 360 | | |
355 | 361 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
43 | 42 | | |
44 | 43 | | |
45 | 44 | | |
| |||
704 | 703 | | |
705 | 704 | | |
706 | 705 | | |
707 | | - | |
708 | | - | |
709 | | - | |
710 | | - | |
711 | | - | |
712 | | - | |
713 | | - | |
714 | | - | |
715 | | - | |
716 | 706 | | |
717 | 707 | | |
718 | 708 | | |
| |||
This file was deleted.
0 commit comments