Same URL, local and CDN
Write a consumer once and point it at a local Dev Server or at the CDN by changing a single base origin, and know the differences you must expect.
- Availability: Experimental
- Evidence: Read from source
- How-to guide
Starting state
You develop against the Packages Dev Server and you release to the CDN. You want one loader, one set of URLs and one way of handling errors, not a development variant and a production variant.
The rule that makes this possible: the same module is the same relative path and query on both services. Only the base origin changes.
Keep the origin in one place
Hold the base origin in configuration and build every URL from it:
const origin = process.env.MODULES_ORIGIN; // http://localhost:<port> or your CDN origin
const url = new URL('/m/@example/[email protected]/modules/text?target=browser&format=esm', origin);Do not branch on the environment anywhere else. If you find yourself writing a different path for production, stop: a difference in the path means something other than the origin changed.
With a resolution document the same holds for a whole application. Its values are origin-relative, so one document feeds a local import map and a published one:
resolution.importmap('http://localhost:<port>');
resolution.importmap('<your CDN origin>');Both origins also answer the resolution themselves: <origin>/resolution.json?target=…&format=… and <origin>/importmap.json?…. On the CDN the origin is the base of a release, /_r/<release number> on the application host. A Node.js process, a Deno program or a SystemJS page given one base or the other runs the same code; see Strategies per environment.
During development, use both at once
Edited sources, the File API and HMR stay with the Dev Server. Released external dependencies come from the CDN. A development import map therefore mixes two origins: your own packages point at the Dev Server, and the released dependencies point at the CDN. Every entry still uses the same relative URL it will use after release.
Differences you must expect
These are deliberate. None of them changes a URL.
| Dev Server | CDN | |
|---|---|---|
| A module whose output is missing | Compiled on request | 404 OUTPUT_NOT_AVAILABLE; never compiled |
| Sources that do not compile | 422 BUILD_FAILED with diagnostics |
The preparation job fails; delivery is unaffected |
| Outputs | esm and system; env=development&min=false with an inline map or none, and env=production&min=true for a module that builds a production conditional |
Whatever was prepared |
/styles/, /assets/ |
Served, compiled on request | Served when prepared |
/maps/ |
OUTPUT_NOT_AVAILABLE: maps are inline |
Served when prepared and allowed by the source map policy |
/resolution.json, /importmap.json |
Computed from the workspace on every request | Stored with the release; none on the shared delivery origin |
| Sources | npm and other registries, as the installation recorded them; Git and archive installations are SOURCE_UNSUPPORTED |
npm, other registries, Git commits and archive digests |
Cache-Control |
no-store |
public or private with max-age, optionally immutable |
| Access | Local | Public, or private with access |
/session, File API, update events |
Yes | No. These are development endpoints and do not exist on the CDN. |
The consequence for options: omitting env and min asks for the published defaults, which a Dev Server rejects with OPTION_UNSUPPORTED. Send every option explicitly, and have your configuration choose the option set together with the origin.
Check it
The client of the quickstart sends one relative request to both origins and prints both answers. For a structured comparison, the conformance tools of @beyond-js/artifact-api send the identical relative request to two origins and report, aspect by aspect, whether status, media type, error code and validator form agree:
import { Parity } from '@beyond-js/artifact-api/conformance';
const parity = new Parity([development, published], module, options);
const { ok, differences, same, sides } = await parity.compare();Cache-Control is reported for each side and never compared, and bodies are compared only when you ask, because development and production artifacts legitimately differ.