Skip to content

sparkles:versions — Scheme Catalogue and Provenance ​

Audience: contributors using or extending the shipped version schemes. This page is the per-scheme catalogue: it records each scheme's declared capabilities, real-world example strings, ordering rules and edge cases, native-range grammar, and the authoritative source every example was checked against. For the desired-state specification of the traits and generic algorithms, see SPEC.md.

1. Overview ​

This catalogue documents each shipped scheme: its purl type, real-world example strings, ordering rules and edge cases, native-range grammar, the capabilities it declares, and the authoritative source every example was checked against. For the concepts these schemes conform to (isVersion!T, isVersionScheme!S) and the capability traits, see SPEC.md.

d
import sparkles.versions.schemes.semver : SemVer;
import sparkles.versions.schemes.pypi   : PypiVersion;
import sparkles.versions.schemes.deb    : DebianVersion;

auto rust   = SemVer      .parse("1.78.0").value;
auto python = PypiVersion .parse("3.13.0a1").value;
auto apt    = DebianVersion.parse("2:4.13.1-0ubuntu0.16.04.1.1~").value;

Eleven schemes ship: semver is the SemVer 2.0.0 reference (it also drives most package managers at the value level); six are D-internal compact encodings; three (pypi, maven, deb) are structural — their ordering cannot be reduced to an integer key; and generic is the opaque baseline.

2. Capability matrix ​

This is the canonical capability matrix for the shipped schemes; adding a scheme adds a row here. Every scheme provides the required isVersion!T surface (opCmp + toString) and differs only in the optional capabilities below. Each yes/no is read against the traits in SPEC §3.2 (version capabilities) and SPEC §6.2 (scheme capabilities); the rationale behind each cell is in the per-scheme sections (§3).

SchemeorderKeyprereleasecomponentsbuildnativeRangeloose
semveryesyesyesyesyesyes
dmdyesyesyesnoyesyes
dmd_compactyesyesyesnoyesno
tinyyesnoyesnoyesyes
calver_yymmyesnoyesnoyesyes
calver_yyyymmddyesnoyesnoyesyes
vimyesnoyesnoyesyes
pypinoyesyesnoyesyes
mavennoyesnonoyesno
debnonononoyesno
genericnononononono

Two cross-cutting rules explain most of the table:

  • The structural schemes (pypi, maven, deb) omit orderKey — their ordering does not pack into an unsigned integer of any width (see §3.8–§3.10), so their opCmp does the full structural walk.
  • components is arity-free; only schemes whose list begins ["major","minor","patch"] also satisfy hasSemVerComponents and get caret ^ / tilde ~. The calendar schemes declare ["year","month","day"] — components yes, caret/tilde no.

3. Per-scheme catalogue ​

Each subsection records: the purl type string, real-world example strings, ordering rules and edge cases, the declared capabilities (and, for structural schemes, why orderKey is omitted), parse modes, the native-range grammar where applicable, and provenance.

