From f5df0d337d3efe6362927e86113faf3fe485b7f2 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 25 Apr 2026 07:32:44 -0300 Subject: [PATCH] fix(build): include wreq-js native assets in standalone --- CHANGELOG.md | 1 + scripts/build-next-isolated.mjs | 44 ++++++++++++++++++++++++ tests/unit/build-next-isolated.test.ts | 47 +++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 128b18096e..180906aa89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - **fix(mitm):** Compile MITM utilities as NodeNext ESM during prepublish, copy the CommonJS MITM server into the standalone artifact, and resolve MITM data paths without relying on Next.js aliases in packaged runtime. - **fix(build):** Move the local `.tmp/wine32` Wine prefix out of the isolated Next.js build path so Windows Electron packaging artifacts cannot trigger `EACCES` scans during Node 24 builds. +- **fix(build):** Copy the `wreq-js` native runtime directory into the isolated Next.js standalone output so packaged Playwright/E2E starts can load the instrumentation hook on Linux. - **fix(api):** Validate the Codex Responses websocket bridge and `/v1/batches` JSON payloads with Zod before use, keeping `request.json()` route validation green and returning explicit 400 responses for invalid bodies. - **fix(providers):** Add explicit typing to provider alias and category helpers so the strict `typecheck:noimplicit:core` CI gate passes. - **fix(ui):** Keep the upstream proxy provider detail page labeled with a fallback "Managed via Upstream Proxy Settings" management surface when translations are unavailable. diff --git a/scripts/build-next-isolated.mjs b/scripts/build-next-isolated.mjs index 1d8d591013..b6ccc994e0 100644 --- a/scripts/build-next-isolated.mjs +++ b/scripts/build-next-isolated.mjs @@ -136,6 +136,41 @@ export async function pruneStandaloneArtifacts(rootDir = projectRoot, fsImpl = f } } +export async function syncStandaloneNativeAssets( + rootDir = projectRoot, + fsImpl = fs, + log = console +) { + const nativeAssetDirs = [ + { + label: "wreq-js native runtime", + sourcePath: path.join(rootDir, "node_modules", "wreq-js", "rust"), + destinationPath: path.join(rootDir, ".next", "standalone", "node_modules", "wreq-js", "rust"), + }, + ]; + + let changed = false; + + for (const entry of nativeAssetDirs) { + if (!(await exists(entry.sourcePath))) continue; + + await fsImpl.mkdir(path.dirname(entry.destinationPath), { recursive: true }); + await fsImpl.cp(entry.sourcePath, entry.destinationPath, { + recursive: true, + force: true, + }); + log.log( + `[build-next-isolated] Copied native standalone asset: ${path.relative( + rootDir, + entry.destinationPath + )}` + ); + changed = true; + } + + return changed; +} + export async function main() { const movedPaths = []; const transientBuildPaths = getTransientBuildPaths(); @@ -175,6 +210,15 @@ export async function main() { pruneErr ); } + + try { + await syncStandaloneNativeAssets(projectRoot); + } catch (nativeAssetErr) { + console.warn( + "[build-next-isolated] Non-fatal error copying native standalone assets:", + nativeAssetErr + ); + } } process.exitCode = result.code; } catch (error) { diff --git a/tests/unit/build-next-isolated.test.ts b/tests/unit/build-next-isolated.test.ts index 2cbd2350ab..938122e7a0 100644 --- a/tests/unit/build-next-isolated.test.ts +++ b/tests/unit/build-next-isolated.test.ts @@ -5,8 +5,13 @@ import fsSync from "node:fs"; import os from "node:os"; import path from "node:path"; -const { getTransientBuildPaths, movePath, pruneStandaloneArtifacts, resolveNextBuildEnv } = - await import("../../scripts/build-next-isolated.mjs"); +const { + getTransientBuildPaths, + movePath, + pruneStandaloneArtifacts, + resolveNextBuildEnv, + syncStandaloneNativeAssets, +} = await import("../../scripts/build-next-isolated.mjs"); async function withTempDir(fn) { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "omniroute-build-next-isolated-")); @@ -74,8 +79,7 @@ test("movePath rethrows non-EXDEV rename failures", async () => { await assert.rejects( movePath(sourceDir, destinationDir, { rename: async () => { - const error = new Error("permission denied"); - error.code = "EACCES"; + const error = Object.assign(new Error("permission denied"), { code: "EACCES" }); throw error; }, cp: async () => { @@ -85,7 +89,7 @@ test("movePath rethrows non-EXDEV rename failures", async () => { throw new Error("remove fallback should not run"); }, }), - (error) => (error as any).code === "EACCES" + (error) => error instanceof Error && "code" in error && error.code === "EACCES" ); }); }); @@ -136,3 +140,36 @@ test("pruneStandaloneArtifacts removes traced _tasks from standalone output", as assert.equal(fsSync.existsSync(path.join(tempDir, ".next", "standalone", "_tasks")), false); }); }); + +test("syncStandaloneNativeAssets copies wreq-js native runtime into standalone output", async () => { + await withTempDir(async (tempDir) => { + const sourceNativeFile = path.join( + tempDir, + "node_modules", + "wreq-js", + "rust", + "wreq-js.linux-x64-gnu.node" + ); + const destinationNativeFile = path.join( + tempDir, + ".next", + "standalone", + "node_modules", + "wreq-js", + "rust", + "wreq-js.linux-x64-gnu.node" + ); + const logs: string[] = []; + + await fs.mkdir(path.dirname(sourceNativeFile), { recursive: true }); + await fs.writeFile(sourceNativeFile, "native module bytes"); + + const changed = await syncStandaloneNativeAssets(tempDir, fs, { + log: (message: unknown) => logs.push(String(message)), + }); + + assert.equal(changed, true); + assert.equal(await fs.readFile(destinationNativeFile, "utf8"), "native module bytes"); + assert.match(logs[0] ?? "", /wreq-js\/rust/); + }); +});