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:

JavaScript
const origin = process.env.MODULES_ORIGIN; // http://localhost:<port> or your CDN origin

const url = new URL('/m/@example/shared@1.0.0/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:

JavaScript
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:

JavaScript
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.