3.1 semver — SemVer (Semantic Versioning 2.0.0) ​

  • purl type: semver. The same value grammar drives npm, cargo, gem, composer, golang, hex, conan, nginx, mozilla, and github — those purl types map to this scheme via the purl→scheme table while keeping their own native-range dialects.
  • Examples: 20.13.1 (Node.js), 1.78.0 (Rust), 1.30.0 (Kubernetes), 17.3.0 (Angular), 18.3.1 (React), 6.8.9 (Linux), 2.45.1 (Git), 8.3.7 (PHP), 3.3.1 (Ruby), 1.26.0 (Nginx), 2.4.59 (Apache httpd), 7.2.4 (Redis), 7.0.8 (MongoDB), 3.45.3 (SQLite), 8.7.1 (cURL), 7.0.1 (FFmpeg), 14.5.1 (macOS), 26.1.1 (Docker). With prerelease/build: 1.0.0-rc.1, 1.0.0-alpha.1+build.5.
  • Ordering (SemVer §11): compare major, then minor, then patch numerically; a version with a prerelease has lower precedence than the same major.minor.patch without one (1.0.0-alpha < 1.0.0); prerelease identifiers compare per SemVer §11.4 (numeric identifiers numerically, alphanumeric lexically, numeric < alphanumeric, more fields wins on a shared prefix); build metadata (+…) is ignored in ordering (SemVer §10).
  • Edge cases: major dominates everything — 2.0.0-alpha > 1.999.999. Leading zeroes in numeric identifiers are rejected in strict mode.
  • Capabilities: orderKey yes — the major:minor:patch:has-no-prerelease shape packs into a ulong with the stable-flag at the LSB, so unsigned compare reproduces SemVer precedence up to the prerelease-identifier tiebreak (equal keys fall through to the full opCmp). prerelease yes, components yes, build yes. nativeRange yes (node-semver grammar). loose yes.
  • Parse modes: strict follows SemVer 2.0.0 exactly. loose additionally accepts a leading v (v1.2.3) and partial versions (1, 1.2), infilling missing components with 0 — this is how PostgreSQL 16.3 round-trips.
  • Native range grammar (node-semver subset): caret ^1.2.0, tilde ~1.2.0, wildcard 1.2.x / 1.*, hyphen 1.2.0 - 1.5.0, union ||, AND-by-space >=1.2.0 <2.0.0, plain comparators > >= < <= =. Prerelease-in-range follows the node-semver rule (a prerelease satisfies a comparator only when some comparator in the same set names a prerelease of the same major.minor.patch).
  • Provenance: semver.org 2.0.0; node-semver grammar at github.com/npm/node-semver. Example strings checked against upstream releases (nodejs/node, rust-lang, kubernetes, git, php, etc.).

3.2 dmd — Dmd (D compiler internal) ​

  • purl type: none (D-internal; not published to a package registry).
  • Examples: 2.111.0, 2.079.0. The minor field is printed zero-padded to 3 digits (079), while values ≥ 100 print at their natural width (111).
  • Ordering: standard 3-component numeric compare with SemVer-style prerelease tiebreak; the zero-padding is a formatting property only and does not affect ordering (2.079.0 and a hypothetical 2.79.0 would compare equal numerically — but the parser only admits the 3-digit form).
  • Capabilities: orderKey yes, prerelease yes, components yes. build no (DMD releases carry no build metadata). nativeRange yes — SemVer-shaped, so it inherits the SemVer/npm range grammar. loose yes.
  • Parse modes: strict requires the 3-digit minor; loose relaxes the leading-v and partial-version rules as for semver.
  • Provenance: dlang.org/changelog (2.079.0 and 2.111.0 release notes).

3.3 dmd_compact — DmdCompact (4-byte bitfield encoding) ​

  • purl type: none (D-internal compact storage).
  • Examples: 2.111.0, 2.111.0-beta.2, 2.111.0-rc.3.
  • Encoding: a 4-byte packed integer. The prerelease is encoded as a 2-bit phase (beta < rc < stable) plus a small number, exploiting the fact that DMD prereleases follow the constrained grammar beta.N / rc.N. Because the (phase, num) pair sits just below the stable marker in the packed integer, a single unsigned compare yields 2.111.0-beta.N < 2.111.0-rc.M < 2.111.0.
  • Ordering: entirely via the packed integer; the phase encoding is monotone by construction.
  • Capabilities: orderKey yes — the 4-byte packed integer is the order key, so OrderKeyType!DmdCompact is uint. prerelease yes, components yes. build no. nativeRange yes (SemVer-shaped). loose no — the compact encoding admits only the exact canonical forms it can represent, so there is no lenient parse.
  • Edge cases: the reserved 4th phase code is rejected by the parser; prerelease numbers beyond the encodable range are a parse error rather than a silent truncation.
  • Provenance: DMD prerelease tags on github.com/dlang/dmd/releases (v2.111.0-beta.1, v2.111.0-rc.1, …).

3.4 tiny — Tiny (4-byte, no prerelease) ​

  • purl type: none (internal storage-sensitive use).
  • Examples: 7.8.9.
  • Ordering: three numeric components packed into 4 bytes; plain unsigned compare.
  • Capabilities: orderKey yes — the three components pack into 4 bytes, so OrderKeyType!Tiny is uint. components yes. prereleaseno, build no. nativeRange yes (SemVer-shaped grammar over the three components). loose yes (partial versions infill with 0).
  • Provenance: internal; no external ecosystem. The example exercises the no-prerelease, no-build packed path.

