Skip to content

fix: load ngspice engine without blob: import (fixes schematic simulation) - #1691

Open
rushabhcodes wants to merge 1 commit into
mainfrom
fix/ngspice-blob-import-node
Open

fix: load ngspice engine without blob: import (fixes schematic simulation)#1691
rushabhcodes wants to merge 1 commit into
mainfrom
fix/ngspice-blob-import-node

Conversation

@rushabhcodes

@rushabhcodes rushabhcodes commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Important

This PR is a proof / stopgap, not the real fix. It patches a dependency's
built output in node_modules so schematic simulation works on the server
today. The actual defect lives in @tscircuit/ngspice-spice-engine

Problem

Schematic simulation fails on the server with:

Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. Received protocol 'blob:'

Root cause: @tscircuit/ngspice-spice-engine@0.0.18 loads its WASM engine via an unconditional URL.createObjectURL + import("blob:..."):

// node_modules/@tscircuit/ngspice-spice-engine/dist/index.js
const source = await response.text();
const moduleUrl = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
return import(moduleUrl); // ← Node ESM loader rejects blob:

Node's ESM loader only accepts file:, data:, and node: schemes, so this throws the moment ngspice loads. It is the missing piece of #1686: preloadNgspice() hits this same import and silently fails, so the error only surfaces at simulate time.

What this PR does (the workaround)

  • Patches the package in postinstall (scripts/apply-dependency-patches.mjs, mirroring the existing occt-import-js patch with idempotency + "upstream changed" guards) to import the bundled @tscircuit/eecircuit-engine directly instead of fetching + blob-importing. This removes the blob import and the runtime CDN fetch (matching Prevent CDN fallback in production by preloading bundled ngspice #1686's goal).
  • Promotes @tscircuit/eecircuit-engine to a runtime dependency (pinned to 1.7.4, the same version the package's hardcoded CDN URL referenced) since a runtime dependency now imports it.
  • Adds a Node-level regression test (tests/ngspice-node-loader.test.ts). The existing bun tests can't catch this — bun accepts blob: imports — so the test spawns a real Node process to run simulate().

The real fix

The proper fix belongs in @tscircuit/ngspice-spice-engine (lib/import-eecircuit-engine.ts). Today it loads the engine the same way in every environment:

const source = await response.text();
const moduleUrl = URL.createObjectURL(new Blob([source], { type: "text/javascript" }));
return import(moduleUrl);

blob: URLs only resolve in browsers/bun; the Node ESM loader rejects them — so the package is broken for any Node consumer, not just this repo. It should be environment-aware. Either:

  1. Branch on environment — keep the CDN fetch, but in non-browser environments use a data: URL (which the Node loader accepts) instead of a blob URL. This is the minimal change and matches the pattern @tscircuit/eval already uses:
    const dataUrl = `data:text/javascript;base64,${Buffer.from(source, "utf8").toString("base64")}`;
    return import(dataUrl);
  2. (Preferred) Import the package directly — declare @tscircuit/eecircuit-engine as a real dependency and return import("@tscircuit/eecircuit-engine"), optionally falling back to the CDN. This avoids the network round-trip entirely and is exactly what this PR forces via the patch.

Once a fixed @tscircuit/ngspice-spice-engine is published, the follow-up here is trivial: bump the dependency and remove patchNgspiceSpiceEngine() from scripts/apply-dependency-patches.mjs.

Verification

  • ✅ Patch applies and is idempotent (re-run → "already patched")
  • ✅ New guard fails without the patch (exact production error) and passes with it
  • ✅ Existing schematic-simulation-svg test still passes
  • ✅ Formatted with biome

…tion)

@tscircuit/ngspice-spice-engine@0.0.18 loads its WASM engine via
URL.createObjectURL + import("blob:..."), which the Node ESM loader
rejects ("Only URLs with a scheme in: file, data, and node are
supported... Received protocol 'blob:'"), breaking schematic simulation
on the server.

- Patch the package in postinstall to import the bundled
  @tscircuit/eecircuit-engine directly, which also avoids a runtime CDN
  fetch (matching the goal of #1686).
- Promote @tscircuit/eecircuit-engine to a runtime dependency since a
  runtime dependency now imports it.
- Add a Node-level regression test: bun accepts blob: imports, so the
  existing bun tests cannot catch this; the test spawns a real Node
  process to simulate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 27, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
svg-tscircuit-com Ready Ready Preview, Comment Jun 27, 2026 9:30pm
svg-tscircuit-com-wke7 Ready Ready Preview, Comment Jun 27, 2026 9:30pm

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.

1 participant