Skip to content

Commit 11a4b16

Browse files
sd2kclaude
andcommitted
fix(js): always fall back to non-streaming WebAssembly.instantiate
wasm-bindgen generates code that rethrows when WebAssembly.instantiateStreaming fails and Content-Type is application/wasm. This breaks in environments where fetch() returns a Proxy-wrapped Response (e.g. OpenTelemetry instrumentation-fetch) that passes JS `instanceof Response` but fails the browser's internal slot checks in WebAssembly.instantiateStreaming/compileStreaming. Add a post-build step that patches the generated __wbg_load function to always fall back to the ArrayBuffer path (response.arrayBuffer() + WebAssembly.instantiate) instead of conditionally rethrowing. The streaming path is purely an optimization; the ArrayBuffer path is always correct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e9dff15 commit 11a4b16

File tree

1 file changed

+53
-0
lines changed

1 file changed

+53
-0
lines changed

js/justfile

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ build: \
1010
(build-inner "seasons") \
1111
(build-inner "transforms")
1212
just fix-package-json
13+
just fix-wasm-streaming-fallback
1314
just copy-readme
1415

1516
build-inner target args='':
@@ -32,6 +33,58 @@ fix-package-json:
3233
jq < augurs/package.json ". | .version = \"$VERSION\"" > augurs/package.json.tmp
3334
mv augurs/package.json.tmp augurs/package.json
3435

36+
# Patch wasm-bindgen's generated __wbg_load to always fall back to the
37+
# non-streaming WebAssembly.instantiate path. The generated code rethrows
38+
# when instantiateStreaming fails and Content-Type is application/wasm,
39+
# but this breaks in environments where fetch() returns a Proxy-wrapped
40+
# Response (e.g. OpenTelemetry instrumentation-fetch) that passes JS
41+
# `instanceof Response` but fails the browser's internal C++ slot check.
42+
# Related: https://github.com/open-telemetry/opentelemetry-js/issues/5477
43+
fix-wasm-streaming-fallback:
44+
#!/usr/bin/env python3
45+
import re, glob, sys
46+
CATCH_RE = re.compile(
47+
r'(\s*)\} catch \(e\) \{\s*\n'
48+
r'\s*const validResponse.*?\n'
49+
r'\s*\n'
50+
r'\s*if \(validResponse.*?\n'
51+
r'\s*console\.warn\(.*?\n'
52+
r'\s*\n'
53+
r'\s*\} else \{\s*\n'
54+
r'\s*throw e;\s*\n'
55+
r'\s*\}'
56+
)
57+
CATCH_REPL = (
58+
r'\1} catch (e) {\1 console.warn('
59+
r'"`WebAssembly.instantiateStreaming` failed. Falling back to '
60+
r'`WebAssembly.instantiate` which is slower. Original error:\\n", e);'
61+
)
62+
EXPECTED_TYPES_RE = re.compile(r"const EXPECTED_RESPONSE_TYPES[^\n]*\n\n")
63+
errors = []
64+
for f in sorted(glob.glob("augurs/*.js")):
65+
text = open(f).read()
66+
if "instantiateStreaming" not in text:
67+
continue
68+
# Replace the conditional rethrow with an unconditional warn + fallback.
69+
text, n = CATCH_RE.subn(CATCH_REPL, text)
70+
if n == 0:
71+
errors.append(f"{f}: expected to patch __wbg_load but regex didn't match")
72+
continue
73+
# Remove the now-unused EXPECTED_RESPONSE_TYPES constant.
74+
text = EXPECTED_TYPES_RE.sub("", text)
75+
# Verify no throw e; remains in the __wbg_load function.
76+
if "throw e;" in text:
77+
errors.append(f"{f}: patched but 'throw e;' still present")
78+
continue
79+
open(f, "w").write(text)
80+
print(f" patched {f} ({n} replacement(s))")
81+
if errors:
82+
print("ERROR: wasm-streaming-fallback patch failed:", file=sys.stderr)
83+
for e in errors:
84+
print(f" {e}", file=sys.stderr)
85+
print("The wasm-bindgen output format may have changed. Update the regex in js/justfile.", file=sys.stderr)
86+
sys.exit(1)
87+
3588
copy-readme:
3689
cp README.md augurs/README.md
3790

0 commit comments

Comments
 (0)