3.5 calver_yymm — CalVerYYMM (Ubuntu-style CalVer) ​

  • purl type: none (calendar scheme; Ubuntu does not publish a purl type for the distro version itself).
  • Examples: 24.04.1 (Ubuntu 24.04.1 LTS). The month is printed zero-padded to 2 digits (04).
  • Ordering: numeric compare on (year, month, patch) — chronological because the fields are most-significant-first.
  • Capabilities: orderKey yes. components yes — declared honestly as ["year","month","patch"], so hasComponents holds (it gets generic compare and truncateTo!"month") but hasSemVerComponents does not (a calendar version has no caret/tilde). prerelease no, build no. nativeRange yes. loose yes.
  • Edge cases: the 2-digit month is a formatting width, not a range constraint — 24.4.1 is not accepted in strict mode because the canonical form pads the month.
  • Provenance: ubuntu.com point-release announcements (24.04.1 LTS, August 2024).

3.6 calver_yyyymmdd — CalVerYYYYMMDD (Arch-style CalVer) ​

  • purl type: none (calendar scheme).
  • Examples: 2024.05.01 (Arch Linux monthly ISO). Both month and day print zero-padded to 2 digits.
  • Ordering: numeric compare on (year, month, day).
  • Capabilities: orderKey yes. components yes — declared as ["year","month","day"]; hasComponents holds, hasSemVerComponents does not (no caret/tilde for a date version). prerelease no, build no. nativeRange yes. loose yes.
  • Provenance:archlinux.org/releng/releases (monthly ISO snapshots).

