From 08bca141cb8c4ab86b9b09fb1f6ab5e8191f9a7a Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Tue, 14 Jul 2026 19:11:35 -0300 Subject: [PATCH 1/8] fix(ci): add the auto-enqueue pull_request_rule to the Mergify config (queue_conditions alone are eligibility-only) (#7179) --- .mergify.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.mergify.yml b/.mergify.yml index 131c6d71a99..88bc431369e 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -46,6 +46,18 @@ queue_rules: merge_method: squash pull_request_rules: + # AUTO-ENQUEUE trigger — queue_conditions alone only define ELIGIBILITY in current + # Mergify semantics (without this rule the PR sits at "use @Mergifyio queue", + # observed live on PR #7175). The conditions mirror queue_conditions on purpose. + - name: queue on owner-applied label + conditions: + - base~=^release/v\d+\.\d+\.\d+$ + - label=queue + - -draft + - -conflict + actions: + queue: + name: release - name: clean up the queue label after merge conditions: - merged From 43ae04a02bf5ab5dd52198685b2ee0095bc56987 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Tue, 14 Jul 2026 23:49:49 -0300 Subject: [PATCH 2/8] fix(ci): migrate Mergify auto-enqueue to merge_protections_settings.auto_merge_conditions (rules-based path is EOL 2026-07-16) (#7216) --- .mergify.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 88bc431369e..d7eb728c0fd 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -17,6 +17,13 @@ # • Fallback path if Mergify misbehaves or the OSS plan changes: the manual # merge-train runbook (docs/ops/MERGE_TRAIN.md) — remove labels, proceed by hand. +# Auto-enqueue (current Mergify model, 2026): auto_merge_conditions in +# merge_protections_settings — the rules-based queue action / autoqueue path is +# deprecated (EOL 2026-07-16). The owner-applied `queue` label IS the approval. +merge_protections_settings: + auto_merge_conditions: + - label = queue + queue_rules: - name: release # Any current or future release branch — the reason GitHub's native queue was @@ -46,18 +53,6 @@ queue_rules: merge_method: squash pull_request_rules: - # AUTO-ENQUEUE trigger — queue_conditions alone only define ELIGIBILITY in current - # Mergify semantics (without this rule the PR sits at "use @Mergifyio queue", - # observed live on PR #7175). The conditions mirror queue_conditions on purpose. - - name: queue on owner-applied label - conditions: - - base~=^release/v\d+\.\d+\.\d+$ - - label=queue - - -draft - - -conflict - actions: - queue: - name: release - name: clean up the queue label after merge conditions: - merged From b47ad1dc778b69e34ce063f37eac74e9dff3a33e Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 15 Jul 2026 00:16:02 -0300 Subject: [PATCH 3/8] fix(ci): drop Mergify batch settings (batching is a paid-tier feature; free plan queue is serial) (#7220) --- .mergify.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index d7eb728c0fd..f7cbe19d7ea 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -45,10 +45,9 @@ queue_rules: - "#check-pending=0" - "#check-success>=1" - check-success=Merge integrity (changelog + generated skills) - # Batching: validate up to 10 queued PRs together (the manual train's sweet spot); - # don't hold a lone PR hostage waiting for siblings. - batch_size: 10 - batch_max_wait_time: 5 min + # NO batching: 'Merge Queue Batch' requires a paid Mergify tier (live finding + # 2026-07-15 — the queue command fails with "Cannot use Merge Queue batch" on + # the free plan). Serial queue (1 PR at a time) still automates the train. # Squash keeps the one-commit-per-PR history the CHANGELOG reconciliation expects. merge_method: squash From 15e98913aece2c2e814165f84aee5e4694a716f2 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 15 Jul 2026 00:48:07 -0300 Subject: [PATCH 4/8] fix(ci): merge queue tolerates the advisory dast-smoke failure (its GH-hosted build hang dequeued every attempt) (#7225) --- .mergify.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.mergify.yml b/.mergify.yml index f7cbe19d7ea..7cc4882127e 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -41,7 +41,14 @@ queue_rules: # is intentionally NOT a condition here: the owner-applied `queue` label IS the # approval in this repo's single-maintainer model (see governance header). merge_conditions: - - "#check-failure=0" + # "Zero failures" — EXCEPT the advisory dast-smoke: it is continue-on-error by + # design and its GH-hosted CLI build hangs recurrently (drafts #7184/#7221 were + # dequeued solely by it). Any OTHER failure still blocks (anti-fail-open kept). + - or: + - "#check-failure=0" + - and: + - "#check-failure=1" + - check-failure=dast-smoke - "#check-pending=0" - "#check-success>=1" - check-success=Merge integrity (changelog + generated skills) From a0a2e061f583439dab86ad831cb627d7ae57f69c Mon Sep 17 00:00:00 2001 From: hoan9an Date: Tue, 14 Jul 2026 10:03:23 +0700 Subject: [PATCH 5/8] fix(electron): strip hashed native modules from standalone and fix removal path Two issues caused better-sqlite3 (and other native modules) to fail with NODE_MODULE_VERSION mismatch in the Electron desktop app: 1. removeNativeModules targeted '.next' instead of the actual distDir ('.build/next'), so Next.js-traced native modules with mangled names (better-sqlite3-{hash}, keytar-{hash}, sqlite-vec-{hash}) were never removed from the standalone bundle. 2. patchTurbopackChunks was not enabled for the Electron build, so server chunks still referenced the mangled module names. The server loaded the stale Node-ABI .node from the mangled copy, while the correctly rebuilt Electron-ABI copy sat unused. Changes: - Enable patchTurbopackChunks in assembleStandalone call (strips hash suffixes from require() calls in server chunks) - Fix removeNativeModules path: '.next' -> NEXT_DIST_DIR - Add sqlite-vec to the native module removal list Fixes: #7082 --- scripts/build/prepare-electron-standalone.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/build/prepare-electron-standalone.mjs b/scripts/build/prepare-electron-standalone.mjs index da04fca00b6..1080168b5dd 100644 --- a/scripts/build/prepare-electron-standalone.mjs +++ b/scripts/build/prepare-electron-standalone.mjs @@ -177,6 +177,7 @@ assembleStandalone({ outDir: ELECTRON_STANDALONE_DIR, projectRoot: ROOT, sanitizePaths: true, + patchTurbopackChunks: true, copyNatives: true, // #6724/#6594: dereference Turbopack hashed-module symlinks — inside the packaged // app they would point at the build machine's absolute paths and break on install. @@ -193,9 +194,10 @@ removeGeneratedElectronArtifacts(); // so it cannot shadow the rebuilt one. rebuildBetterSqlite3ForElectron(join(ELECTRON_STANDALONE_DIR, "node_modules")); removeNativeModules(join(ELECTRON_STANDALONE_DIR, "node_modules"), ["keytar"]); -removeNativeModules(join(ELECTRON_STANDALONE_DIR, ".next", "node_modules"), [ +removeNativeModules(join(ELECTRON_STANDALONE_DIR, NEXT_DIST_DIR, "node_modules"), [ "better-sqlite3", "keytar", + "sqlite-vec", ]); console.log( From e809479c5c7ebf010f495f9ddb5d21b3cb8a1d69 Mon Sep 17 00:00:00 2001 From: hoan9an Date: Wed, 15 Jul 2026 22:48:22 +0700 Subject: [PATCH 6/8] test: add unit tests for removeNativeModules and extract to pure module Extract removeNativeModules() into a side-effect-free module (scripts/build/lib/removeNativeModules.mjs) so it can be unit-tested without importing the full Electron build harness. Add 6 tests covering: - no-op when baseDir does not exist / is empty - removal of hashed native modules (better-sqlite3-{hash}, keytar-{hash}) - removal of unhashed native modules matching prefixes - default prefixes only remove keytar - preservation of non-matching modules (prefix not at start) Also clean up unused readdirSync import from prepare-electron-standalone.mjs. --- scripts/build/lib/removeNativeModules.mjs | 26 +++ scripts/build/prepare-electron-standalone.mjs | 17 +- .../build/prepare-electron-standalone.test.ts | 163 ++++++++++++++++++ 3 files changed, 191 insertions(+), 15 deletions(-) create mode 100644 scripts/build/lib/removeNativeModules.mjs create mode 100644 tests/unit/build/prepare-electron-standalone.test.ts diff --git a/scripts/build/lib/removeNativeModules.mjs b/scripts/build/lib/removeNativeModules.mjs new file mode 100644 index 00000000000..b8cf2c4c28e --- /dev/null +++ b/scripts/build/lib/removeNativeModules.mjs @@ -0,0 +1,26 @@ +/** + * Pure helper: remove native modules from a node_modules directory. + * + * Used by prepare-electron-standalone.mjs to strip stale Node-ABI native + * modules that Next.js output tracing copied and that would shadow the + * correctly rebuilt Electron-ABI copies at runtime. + * + * Extracted as a pure, side-effect-free module so it can be unit-tested + * without importing the full Electron build harness. + * + * @param {string} baseDir - directory to scan (e.g. .../node_modules) + * @param {string[]} [prefixes=["keytar"]] - directory name prefixes to remove + */ +import { existsSync, readdirSync, rmSync } from "node:fs"; +import { join } from "node:path"; + +export function removeNativeModules(baseDir, prefixes = ["keytar"]) { + if (!existsSync(baseDir)) return; + const dirs = readdirSync(baseDir); + for (const dir of dirs) { + if (prefixes.some((p) => dir.startsWith(p))) { + const fullPath = join(baseDir, dir); + rmSync(fullPath, { recursive: true, force: true }); + } + } +} diff --git a/scripts/build/prepare-electron-standalone.mjs b/scripts/build/prepare-electron-standalone.mjs index 1080168b5dd..25a32f3a99f 100644 --- a/scripts/build/prepare-electron-standalone.mjs +++ b/scripts/build/prepare-electron-standalone.mjs @@ -1,11 +1,11 @@ #!/usr/bin/env node -import { cpSync, existsSync, lstatSync, readFileSync, readdirSync, rmSync } from "node:fs"; +import { cpSync, existsSync, lstatSync, readFileSync, rmSync } from "node:fs"; import { basename, dirname, join, relative } from "node:path"; import { fileURLToPath } from "node:url"; import { spawnSync } from "node:child_process"; import { assembleStandalone } from "./assembleStandalone.mjs"; -import { buildRebuildSpawnPlan } from "./electronRebuildPlan.mjs"; +import { removeNativeModules } from "./lib/removeNativeModules.mjs"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -70,19 +70,6 @@ function removeGeneratedElectronArtifacts() { } } -// --- Electron-UNIQUE: remove native modules for electron-builder ABI rebuild --- - -function removeNativeModules(baseDir, prefixes = ["keytar"]) { - if (!existsSync(baseDir)) return; - const dirs = readdirSync(baseDir); - for (const dir of dirs) { - if (prefixes.some((p) => dir.startsWith(p))) { - const fullPath = join(baseDir, dir); - rmSync(fullPath, { recursive: true, force: true }); - } - } -} - // --- Electron-UNIQUE: rebuild better-sqlite3 against the Electron ABI -------- // // The `npm ci` at the repo root compiles better-sqlite3 for the CI *Node* ABI diff --git a/tests/unit/build/prepare-electron-standalone.test.ts b/tests/unit/build/prepare-electron-standalone.test.ts new file mode 100644 index 00000000000..77890df0821 --- /dev/null +++ b/tests/unit/build/prepare-electron-standalone.test.ts @@ -0,0 +1,163 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { removeNativeModules } from "../../../scripts/build/lib/removeNativeModules.mjs"; + +/** + * Creates a fake node_modules directory with mixed modules — + * some that should be removed (native modules matching prefixes) + * and some that should be preserved (regular npm packages). + */ +function seedNodeModules(baseDir: string) { + const keep = [ + "@swc/helpers/package.json", + "playwright-core/index.js", + "zod/package.json", + "pino/index.js", + "typescript/package.json", + "ws/package.json", + ]; + for (const rel of keep) { + const full = path.join(baseDir, rel); + fs.mkdirSync(path.dirname(full), { recursive: true }); + fs.writeFileSync(full, `// ${rel}`); + } +} + +function seedHashedNativeModules(baseDir: string) { + const remove = [ + "better-sqlite3-90e2652d1716b047/build/Release/better_sqlite3.node", + "better-sqlite3-a1b2c3d4e5f6a7b8/build/Release/better_sqlite3.node", + "keytar-eb44cd511463a26b/build/Release/keytar.node", + "sqlite-vec-63899bd51e44e247/build/Release/vec0.node", + ]; + for (const rel of remove) { + const full = path.join(baseDir, rel); + fs.mkdirSync(path.dirname(full), { recursive: true }); + fs.writeFileSync(full, `// ${rel}`); + } +} + +/** Verify that a directory still exists (was NOT removed). */ +function assertExists(dir: string, label: string) { + assert.ok(fs.existsSync(dir), `expected ${label} to still exist`); +} + +/** Verify that a directory was removed. */ +function assertRemoved(dir: string, label: string) { + assert.ok(!fs.existsSync(dir), `expected ${label} to have been removed`); +} + +// ── removeNativeModules() tests ─────────────────────────────────────── + +test("removeNativeModules: no-op when baseDir does not exist", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const nonexistent = path.join(tmp, "does-not-exist"); + + // Must not throw + assert.doesNotThrow(() => { + removeNativeModules(nonexistent); + }); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); + +test("removeNativeModules: no-op when baseDir is empty", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const emptyDir = path.join(tmp, "empty"); + fs.mkdirSync(emptyDir); + + assert.doesNotThrow(() => { + removeNativeModules(emptyDir); + }); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); + +test("removeNativeModules: removes hashed native modules with custom prefixes", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const nodeModules = path.join(tmp, "node_modules"); + fs.mkdirSync(nodeModules, { recursive: true }); + + seedNodeModules(nodeModules); + seedHashedNativeModules(nodeModules); + + removeNativeModules(nodeModules, ["better-sqlite3", "keytar", "sqlite-vec"]); + + // Hashed native modules should be removed + assertRemoved(path.join(nodeModules, "better-sqlite3-90e2652d1716b047"), "better-sqlite3-90e2652d1716b047"); + assertRemoved(path.join(nodeModules, "better-sqlite3-a1b2c3d4e5f6a7b8"), "better-sqlite3-a1b2c3d4e5f6a7b8"); + assertRemoved(path.join(nodeModules, "keytar-eb44cd511463a26b"), "keytar-eb44cd511463a26b"); + assertRemoved(path.join(nodeModules, "sqlite-vec-63899bd51e44e247"), "sqlite-vec-63899bd51e44e247"); + + // Regular (non-hashed, non-native) modules must be preserved + assertExists(path.join(nodeModules, "@swc"), "@swc"); + assertExists(path.join(nodeModules, "playwright-core"), "playwright-core"); + assertExists(path.join(nodeModules, "zod"), "zod"); + assertExists(path.join(nodeModules, "pino"), "pino"); + assertExists(path.join(nodeModules, "typescript"), "typescript"); + assertExists(path.join(nodeModules, "ws"), "ws"); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); + +test("removeNativeModules: removes unhashed native modules matching prefix", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const nodeModules = path.join(tmp, "node_modules"); + fs.mkdirSync(nodeModules, { recursive: true }); + + // Unhashed native modules (installed by root npm install) + fs.mkdirSync(path.join(nodeModules, "better-sqlite3", "build", "Release"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "better-sqlite3", "build", "Release", "better_sqlite3.node"), "// native"); + fs.mkdirSync(path.join(nodeModules, "keytar"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "keytar", "index.js"), "// keytar"); + + removeNativeModules(nodeModules, ["better-sqlite3", "keytar"]); + + // Both should be removed (prefix "better-sqlite3" matches "better-sqlite3", "keytar" matches "keytar") + assertRemoved(path.join(nodeModules, "better-sqlite3"), "better-sqlite3 (unhashed)"); + assertRemoved(path.join(nodeModules, "keytar"), "keytar (unhashed)"); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); + +test("removeNativeModules: default prefixes only remove keytar", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const nodeModules = path.join(tmp, "node_modules"); + fs.mkdirSync(nodeModules, { recursive: true }); + + fs.mkdirSync(path.join(nodeModules, "better-sqlite3"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "better-sqlite3", "index.js"), "//"); + fs.mkdirSync(path.join(nodeModules, "keytar"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "keytar", "index.js"), "//"); + + // No custom prefixes → defaults to ["keytar"] only + removeNativeModules(nodeModules); + + assertExists(path.join(nodeModules, "better-sqlite3"), "better-sqlite3 (not in default prefixes)"); + assertRemoved(path.join(nodeModules, "keytar"), "keytar (in default prefixes)"); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); + +test("removeNativeModules: preserves non-matching modules with similar names", () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "remove-native-")); + const nodeModules = path.join(tmp, "node_modules"); + fs.mkdirSync(nodeModules, { recursive: true }); + + // Modules with names that contain the prefix but don't START with it + fs.mkdirSync(path.join(nodeModules, "x-better-sqlite3"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "x-better-sqlite3", "index.js"), "//"); + fs.mkdirSync(path.join(nodeModules, "@better-sqlite3"), { recursive: true }); + fs.writeFileSync(path.join(nodeModules, "@better-sqlite3", "index.js"), "//"); + + removeNativeModules(nodeModules, ["better-sqlite3"]); + + // These should survive because they don't START with "better-sqlite3" + assertExists(path.join(nodeModules, "x-better-sqlite3"), "x-better-sqlite3 (prefix not at start)"); + assertExists(path.join(nodeModules, "@better-sqlite3"), "@better-sqlite3 (prefix not at start)"); + + fs.rmSync(tmp, { recursive: true, force: true }); +}); From cd6140a32e4e61c0aeb2104e579763ab031e6e53 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:14:03 -0300 Subject: [PATCH 7/8] fix(electron): restore buildRebuildSpawnPlan import + drop unrelated .mergify.yml scope creep (#7123) The extract-removeNativeModules refactor dropped the buildRebuildSpawnPlan import (still used at rebuildBetterSqlite3ForElectron), which would crash the Electron packaging step with ReferenceError. Restore it. Also revert the unrelated .mergify.yml merge-queue-config changes that had crept into this Electron packaging PR (release-infra config, out of scope). Rebased onto release/v3.8.49 so #6794's materializeSymlinks:true and this PR's patchTurbopackChunks:true both remain in the assembleStandalone() call. --- .mergify.yml | 23 ++++--------------- scripts/build/prepare-electron-standalone.mjs | 1 + 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.mergify.yml b/.mergify.yml index 7cc4882127e..131c6d71a99 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -17,13 +17,6 @@ # • Fallback path if Mergify misbehaves or the OSS plan changes: the manual # merge-train runbook (docs/ops/MERGE_TRAIN.md) — remove labels, proceed by hand. -# Auto-enqueue (current Mergify model, 2026): auto_merge_conditions in -# merge_protections_settings — the rules-based queue action / autoqueue path is -# deprecated (EOL 2026-07-16). The owner-applied `queue` label IS the approval. -merge_protections_settings: - auto_merge_conditions: - - label = queue - queue_rules: - name: release # Any current or future release branch — the reason GitHub's native queue was @@ -41,20 +34,14 @@ queue_rules: # is intentionally NOT a condition here: the owner-applied `queue` label IS the # approval in this repo's single-maintainer model (see governance header). merge_conditions: - # "Zero failures" — EXCEPT the advisory dast-smoke: it is continue-on-error by - # design and its GH-hosted CLI build hangs recurrently (drafts #7184/#7221 were - # dequeued solely by it). Any OTHER failure still blocks (anti-fail-open kept). - - or: - - "#check-failure=0" - - and: - - "#check-failure=1" - - check-failure=dast-smoke + - "#check-failure=0" - "#check-pending=0" - "#check-success>=1" - check-success=Merge integrity (changelog + generated skills) - # NO batching: 'Merge Queue Batch' requires a paid Mergify tier (live finding - # 2026-07-15 — the queue command fails with "Cannot use Merge Queue batch" on - # the free plan). Serial queue (1 PR at a time) still automates the train. + # Batching: validate up to 10 queued PRs together (the manual train's sweet spot); + # don't hold a lone PR hostage waiting for siblings. + batch_size: 10 + batch_max_wait_time: 5 min # Squash keeps the one-commit-per-PR history the CHANGELOG reconciliation expects. merge_method: squash diff --git a/scripts/build/prepare-electron-standalone.mjs b/scripts/build/prepare-electron-standalone.mjs index 25a32f3a99f..fe3206507b9 100644 --- a/scripts/build/prepare-electron-standalone.mjs +++ b/scripts/build/prepare-electron-standalone.mjs @@ -5,6 +5,7 @@ import { basename, dirname, join, relative } from "node:path"; import { fileURLToPath } from "node:url"; import { spawnSync } from "node:child_process"; import { assembleStandalone } from "./assembleStandalone.mjs"; +import { buildRebuildSpawnPlan } from "./electronRebuildPlan.mjs"; import { removeNativeModules } from "./lib/removeNativeModules.mjs"; const __filename = fileURLToPath(import.meta.url); From a0b0f090869d439058b6672c2644843bfaf77bb8 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Fri, 17 Jul 2026 08:27:15 -0300 Subject: [PATCH 8/8] docs(changelog): add #7123 electron native-module fragment --- changelog.d/fixes/7123-electron-strip-hashed-native-modules.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/fixes/7123-electron-strip-hashed-native-modules.md diff --git a/changelog.d/fixes/7123-electron-strip-hashed-native-modules.md b/changelog.d/fixes/7123-electron-strip-hashed-native-modules.md new file mode 100644 index 00000000000..49c10831e67 --- /dev/null +++ b/changelog.d/fixes/7123-electron-strip-hashed-native-modules.md @@ -0,0 +1 @@ +- fix(electron): strip hashed native-module duplicates from the Electron standalone and fix the removal path. Enables the already-proven `patchTurbopackChunks` on the Electron packaging step (so server chunks stop `require()`-ing hashed names like `better-sqlite3-`), and points `removeNativeModules()` at the real `NEXT_DIST_DIR` (`.build/next`) instead of a hardcoded `.next` that never matched — so the stale Node-ABI native copies that shadowed the Electron-ABI-rebuilt `better-sqlite3` are actually deleted. Also strips `sqlite-vec`. Fixes the Electron `Internal Server Error` (better-sqlite3 ABI mismatch → sql.js WASM OOM fallback). `removeNativeModules()` extracted to a pure module with unit tests (#7123, closes #7082)