From ae48fd462ad5d3ddf0d601ac3b970c427cfece7d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:23:34 +0000 Subject: [PATCH 01/11] [miniflare/wrangler] Bump undici to v7.24.8 and remove pnpm patch Update undici from 7.24.4 to 7.24.8 which includes the upstream fix for isTraversableNavigable() that caused POST/PUT requests with non-2xx responses to throw 'fetch failed'. Remove the now-unnecessary pnpm patch. Add test covering POST/PUT with various non-2xx status codes to prevent regression. Fixes #13013 Co-Authored-By: pbacondarwin --- .changeset/bump-undici-remove-patch.md | 8 ++ packages/miniflare/test/index.spec.ts | 57 +++++++++++ patches/undici@7.24.4.patch | 19 ---- pnpm-lock.yaml | 129 +++++++++++++------------ pnpm-workspace.yaml | 5 +- 5 files changed, 133 insertions(+), 85 deletions(-) create mode 100644 .changeset/bump-undici-remove-patch.md delete mode 100644 patches/undici@7.24.4.patch diff --git a/.changeset/bump-undici-remove-patch.md b/.changeset/bump-undici-remove-patch.md new file mode 100644 index 0000000000..7a735058aa --- /dev/null +++ b/.changeset/bump-undici-remove-patch.md @@ -0,0 +1,8 @@ +--- +"miniflare": patch +"wrangler": patch +--- + +Fix POST/PUT requests with non-2xx responses throwing "fetch failed" + +Previously, sending a POST or PUT request that received a non-2xx response (e.g. 401, 400, 403) would throw a `TypeError: fetch failed` error. This was caused by an undici bug where `isTraversableNavigable()` incorrectly returned `true`, causing the 401 credential-retry block to execute in Node.js and fail on stream-backed request bodies. This has been fixed upstream in undici v7.24.8, so we've bumped our dependency and removed the previous pnpm patch workaround. diff --git a/packages/miniflare/test/index.spec.ts b/packages/miniflare/test/index.spec.ts index 1813366f20..65d101f32a 100644 --- a/packages/miniflare/test/index.spec.ts +++ b/packages/miniflare/test/index.spec.ts @@ -3745,3 +3745,60 @@ test("Miniflare: MINIFLARE_WORKERD_CONFIG_DEBUG controls workerd config file cre expect(existsSync(configFilePath)).toBe(true); await mf.dispose(); }); + +// https://github.com/cloudflare/workers-sdk/issues/13013 +test("Miniflare: dispatchFetch handles POST/PUT with non-2xx status", async ({ + expect, +}) => { + const mf = new Miniflare({ + modules: true, + script: `export default { + async fetch(request) { + const url = new URL(request.url); + const status = parseInt(url.searchParams.get("status") ?? "200"); + return Response.json( + { method: request.method, status }, + { status } + ); + } + }`, + }); + useDispose(mf); + + // GET + non-2xx (worked before the fix too) + for (const status of [400, 401, 403, 404, 500]) { + const res = await mf.dispatchFetch(`http://localhost/?status=${status}`); + expect(res.status).toBe(status); + expect(await res.json()).toEqual({ method: "GET", status }); + } + + // POST + 2xx + for (const status of [200, 201]) { + const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, { + method: "POST", + body: JSON.stringify({ data: "test" }), + }); + expect(res.status).toBe(status); + expect(await res.json()).toEqual({ method: "POST", status }); + } + + // POST + non-2xx (previously threw "fetch failed" due to undici bug) + for (const status of [400, 401, 403, 404, 500]) { + const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, { + method: "POST", + body: JSON.stringify({ data: "test" }), + }); + expect(res.status).toBe(status); + expect(await res.json()).toEqual({ method: "POST", status }); + } + + // PUT + non-2xx (also affected by the same bug) + for (const status of [400, 401, 403, 404, 500]) { + const res = await mf.dispatchFetch(`http://localhost/?status=${status}`, { + method: "PUT", + body: JSON.stringify({ data: "test" }), + }); + expect(res.status).toBe(status); + expect(await res.json()).toEqual({ method: "PUT", status }); + } +}); diff --git a/patches/undici@7.24.4.patch b/patches/undici@7.24.4.patch deleted file mode 100644 index 8f64ee9e99..0000000000 --- a/patches/undici@7.24.4.patch +++ /dev/null @@ -1,19 +0,0 @@ -diff --git a/lib/web/fetch/util.js b/lib/web/fetch/util.js -index fe63cb3a9b07e57198f08d6237d2b7b39bfd057d..78c5be1d3e40278354943517bf3a74ba82e689e5 100644 ---- a/lib/web/fetch/util.js -+++ b/lib/web/fetch/util.js -@@ -1447,8 +1447,12 @@ function includesCredentials (url) { - * @param {object|string} navigable - */ - function isTraversableNavigable (navigable) { -- // TODO -- return true -+ // Node.js has no traversable navigable (no browser UI to prompt for credentials), -+ // so this should always return false. Returning true causes fetch() to enter a -+ // 401 credential-retry path that crashes when the request body's source is null. -+ // See: https://github.com/cloudflare/workers-sdk/issues/12967 -+ // See: https://github.com/nodejs/undici/issues/4910 -+ return false - } - - class EnvironmentSettingsObjectBase { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34aed9ff86..b285701b33 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -58,8 +58,8 @@ catalogs: specifier: ~5.8.3 version: 5.8.3 undici: - specifier: 7.24.4 - version: 7.24.4 + specifier: 7.24.8 + version: 7.24.8 vite: specifier: ^8.0.0 version: 8.0.1 @@ -76,7 +76,7 @@ overrides: '@types/react-transition-group>@types/react': ^18 '@cloudflare/elements>@types/react': ^18 '@types/node': ^22.10.1 - '@types/node>undici-types': 7.24.4 + '@types/node>undici-types': 7.24.8 patchedDependencies: '@cloudflare/component-listbox@1.10.6': @@ -94,9 +94,6 @@ patchedDependencies: toucan-js@4.0.0: hash: 60bdb1dcdbde0a135bb56d6fa1a1027caba891b149e2cfcb48d6a5b3524e0a91 path: patches/toucan-js@4.0.0.patch - undici@7.24.4: - hash: 957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990 - path: patches/undici@7.24.4.patch youch@4.1.0-beta.10: hash: 3e73fc48581841f22ea1d2cf5a3560bde280fdba04940253073fb121a70a9269 path: patches/youch@4.1.0-beta.10.patch @@ -179,7 +176,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -242,7 +239,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -263,7 +260,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -302,7 +299,7 @@ importers: version: link:../../packages/workers-tsconfig undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -320,7 +317,7 @@ importers: version: 4.20260413.1 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -356,7 +353,7 @@ importers: version: 2.2.0 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -383,7 +380,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -428,7 +425,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -445,7 +442,7 @@ importers: version: link:../shared undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -508,7 +505,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -563,7 +560,7 @@ importers: version: 4.20260413.1 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -596,7 +593,7 @@ importers: version: 1.2.7 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -617,7 +614,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -638,7 +635,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -666,7 +663,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -687,7 +684,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -705,7 +702,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -726,7 +723,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -747,7 +744,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -768,7 +765,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -808,7 +805,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -829,7 +826,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -844,7 +841,7 @@ importers: version: link:../../packages/workers-tsconfig undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -865,7 +862,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -886,7 +883,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -904,7 +901,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -922,7 +919,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -940,7 +937,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -958,7 +955,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -976,7 +973,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1015,7 +1012,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1030,7 +1027,7 @@ importers: version: link:../../packages/workers-tsconfig undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1045,7 +1042,7 @@ importers: version: link:../../packages/workers-tsconfig undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1251,7 +1248,7 @@ importers: version: link:../shared undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 wrangler: specifier: workspace:* version: link:../../packages/wrangler @@ -1273,7 +1270,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1369,7 +1366,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1390,7 +1387,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1411,7 +1408,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1432,7 +1429,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1453,7 +1450,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1486,7 +1483,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1513,7 +1510,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1534,7 +1531,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1552,7 +1549,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -1781,7 +1778,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vite: specifier: catalog:default version: 8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1) @@ -2034,7 +2031,7 @@ importers: version: 0.34.5 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 workerd: specifier: 1.20260413.1 version: 1.20260413.1 @@ -2292,7 +2289,7 @@ importers: version: 4.0.0(patch_hash=60bdb1dcdbde0a135bb56d6fa1a1027caba891b149e2cfcb48d6a5b3524e0a91) undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 wrangler: specifier: workspace:* version: link:../wrangler @@ -3638,7 +3635,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vitest: specifier: catalog:default version: 4.1.0(@opentelemetry/api@1.7.0)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)) @@ -3787,7 +3784,7 @@ importers: version: 4.13.0 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 vite: specifier: catalog:default version: 8.0.1(@types/node@22.15.17)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1) @@ -4205,7 +4202,7 @@ importers: version: 5.8.3 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 update-check: specifier: ^1.5.4 version: 1.5.4 @@ -4263,7 +4260,7 @@ importers: version: 2.2.0 undici: specifier: catalog:default - version: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + version: 7.24.8 wrangler: specifier: workspace:* version: link:../packages/wrangler @@ -14552,8 +14549,8 @@ packages: unconfig@7.3.3: resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} - undici-types@7.24.4: - resolution: {integrity: sha512-cRaY9PagdEZoRmcwzk3tUV3SVGrVQkR6bcSilav/A0vXsfpW4Lvd0BvgRMwTEDTLLGN+QdyBTG+nnvTgJhdt6w==} + undici-types@7.24.8: + resolution: {integrity: sha512-YqWg3ldzJEZ5NXBSIs+FJwgx1/c71Noi9dDfz6CWh32MvnrPmBxqOUsZM6PyY7P16/TU8jVyNlIU3LSsJ3PcUQ==} undici@5.28.5: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} @@ -14563,6 +14560,10 @@ packages: resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} engines: {node: '>=20.18.1'} + undici@7.24.8: + resolution: {integrity: sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ==} + engines: {node: '>=20.18.1'} + unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -19725,7 +19726,7 @@ snapshots: '@types/node@22.15.17': dependencies: - undici-types: 7.24.4 + undici-types: 7.24.8 '@types/normalize-package-data@2.4.4': {} @@ -23251,7 +23252,7 @@ snapshots: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 - undici: 7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990) + undici: 7.24.4 workerd: 1.20260317.1 ws: 8.18.0 youch: 4.1.0-beta.10(patch_hash=3e73fc48581841f22ea1d2cf5a3560bde280fdba04940253073fb121a70a9269) @@ -26017,13 +26018,15 @@ snapshots: jiti: 2.6.0 quansync: 0.2.11 - undici-types@7.24.4: {} + undici-types@7.24.8: {} undici@5.28.5: dependencies: '@fastify/busboy': 2.1.1 - undici@7.24.4(patch_hash=957063fa4cdb086f68ebd6f837888aa6a1240dbd31e3d3eaee4fb4fd54d24990): {} + undici@7.24.4: {} + + undici@7.24.8: {} unenv@2.0.0-rc.24: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dabe1a56f3..246a1b59d1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -75,7 +75,6 @@ patchedDependencies: "youch@4.1.0-beta.10": "patches/youch@4.1.0-beta.10.patch" "@netlify/build-info": "patches/@netlify__build-info.patch" "buffer-equal-constant-time@1.0.1": "patches/buffer-equal-constant-time@1.0.1.patch" - "undici@7.24.4": "patches/undici@7.24.4.patch" # ────────────────────────────────────────────────────────────────────────────── # Catalog @@ -89,10 +88,10 @@ catalog: "@vitest/snapshot": 4.1.0 "@vitest/ui": 4.1.0 typescript: "~5.8.3" - undici: "7.24.4" + undici: "7.24.8" # Override undici-types from @types/node so that the Cloudflare SDK typings match our installed # version of Undici - undici-types: "7.24.4" + undici-types: "7.24.8" vitest: "4.1.0" vite: "^8.0.0" "ws": "8.18.0" From 6b28c264f4c92693e59739e49d13c6c298ed0ae7 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:48:02 +0000 Subject: [PATCH 02/11] Remove unused @ts-expect-error directives for clone() overrides undici 7.24.8 no longer considers these overrides as type errors, so the @ts-expect-error directives are now unused and cause TS2578. Co-Authored-By: pbacondarwin --- packages/miniflare/src/http/request.ts | 1 - packages/miniflare/src/http/response.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/miniflare/src/http/request.ts b/packages/miniflare/src/http/request.ts index 2092f1982a..ee3767a69e 100644 --- a/packages/miniflare/src/http/request.ts +++ b/packages/miniflare/src/http/request.ts @@ -42,7 +42,6 @@ export class Request< } // JSDoc comment so retained when bundling types with api-extractor - /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Request { // @ts-ignore const request = super.clone() as Request; diff --git a/packages/miniflare/src/http/response.ts b/packages/miniflare/src/http/response.ts index cdcc681f97..2724082a1a 100644 --- a/packages/miniflare/src/http/response.ts +++ b/packages/miniflare/src/http/response.ts @@ -70,7 +70,6 @@ export class Response extends BaseResponse { } // JSDoc comment so retained when bundling types with api-extractor - /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Response { if (this[kWebSocket]) { throw new TypeError("Cannot clone a response to a WebSocket handshake."); From 88a00b22de4f584d7b87132878a7db32852ad593 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:03:06 +0000 Subject: [PATCH 03/11] Fix R2 test type errors with undici 7.24.8 Use structural types for keys/delimitedPrefixes helpers instead of R2Objects from @cloudflare/workers-types to avoid SpecIterableIterator incompatibility between undici 7.24.8's Headers and workers-types' Headers. Co-Authored-By: pbacondarwin --- packages/miniflare/test/plugins/r2/index.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/miniflare/test/plugins/r2/index.spec.ts b/packages/miniflare/test/plugins/r2/index.spec.ts index 21b4ad9311..b9fae72624 100644 --- a/packages/miniflare/test/plugins/r2/index.spec.ts +++ b/packages/miniflare/test/plugins/r2/index.spec.ts @@ -916,9 +916,9 @@ test("list: returns correct delimitedPrefixes for delimiter and prefix", async ( const allKeys = Object.keys(values); for (const [key, value] of Object.entries(values)) await r2.put(key, value); - const keys = (result: R2Objects) => + const keys = (result: { objects: { key: string }[] }) => result.objects.map(({ key }) => key.substring(ns.length)); - const delimitedPrefixes = (result: R2Objects) => + const delimitedPrefixes = (result: { delimitedPrefixes: string[] }) => result.delimitedPrefixes.map((prefix) => prefix.substring(ns.length)); const allKeysWithout = (...exclude: string[]) => allKeys.filter((value) => !exclude.includes(value)); From 319817575a9fd6014e0fa20b41562a634cecd20f Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:06:00 +0000 Subject: [PATCH 04/11] Remove unused R2Objects import after switching to structural types Co-Authored-By: pbacondarwin --- packages/miniflare/test/plugins/r2/index.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/miniflare/test/plugins/r2/index.spec.ts b/packages/miniflare/test/plugins/r2/index.spec.ts index b9fae72624..e57ccb09a4 100644 --- a/packages/miniflare/test/plugins/r2/index.spec.ts +++ b/packages/miniflare/test/plugins/r2/index.spec.ts @@ -26,7 +26,6 @@ import type { R2ListOptions, R2Object, R2ObjectBody, - R2Objects, } from "@cloudflare/workers-types/experimental"; import type { MiniflareOptions, ReplaceWorkersTypes } from "miniflare"; From e783295df69bbae621ccbb4b65fbc0aa262cf118 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:13:02 +0000 Subject: [PATCH 05/11] Fix vite-plugin Headers type incompatibility with undici 7.24.8 Convert @remix-run/node-fetch-server Headers to a plain Record to avoid SpecIterableIterator type mismatch between undici 7.24.8 and the remix Headers implementation. Co-Authored-By: pbacondarwin --- packages/vite-plugin-cloudflare/src/websockets.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index 789cf8c397..33184ae9e2 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -44,8 +44,16 @@ export function handleWebSocket( headers.set(CoreHeaders.ROUTE_OVERRIDE, entryWorkerName); } + // Convert to a plain record to avoid type incompatibility between + // @remix-run/node-fetch-server's Headers and undici's Headers + // (IterableIterator vs SpecIterableIterator). + const headerRecord: Record = {}; + headers.forEach((value, key) => { + headerRecord[key] = value; + }); + const response = await miniflare.dispatchFetch(url, { - headers, + headers: headerRecord, method: request.method, }); const workerWebSocket = response.webSocket; From d692a608d6f01ecf9a96d9bafcafd93e0cd32745 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:20:12 +0000 Subject: [PATCH 06/11] Fix vite-plugin websockets: use undici Headers instead of plain record The test expects headers passed to dispatchFetch to be a Headers instance. Instead of converting to Record, construct a new undici Headers object from the remix Headers to satisfy both the type system and the instanceof check. Co-Authored-By: pbacondarwin --- packages/vite-plugin-cloudflare/src/websockets.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index 33184ae9e2..ab5c489349 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -1,5 +1,6 @@ import { createHeaders } from "@remix-run/node-fetch-server"; import { CoreHeaders, coupleWebSocket } from "miniflare"; +import { Headers } from "undici"; import { WebSocketServer } from "ws"; import { UNKNOWN_HOST } from "./shared"; import type { Miniflare } from "miniflare"; @@ -44,16 +45,15 @@ export function handleWebSocket( headers.set(CoreHeaders.ROUTE_OVERRIDE, entryWorkerName); } - // Convert to a plain record to avoid type incompatibility between - // @remix-run/node-fetch-server's Headers and undici's Headers - // (IterableIterator vs SpecIterableIterator). - const headerRecord: Record = {}; + // Construct a new undici Headers from the remix Headers to avoid + // type incompatibility (IterableIterator vs SpecIterableIterator). + const undiciHeaders = new Headers(); headers.forEach((value, key) => { - headerRecord[key] = value; + undiciHeaders.set(key, value); }); const response = await miniflare.dispatchFetch(url, { - headers: headerRecord, + headers: undiciHeaders, method: request.method, }); const workerWebSocket = response.webSocket; From dbba25d09ed9a0d9026953ccf9d98dc95fecc00b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 12:24:03 +0000 Subject: [PATCH 07/11] Fix: import Headers from miniflare instead of undici The vite-plugin package doesn't have undici as a dependency. Import Headers from miniflare which re-exports it. Co-Authored-By: pbacondarwin --- packages/vite-plugin-cloudflare/src/websockets.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index ab5c489349..e3d8b52c2f 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -1,6 +1,5 @@ import { createHeaders } from "@remix-run/node-fetch-server"; -import { CoreHeaders, coupleWebSocket } from "miniflare"; -import { Headers } from "undici"; +import { CoreHeaders, Headers, coupleWebSocket } from "miniflare"; import { WebSocketServer } from "ws"; import { UNKNOWN_HOST } from "./shared"; import type { Miniflare } from "miniflare"; From 5de5dacc6455d0ff8f489b7a06377ef605c207bc Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 13 Apr 2026 13:45:13 +0100 Subject: [PATCH 08/11] vite-plugin: use imported (not ambient) Headers type from miniflare in websocket test --- .../vite-plugin-cloudflare/src/__tests__/websockets.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts index 564f32bcd5..a3ad08028f 100644 --- a/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts +++ b/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts @@ -1,6 +1,6 @@ import http from "node:http"; import net from "node:net"; -import { DeferredPromise, Miniflare, Response } from "miniflare"; +import { DeferredPromise, Miniflare, Response, Headers } from "miniflare"; import { afterEach, assert, beforeEach, describe, test, vi } from "vitest"; import { handleWebSocket } from "../websockets"; import type { AddressInfo } from "node:net"; From f3ee8b95f2b35cc3a2f3ffc244fca7d2e606d435 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 13 Apr 2026 14:04:04 +0100 Subject: [PATCH 09/11] vite-plugin: construct Headers directly from rawHeaders instead of using createHeaders Replace @remix-run/node-fetch-server's createHeaders() with direct construction of miniflare's Headers from request.rawHeaders. This avoids the type incompatibility between remix and undici Headers entirely, removes the redundant intermediate copy step, and correctly preserves multi-value headers by using append() instead of set(). --- .../vite-plugin-cloudflare/src/websockets.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index e3d8b52c2f..91ff0d0089 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -1,4 +1,3 @@ -import { createHeaders } from "@remix-run/node-fetch-server"; import { CoreHeaders, Headers, coupleWebSocket } from "miniflare"; import { WebSocketServer } from "ws"; import { UNKNOWN_HOST } from "./shared"; @@ -38,21 +37,21 @@ export function handleWebSocket( return; } - const headers = createHeaders(request); + const headers = new Headers(); + const rawHeaders = request.rawHeaders; + for (let i = 0; i < rawHeaders.length; i += 2) { + if (rawHeaders[i].startsWith(":")) { + continue; + } + headers.append(rawHeaders[i], rawHeaders[i + 1]); + } if (entryWorkerName) { headers.set(CoreHeaders.ROUTE_OVERRIDE, entryWorkerName); } - // Construct a new undici Headers from the remix Headers to avoid - // type incompatibility (IterableIterator vs SpecIterableIterator). - const undiciHeaders = new Headers(); - headers.forEach((value, key) => { - undiciHeaders.set(key, value); - }); - const response = await miniflare.dispatchFetch(url, { - headers: undiciHeaders, + headers, method: request.method, }); const workerWebSocket = response.webSocket; From 7f7c8530a2f22ec85ed21a76bec8c060ae4f835f Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 13 Apr 2026 18:16:50 +0100 Subject: [PATCH 10/11] fix typing --- packages/miniflare/src/http/request.ts | 1 + packages/miniflare/src/http/response.ts | 1 + packages/vite-plugin-cloudflare/src/websockets.ts | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/miniflare/src/http/request.ts b/packages/miniflare/src/http/request.ts index ee3767a69e..2092f1982a 100644 --- a/packages/miniflare/src/http/request.ts +++ b/packages/miniflare/src/http/request.ts @@ -42,6 +42,7 @@ export class Request< } // JSDoc comment so retained when bundling types with api-extractor + /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Request { // @ts-ignore const request = super.clone() as Request; diff --git a/packages/miniflare/src/http/response.ts b/packages/miniflare/src/http/response.ts index 2724082a1a..cdcc681f97 100644 --- a/packages/miniflare/src/http/response.ts +++ b/packages/miniflare/src/http/response.ts @@ -70,6 +70,7 @@ export class Response extends BaseResponse { } // JSDoc comment so retained when bundling types with api-extractor + /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Response { if (this[kWebSocket]) { throw new TypeError("Cannot clone a response to a WebSocket handshake."); diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index 91ff0d0089..a44a578eb0 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -40,10 +40,12 @@ export function handleWebSocket( const headers = new Headers(); const rawHeaders = request.rawHeaders; for (let i = 0; i < rawHeaders.length; i += 2) { - if (rawHeaders[i].startsWith(":")) { + // oxlint-disable-next-line typescript/no-non-null-assertion + if (rawHeaders[i]!.startsWith(":")) { continue; } - headers.append(rawHeaders[i], rawHeaders[i + 1]); + // oxlint-disable-next-line typescript/no-non-null-assertion + headers.append(rawHeaders[i]!, rawHeaders[i + 1]!); } if (entryWorkerName) { From 5b3c9f0cd36ece1ed0ba68bc9755aaa8406b5930 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Tue, 14 Apr 2026 13:31:13 +0100 Subject: [PATCH 11/11] PR feedback... --- packages/miniflare/src/http/request.ts | 3 --- packages/miniflare/src/http/response.ts | 3 --- .../miniflare/test/plugins/r2/index.spec.ts | 4 ++-- .../src/__tests__/websockets.spec.ts | 2 +- .../vite-plugin-cloudflare/src/websockets.ts | 18 +++++------------- 5 files changed, 8 insertions(+), 22 deletions(-) diff --git a/packages/miniflare/src/http/request.ts b/packages/miniflare/src/http/request.ts index 2092f1982a..4a2783a4e5 100644 --- a/packages/miniflare/src/http/request.ts +++ b/packages/miniflare/src/http/request.ts @@ -41,10 +41,7 @@ export class Request< return this[kCf]; } - // JSDoc comment so retained when bundling types with api-extractor - /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Request { - // @ts-ignore const request = super.clone() as Request; // Update prototype so cloning a clone clones `cf` Object.setPrototypeOf(request, Request.prototype); diff --git a/packages/miniflare/src/http/response.ts b/packages/miniflare/src/http/response.ts index cdcc681f97..082a78ae76 100644 --- a/packages/miniflare/src/http/response.ts +++ b/packages/miniflare/src/http/response.ts @@ -69,13 +69,10 @@ export class Response extends BaseResponse { return this[kWebSocket]; } - // JSDoc comment so retained when bundling types with api-extractor - /** @ts-expect-error `clone` is actually defined as a method internally */ clone(): Response { if (this[kWebSocket]) { throw new TypeError("Cannot clone a response to a WebSocket handshake."); } - // @ts-ignore const response = super.clone() as Response; Object.setPrototypeOf(response, Response.prototype); return response; diff --git a/packages/miniflare/test/plugins/r2/index.spec.ts b/packages/miniflare/test/plugins/r2/index.spec.ts index e57ccb09a4..077f1b6f1b 100644 --- a/packages/miniflare/test/plugins/r2/index.spec.ts +++ b/packages/miniflare/test/plugins/r2/index.spec.ts @@ -915,9 +915,9 @@ test("list: returns correct delimitedPrefixes for delimiter and prefix", async ( const allKeys = Object.keys(values); for (const [key, value] of Object.entries(values)) await r2.put(key, value); - const keys = (result: { objects: { key: string }[] }) => + const keys = (result: Awaited>) => result.objects.map(({ key }) => key.substring(ns.length)); - const delimitedPrefixes = (result: { delimitedPrefixes: string[] }) => + const delimitedPrefixes = (result: Awaited>) => result.delimitedPrefixes.map((prefix) => prefix.substring(ns.length)); const allKeysWithout = (...exclude: string[]) => allKeys.filter((value) => !exclude.includes(value)); diff --git a/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts index a3ad08028f..564f32bcd5 100644 --- a/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts +++ b/packages/vite-plugin-cloudflare/src/__tests__/websockets.spec.ts @@ -1,6 +1,6 @@ import http from "node:http"; import net from "node:net"; -import { DeferredPromise, Miniflare, Response, Headers } from "miniflare"; +import { DeferredPromise, Miniflare, Response } from "miniflare"; import { afterEach, assert, beforeEach, describe, test, vi } from "vitest"; import { handleWebSocket } from "../websockets"; import type { AddressInfo } from "node:net"; diff --git a/packages/vite-plugin-cloudflare/src/websockets.ts b/packages/vite-plugin-cloudflare/src/websockets.ts index a44a578eb0..07c53320e1 100644 --- a/packages/vite-plugin-cloudflare/src/websockets.ts +++ b/packages/vite-plugin-cloudflare/src/websockets.ts @@ -1,7 +1,8 @@ -import { CoreHeaders, Headers, coupleWebSocket } from "miniflare"; +import { createHeaders } from "@remix-run/node-fetch-server"; +import { CoreHeaders, coupleWebSocket } from "miniflare"; import { WebSocketServer } from "ws"; import { UNKNOWN_HOST } from "./shared"; -import type { Miniflare } from "miniflare"; +import type { Headers, Miniflare } from "miniflare"; import type { IncomingMessage } from "node:http"; import type { Duplex } from "node:stream"; import type * as vite from "vite"; @@ -37,23 +38,14 @@ export function handleWebSocket( return; } - const headers = new Headers(); - const rawHeaders = request.rawHeaders; - for (let i = 0; i < rawHeaders.length; i += 2) { - // oxlint-disable-next-line typescript/no-non-null-assertion - if (rawHeaders[i]!.startsWith(":")) { - continue; - } - // oxlint-disable-next-line typescript/no-non-null-assertion - headers.append(rawHeaders[i]!, rawHeaders[i + 1]!); - } + const headers = createHeaders(request); if (entryWorkerName) { headers.set(CoreHeaders.ROUTE_OVERRIDE, entryWorkerName); } const response = await miniflare.dispatchFetch(url, { - headers, + headers: headers as unknown as Headers, method: request.method, }); const workerWebSocket = response.webSocket;