This repository was archived by the owner on Oct 26, 2026. It is now read-only.
Repository navigation
Commit 899f381
feat(core,types)!: retire the module widget source and its runtime dynamic import (objectui#13450) (#13473)
Fixes #13450
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Retires the `module` widget source. `WidgetRegistry.load()` resolved `{
type: 'module', url }` with `new Function('url', 'return import(url)')`.
That is the runtime dynamic import AGENTS.md commandment `#6` forbids
("No `eval()` / runtime dynamic imports to load components"). The claim
is the PM's comment `6386913269` on the card, for branch
`claude/issue-13450-retire-widget-module-source`, session
`https://claude.ai/code/session_01432GBvPzfZ6CsLanp9xzkZ`.
## What changes
- **`@object-ui/types`**: the `module` member and its
`WidgetSourceModule` interface are removed. `RuntimeWidgetSource` is now
`WidgetSourceInline | WidgetSourceRegistry`, and the barrel no longer
exports `WidgetSourceModule`. The union's docblock records the
retirement and the migration. The manifest's JSDoc `@example` now uses
`{ type: 'inline', component }` with a locally defined `CustomChart`
component (the build-time import is named in a comment, so the block
compiles in isolation under `check-doc-example-types`) instead of a URL.
- **`@object-ui/core`**: the `case 'module'` arm and its
Function-constructor sink are removed from
`WidgetRegistry.resolveComponent()`. The `default:` arm is now
exhaustive (`const unhandled: never = source`), so a union member added
without an arm fails to compile.
- An untyped manifest that still reaches `load()` with `type: 'module'`
is refused by name before anything is fetched. Untyped means plain JS, a
cast, or an older `@object-ui/types`. The error names the widget, the
retired `"module"` source and both replacements.
- Any other unknown type is refused with the supported types listed.
- In both cases `load()` emits `widget:error` and `loadAll()` reports
the error. The class docblock and its example no longer mention URL
loading.
- **Changeset** `.changeset/13450-retire-widget-module-source.md`:
`minor` for core and types. objectui never declares `major`
(`check-changeset-no-major`); the break is stated in the body as
**BREAKING**. The changeset gives the FROM → TO migration and the
one-line fix, and carries the `Clause-②:` / `Metadata-protocol:` lines.
- `page-nav-misc-spec-parity.test.ts`: the reason string in one
`it.each` row said "module/inline/registry loader union" and now says
"inline/registry". It is a test title, not an assertion.
## Migration (FROM → TO)
- FROM `source: { type: 'module', url: '/widgets/chart.js', exportName:
'Chart' }`
- TO `import { Chart } from './widgets/chart'` and `source: { type:
'inline', component: Chart }` (imported at build time, so the bundler
sees it)
- OR `ComponentRegistry.register('chart', Chart)` once, and `source: {
type: 'registry', registryKey: 'chart' }`
## Premise check: the card's "four emitting sites" are not widget
sources
The card and the triage route name four `type: 'module'` producers. Each
one was read in context at `a3c0da9`. All four are the generated
**`package.json`'s ESM marker** (`"type": "module"`), not a widget
manifest source. Deleting them would make the scaffolded packages
CommonJS and break their `import` syntax. **None was changed:**
| Site (by symbol) | What it builds | Verdict |
|---|---|---|
| `buildAppPackageJson` in `packages/cli/src/utils/app-generator.ts` |
temp app `package.json` (`name`, `private`, `type`, deps) | package.json
ESM marker; kept |
| `buildRoutedAppPackageJson` in the same file | routed temp app
`package.json` | package.json ESM marker; kept |
| `buildInitPackageJson` in `packages/cli/src/commands/init.ts` |
`objectui init` project `package.json` (`scripts.dev: 'vite'`) |
package.json ESM marker; kept |
| `buildPackageJson` in `packages/create-plugin/src/templates.ts` |
generated plugin `package.json` (`main`, `module`, `exports`) |
package.json ESM marker; kept |
So `@object-ui/cli` and `@object-ui/create-plugin` carry no change and
no changeset, which narrows the route. No widget-manifest producer of a
`module` source exists in this tree. A `git grep` for
`WidgetSourceModule` before this change hit only its declaration and the
barrel. `apps/console/src` and `packages/app-shell/src` construct no
`module` source (triage's reading). `objectstack` and `hotcrm` reference
neither `WidgetRegistry` nor `RuntimeWidgetSource` in code (the only
`objectstack` hit is a doc paragraph that does not name the `module`
source). Out-of-tree consumers are NOT MEASURED.
No user-facing doc names the `module` widget source. `content/docs`, the
package READMEs and `docs/` were checked with `git grep`; the control
`ComponentRegistry` hits in `content/docs`. The two JSDoc examples were
the only documentation of it, and both are rewritten.
## Tests (head `e15036c`)
- `be01ada` (patch round): `check-doc-example-types` exit 1 → 0 (34 → 35
compile; `WidgetRegistry #1` back on its TS2304 ledger row). JSDoc text
only.
New `describe('the retired \`module\` source (objectui#13450)')` in
`packages/core/src/registry/__tests__/WidgetRegistry.test.ts`:
1. A manifest with `source: { type: 'module', url }` is refused. The
rejection is an `Error` that names `"test-widget"`, `"module"`, `type:
'inline'` and `type: 'registry'`; these are the named subjects, not the
wording. The global `Function` constructor, proxied for construct and
apply, records zero calls. Nothing is marked loaded or synced into the
component registry. The literal carries `@ts-expect-error`, so the
type-level refusal is pinned too: core's `type-check` runs
`tsconfig.test.json`, which includes `src/**/*.test.ts`.
2. `loadAll()` reports the refusal for that widget, a sibling still
loads, and `widget:error` fires.
3. An unknown type (`remote`) is refused with `"inline"` and
`"registry"` named and no "retired" wording.
- `pnpm exec vitest run packages/core/ --maxWorkers=2`: `Test Files 211
passed (211)`, `Tests 4089 passed | 27 skipped (4116)`
- `pnpm exec vitest run packages/types/ --maxWorkers=2`: `Test Files 388
passed (388)`, `Tests 10568 passed (10568)`
- `pnpm --filter @object-ui/types --filter @object-ui/core run
type-check`: both `Done`, exit 0. This ran after both packages were
rebuilt, so core's test program read the new `dist/*.d.ts`.
**Ablation, runtime** (one-shot, through `ablation-replace.mjs`,
restored to the HEAD blob): a `case 'module'` arm with the
Function-constructor import was re-planted ahead of `default:`. On-disk
marker count was 1, sink count 1. Result: `Tests 2 failed | 24 passed
(26)`. Test 1 received `"A dynamic import callback was not specified."`
instead of the refusal; test 2 failed the same way. Test 3 stayed green,
which is the expected direction. After the restore, `git diff HEAD` was
empty and the blob equalled HEAD. A first attempt used an anchor that
the replacement re-contained; the tool refused it and nothing ran.
**Reverse verification, types** (cross-package): a `{ type: 'module';
url: string }` member was re-added to `RuntimeWidgetSource` and
`@object-ui/types` rebuilt; the dist marker count was 1. Core's `tsc -p
tsconfig.test.json` then reported `TS2578: Unused '@ts-expect-error'
directive` on the new test and `TS2322 ... is not assignable to type
'never'` on the exhaustive arm. After the restore and a rebuild, the
dist marker count was 0 and the same `tsc` was green.
## Gates (local, at `e15036c`)
Exit codes were captured to a file before reading:
- `lint` (core + types): exit 0. 0 errors; the warnings are all
pre-existing `no-explicit-any`, and none is on a changed line.
- `check:new-line-citations` 0 · `check:control-bytes` 0 ·
`check-changeset-presence` 0 · `changeset:check` (fixed + no-major) 0 ·
`check:changeset-claims` 0 · `check:pending-changeset-literals` 0 ·
`check:spec-symbols` 0 · `check:unreferenced-sources` 0 ·
`check:doc-example-readers` 0 · `check:shell-escape-residue` 0 ·
`check:test-path-roots` 0
- `check:readme-exports`: exit 1, NOT MEASURED for the repo as a whole.
The only unjudged population is `@object-ui/app-shell`, which was
unbuilt; every other package was judged with `0 fabricated, 0
wrong-path`. No README names `WidgetSourceModule`.
- `check:published-dist`: exit 1, NOT MEASURED. Its internal
whole-workspace `turbo run build` hit a heap OOM in
`@object-ui/app-shell`, so it judged nothing. That is environmental and
not this diff. CI owns this gate.
`origin/main` was merged before opening; it had not moved from the
branch base `a3c0da9`, so the merge was a no-op. objectui#13442 is in
flight in the same batch on disjoint files
(`packages/core/src/actions/**` and the `DataSource` type). The only
shared surface is the `@object-ui/types` barrel, where this PR removes a
single line in the widget block.
## Acceptance notes
- Remote widgets as a product capability (a signed manifest and an
allow-listed origin, decided in spec) are the maintainer's call and are
not part of this card, per the triage ruling.
- `register()` still accepts a manifest with a retired or unknown
source, and the refusal happens at `load()`. That matches the ruling's
"refuse an old manifest loudly in `default:`" and how the registry
already treats a missing `registryKey`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01432GBvPzfZ6CsLanp9xzkZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 320ec35 commit 899f381
6 files changed
Lines changed: 194 additions & 52 deletions
File tree
- .changeset
- packages
- core/src/registry
- __tests__
- types/src
- __tests__
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
15 | 18 | | |
16 | 19 | | |
17 | 20 | | |
| |||
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
43 | 52 | | |
44 | 53 | | |
45 | 54 | | |
46 | 55 | | |
47 | 56 | | |
48 | 57 | | |
49 | 58 | | |
50 | | - | |
| 59 | + | |
51 | 60 | | |
52 | 61 | | |
53 | 62 | | |
| |||
279 | 288 | | |
280 | 289 | | |
281 | 290 | | |
282 | | - | |
283 | | - | |
284 | | - | |
285 | | - | |
286 | | - | |
287 | | - | |
288 | | - | |
289 | | - | |
290 | | - | |
291 | | - | |
292 | | - | |
293 | | - | |
294 | | - | |
295 | | - | |
296 | | - | |
297 | | - | |
298 | | - | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
299 | 304 | | |
300 | | - | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
301 | 309 | | |
302 | 310 | | |
303 | | - | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
304 | 316 | | |
305 | | - | |
306 | | - | |
307 | | - | |
308 | 317 | | |
309 | 318 | | |
310 | 319 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
9 | 13 | | |
10 | 14 | | |
11 | 15 | | |
| |||
318 | 322 | | |
319 | 323 | | |
320 | 324 | | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
321 | 428 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
789 | 789 | | |
790 | 790 | | |
791 | 791 | | |
792 | | - | |
| 792 | + | |
793 | 793 | | |
794 | 794 | | |
795 | 795 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1557 | 1557 | | |
1558 | 1558 | | |
1559 | 1559 | | |
1560 | | - | |
1561 | 1560 | | |
1562 | 1561 | | |
1563 | 1562 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
28 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
29 | 32 | | |
30 | 33 | | |
31 | 34 | | |
| |||
61 | 64 | | |
62 | 65 | | |
63 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
64 | 73 | | |
65 | 74 | | |
66 | 75 | | |
| |||
69 | 78 | | |
70 | 79 | | |
71 | 80 | | |
72 | | - | |
| 81 | + | |
| 82 | + | |
73 | 83 | | |
74 | 84 | | |
75 | 85 | | |
| |||
139 | 149 | | |
140 | 150 | | |
141 | 151 | | |
142 | | - | |
| 152 | + | |
143 | 153 | | |
144 | 154 | | |
145 | 155 | | |
146 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
147 | 171 | | |
148 | 172 | | |
149 | 173 | | |
| |||
152 | 176 | | |
153 | 177 | | |
154 | 178 | | |
155 | | - | |
156 | 179 | | |
157 | 180 | | |
158 | 181 | | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
165 | | - | |
166 | | - | |
167 | | - | |
168 | | - | |
169 | | - | |
170 | | - | |
171 | | - | |
172 | | - | |
173 | | - | |
174 | | - | |
175 | | - | |
176 | | - | |
177 | | - | |
178 | 182 | | |
179 | 183 | | |
180 | 184 | | |
| |||
0 commit comments