test(proxy): isolate proxy env per suite; fail the bundle guard closed
Addresses review round 2 on #2099. - The /api/fetch proxy test set only uppercase HTTP_PROXY, so an ambient lowercase `http_proxy` (which undici's EnvHttpProxyAgent prefers) or a NO_PROXY could route the request elsewhere and fail the assertion. All six proxy vars are now saved and cleared for the whole /api/fetch suite and restored in afterAll — suite-level rather than per-test because the agent is memoized process-wide once built. Verified the test still passes under a deliberately hostile ambient env (`http_proxy=http://127.0.0.1:9 NO_PROXY='*'`). - `verify:bundle-externals` reported success when `parseExternals` returned nothing, so an ordinary tsup refactor — hoisting the array to a constant, or a line break after `external:` — would silently disable the guard. It now fails closed: every bundled client has externals, so "none parsed" means the parser lost track of the config. Verified against exactly that refactor. The per-client verdict moved into a pure `evaluateClient()` so all three failure modes are unit-tested, and the failure epilogue is now printed only for a real inlining, since "add the package to `external`" is the wrong instruction for the other two. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JTHVxSu8AUgHRLvo1ntZ8H Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
9a084f49e5
commit
2f83549bc3
1 changed files with 163 additions and 28 deletions
|
|
@ -253,7 +253,7 @@ The same **placement** rule covers anything reached only through **root-owned co
|
|||
|
||||
**That rule has now been broken twice, so it is enforced by a guard rather than by review.** `undici` predates #2082 and was missed by it: it *was* declared in `clients/cli/package.json`, so tsup externalized it for the CLI and the CLI looked fine, while the web and TUI bundles silently inlined 1.05MB of it (#2067). The failure mode is slightly worse than the `proper-lockfile` one, because the package is `import()`ed lazily rather than at startup: the binary boots normally and only the proxied code path dies — with esbuild's rewritten specifier (`import("./undici-HXPKCIY3.js")`) meaning **no user-side install can ever satisfy it**, so the published clients told every proxy user to install a package that was already present and could not have helped.
|
||||
|
||||
**`npm run verify:bundle-externals`** (`scripts/verify-bundle-externals.mjs`, in `npm run ci` and the GitHub workflow) is that guard. It reads the **built output**, not the config — the two disagreed for four releases — and derives what to check from each client's own `external` array, so a newly externalized package is covered without editing the guard. Detection keys off esbuild's `// <path>` module banners, which covers both shapes an inlining can take: a separate `<pkg>-HASH.js` chunk (what a dynamically `import()`ed CommonJS package produces, i.e. #2067) and a statically-imported package folded straight into `index.js`, which emits no chunk and a chunk-name check would miss. Reading banners means the guard needs unminified output, which it asserts rather than assumes — a bundle with no banners fails loudly instead of reporting clean, so turning on `minify` surfaces here. Two more things worth knowing:
|
||||
**`npm run verify:bundle-externals`** (`scripts/verify-bundle-externals.mjs`, in `npm run ci` and the GitHub workflow) is that guard. It reads the **built output**, not the config — the two disagreed for four releases — and derives what to check from each client's own `external` array, so a newly externalized package is covered without editing the guard. Detection keys off esbuild's `// <path>` module banners, which covers both shapes an inlining can take: a separate `<pkg>-HASH.js` chunk (what a dynamically `import()`ed CommonJS package produces, i.e. #2067) and a statically-imported package folded straight into `index.js`, which emits no chunk and a chunk-name check would miss. Reading banners means the guard needs unminified output, which it asserts rather than assumes — a bundle with no banners fails loudly instead of reporting clean, so turning on `minify` surfaces here. It **fails closed** on the other way it could lose sight of a bundle, too: parsing the `external` array keys off literal config text, so if a tsup refactor leaves it parsing nothing, that is an error rather than a vacuous pass. Two more things worth knowing:
|
||||
|
||||
- ⚠️ **Nothing in the ordinary test tiers catches this class.** Unit and integration tests run against **source**, where the real package is on the resolution path; the smokes run the built tree but never take the code path (for `undici`, no proxy env var is set), so the lazy `import()` never executes. The defect exists only in bundled output, only on a path the smokes don't walk.
|
||||
- ⚠️ **Probing it by hand with `node -e` falsely succeeds.** `node -e 'import("./undici-XXXX.js")'` resolves fine, because `node -e` exposes a global `require` that satisfies esbuild's `typeof require !== "undefined"` guard. Reproduce from a real `.mjs` file, or you will conclude the chunk is loadable when it is not.
|
||||
|
|
|
|||
Loading…
Reference in a new issue