Repository navigation
EPBDS-16211: the published package is driven in a browser on React 16.14, 17 and 19 too - #200
Merged
Merged
Conversation
….14, 17 and 19 too Every 0.34.x host runs React 16.14, and no gate drove it in a browser: the jest legs run jsdom, the demo mounts through createRoot, and the packed tarball's browser smoke ran on the repository's React 18. The smoke now takes --react-dir, and --peers (npm run test:pack:browser:peers, in CI's browser job) runs its page on 16.14, 17 and 19 from the jest legs' fixtures, mounting through ReactDOM.render on 16 and 17 as those hosts do. Each run checks the React that rendered. A fourth document covers what React 16 and 17 handle differently from 18 and 19 (events delegated to the document rather than the root): a popup, which opens into the wrapper's popup root; a date field in it, whose calendar rc-picker portals into <body>; and a tooltip on hover, from a timer. All four tests pass on all four Reacts. Found on the way, and kept: after a pick in that calendar, focus sits on <body> and Escape does not close the dialog, which listens inside itself. 0.34.3 did the same (measured), so the test refocuses the field.
AlexSamBY
added a commit
that referenced
this pull request
Oct 9, 2026
…s at what is measured, and the plan's stale lines (#203) ## What The process half of the 2026-10-08 audit: lint for the code nobody linted, a gate against focused tests, coverage thresholds at what the suite covers, and the plan's stale lines. ### Lint covers the tooling `npm run lint:js` now runs over `scripts/`, `e2e/` and the config files at the root, as well as `src`. That is 63 more files, under `eslint-config-react-app`'s rules without the React, JSX and hooks ones (`TOOLING_RULES` in `eslint.config.js`). Each file gets the node, browser or jest globals of the place it runs. The first run found 16 problems: - **Fixed, 11.** - Seven unused names in the browser specs: five imports (`DISMISSAL`, `TOUCH`, `tooltipA11yWiring`, `topmostAt`, `BUBBLE`), and two `bubble` helpers nothing called. Their `BUBBLE` imports went with them. - `scripts/jest-console-guard.js` reassigned its buffer under a function created in a loop (`no-loop-func`). The buffer is now a constant array cleared in place, with the same behaviour. - The two webpack configs export a named value (`import/no-anonymous-default-export`). - A `/* global globalThis */` redeclared a built-in, and is gone. - **Configuration, 4.** - `document` was undefined in `scripts/fixtures/packed-browser-entry.js`, which runs in a page; that file has browser globals now. - The hooks rules read a Playwright fixture's `use` as React's `use`; they are not applied to the tooling. - **Suppressed, 1.** A string of prose in `scripts/wrapper-prop-curation.js` names the `${id}-help` template. The suppression states that reason. ### A focused test fails lint The ban covers `it.only`, `describe.only`, `test.only`, `test.describe.only`, `test.concurrent.only`, `.only.each`, `fit` and `fdescribe`. It applies in `src/**/__tests__`, `scripts/__tests__`, `e2e/**/*.pw.js` and `e2e/**/*.packed.js`. A focused test runs alone: the rest of its file is skipped, and the run still passes. Jest has no `--forbid-only`, and Playwright's `forbidOnly` is set for CI only. `eslint-plugin-jest` is not installed, so the gate uses two core rules: `no-restricted-properties` on `.only` and `no-restricted-globals` on `fit`/`fdescribe`. ### Coverage thresholds at what the suite measures Each threshold is the measured value, rounded down to a whole percent (statements/branches/functions/lines): - global: 89/79/85/90 → **98/95/97/99**; - `rules.tsx`: 89/76/98/91 → **96/87/100/98**; - ten more entries rise by 1 to 3 points; - `function.ts` goes 96 → 100 on branches and 75 → 80 on functions, and its comment says why; - six files get entries of their own: `documentHost.ts`, `formData.ts`, `errorMapping.ts`, `dataMapping.ts`, `popupScope.ts` and `lodash-lite.ts`. `jest.config.js` states the rule above the block: a change that covers less either covers what it adds or lowers the number in the same commit, saying why. ### The plan's stale lines 13 lines in `docs/UPGRADE-PLAN.md` changed, each one in place, with a date and the evidence: - **Sequencing (§6, §9.3, §9.9).** "Never mix an upgrade with a refactor in one release" is recorded as not held. Nothing has been released since Phase 2 (the last tag is v0.34.3, 2026-08-12), and §9.2's hooks migration and §9.3's decomposition landed after it. §9.3's "do not start" and §9.9's sequencing get the dates the work was done. - **§9.3 step 2.** "STARTED" and "Still to extract" now name what §9.3 closed without extracting: `transformConfig`/`toOpenLConfig` and `initSelectStatesFromData`, still in `rules.tsx`. - **R8.** `javascriptEnabled` went on 2026-10-08, and `size()`/`px()` are called at 133 sites, not 186. - **R10.** The audit and the prop list are done. The owners' MINOR call overruled "anything dropped ⇒ major", and the migration notes are in the changelog. - **Appendix B.** The `npm audit` baseline was 0 on 2026-09-30 and 35 on 2026-10-06, and has not been re-measured since. The line also says why the `"prop-types":` pattern has a colon. - **Appendix C.** - The upstream form-stack line is qualified by F4's finding. - There are two `cloneElement` sites, not one. - `prop-types` is retired. - The React 19 box no longer contradicts itself. ## Measured - **Lint.** 445 files, 0 problems: 382 in `src` and 63 in the tooling. Master's versions of five of the fixed files each fail the new configuration, with the rule named. - **Focused tests.** A probe with nine focused forms was linted as a file in each of the three test locations. All nine are reported in each; `describe.skip` is not, and neither is `.only` outside a test file. - **The open PRs.** - Under this configuration, #200's and #201's changed tooling and test files lint clean. - #202's one finding is the unused `BUBBLE` this PR removes, in a different hunk of the same file. - **Coverage.** - Two runs, one in the local time zone and one in UTC as on CI, were identical on every file and metric (214 suites, 2776 tests). - The thresholds are the lower of the two, rounded down. - **Local gates, all exit 0.** That covers all 21 gates plus the manifest contract, on Node 24.14.0: - lint and types: `lint:js`, `lint:css`, `typecheck`, `typecheck:contract`; - generated files: `docs:props:check`, `docs:views:check`, `css:fixture:check`, `build-css:check`; - builds and jest: `test:env-flags`, `test:coverage`, `build`, `test:react16`, `test:react17`, `test:react19`; - packaging: `test:pack`, `test:css:built`, `test:pack:peers`, `test:types`, `test:pack:browser`; - browser: `test:e2e`, `test:e2e:react19`. They ran on this commit's tree before two wording fixes, one in a comment in `eslint.config.js` and one in the changelog entry. After those, `lint:js` was rerun, along with the two suites that read those files (`wrapper-prop-reference.contract.test.js`, `NavTabs.history.test.js`), all exit 0. - **Against the open PRs.** `git merge-tree` merges this commit cleanly with #200, #201 and #202. The file this PR and #202 both change, `e2e/keyboard-a11y.pw.js`, lints clean once merged. ## Left for the owners GitHub holds eight branches; merged PR branches are deleted on merge. Besides `master`, `gh-pages` and the three open PRs' branches, three remain: - `main`: a 2021 initial commit holding only `LICENSE`, outside master's history. - `h1-dead-weight`: PR #52 was closed unmerged, and its content merged as #53 from `h1-dead-weight-rebased`. - `release/v0.34`: the 0.34 patch line §10 names. It carries two commits outside master's history (#27 and v0.34.3, 2026-08-12) and has no workflows. Whether to delete the first two, and whether the third gets CI or a statement that 0.34 takes no more fixes, is not decided here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
AlexSamBY
added a commit
that referenced
this pull request
Oct 12, 2026
…ed packages, and CI that keeps every master run (#205) ## What The security and CI items of the 2026-10-08 audit. ### What a meta can do, written down The README gains a **Security model** section. Each claim in it was checked against the code, and the two strongest also in a test run: - **A meta can render raw HTML.** A `Text` node given `dangerouslySetInnerHTML: {__html: '<b id="raw">raw</b>'}` puts `<b id="raw">` in the page: through the published entry, in jsdom, it was found by id. No URL scheme is checked in an attribute a meta sets. - **A meta can call `fetch`.** With `mapArgs`, the built-in `fetch` action calls the page's `fetch` with exactly the URL the meta names. With a stand-in `fetch`, the call arrived as `["https://example.test/mapped"]`. With `args`, the click event goes first. - **A data value is text,** escaped by React, unless a meta binds it to an attribute with `{name: 'path'}`. - **Upload checks run in the browser only.** `formats` and `maxSize` filter in the browser, and `uploadFile` receives the files as chosen. - **Downloads come from the host's `downloadFile`.** The renderer only saves what that function answers. `SECURITY.md` says how to report a vulnerability and what is in scope: anything a data value can do, the published package and what it bundles, the stylesheet leaving `.ui-render`, and the build and CI. What a meta may do is out of scope. **Before this merges, private vulnerability reporting needs turning on.** `SECURITY.md` points reporters at it, and `gh api repos/eisgroup/ui-render/private-vulnerability-reporting` answers `{"enabled":false}`. It is under Settings → Code security. The same API shows Dependabot security updates, secret scanning and push protection off too. Those are repository settings, so they are not changed here. ### The bundled packages, audited every week `npm run audit:bundled` (`scripts/audit-bundled.js`) runs `npm audit` and keeps only the findings on the packages `dist/sbom.cdx.json` lists, at the version the bundle carries. That matters because: - `dependencies` is empty, so `npm audit --omit=dev` audits nothing; - a full `npm audit` reports the development tree. It fails on a finding at moderate or above. `.github/workflows/audit-bundled.yml` runs it on Mondays and on demand, not on pull requests, so a new advisory cannot turn an unrelated change red. ### CI - **`cancel-in-progress` applies to pull requests only.** On master each push cancelled the run before it: on 2026-10-09 four PRs merged within a minute, and three of those master commits never finished a run (#200's 24a0900, #201's 0fa9296, #202's a506f0b). - **Every action is pinned to a commit**, the one its major tag points to (checked again on 2026-10-12), with the release in a trailing comment: - `checkout` v7.0.1, `setup-node` v7.1.0, `cache` v6.1.0, `upload-artifact` v7.0.2; - 19 `uses` in `ci.yml` and 2 in the new workflow. Dependabot updates the commit and the comment together; GitHub [documents this](https://github.blog/changelog/2022-10-31-dependabot-now-updates-comments-in-github-actions-workflows-referencing-action-versions/). ## Measured - **`npm run audit:bundled` on this branch:** "npm audit: 36 finding(s) in the installed tree; 19 package(s) bundled, 0 of them with a finding.", exit 0. The plan's Appendix B now records 36, which it last counted as 35 on 2026-10-06. - **`scripts/__tests__/audit-bundled.contract.test.js`, 4 tests, covering:** - a finding on a bundled package at the bundled version is kept; - a vulnerable copy at another version, and an unbundled package, are not; - npm's severity order. - **The workflows parse.** `ci.yml` has 7 jobs, and `cancel-in-progress` reads `${{ github.event_name == 'pull_request' }}`. - **Local gates on HEAD, all exit 0.** That is all 22 gates plus the manifest contract, on Node 24.14.0. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Every 0.34.x host runs React 16.14, and no gate drove it in a browser:
createRoot;What changed
scripts/test-packed-browser.jstakes--react-dir=<node_modules>.--peersruns the page on 16.14, 17 and 19, from the jest legs' fixtures; the npm script isnpm run test:pack:browser:peers, and it is a new step in CI'sbrowserjob.ReactDOM.render, as those hosts do (scripts/fixtures/packed-mount-legacy.js); on 18 and 19 throughcreateRoot.<body>;<body>, so Escape does not close the dialog. 0.34.3 does the same (measured), so the test refocuses the field.Measured
All 4 tests pass in Chromium on React 18.3.1 (
createRoot), 16.14.0 and 17.0.2 (ReactDOM.render), and 19.3.0 (createRoot).All 22 local gates exit 0 on 5711b23:
lint:js,lint:css,typecheck,typecheck:contract;docs:props:check,docs:views:check,css:fixture:check,build-css:check;test:env-flags,test:coverage,build,test:react16,test:react17,test:react19;test:pack,test:css:built,test:pack:peers,test:types,test:pack:browser;test:e2e,test:e2e:react19;🤖 Generated with Claude Code