Strategies per environment
One resolution and delivery model, consumed four ways: browsers with native ES modules or SystemJS, Node.js through BEE Node and Deno through its import map, with the development updates that work in all of them and the local measurements behind the defaults.
- Availability: Experimental
- Evidence: Recorded run
- Explanation
One model
Every origin of the delivery contract answers the same two things: the modules at their /m/… addresses, and the resolution that says which address delivers each public specifier, at /resolution.json and /importmap.json. An origin is a development server, a Workspace environment or the base of one CDN release (/_r/<release number>).
An environment does not need a delivery of its own. It needs a way to consume those two documents: turn a bare specifier into an address, fetch it from that origin, and run it. That is the only part that changes from one environment to another.
The strategies
| Environment | How it resolves | Which outputs |
|---|---|---|
| Browser, native ES modules (default) | An import map inline in the page; the eager modules are preloaded | target=browser&format=esm |
| Browser, SystemJS | SystemJS 6.15.1, stored with the release as loader.js, reading a systemjs-importmap |
target=browser&format=system |
| Node.js | BEE Node's resolution adapter reads resolution.json of the base |
target=node&format=esm |
| Deno | Deno reads importmap.json of the base natively |
target=node&format=esm, or platform-neutral browser outputs through the browser map |
A release chooses its browser loader when it is prepared (loader of the application: esm or system). Node.js and Deno use the backend target of the application, whose outputs and resolution a release stores as well. See Backend targets.
Browsers
Native ES modules are the default. The shell of a release inlines its import map with release-bound addresses and announces the eager modules with <link rel="modulepreload">, so the browser fetches them in parallel. See Native ESM and import maps.
SystemJS is the mode for environments without import maps. The release stores SystemJS as its own loader.js, so no page reaches a third-party origin. See SystemJS.
In both, stylesheets are applied explicitly: the document links them, a widget adopts them in its shadow root. See Who applies a stylesheet.
Node.js
BEE Node loads Beyond modules into Node.js through module hooks. Its resolution adapter is given a base, reads <base>resolution.json?target=node&format=esm once when the process starts, and resolves every bare specifier of a served module through it, scopes included:
BEE_URL=https://<application host>/_r/12/ BEE_ADAPTER=resolution BEE_CACHE=.beyond/cache \
node --import @beyond-js/bee-node/register app.mjs- A module is fetched only from under the base. A specifier the document does not resolve is an error naming the specifier and the importer; a Node builtin goes to Node.
- Your own script keeps Node's resolution for your installed packages; the document answers first.
- Responses are kept for the process. With
BEE_CACHE,immutableanswers are also kept on disk and reused without a request, which is what a release base answers: a process that starts again with a warm cache makes no request at all. A development server answersno-store, so it is asked on every start. - A handshake that fails stops the process, naming the adapter. Nothing falls back to another adapter.
Deno
Deno reads the import map of the base directly:
deno run --allow-import=<application host> \
--import-map='https://<application host>/_r/12/importmap.json?target=node&format=esm' app.mjsIts addresses are relative to the map's URL, so Deno requests every module from the base. Node-target outputs run, node: builtins included; a platform-neutral module of a browser target runs through the browser map (?target=browser&format=esm). Deno needs --allow-import for the delivery host.
During development
A development server answers the same /resolution.json and /importmap.json, computed from the workspace, and format=system as well. The same strategies therefore work against it, with the development options.
The development runtime (@beyond-js/local-2026, a provisional name) applies updates with one mechanism in all four environments: it replaces the internal modules whose hash changed and keeps the state of the others. Each environment only supplies how it imports an update (native import(), or SystemJS's own import in a SystemJS page) and whether it has a document for stylesheets.
- Where notifications come from. By default the service's event stream,
<origin>/events. Any emitter that speaks the same event protocol can replace it, at another URL or as an object in the same process; Workspace is one such emitter.local.hmr.notify(event)delivers a notification by hand. The updates themselves are always requested from the origin. - Failures keep the last valid state. A source that does not compile changes nothing and its correction is applied; code that throws when evaluated fails that update and the next one evaluates it again; a stylesheet that is invalid or fails to load keeps the last valid one.
- A consumer that missed events is stale. After the service restarts, the runtime reports
staleand applies nothing more: restart the consumer, or reload the page.
This was executed in Chrome with native ES modules and with SystemJS, in Node.js through BEE Node and in Deno, with the service's own stream, an external emitter and notify().
Measurements
Measured on 2026-09-23 on one development machine under heavy shared load, on a loopback network, with releases prepared by the real pipeline; medians of five runs. In Chrome a 40 ms round trip was emulated on every request. Compare the variants with each other; these are not hosted numbers.
| Case, Chrome, cold start | Ready, no preload | Ready, preload | Transferred |
|---|---|---|---|
| React, native ESM | 244 ms | 147 ms | 233.9 kB |
| React, SystemJS | 247 ms | 148 ms | 453.2 kB |
| Four widget families, native ESM | 429 ms | 335 ms | 577.4 kB |
| Four widget families, SystemJS | 432 ms | 345 ms | 981.3 kB |
| Case, a React server renderer | Cold | Warm |
|---|---|---|
Node.js 22, BEE Node resolution adapter |
89 ms, 5 requests | 41 ms, no request |
Deno 2.9.7, --import-map |
55 ms, 5 requests | 19 ms, no request |
The defaults follow from them:
- Preload is on. Announcing the eager modules started a cold page 23–40 % sooner once a round trip exists; on loopback it cost nothing measurable.
- Native ES modules stay the default. SystemJS was as fast here but transferred 1.7–1.9 times the bytes, because its conversion reprints the minified output.
- Import maps are inline in the shell: 1.1 to 6.1 kB in these cases, one request fewer than a map loaded with
src. - Node.js processes that start often use
BEE_CACHE: a warm cache starts without a request and in half the time.
Another environment
A new environment is a new consumer of the same two documents, not a new delivery. It needs to read resolution.json or importmap.json of an origin, fetch modules only from that origin, and run ES modules or System.register modules. For development updates it gives the runtime its import function and, where it has one, a document.