From e8bf9e20c71f7e04a095ea760d9ef8f3a73818f0 Mon Sep 17 00:00:00 2001 From: rob thijssen Date: Tue, 15 Sep 2026 11:53:18 +0300 Subject: [PATCH] fix(quantus-crypto): strip wasm-bindgen's fetch-based init from the shipped glue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package could not be bundled. Every webpack consumer failed with: Module not found: Error: Can't resolve 'quantus_crypto_bg.wasm' in node_modules/@quantus/crypto/generated wasm-bindgen's async `__wbg_init` contains module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url); and webpack resolves `new URL(..., import.meta.url)` statically, at build time, whether or not the branch can run. The file is not in the package — the wasm ships base64'd in bytes.js, which is the entire point of this package — so the build failed on a code path we never call. node never sees it, which is why ten Rust tests, twelve consumer assertions and a browser probe all passed while the package was unusable in a bundler. It took a real extension build to surface, and that is the useful lesson: this package's consumers bundle, and nothing in its own test suite does. So the dead init is removed after bindgen runs. Shipping a second copy of the wasm to satisfy a path we do not use would be the wide fix; deleting generated code we never call is the narrow one. The stripper asserts the shape it expects and throws if wasm-bindgen changes it, rather than silently no-opping — a build that quietly stopped stripping would ship the broken package again. It also re-checks that no reference to the .wasm filename survives. Published as 0.1.1. Refs quantus/wasm#1, quantus/extension#2 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_012uDUodEcRbBwNRi3UCmw8f --- packages/quantus-crypto/package.json | 2 +- .../src/generated/quantus_crypto.js | 28 +--------- scripts/build-quantus.sh | 4 ++ scripts/strip-wasm-fetch-init.mjs | 53 +++++++++++++++++++ 4 files changed, 59 insertions(+), 28 deletions(-) create mode 100644 scripts/strip-wasm-fetch-init.mjs diff --git a/packages/quantus-crypto/package.json b/packages/quantus-crypto/package.json index 6b147191..30ffb76b 100644 --- a/packages/quantus-crypto/package.json +++ b/packages/quantus-crypto/package.json @@ -15,7 +15,7 @@ }, "sideEffects": false, "type": "module", - "version": "0.1.0", + "version": "0.1.1", "main": "index.js", "dependencies": { "fflate": "^0.8.2", diff --git a/packages/quantus-crypto/src/generated/quantus_crypto.js b/packages/quantus-crypto/src/generated/quantus_crypto.js index 9689b1e8..97d800aa 100644 --- a/packages/quantus-crypto/src/generated/quantus_crypto.js +++ b/packages/quantus-crypto/src/generated/quantus_crypto.js @@ -408,30 +408,4 @@ function initSync(module) { return __wbg_finalize_init(instance, module); } -async function __wbg_init(module_or_path) { - if (wasm !== undefined) return wasm; - - - if (module_or_path !== undefined) { - if (Object.getPrototypeOf(module_or_path) === Object.prototype) { - ({module_or_path} = module_or_path) - } else { - console.warn('using deprecated parameters for the initialization function; pass a single object instead') - } - } - - if (module_or_path === undefined) { - module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url); - } - const imports = __wbg_get_imports(); - - if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) { - module_or_path = fetch(module_or_path); - } - - const { instance, module } = await __wbg_load(await module_or_path, imports); - - return __wbg_finalize_init(instance, module); -} - -export { initSync, __wbg_init as default }; +export { initSync }; diff --git a/scripts/build-quantus.sh b/scripts/build-quantus.sh index 18497240..4e0120d9 100755 --- a/scripts/build-quantus.sh +++ b/scripts/build-quantus.sh @@ -35,6 +35,10 @@ echo "*** Converting to WASM" # The glue is a build artifact but is checked in, so the package can be built # without a Rust toolchain — the same reasoning as upstream checking in an empty # bytes.js. Copy it back so the two never drift. +# Must run before the glue is copied into src/generated — see the script's own +# comment for why the fetch path cannot ship. +node ./scripts/strip-wasm-fetch-init.mjs packages/$PKG/build-wasm/$CRATE.js + echo "*** Updating checked-in bindings" cp packages/$PKG/build-wasm/$CRATE.js packages/$PKG/src/generated/$CRATE.js cp packages/$PKG/build-wasm/$CRATE.d.ts packages/$PKG/src/generated/$CRATE.d.ts diff --git a/scripts/strip-wasm-fetch-init.mjs b/scripts/strip-wasm-fetch-init.mjs new file mode 100644 index 00000000..59c1b824 --- /dev/null +++ b/scripts/strip-wasm-fetch-init.mjs @@ -0,0 +1,53 @@ +// Copyright 2026 @quantus/crypto authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +// Removes wasm-bindgen's async `__wbg_init` from the generated glue. +// +// We never call it — the whole point of this package is `initSync` over bytes +// compiled into the JS, because the consumer is an MV3 service worker that +// cannot usefully fetch. But `__wbg_init` contains +// +// module_or_path = new URL('quantus_crypto_bg.wasm', import.meta.url); +// +// and webpack resolves `new URL(..., import.meta.url)` statically, at build +// time, whether or not the branch can ever run. The file is not in the package — +// the wasm ships base64'd in bytes.js — so every bundling consumer fails with +// "Can't resolve 'quantus_crypto_bg.wasm'". node never sees this, which is why +// it took a real extension build to surface. +// +// Deleting dead generated code is the narrow fix; shipping a second copy of the +// wasm to satisfy a code path we do not use would be the wide one. + +import fs from 'node:fs'; + +const [file] = process.argv.slice(2); +const source = fs.readFileSync(file, 'utf-8'); + +// Fail loudly rather than silently no-op if wasm-bindgen changes shape: a build +// that quietly stopped stripping would ship the broken package again. +const START = '\nasync function __wbg_init(module_or_path) {'; +const EXPORTS = 'export { initSync, __wbg_init as default };'; + +const start = source.indexOf(START); + +if (start === -1 || !source.includes(EXPORTS)) { + throw new Error(`${file}: wasm-bindgen output is not the shape this expects; re-check whether __wbg_init still needs stripping`); +} + +const end = source.indexOf('\n}\n', start); + +if (end === -1) { + throw new Error(`${file}: could not find the end of __wbg_init`); +} + +const stripped = source + .slice(0, start) + .concat(source.slice(end + 3)) + .replace(EXPORTS, 'export { initSync };'); + +if (stripped.includes('quantus_crypto_bg.wasm')) { + throw new Error(`${file}: a reference to the .wasm file survived stripping`); +} + +fs.writeFileSync(file, stripped); +console.log(`*** Stripped fetch-based init from ${file}`);