Skip to content

feat(compile): map locator variables into a provider's own vocabulary #71

Description

@maehr

Motivation

A citation system pins one canonical vocabulary for each locator component. For bible-book-chapter-verse that is the OSIS book abbreviationsGen, John, 1Cor — settled in textrefs/registry#3.

Providers do not all agree with it. Two of the most important scholarly Bible editions address books by USFM code instead:

  • Nestle-Aland 28 — https://www.die-bibel.de/bibel/NA28/JHN.3/#JHN.3.16
  • Biblia Hebraica Stuttgartensia — https://www.die-bibel.de/bibel/BHS/PSA.23/#PSA.23.1

Neither can be expressed today. url: substitutes capture groups verbatim, so it produces .../NA28/John.3/, which die-bibel.de does not recognise. url_by: maps a variable to a whole URL, but the URL still needs {chapter} and {verse} after the book segment, so a per-book map cannot express it either.

The result is that the registry can enumerate the New Testament and the Tanakh but cannot point either at its standard critical edition — the gap the scholarly resolver review called out as the most serious.

Proposed change

A new optional per-resolver vars: field that translates one locator value into the provider's spelling, leaving the rest of the template intact:

- provider: Deutsche Bibelgesellschaft
  edition: 'Nestle-Aland, Novum Testamentum Graece, 28th edn (NA28)'
  language: grc
  access: open
  vars:
    bookUsfm:
      from: book
      map:
        Matt: MAT
        John: JHN
        1Cor: 1CO
  url: 'https://www.die-bibel.de/bibel/NA28/{bookUsfm}.{chapter}/#{bookUsfm}.{chapter}.{verse}'

Each entry reads the value of from out of the locator-derived variables, looks it up in map, and binds the result under a new name usable in url: or as the url_by: key.

Compile-time only: the mapping produces a different expanded URL and nothing new reaches the published record, so ResolverTargetEntry, standard/schema/, and public/contexts/v1.jsonld are untouched.

Two deliberate rules:

  • A value with no entry in the map is treated exactly like a missing template variable — the entry is skipped for that reference and the compiler warns. A hole in a book table then shows up as a non-zero skipped-entry count in the build output instead of quietly emitting a wrong URL. This matters more than it sounds: die-bibel.de answers an unknown book code with HTTP 200 and a default page (NA28 renders Matthäus 1, BHS renders Genesis 1), so a typo would otherwise produce 1,000 silently wrong links rather than an error.
  • A vars name that shadows a locator capture group is rejected, so {book} always means the canonical OSIS code no matter which resolver you are reading.

Alternatives considered

Model USFM as its own CitationSystem. Rejected. USFM codes are a provider's internal book vocabulary, not a citation grammar — nobody writes "JHN.3.16" in a footnote. Registering it as a system would mint a second UUID for every one of the ~31,000 NT and Tanakh verses and duplicate every references_range block, for no gain in citation practice. The same argument would then repeat for every provider with its own book list.

Extend url_by to substitute a fragment rather than a whole URL. Rejected as a silent semantic change to an existing field; url_by means "this provider's URLs are arbitrary" and should keep meaning that. The two now sit side by side: vars when a provider renames part of a URL, url_by when the whole URL is arbitrary.

Per-reference extra_resolvers. Works, but requires hand-writing ~31,000 URLs.

Acceptance criteria

  • vars parses on ResolverEntrySource and is rejected when malformed (strict object).
  • A mapped variable expands in url: and works as the url_by: key.
  • An unmapped source value skips the resolver entry and increments the compiler's warning count, rather than emitting a URL.
  • A vars name colliding with a locator capture group fails the build with a clear message.
  • scripts/compile.test.ts covers all of the above. (Resolver URL expansion has no test coverage at all today, so the pre-existing url and url_by behaviour gets pinned in the same pass.)
  • The authoring guide documents vars, when to prefer it over url_by, and the skip-and-warn rule.
  • No change to the published ResolverTargetEntry shape or the JSON-LD context.

Metadata

Metadata

Assignees

No one assigned

    Labels

    addressed-in-v0.1.0Addressed in the v0.1.0 release (PR #4)enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions