Resolution

The beyond-resolution/1 document and the import map every origin answers at /resolution.json and /importmap.json, mapping each public specifier to an origin-relative URL, with scopes for coexisting versions.

  • Availability: Experimental
  • Evidence: Reproduced run
  • Reference

What it is

A beyond-resolution/1 document says which URL delivers each public specifier, for one target and one format. Every value is origin-relative: a canonical path plus the canonical query. One document therefore works on a development origin and on a published one, and a consumer prepends the origin it was configured with.

JSON
{
  "protocol": "beyond-resolution/1",
  "release": "r-42",
  "target": "browser",
  "format": "esm",
  "imports": {
    "@example/app": "/m/@example/app@1.0.0/modules/~root?target=browser&format=esm&env=production&min=true&sourcemap=external&types=false&css=false"
  },
  "scopes": {
    "/m/@example/legacy@1.0.0/": {
      "library/main": "/m/library@1.2.0/modules/main?target=browser&format=esm&env=production&min=true&sourcemap=external&types=false&css=false"
    }
  }
}

Members

Member Required Rule
protocol Yes beyond-resolution/1
release No The release the document belongs to. Opaque to the contract. Development output has none.
target Yes browser or node
format Yes esm, cjs or system
imports Yes Origin-relative URL by public specifier
scopes Yes For each scope prefix, the imports that override imports for the importers under it. May be empty.

Rules

  • A specifier is one public module and is matched exactly. There are no prefix mappings. It resolves to a module path, or to a style path only when the specifier ends with .css (output selection). A specifier without .css never resolves to a stylesheet.
  • A value is accepted only when the shared codec reads it and writes it back unchanged: the canonical path (no /m/npm/ alias), the canonical query, and the target and format of the document. A scheme, a host, a fragment or a missing query is rejected.
  • A scope is an origin-relative path under /m/. A scope that ends with / matches every importer under it; any other scope matches one path. The longest scope that lists the specifier wins, and imports answers otherwise.
  • A document that breaks a rule is rejected with RESOLUTION_INVALID. This is an error of the client-side codec; it is not one of the HTTP error codes of the delivery routes.

Scopes are how two versions of one package coexist in one application: the modules of @example/legacy above keep library@1.2.0 while everyone else gets the version in imports.

The routes

Every origin of the contract answers the resolution it serves, in two forms:

Text
GET <base>/resolution.json[?target=<browser|node>&format=<esm|cjs|system>]    beyond-resolution/1, application/json
GET <base>/importmap.json[?target=<browser|node>&format=<esm|cjs|system>]     import map, application/importmap+json
  • target and format go together, or neither: without them the service answers its default document. Any other option, a repeated one, an invalid value or one of the two alone is 400 OPTION_INVALID.
  • importmap.json is the same resolution as a standard import map whose every address and scope key is relative to the document (./m/…). A consumer that loads the map from its URL resolves it on the origin that served it, path prefix included: Deno with --import-map=<base>/importmap.json?…, and SystemJS with <script type="systemjs-importmap" src="…">. A page that inlines a map resolves addresses against itself, so it needs absolute addresses: resolution.importmap(origin).
  • The answers carry a strong ETag, answer If-None-Match with 304, and can be read by a page of another origin, like every resource of the contract.
Development server CDN
Where The origin of the service The base of one release, /_r/<release number>, and on an application host the alias of the bound release. The shared delivery origin has none (404 NOT_FOUND).
What Computed from the workspace on every request, no-store. For browsers it also lists the installed packages and stylesheets that the builds reach; the Node document lists the modules of the workspace only. The documents stored when the release was prepared, one per target and format, under the release's cache policy. They never change.
Without a query The Node ES module document The document of the default target
A target or format it does not have 400 OPTION_UNSUPPORTED (format=cjs) 404 OUTPUT_NOT_AVAILABLE, never a build

Using it

This example is executed by the verification harness of this site against the shared codec:

JavaScriptresolve.mjs
// Read a beyond-resolution/1 document with the shared codec. Its values are
// origin-relative, so the same document serves any base origin.
import { Resolution } from '@beyond-js/artifact-api';

const query = 'target=browser&format=esm&env=production&min=true&sourcemap=external&types=false&css=false';

const resolution = new Resolution({
	protocol: 'beyond-resolution/1',
	release: 'r-42',
	target: 'browser',
	format: 'esm',
	imports: {
		'@example/app': `/m/@example/app@1.0.0/modules/~root?${query}`,
		'library/main': `/m/library@2.0.0/modules/main?${query}`,
		// Only a specifier that ends with ".css" resolves to a stylesheet
		'@example/app/theme.css': `/m/@example/app@1.0.0/styles/theme?${query}`
	},
	scopes: {
		// Modules of @example/legacy keep the version of "library" they were tested with
		'/m/@example/legacy@1.0.0/': { 'library/main': `/m/library@1.2.0/modules/main?${query}` }
	}
});

const importer = '/m/@example/legacy@1.0.0/modules/widget';

console.log(resolution.resolve('library/main'));
console.log(resolution.resolve('library/main', importer));
console.log(resolution.url('https://cdn.example', '@example/app'));
console.log(resolution.resolve('@example/app/theme.css'));

// Absolute addresses, for a map inlined in a page of another origin
console.log(JSON.stringify(resolution.importmap('http://localhost:8080'), null, 2));
// Addresses relative to the document, as a service answers /importmap.json
console.log(JSON.stringify(resolution.importmap(), null, 2));
Call Result
new Resolution(values), Resolution.parse(text) Validates the document; throws RESOLUTION_INVALID
resolve(specifier, importer?) The origin-relative URL, or undefined. The importer is an origin-relative URL or path.
url(origin, specifier, importer?) The absolute URL on one service
importmap(origin) { imports, scopes } with absolute URLs, for a map inlined in a page
importmap() { imports, scopes } relative to a document at the base (./m/…), which is what /importmap.json answers
serialize() The document with a stable key order, so equal resolutions serialize equally

ResolutionRequest.parse(pathname, query) reads a request of either route and says which target and format it selects, for a service that implements them.

A release never re-resolves

A release carries its resolution. The management API reports its location as urls.resolution of the release, as an origin-relative URL, and the backend closure of a target embeds the frozen resolution of that target. The resolution of a release never changes: promotion and rollback select a release, they do not re-resolve it.

Each environment consumes these documents in its own way; see Strategies per environment.