3.7 vim — VimVer (4-digit zero-padded patch) ​

  • purl type: none (Vim's patch scheme is project-specific).
  • Examples: 9.1.0400 (Vim patch 9.1.0400). The patch field prints zero-padded to 4 digits.
  • Ordering: numeric compare on (major, minor, patch).
  • Capabilities: orderKey yes, components yes. prereleaseno, build no. nativeRange yes. loose yes.
  • Edge cases: the 4-digit patch is a formatting width; Vim's running patch counter has millennia of headroom in the packed field.
  • Provenance: github.com/vim/vim release tags (patch 9.1.0400).

3.8 pypi — PypiVersion (PEP 440) — structural, no orderKey ​

  • purl type: pypi.

  • Examples: 3.13.0a1 (alpha), 1.0.0.post1 (post-release), 2.0.0.dev1 (dev-release), 1.0.0+local (local version label), 1!2.0.0 (explicit epoch 1).

  • Ordering (PEP 440): compare lexicographically by structured segments in this order: epoch, the release tuple (numeric, zero-padded to equal length), then the pre/post/dev ranking, then the local version. Within a release, the segment classes sort:

    1.dev0 < 1.0.dev456 < 1.0a1 < 1.0a2.dev456 < 1.0a12 < 1.0b1
          < 1.0b2 < 1.0rc1 < 1.0 < 1.0.post456 < 1.0.15 < 1.1.dev1

    Key facts: a dev release sorts before a pre-release of the same version; a post release sorts after the final release; a local version (+local) sorts after the same public version, and a numeric local segment outranks a lexicographic one. An explicit epoch dominates everything (1!1.0 > 1.0).

  • Why orderKey is omitted: the comparison is genuinely structural. The release tuple is unbounded in length, the pre/post/dev ranking is a small enum crossed with an unbounded number, and the local version is an arbitrary dot-separated mix of numeric and lexicographic segments. There is no fixed-width integer whose unsigned compare reproduces this order, so pypi declares no hasOrderKey primitive and its opCmp walks the structure. Generic sort and Ranges!PypiVersion therefore use the comparison-based fallback path — exactly the contract the DbI design guarantees.

  • Capabilities: prerelease yes (isPrerelease true for aN/bN/rcN/.devN), components yes (the release tuple exposes major/minor/patch). build no — the local version participates in ordering, so it is not SemVer-style build metadata. orderKey no. nativeRange yes. loose yes (PEP 440 normalisation: case-folding, v-prefix stripping, separator canonicalisation 1.0-a1 → 1.0a1).

  • Native range grammar (PEP 440 specifier set): comma-separated AND of clauses using ==, !=, <, <=, >, >=, ~= (compatible release), and === (arbitrary equality). ~=1.4.5 means >=1.4.5, ==1.4.*. Trailing .* wildcards are supported on ==/!=.

  • Provenance:PEP 440 / packaging.python.org version specifiers (canonical ordering example verified verbatim). Example strings checked against python.org/downloads (CPython 3.13.0a1).

3.9 maven — MavenVersion (ComparableVersion) — structural, no orderKey ​

  • purl type: maven.

  • Examples: 1.0, 1.0-SNAPSHOT, 1.0-alpha-1, 1.0-rc1, 1.0-1.

  • Ordering (Maven ComparableVersion): the string is tokenised at ., -, _, and digit↔letter transitions; trailing "null" tokens (0, empty, final, ga, release) are trimmed so that 1.0.0 == 1. Numeric tokens compare numerically; qualifier tokens compare by the fixed rank:

    alpha < beta < milestone < rc (= cr) < snapshot < ""(= ga = final = release) < sp

    Hence 1.0-alpha-1 < 1.0-beta-1 < 1.0-milestone-1 < 1.0-rc1 < 1.0-SNAPSHOT < 1.0 < 1.0-sp1. Comparison is case-insensitive (1.0-RC1 == 1.0-rc1), and alpha/beta/milestone may be abbreviated a/b/m when directly followed by a number.

  • Why orderKey is omitted: like PyPI, the order is structural — an unbounded alternation of numeric and qualifier tokens with the "ga beats snapshot beats pre-release" rule and trailing-null trimming. No fixed-width integer key reproduces it, so maven declares no hasOrderKey and opCmp compares token lists.

  • Capabilities: prerelease yes (the qualifier rank below the empty/ga release). components no — MavenVersion declares no components list because its token model has no fixed ordered set of numeric fields (the token count and kinds vary per value), and the trait is all-or-nothing so a sometimes-available prefix does not qualify. build no, orderKey no. nativeRange yes (interval notation, below). loose no — ComparableVersion already accepts essentially anything, so there is no separate lenient mode.

  • Native range grammar (Maven version requirements): bracket interval notation — [1.0] (exactly), (,1.0] (≤), [1.2,1.3] (closed), [1.0,2.0) (half-open), [1.5,) (≥), and unions via comma-separated intervals (,1.0],[1.2,). Caveat preserved from the spec: because 2.0-rc1 < 2.0, [1.0,2.0) includes 2.0-rc1.

  • Provenance:maven.apache.org POM version order specification and the maven-artifact ComparableVersion test corpus.

3.10 deb — DebianVersion (dpkg) — structural, no orderKey ​

  • purl type: deb.
  • Examples: 1.2.3-4 (upstream 1.2.3, Debian revision 4), 2:4.13.1-0ubuntu0.16.04.1.1~ (epoch 2, upstream 4.13.1, an Ubuntu security revision ending in ~).
  • Structure: [epoch:]upstream_version[-debian_revision]. The epoch is an optional unsigned integer (default 0) compared first; then the upstream version; then the Debian revision.
  • Ordering (dpkg algorithm): epoch numerically first; then upstream and revision are each compared left-to-right by alternating non-digit and digit runs. In a non-digit run, all letters sort before all non-letters, and the tilde ~ sorts before everything — even the end of the string, so 1.0~beta1 < 1.0 and ~~ < ~~a < ~ < "" < a. In a digit run, leading zeroes are ignored and an empty run counts as 0.
  • Why orderKey is omitted: the dpkg algorithm is the canonical example of a non-packable order — the ~-before-empty rule alone defeats any fixed-width integer key (a shorter string can sort after a longer one that ends in ~). deb declares no hasOrderKey; its opCmp implements the dpkg two-phase walk directly.
  • Capabilities: nativeRange yes (dpkg relations). prereleaseno (the ~ mechanism subsumes it but is not exposed as an isPrerelease boolean), components no (the upstream version is free-form, not a guaranteed triple), build no, orderKeyno, loose no (dpkg parsing is already permissive; there is no stricter mode to relax from).
  • Native range grammar (dpkg relations): the comparison relations >=, <=, << (strictly less), >> (strictly greater), =, each followed by a version, as used in dpkg --compare-versions and Depends: fields (e.g. >= 2.0, << 3.0).
  • Provenance:Debian Policy §5.6.12 (version) and the dpkg --compare-versions algorithm (~-ordering example verified verbatim).

3.11 generic — Generic (void-hook baseline) ​

  • purl type: generic.
  • Examples: any opaque string — "build-2024-05-30", "r1234", "snapshot-xyz".
  • Ordering: plain lexicographic (code-point) comparison of the raw string. No structure is parsed.
  • Capabilities: none. orderKey no, prerelease no, components no, build no, nativeRange no, loose no. This is the mandated void-hook baseline: it provides only the required isVersion!T surface (opCmp + toString) and therefore exercises every generic algorithm's fallback path — comparison-based sort, comparison-based Ranges!Generic, and the "no native range, exact versions only" branch of the VERS/purl layers.
  • Parse: parse always succeeds (any string is a valid Generic); there is no parseLoose or parseNativeRange.
  • Provenance: none required — it is a structural baseline, not an ecosystem mapping.

4. Adding a new scheme ​

A scheme is just a struct conforming to isVersionScheme!S. For the step-by-step recipe — required surface, capability declaration, conformance assertions, registration, and tests — see Add a new scheme.

5. Deferred schemes ​

These are catalogued for the test corpus but not shipped. The corrected real-world example strings are recorded here so future implementers do not copy the wrong forms (several appeared mangled in the original analyst catalogue).

5.1 Bucket F — hyphenless pseudo-SemVer ​

Versions whose alphanumeric prerelease segment lacks the SemVer - separator. Supporting them needs a per-scheme custom tokeniser at the numeric→alphanumeric boundary, which is its own design problem.

EcosystemWrong form (do not use)Correct real-world formNote
Go1.22.3rc1go1.22rc1 or go1.22.3go prefix; prerelease glued without a hyphen
Python (alt)3.12.3a13.13.0a1covered by pypi; listed here as the glued form
OpenSSL legacy—1.1.1wsingle-letter patch suffix
OpenSSH—9.7p1pN portable suffix, no hyphen
Unity—2023.2.1f1fN "final" glue suffix

5.2 Part-2 heavyweight schemes ​

Schemes needing wider cores, 4+ components, or pure-alphanumeric fallback. Tracked in the design notes — open questions.

Note — 4-component arity is no longer a design blocker. With the list-based components capability, the 4-part schemes below (.NET, Windows, Chrome) are now directly expressible — each declares ["major","minor","build","revision"] and packs into a ulongorderKey. They remain unshipped only on implementation bandwidth (parser + native-range grammar per ecosystem), not on a missing abstraction. Java's 21.0.1+12 and Android's pure-alphanumeric UP1A.231005.007 still need, respectively, build-metadata-aware parsing and a no-numeric-core fallback.

EcosystemCorrect real-world formNote
Java (modern)21.0.1+123-part + build; not the 4-part 21.0.1.2
.NET assemblies8.0.0.04-part Major.Minor.Build.Revision
Windows10.0.19045.33244-part heavyweight
Google Chrome125.0.6422.604-part heavyweight
Android buildsUP1A.231005.007pure-alphanumeric; bypasses any numeric core

6. Provenance appendix ​

Every example string baked into this catalogue was checked against an authoritative source before inclusion. The structural-ordering rules for the three non-packable schemes were verified verbatim against their canonical specifications.

SchemeWhat was verifiedSource
semver2.0.0 grammar + §11 ordering; npm range grammarsemver.org/spec/v2.0.0; node-semver
dmd / dmd_compact2.079.0, 2.111.0, beta/rc prerelease tagsdlang.org/changelog; dlang/dmd releases
tinypacked no-prerelease pathinternal
calver_yymmUbuntu 24.04.1 LTS point releaseubuntu.com
calver_yyyymmddArch 2024.05.01 monthly ISOarchlinux.org/releng/releases
vimpatch 9.1.0400github.com/vim/vim
pypiPEP 440 ordering example (verbatim); 3.13.0a1packaging.python.org version specifiers; python.org
mavenqualifier order + interval notation (verbatim)maven.apache.org POM version order
debdpkg ~-ordering rule (verbatim); epoch/revisionDebian Policy §5.6.12
genericstructural baseline; no ecosystemn/a

→ SPEC.md — desired-state specification (traits, Ranges!V, VERS, purl) → PLAN.md — delivery milestones