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
11 changes: 11 additions & 0 deletions .changeset/plugin-report-react-19-peer-3690.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@object-ui/plugin-report': patch
---

Accept React 19 in `@object-ui/plugin-report`'s peer range, the last UI package still declaring React 18 alone (objectui#3690).

`peerDependencies.react` and `peerDependencies.react-dom` widen from `^18.0.0` to `^18.0.0 || ^19.0.0`, matching the other 29 packages in the fixed version group. With npm 7+ resolving peers strictly, a React 19 consumer installing this published package hit an `ERESOLVE` on first install while every sibling installed clean — and the package's own README already documented the wider range, so the manifest was the half that was wrong.

The narrow range was never a constraint anyone stated. `packages/plugin-report/package.json` was hand-authored on 2026-02-06 (`1e557cbda`), by which point nineteen sibling packages already carried `^18.0.0 || ^19.0.0` and every package created afterwards was born with it; the one other package born narrow, `plugin-dashboard`, was corrected on 2026-05-08 (`d2b6ecec6`) in a build fix that touched only itself. No commit in the file's 172-commit history ever revisited the peer line, and no commit message mentions a React 18 requirement.

Nothing in the package needs React 18. Its entire React surface is `React.FC`, `useState`, `useEffect`, `useMemo`, `useReducer`, `useContext`, `Fragment`, `ComponentType`, `CSSProperties` and `ReactNode` — all unchanged in React 19 — with zero uses of anything React 19 removed (`ReactDOM.render`, `unmountComponentAtNode`, `findDOMNode`, legacy context, string refs, `defaultProps` / `propTypes` on function components, `createFactory`, `useFormState`, `react-dom/test-utils`). `react-dom` is not imported by the source at all; it appears only as a UMD global name in the Vite externals config. The workspace pins `react` to 19.2.8 via a root `pnpm.overrides`, so this package's 78 tests have been running against React 19 the whole time it declared it did not support it.
4 changes: 2 additions & 2 deletions packages/plugin-report/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
"tailwind-merge": "^3.6.0"
},
"peerDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"devDependencies": {
"@objectstack/spec": "^17.0.0-rc.5",
Expand Down
61 changes: 34 additions & 27 deletions scripts/__tests__/doc-version-claims.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,20 @@ import { fileURLToPath } from 'node:url';
* recorded the debt instead of blessing it, and still stopped a tenth from joining
* them silently.
*
* EIGHT of those nine have since been paid off — objectui#3708 (the two spec claims and
* the TypeScript one), #3709 (the three scaffolder-output lines) and #3710 (layout's
* peer line and plugin-chatbot's `@ai-sdk/react` major). Their entries left this file in
* the same change, which is the downward half of the ratchet doing its job rather than
* a courtesy. One `stale` entry remains, `packages/plugin-report/README.md`, and it is
* the one repair that belongs on the MANIFEST side: objectui#3690 widens the package's
* `peerDependencies.react`, after which the README is correct without being touched.
* ALL NINE have since been paid off — objectui#3708 (the two spec claims and the
* TypeScript one), #3709 (the three scaffolder-output lines), #3710 (layout's peer line
* and plugin-chatbot's `@ai-sdk/react` major) and #3690 (the last one, below). Their
* entries left this file in the same changes that repaired them, which is the downward
* half of the ratchet doing its job rather than a courtesy. NO `stale` entry remains.
*
* The last one out, `packages/plugin-report/README.md`, was also the only one whose
* repair belonged on the MANIFEST side rather than in prose: it was the one entry where
* the README was RIGHT and the manifest lagged it, so objectui#3690 widened that
* package's `peerDependencies.react`/`react-dom` to `^18.0.0 || ^19.0.0` and the claim
* became true without a character of prose being touched. Its entry is now a plain
* `restatement`. That direction — fix the anchor, not the sentence — is the cheapest way
* an entry ever leaves this list. The class is empty, not abolished: `stale` stays in
* `ClaimKind` so the next known-false literal can still be recorded rather than blessed.
*
* ## Fences are SCANNED — the opposite of `check-doc-links.mjs`, on purpose
*
Expand Down Expand Up @@ -326,13 +333,15 @@ const keyOf = (c: Pick<Claim, 'file' | 'claim'>): string => `${c.file} :: ${c.cl
* `anchored` - true today AND checkable against a machine-readable truth in this
* tree, so a reviewer can re-verify it in one command.
* `restatement` - a README restating its OWN package.json. Formally a subclass of
* `anchored`, kept separate because it is the largest class (12 of 21)
* `anchored`, kept separate because it is the largest class (13 of 21)
* and the one that had already drifted: the two drifted members were
* repaired by objectui#3710 and re-filed here as restatements. Note
* what this class does and does not buy — the equality it names is
* re-verified by a HUMAN reading the manifest, not by this gate, which
* only asks whether a literal was recorded at all. Pinning README to
* manifest mechanically is still the payoff this class is pointing at.
* repaired by objectui#3710 and re-filed here as restatements, and a
* third joined from `stale` when objectui#3690 widened the manifest its
* README already described. Note what this class does and does not buy
* — the equality it names is re-verified by a HUMAN reading the
* manifest, not by this gate, which only asks whether a literal was
* recorded at all. Pinning README to manifest mechanically is still the
* payoff this class is pointing at.
* `sample` - illustrative or template content. NOT an assertion about this
* repository's versions: a changelog a plugin renders as demo data, or
* a package.json skeleton the reader owns after copying it.
Expand All @@ -341,11 +350,12 @@ const keyOf = (c: Pick<Claim, 'file' | 'claim'>): string => `${c.file} :: ${c.cl
* can tell us when it stops being true.
* `stale` - measured WRONG at the time of writing. Recorded, not blessed.
*
* Nine of the 27 were `stale` at the census. Eight have been paid off (objectui#3708,
* #3709, #3710) and their entries deleted in the same change; ONE remains, and it is
* the one whose repair is a manifest edit rather than a docs edit (objectui#3690).
* Inventorying a known-false line records the debt where the next reader will trip over
* it, and the ratchet still stops a tenth from joining them unnoticed.
* Nine of the 27 were `stale` at the census. ALL NINE have been paid off — objectui#3708,
* #3709 and #3710 on the docs side, and #3690 on the manifest side (the one entry whose
* repair was a manifest edit, not a docs edit) — and their entries were deleted in the
* same changes, so NONE remains today. Inventorying a known-false line records the debt
* where the next reader will trip over it, and the ratchet still stops a tenth from
* joining them unnoticed, which is why the class outlives its last member.
*/
type ClaimKind = 'anchored' | 'restatement' | 'sample' | 'unanchored' | 'stale';

Expand All @@ -357,10 +367,12 @@ interface KnownClaim {
}

/**
* The peer-dependency line 10 package READMEs carry verbatim from their manifests —
* The peer-dependency line 11 package READMEs carry verbatim from their manifests —
* `layout` joined them in objectui#3710, which narrowed its over-promising `>=` spelling
* to the range its manifest actually declares. An eleventh README, `plugin-report`,
* carries the same line WITHOUT the manifest to back it; that one stays `stale` below.
* to the range its manifest actually declares, and `plugin-report` joined in
* objectui#3690 from the opposite direction: it had carried this line WITHOUT the
* manifest to back it, so the manifest was widened to `^18.0.0 || ^19.0.0` rather than
* the sentence rewritten.
*/
const PEER_18_19 = 'react' + TICK + ' ^18.0.0';
const PEER_RESTATEMENT_OK =
Expand Down Expand Up @@ -427,19 +439,14 @@ const KNOWN_CLAIMS: KnownClaim[] = [
{ file: 'packages/permissions/README.md', claim: PEER_18_19, kind: 'restatement', why: PEER_RESTATEMENT_OK },
{ file: 'packages/plugin-ai/README.md', claim: PEER_18_19, kind: 'restatement', why: PEER_RESTATEMENT_OK },
{ file: 'packages/plugin-designer/README.md', claim: PEER_18_19, kind: 'restatement', why: PEER_RESTATEMENT_OK },
{ file: 'packages/plugin-report/README.md', claim: PEER_18_19, kind: 'restatement', why: PEER_RESTATEMENT_OK },
{ file: 'packages/react/README.md', claim: PEER_18_19, kind: 'restatement', why: PEER_RESTATEMENT_OK },
{
file: 'packages/react-runtime/README.md',
claim: 'react >= 18',
kind: 'restatement',
why: 'Restates this package peerDependencies.react, which is literally ">=18" — the one README whose looser spelling is the manifest spelling.',
},
{
file: 'packages/plugin-report/README.md',
claim: PEER_18_19,
kind: 'stale',
why: 'Claims ^18.0.0 || ^19.0.0 while this package peerDependencies.react is ^18.0.0 alone — the sole UI package without React 19, first noticed as an out-of-scope finding in PR #3688.',
},
{
file: 'packages/plugin-chatbot/README.md',
claim: '@ai-sdk/react' + TICK + ' v4',
Expand Down
Loading