Skip to content

fix(electron): normalize hashed standalone externals - #7353

Merged
diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.49from
tianrking:agent/fix-electron-hashed-externals
Jul 19, 2026
Merged

diegosouzapw merged 2 commits into
cheaper-inference:release/v3.8.49from
tianrking:agent/fix-electron-hashed-externals

Conversation

@tianrking

Copy link
Copy Markdown
Contributor

Summary

Fixes the Electron packaging path that left Turbopack-hashed external imports such as ws-a972e7ffa40ff725 in server instrumentation chunks.

assembleStandalone already has patchTurbopackChunks for this exact normalization and the npm prepublish flow enables it. The Electron staging flow did not, so the packaged app copied canonical node_modules/ws but attempted to import the non-existent hashed name at startup.

This enables the existing normalization step for Electron builds and adds regression coverage for both the package-name rewrite and the Electron assembly wiring.

Validation

  • node --import tsx/esm --test --test-name-pattern "patchTurbopackChunks restores canonical external package names|electron standalone assembly normalizes Turbopack hashed external imports" tests/unit/build/assemble-standalone.test.ts tests/unit/electron-packaging.test.ts
  • npx prettier --check scripts/build/prepare-electron-standalone.mjs tests/unit/build/assemble-standalone.test.ts tests/unit/electron-packaging.test.ts
  • npx eslint tests/unit/build/assemble-standalone.test.ts tests/unit/electron-packaging.test.ts --no-error-on-unmatched-pattern --suppressions-location config/quality/eslint-suppressions.json
  • git diff --check

The complete assemble-standalone.test.ts suite has an existing Windows-only path failure in its unrelated standalone-server-ws.mjs test: it derives a file: URL with .pathname, producing F:\\F:\\.... The selected new regression and Electron packaging test both pass.

Scope

No provider behavior, route handling, or bundled dependency list changes. The staging step now rewrites only the erroneous hash suffixes before electron-builder copies the standalone bundle.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@tianrking
tianrking marked this pull request as ready for review July 16, 2026 04:55
@tianrking
tianrking requested a review from diegosouzapw as a code owner July 16, 2026 04:55
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Thanks for tracking this down — this lines up exactly with #7346 (macOS Electron ws-a972e7ffa40ff725 ERR_MODULE_NOT_FOUND). Good catch that patchTurbopackChunks was already wired for the npm prepublish path but never enabled for the Electron staging step; reusing the existing, already-tested normalization rather than writing a new one is the right call here.

I verified locally in an isolated worktree: both new regression tests pass, the full assemble-standalone.test.ts and electron-packaging.test.ts suites pass, eslint is clean, and confirmed this doesn't touch the non-Electron standalone path (build-next-isolated.mjs keeps the flag off by default) or overlap with the v3.8.48 win-spawn/better-sqlite3 rebuild hotfix.

One tiny nit (non-blocking, won't hold up the merge): the inventory-table comment at the top of assembleStandalone.mjs (around line 33) still lists the Electron column for the Turbopack hashed-chunk patch as "-" — worth a follow-up doc tweak to flip it to "Y" now that this lands, just for future readers of that table.

Planning to merge this as-is. Will also link it to close #7346.

@diegosouzapw
diegosouzapw merged commit 1e29b5c into cheaper-inference:release/v3.8.49 Jul 19, 2026
3 checks passed
@diegosouzapw

Copy link
Copy Markdown
Collaborator

Merged into release/v3.8.49 — thank you for the contribution, @tianrking! 🎉 Validated in today's full-suite merge-train (33 PRs, 19k+ tests green) before landing.

@diegosouzapw diegosouzapw mentioned this pull request Jul 23, 2026
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
…ce#7353)

* fix(electron): normalize hashed standalone externals

* chore(release): add cheaper-inference#7353 changelog fragment
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ce#7353)

* fix(electron): normalize hashed standalone externals

* chore(release): add cheaper-inference#7353 changelog fragment
for-the-zero added a commit to for-the-zero/fork--OmniRoute that referenced this pull request Oct 1, 2026
…one bundles

The standalone tracer emits externalized packages as REAL directories named
`playwright-core-<16hex>`, but the Turbopack server runtime asks for the BARE
specifier at request time: the externals chunk does
`await ctx.externalImport("playwright-core")`, and that literal carries no hash
for patchTurbopackChunks() to strip. Node then walks up from
`<outDir>/<relDistDir>/server/chunks/`, finds only the hashed sibling, and throws
ERR_MODULE_NOT_FOUND.

The failure is lazy and route-shaped, so it reads like a data bug. The externals
chunk only loads when a route first reaches the module, so the server boots
cleanly and only the affected route 500s — with an EMPTY body, because the route
module never evaluates. On a Windows Electron build `GET /api/providers`
returned 500/empty, which the dashboard's `safeJson()` silently degrades to `[]`,
blanking /dashboard/providers and /dashboard/combos while /dashboard/models (no
/api/providers dependency) kept working.

Each existing repair misses it, and the misses compose:

- materializeBundledSymlinks (cheaper-inference#6724/cheaper-inference#6594) only touches symlinks; its
  `if (!stat.isSymbolicLink()) continue;` skips a real hashed directory.
- patchTurbopackChunks (cheaper-inference#7353) strips the hash off the reference, which is what
  creates the bare-name requirement in the first place.
- repairEmptyExternalPackageDirs (cheaper-inference#9913/cheaper-inference#7346) only overlays dirs that already
  EXIST but are hollow, and needs the package in the source node_modules. Here
  the bare dir does not exist at all and the dependency is Turbopack-only, so
  neither precondition holds.

Add materializeHashedModuleAliases() and run it unconditionally as step 8, after
materializeBundledSymlinks so hashed symlinks are already real directories and
can serve as copy sources. It keeps both names (so a hashed `require()` still
resolves), never clobbers an already-traced bare package, skips symlinks, and is
idempotent.

Related: cheaper-inference#7346
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants