Blocked-by: #5439
Found while implementing #5365. That card's acceptance was "the consumer-side pin goes green", and it does for @object-ui/fields (235 errors to 0, pin landed). @object-ui/app-shell went from 1097 to 23, so the pin was used as a measuring instrument there and deliberately NOT landed — the residue is not #5365's defect and would have made the pin a red enforcement on day one.
(Generics below are written with a space after < — GitHub's body sanitizer strips < followed by a letter as an HTML tag, and it ate them on the first revision of this issue.)
What was measured, 2026-08-20
Worktree off main32ef595f4, full closure built, pnpm --filter @object-ui/app-shell exec tsc --noEmit:
state of packages/app-shell/tsconfig.json | typings of components/layout | errors |
|---|
nodenext pin | before #5365 | 1097 — 880 TS2305, 215 TS7006, 2 TS2345 |
nodenext pin | after #5365 | 23 — 21 TS7006, 2 TS2345 |
| no pin (control) | after #5365 | 0 |
The control is the load-bearing row: app-shell type-checks clean today, so all 23 are produced by the pin.
The 21 TS7006 are #5439, not a defect here
They are implicit-any parameters at call sites of symbols imported from @object-ui/plugin-chatbot, which ships extensionless typings of its own. Because that package re-exports through NAMED re-exports rather than export *, the names still exist and merely lose their types — see #5439 for the two-way probe. Fixing that package should clear all 21; nothing in app-shell needs to change for them.
The 2 TS2345 are a real, separate decision
src/views/metadata-admin/JsonSourceEditor.tsx(30,31): error TS2345:
Argument of type '() => Promise< { default: typeof import("@monaco-editor/react"); } >'
is not assignable to parameter of type '() => Promise< { default: ComponentType< any >; } >'.
Same shape at src/views/metadata-admin/previews/SourcePageEditor.tsx(30,31). Both are React.lazy(() => import('@monaco-editor/react')).
Under bundler resolution the module's default export reads as the component. Under node16/nodenext the CJS/ESM interop shape of @monaco-editor/react@4.7.0 resolves differently and the namespace object — not the component — lands in default, so React.lazy rejects it. This is a genuine interop question about a third-party dependency, not an extension question, and the answer is a choice:
- take the named export (
import('@monaco-editor/react').then(m => ({ default: m.Editor }))) — smallest, and arguably more correct than relying on a default; - add the dependency to an interop-shim surface;
- leave
app-shell unpinned until the dependency itself ships dual typings.
Worth a decision rather than a guess, because the same interop class will meet every other CJS-shaped dependency the moment another consumer pins.
Acceptance
packages/app-shell/tsconfig.json carries the two module/moduleResolution: nodenext lines that packages/react and packages/fields already carry, and pnpm --filter @object-ui/app-shell type-check is green.
Refs #5365, #5357, #4538.
Blocked-by: #5439
Found while implementing #5365. That card's acceptance was "the consumer-side pin goes green", and it does for
@object-ui/fields(235 errors to 0, pin landed).@object-ui/app-shellwent from 1097 to 23, so the pin was used as a measuring instrument there and deliberately NOT landed — the residue is not #5365's defect and would have made the pin a red enforcement on day one.(Generics below are written with a space after
<— GitHub's body sanitizer strips<followed by a letter as an HTML tag, and it ate them on the first revision of this issue.)What was measured, 2026-08-20
Worktree off
main32ef595f4, full closure built,pnpm --filter @object-ui/app-shell exec tsc --noEmit:packages/app-shell/tsconfig.jsonnodenextpinnodenextpinThe control is the load-bearing row:
app-shelltype-checks clean today, so all 23 are produced by the pin.The 21 TS7006 are #5439, not a defect here
They are implicit-
anyparameters at call sites of symbols imported from@object-ui/plugin-chatbot, which ships extensionless typings of its own. Because that package re-exports through NAMED re-exports rather thanexport *, the names still exist and merely lose their types — see #5439 for the two-way probe. Fixing that package should clear all 21; nothing inapp-shellneeds to change for them.The 2 TS2345 are a real, separate decision
Same shape at
src/views/metadata-admin/previews/SourcePageEditor.tsx(30,31). Both areReact.lazy(() => import('@monaco-editor/react')).Under
bundlerresolution the module's default export reads as the component. Undernode16/nodenextthe CJS/ESM interop shape of@monaco-editor/react@4.7.0resolves differently and the namespace object — not the component — lands indefault, soReact.lazyrejects it. This is a genuine interop question about a third-party dependency, not an extension question, and the answer is a choice:import('@monaco-editor/react').then(m => ({ default: m.Editor }))) — smallest, and arguably more correct than relying on a default;app-shellunpinned until the dependency itself ships dual typings.Worth a decision rather than a guess, because the same interop class will meet every other CJS-shaped dependency the moment another consumer pins.
Acceptance
packages/app-shell/tsconfig.jsoncarries the twomodule/moduleResolution: nodenextlines thatpackages/reactandpackages/fieldsalready carry, andpnpm --filter @object-ui/app-shell type-checkis green.Refs #5365, #5357, #4538.