Unreleased changes
These pages document main, which has changes 2.1.1 does not. npm install @evanion/urn gives you 2.1.1, documented here. Values on these pages are produced by running the current source on every build.
r-, q- and f-components
@evanion/urn’s parse splits the r-, q- and f-components off a URN into fields of their own, and stringify, given the parts as an object, writes them back.
The Problem: Identifying Specific Versions or Sections
A simple URN identifies a resource, but it doesn’t identify a specific version or subset of that resource. For example, if you need to link to a specific edition of a board game, or a specific section of its rules, you can’t do that with a bare identifier like urn:game:brass-birmingham.
Adding this information as part of the main ID (the NSS) would break the identity of the resource—you’d be treating the “2018 Edition” as a different game entirely rather than a version of the same game.
Solution: URN Components
RFC 8141 §2.3 defines three components that can be appended to a URN. These allow you to carry metadata and specific pointers without changing the identity of the resource. Each component starts with an introducer:
| Component | Introducer | RFC 8141 | Purpose | Example Use Case |
|---|---|---|---|---|
| r | ?+ | §2.3.1 | Parameters for the resolution service | ?+lang=en (Request English listing) |
| q | ?= | §2.3.2 | Parameters for the named resource | ?=edition=2018 (Specific edition) |
| f | # | §2.3.3 | A fragment within the resource | #setup (The “Setup” section of the rules) |
Extracting Components with parse
parse in @evanion/urn returns each component in its own field and never folds one into the nss. GameURN can read an edition and a section of the rules off a single URN:
.('urn:game:brass-birmingham?=edition=2018#setup'); // -> { urn: 'urn', nid: 'game', nss: 'brass-birmingham', fComponent: 'setup', qComponent: 'edition=2018' }If a URN carries no component, the corresponding keys are simply absent from the returned object. The URNComponents type describes these three optional fields:
function ({ , }: URNComponents): string {
return [, ].().(' / ');
}
(.('urn:game:azul?=edition=2017#scoring')); // -> 'edition=2017 / scoring'
'qComponent' in .('urn:game:azul'); // -> falseGameURN.parse('urn:game:brass-birmingham?=edition=2018#setup')Put ?+lang=en before ?= for an r-component, or a bare ? to see it refused.
Try typing a URN above to see how GameURN.parse extracts the components.
Writing Components with stringify
To write components, you must pass the parts to stringify as an object: { nss, rComponent?, qComponent?, fComponent? }.
The library automatically ensures the introducers appear in the correct order (r, then q, then f), regardless of the order of keys in your object. An empty fComponent writes a bare #.
const = {
: 'brass-birmingham',
: 'setup',
: 'edition=2018',
};
.(); // -> 'urn:game:brass-birmingham?=edition=2018#setup'
const = {
: 'brass-birmingham',
: 'lang=en',
: '',
};
.(); // -> 'urn:game:brass-birmingham?+lang=en#'
const = 'urn:game:brass-birmingham?=edition=2018#setup';
.(.()) === ; // -> trueHow Components are Identified
Because the identifier itself (the NSS) cannot contain ? or #, @evanion/urn uses these characters as delimiters. It parses components in the following order:
- f-component: Everything after the first
#. This is the final part of the URN. - r- and q-components: In the remaining string, the first
?after the NSS starts the component. If+follows, it’s an r-component; if=follows, it’s a q-component. Any other character following?throws aValidationError. - r-component bounds: An r-component continues until it hits
?=, which marks the start of the q-component.
Components and URN Equivalence
Crucially, components are not part of the resource’s identity. RFC 8141 §3.1 excludes all three components from URN equivalence.
@evanion/urn’s equals and extractId both ignore components. This means a URN with metadata is considered equivalent to the bare URN it describes.
const = 'urn:game:brass-birmingham?=edition=2018';
const = 'urn:game:brass-birmingham#setup';
.(, ); // -> true
.('urn:game:brass-birmingham?=edition=2018#setup'); // -> 'brass-birmingham'sameNamespace and belongsToNamespace also ignore components, as they only care about the scheme and NID.
scheme Always the same three letters, and they say what the rest of the string is: a name for something, not an address where it lives. A URL tells you where to go; this tells you what you mean.:namespace What kind of thing this names. Two systems can both number their records from 1, and this is what keeps those two 1s apart.:name The identifier itself, in whatever form the system it came from uses. Unique inside the namespace above, and nowhere else.
Next steps: Encoding and Comparison
Now that you can handle metadata, you need to know how to deal with “messy” identifiers—like those containing spaces or colons. Encoding and comparing URNs shows you how to use encodeNss to safely integrate titles into your URNs.