@penumbra-realm/shim
v0.1.2
Published
Research-substrate ShadowRealm shim (TC39 Stage 2.7) backed by iframes — browser/DOM only, not a production polyfill
Readme
@penumbra-realm/shim
A ShadowRealm shim backed by isolated iframes, providing a spec-conformant ShadowRealm on browsers that lack a native implementation (or where native ShadowRealm is not enabled, e.g. Safari TP without JSC_useShadowRealm).
Implements the TC39 ShadowRealm proposal surface (new ShadowRealm(), realm.evaluate(), realm.importValue()) as specified in Stage 2.7 Draft / February 10, 2025.
Browser-only — requires a DOM document; cannot run in Workers or pure-JS contexts.
Design
Each ShadowRealm is backed by a hidden <iframe> whose global is wrapped by a Proxy. The blocking walk replaces non-permitted globals (not in SAFE_ECMA_INTRINSICS + PERMITTED_HOST_GLOBALS in src/constants.js) with getters that log warnings.
Static analysis (src/parser.js) checks for top-level return, new.target, and super using a hand-rolled scanner.
Module loading (src/module-loader.js) uses regex-based ESM transform to CommonJS-style assignments.
Cross-realm interop (src/boundary.js) wraps functions—only functions, primitives, null, undefined cross; non-callable objects are rejected.
Lifecycle: No explicit teardown; cleanup via FinalizationRegistry (degrades on engines without it—there's a leak).
Usage
<script type="module">
await import('@penumbra-realm/shim');
const realm = new ShadowRealm();
realm.evaluate('1 + 2'); // → 3
realm.importValue('./mod.js', 'default'); // → Promise
</script>For classic <script>: load dist/shadowrealm.iife.js directly.
import { ShadowRealm, createRealm } from '@penumbra-realm/shim';
const realm = createRealm();
realm.evaluate('Math.max(3, 7)');Build
npm run build # produces dist/shadowrealm.esm.js and dist/shadowrealm.iife.jsFile map
src/
bootstrap.js # entry: installs ShadowRealm, wires debug flag
shadowrealm.js # ShadowRealm class + iframe interception
create-realm.js # createRealm() factory
realm-record.js # iframe + shadow-global proxy
intrinsics.js # host intrinsics + cross-frame specialization
boundary.js # GetWrappedValue / WrappedFunctionCreate / CreateTypeErrorCopy
module-loader.js # ESM loader for importValue
parser.js # static restriction scanner
constants.js # SAFE_ECMA_INTRINSICS / PERMITTED_HOST_GLOBALS
lifecycle.js # FinalizationRegistry cleanup
debug.js # shared debug flagLicense
MIT — see LICENSE.
