near-membrane
A DOM membrane library for creating fast, secure sandboxed JavaScript environments in the browser.
Repository Health
Technical Analysis
@locker/near-membrane-dom implements Salesforce’s near-membrane sandboxing technique for browsers, using a detached iframe to create an isolated Red Realm that mirrors the capabilities of the host Blue Realm. It powers Lightning Locker Service, letting untrusted or third-party code run inside a same-origin sandbox with distortions controlling exactly which globals, methods, and properties are exposed.
Unlike heavier VM-based isolation, near-membrane relies on lazily-initialized Proxies so sandbox creation stays fast and memory-light, activating only the portion of the global object graph that the sandboxed code actually touches. It has no runtime dependencies beyond its sibling @locker/near-membrane-* packages and ships pre-built ESM/CJS bundles alongside full TypeScript types.
What You Get
- Detached-iframe sandboxing - Creates a hidden, same-origin iframe as an isolated Red Realm without leaking references back to the opener window.
- Distortion-based capability control - A
distortionCallbacklets you swap, hide, or wrap any global before it crosses the membrane. - Lazy Proxy activation - Red Proxies initialize only on first trap invocation, keeping sandbox creation overhead minimal.
- Full TypeScript types - Ships
.d.tsdeclarations generated straight from the TypeScript source forcreateIframeVirtualEnvironmentand related APIs.
Common Use Cases
- Third-party script isolation - Run ads, widgets, or plugin code without letting it observe or mutate the host page’s real globals.
- Multi-tenant component sandboxes - Salesforce’s Lightning Locker Service uses it to isolate each Lightning component’s execution context.
- Polyfill/environment testing - Evaluate code against a distinct set of polyfills or timers without touching the host environment.
- Capability-limited plugin systems - Grant plugins only the DOM APIs and globals you explicitly endow, blocking the rest by default.
Under The Hood
Architecture
near-membrane-dom’s createIframeVirtualEnvironment (in src/browser-realm.ts) creates a hidden, detached <iframe> via createDetachableIframe, then wires its contentWindow (the Red Realm) to the host window (the Blue Realm) through @locker/near-membrane-base’s VirtualEnvironment class, createBlueConnector, and createRedConnector. Each side runs an init()-style membrane marshall (membrane.ts) that exchanges opaque Pointer references and a large set of Callable* hook functions (get/set/apply/construct/ownKeys, etc.) so that Proxy traps on one realm’s objects forward through to the other without ever exposing raw cross-realm identities. browser-realm.ts layers DOM-specific concerns on top of that generic protocol: filtering window’s own keys, special-casing unforgeable descriptors on Window/WindowProperties/EventTarget prototypes that cannot be proxied away, and registering a custom devtools formatter so proxied values remain inspectable. If the core connector/environment abstraction in near-membrane-base changed its pointer/callable protocol, every consuming package (-dom, -node) would need to update in lockstep, since they share the exact same marshalling wire format.
Tech Stack
The project is a Lerna/Yarn-workspaces monorepo written in strict TypeScript ("strict": true, noUnusedLocals, noImplicitReturns) targeting es2020, compiled with Babel (@babel/preset-typescript, @babel/preset-env) and bundled per-package with Rollup (@rollup/plugin-babel, @rollup/plugin-node-resolve, @rollup/plugin-replace) into both CJS (dist/index.cjs.js) and ESM (dist/index.mjs.js) outputs, with .d.ts types emitted separately via tsc --emitDeclarationOnly. near-membrane-dom itself depends only on its sibling packages @locker/near-membrane-base, @locker/near-membrane-shared, and @locker/near-membrane-shared-dom — no third-party runtime dependencies. Node-side tests run under Jest with jsdom, while actual browser/DOM behavior is verified through Karma against headless Chrome via Puppeteer, with istanbul merging coverage from both runners into one report.
Code Quality
The repo shows extensive test coverage - roughly 88 spec files across the near-membrane-base, near-membrane-node, near-membrane-shared, and near-membrane-shared-dom packages (the DOM-specific package is instead covered by the Karma/browser suite under test/). ESLint is configured with eslint-config-airbnb-base/airbnb-typescript plus eslint-plugin-import and Prettier integration, enforced in CI via a dedicated Run-linter job. Husky + commitlint (@commitlint/config-conventional) enforce Conventional Commits at the git-hook level. Error handling favors explicit TypeError throws on invalid inputs (e.g. missing global object) rather than silent fallbacks. GitHub Actions CI runs the linter, Jest suite, and Karma suite with merged coverage reporting published to a pages branch on every push/PR.
What Makes It Unique
Rather than isolating code via a full JS engine sandbox (Web Workers, VMs, or a real ShadowRealm), near-membrane achieves isolation with same-origin detached iframes plus a lazily-initialized Proxy membrane - Red Proxies are only materialized the first time a trap actually fires, which keeps sandbox-creation cost low even though browser global objects expose an enormous API surface. The library explicitly documents its own boundaries (it is not a security boundary on its own - that’s left to the distortion mechanism) and handles browser-specific edge cases most membrane implementations ignore, like unforgeable Window prototype descriptors and the WindowProxy identity/GC quirks that arise when trying to sandbox cross-iframe references.
Used by 2 apps in this directory
Grafana
Monitoring · Analytics
The open-source observability platform that unifies metrics, logs, and traces from any data source into dynamic, queryable dashboards.
Metabase
Analytics
The open-source BI platform that lets anyone ask questions and build dashboards without writing SQL — with an embedded analytics SDK and AI-powered query assistant included.