From 7c85f60f21524abbde1afcc3705efeaf4fcc0c3a Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 8 Nov 2026 18:04:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?site:=20port=20the=20archived=20pubid-*/REA?= =?UTF-8?q?DME=20content=20=E2=80=94=20ISO/IEC/PLATEAU=20page=20ports,=206?= =?UTF-8?q?=20missing=20flavor=20pages,=20construction=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The website was missing six flavors the pubid 2 registry carries (doi, evs, gb, isbn, omg, un) and the usage narrative the archived per-gem READMEs held: the ISO format options, the IEC TRF grammars, and the PLATEAU Handbook #11 edition split. - New publisher pages: doi, evs, gb, isbn, omg, un — built from the monogem's identifier classes and pass fixtures, every example verified by parsing it with the gem (bare ISBN/DOI forms noted as flavor-entry-point parses; ISBN hyphenation preserved as given). `Publisher.logo` becomes optional so a page renders the two-letter badge until a logo asset exists. - ISO page: a Rendering Styles section documents the live rendering options (lang_single, stage_format_long, with_date, with_edition — the v1 `format:`-symbol API is not a v2 API); the amendment/corrigendum docTypes now carry the DAM/FDAM and DCor/FDCor stage rules with verified normalization examples; the Typed Stage component lists the supplement stages. - IEC page: the iecee_trf docType spells out the full TRF pattern (& join, F_II-style sub-versions, _EMC/_PS/_SOF test types) and a new iecex_trf docType documents the `_`/`v` separator and std-edition with verified examples. - PLATEAU page: the Handbook #11 edition-split rule (#06/#04 for editions 1-3) documented with verified rewrite examples. - Library: a Constructing Identifiers page — the v2 replacement for the v1 `Identifier.create` (typed `.new`, from_hash, supplement-with-base, copublishers, stages) — linked from the library index. - The publisher-count spec updates 38 -> 44 for the six new pages; 3GPP already had a page (flavor key "tgpp"). Sources: pubid/pubid-iso, pubid-iec, pubid-plateau README.adoc (archived); the pubid gem's lib/ and spec/fixtures/ as the correctness authority. --- .../library/constructing-identifiers.md | 151 ++++++++++++++++++ src/content/library/index.mdx | 1 + src/data/__tests__/registry.spec.ts | 6 +- src/data/flavors/doi.ts | 58 +++++++ src/data/flavors/evs.ts | 87 ++++++++++ src/data/flavors/gb.ts | 94 +++++++++++ src/data/flavors/iec.ts | 17 +- src/data/flavors/isbn.ts | 71 ++++++++ src/data/flavors/iso.ts | 23 ++- src/data/flavors/omg.ts | 64 ++++++++ src/data/flavors/plateau.ts | 7 +- src/data/flavors/un.ts | 55 +++++++ src/data/types.ts | 4 +- 13 files changed, 625 insertions(+), 13 deletions(-) create mode 100644 src/content/library/constructing-identifiers.md create mode 100644 src/data/flavors/doi.ts create mode 100644 src/data/flavors/evs.ts create mode 100644 src/data/flavors/gb.ts create mode 100644 src/data/flavors/isbn.ts create mode 100644 src/data/flavors/omg.ts create mode 100644 src/data/flavors/un.ts diff --git a/src/content/library/constructing-identifiers.md b/src/content/library/constructing-identifiers.md new file mode 100644 index 0000000..f529b68 --- /dev/null +++ b/src/content/library/constructing-identifiers.md @@ -0,0 +1,151 @@ +--- +title: "Constructing Identifiers" +description: "Build identifiers programmatically with the pubid gem." +--- + +# Constructing Identifiers + +There are three ways to obtain an identifier: parsing a printed reference, +rebuilding from a serialized hash, and constructing from typed attributes. +The v1 entry point — `Pubid::Iso::Identifier.create(...)` — no longer exists; +the v2 constructor is `.new` with keyword arguments. + +## Parsing + +`Pubid.parse` routes on the leading prefix token (longest match first), so the +right grammar is chosen even when the caller does not know the flavor: + +```ruby +require 'pubid' + +Pubid.parse("ISO 8601-1:2019").class # => Pubid::Iso::Identifiers::InternationalStandard +Pubid.parse("JIS Z 8301:2019").class # => Pubid::Jis::Identifier +``` + +When the flavor is known, name it directly: `Pubid::Iso.parse("ISO 9001:2015")`. + +## The constructor + +`Pubid::::Identifier.new` takes one keyword argument per declared +attribute, coerces scalars into the components the attributes declare, and +**raises `ArgumentError` naming any key the class does not declare** — a +silently-dropped key would otherwise produce an identifier with no year and +no diagnostic. + +```ruby +Pubid::Iso::Identifier.new(number: "8601", part: "1", year: "2019") +# => ISO 8601-1:2019 +``` + +For a non-default document type, construct the concrete class — the class is +the type: + +```ruby +Pubid::Iso::Identifiers::TechnicalReport.new(number: 123).to_s +# => "ISO/TR 123" +``` + +Scalars coerce into degenerate components — `year:`, `month:`, `day:` fold +into the date, `edition: 2` wraps into the edition component, and IEC accepts +`languages: ["en"]` and `copublishers: ["ISO"]`: + +```ruby +Pubid::Iec::Identifiers::InternationalStandard.new( + number: "1000", part: "1", year: 2023, edition: 2, languages: ["en"], +).to_s # => "IEC 1000-1:2023 ED2(en)" +``` + +The coercion is type-aware: a flavor that declares an attribute as a plain +string keeps the string, and `number` is never wrapped. + +## Stages (ISO) + +ISO stages are typed stages — a stage and document type combined (`WD`, +`DIS`, `FDIS`, `WD Amd`, `DAM`, `FDCor`). Look the registry entry up and set +it: + +```ruby +Pubid::Iso::Identifiers::InternationalStandard.new( + number: 123, typed_stage: Pubid::Iso.locate_stage("WD"), +).to_s # => "ISO/WD 123" + +ts = Pubid::Iso.locate_stage("FDIS") +ts.abbreviation # => "FDIS" +ts.name # => "Final Draft International Standard" +``` + +ISO type and stage are derived from `typed_stage` — there is no separate +`type:` or `stage:` keyword to set. The stage iteration (`.2` in +`ISO/DIS 123-1.2`) is its own keyword, `stage_iteration:`. + +## Supplements with a base + +A supplement wraps the document it modifies under the uniform `base` +attribute; the supplement is its own class with the ordinal in `number`: + +```ruby +base = Pubid::Iso::Identifiers::InternationalStandard.new( + number: "123", part: "1", year: 2019, +) + +Pubid::Iso::Identifiers::Amendment.new(number: 1, year: 2021, base: base).to_s +# => "ISO 123-1:2019/Amd 1:2021" + +Pubid::Iso::Identifiers::Corrigendum.new(number: "1", year: "2021", base: base).to_s +# => "ISO 123-1:2019/Cor 1:2021" +``` + +IEC renders the compact supplement face: + +```ruby +base = Pubid::Iec::Identifiers::InternationalStandard.new( + number: "60050", part: "351", year: 2013, +) +Pubid::Iec::Identifiers::Amendment.new(number: "1", year: 2016, base: base).to_s +# => "IEC 60050-351:2013/AMD1:2016" +``` + +IEEE carries its identity in a `code:` keyword: + +```ruby +Pubid::Ieee::Identifier.new(code: "802.11", year: 2020).to_s +# => "IEEE Std 802.11-2020" +``` + +## Copublishers + +Joint publications are modeled per flavor. IEC coerces copublisher scalars; +ISO takes its own `Publisher` component: + +```ruby +Pubid::Iec::Identifiers::InternationalStandard.new( + number: "1000", copublishers: ["ISO"], +).to_s # => "IEC/ISO 1000" + +Pubid::Iso::Identifier.new( + publisher: Pubid::Iso::Components::Publisher.new(copublisher: ["IEC"]), + number: 123, +).to_s # => "ISO/IEC 123" +``` + +## from_hash + +`#to_hash` emits the canonical hash, and `Pubid.from_hash` rebuilds the +concrete class from it — reading the `_type` key +(`pubid::`), so the caller never names the flavor: + +```ruby +id = Pubid::Iso.parse("ISO 9001:2015/Amd 1:2020/Cor 1:2021") +Pubid.from_hash(id.to_hash).to_s # => "ISO 9001:2015/Amd 1:2020/Cor 1:2021" +``` + +The round-trip invariant `from_hash(id.to_hash) == id` holds for every flavor +— equality is what index lookups are made of, so a construction path that +holds a value the other path does not would break matching. + +## Errors + +`Pubid.parse` raises `Pubid::Errors::ParseError` (a `Parslet::ParseFailed`) +for an unparseable string; unknown constructor keys raise `ArgumentError`; +a hash with no or unknown `_type` raises `Pubid::Errors::InvalidInputError`. +Rescue `Pubid::Errors::Error` to catch all of them. diff --git a/src/content/library/index.mdx b/src/content/library/index.mdx index 0a13685..0300cc6 100644 --- a/src/content/library/index.mdx +++ b/src/content/library/index.mdx @@ -97,6 +97,7 @@ The library uses a layered architecture: ## Next Steps +- [Constructing Identifiers](/library/constructing-identifiers) - [Quick Start Guide](/library/quick-start) - [API Reference](/library/api) - [Contributing](/library/contributing) diff --git a/src/data/__tests__/registry.spec.ts b/src/data/__tests__/registry.spec.ts index 163d375..e71c22a 100644 --- a/src/data/__tests__/registry.spec.ts +++ b/src/data/__tests__/registry.spec.ts @@ -8,8 +8,10 @@ import type { Publisher, Category } from '~/data/types' describe('registry', () => { it('contains exactly the expected publisher count', () => { - // 38 = 37 (post-Astro-cutover baseline on main) + CalConnect (added 2026-07-16) - expect(publishers).toHaveLength(38) + // 44 = 38 (37 post-Astro-cutover baseline + CalConnect, 2026-07-16) + // + 6 flavors ported from the pubid 2 registry (doi, evs, gb, isbn, + // omg, un — 2026-11). 3GPP already had a page (flavor key "tgpp"). + expect(publishers).toHaveLength(44) }) it('publishers are sorted alphabetically by flavor', () => { diff --git a/src/data/flavors/doi.ts b/src/data/flavors/doi.ts new file mode 100644 index 0000000..1411ab9 --- /dev/null +++ b/src/data/flavors/doi.ts @@ -0,0 +1,58 @@ +import type { Publisher } from '../types' + +export const doi: Publisher = { + flavor: "doi", + name: "DOI", + fullName: "Digital Object Identifier", + category: "international", + description: "The DOI system provides an actionable, persistent identifier for digital objects. PubID parses DOI names — including NIST's DOI-based standard designations such as \"doi:10.6028/NIST.SP.800-53r5\" — as identifiers alongside the SDO schemes.", + website: "https://www.doi.org", + syntaxNotes: "DOI syntax is \"doi:10.{prefix}/{suffix}\" (case-insensitive scheme, normalized to lowercase). PubID accepts the canonical \"doi:\" form, the bare \"10.x/suffix\" name, the \"DOI:\" upper-scheme spelling, and the resolver URL \"https://doi.org/10.x/suffix\" — all render canonical.", + urnPattern: "urn:pubid:doi:resource:prefix-{10.NNNN}:suffix-{suffix}", + relatedFlavors: ["nist", "isbn"], + docTypes: [ + { + key: "resource", + title: "DOI Resource", + abbr: ["doi"], + description: "A DOI name for any resource. The prefix (10.NNNN) names the registrant; the suffix is registrant-defined — an opaque string, a journal-style path (10.1006/jmbi.1998.2354), or an SDO designation (NIST.SP.800-53r5).", + examples: [ + { input: "doi:10.1000/182" }, + { input: "10.1006/jmbi.1998.2354", output: "doi:10.1006/jmbi.1998.2354" }, + { input: "DOI:10.6028/NIST.2022-04-15.001", output: "doi:10.6028/NIST.2022-04-15.001" }, + { input: "https://doi.org/10.1000/182", output: "doi:10.1000/182" }, + ], + }, +], + components: [ + { + name: "Scheme", + description: "The \"doi:\" scheme token", + dataType: "enum", + values: ["doi:"], + format: "Case-insensitive on parse (DOI: accepted); rendered lowercase", + example: "doi:", + }, + { + name: "Prefix", + description: "The registrant prefix, always beginning 10.", + attribute: "prefix", + dataType: "string", + format: "10.{NNNN} — 4+ digits naming the registrant", + example: "10.6028", + }, + { + name: "Suffix", + description: "The registrant's opaque resource suffix", + attribute: "suffix", + dataType: "string", + format: "Any string; SDO designations use dotted series (NIST.SP.800-53r5)", + example: "NIST.2022-04-15.001", + }, +], + algebra: [ + { type: "URL form", description: "The resolver URL parses and renders as the canonical DOI name", syntax: "https://doi.org/10.{prefix}/{suffix}", example: "https://doi.org/10.1000/182" }, +], +} + +export default doi diff --git a/src/data/flavors/evs.ts b/src/data/flavors/evs.ts new file mode 100644 index 0000000..37dcb33 --- /dev/null +++ b/src/data/flavors/evs.ts @@ -0,0 +1,87 @@ +import type { Publisher } from '../types' + +export const evs: Publisher = { + flavor: "evs", + name: "EVS", + fullName: "Estonian Centre for Standardisation and Accreditation", + category: "national", + description: "EVS is Estonia's national standardization body. It publishes national EVS standards and adopts European (EN) and international (ISO, ISO/IEC) standards under the EVS prefix — e.g. \"EVS-EN ISO 9001:2015\".", + website: "https://www.evs.ee", + syntaxNotes: "EVS identifiers follow: EVS[-EN][-ISO[/IEC]] [Number]:[Year] with supplements after a slash (\"/A1:2024\"). Types: EVS (national), EVS-EN (European adoption), EVS-EN ISO / EVS-EN ISO/IEC (international adoption via EN), EVS-TS (technical specification), EVS-TR (technical report).", + relatedFlavors: ["cen-cenelec", "iso", "iec"], + docTypes: [ + { + key: "estonian_standard", + title: "Estonian Standard", + abbr: ["EVS", "EVS-TS", "EVS-TR"], + description: "A national EVS deliverable: the main EVS standard, plus EVS-TS (Technical Specification) and EVS-TR (Technical Report).", + examples: [ + { input: "EVS 921:2019" }, + { input: "EVS-TS 1:2019" }, + ], + }, + { + key: "international_adoption", + title: "European Adoption", + abbr: ["EVS-EN"], + description: "A European Norm adopted as an Estonian standard: \"EVS EN\". The EN itself may adopt an ISO or ISO/IEC document, giving three levels (EVS-EN ISO 9001).", + examples: [ + { input: "EVS-EN 18216:2026" }, + ], + }, + { + key: "national_adoption", + title: "International Adoption", + abbr: ["EVS-EN ISO", "EVS-EN ISO/IEC"], + description: "An ISO or ISO/IEC document adopted through a European Norm (EVS-EN ISO…) or directly. Supplements of the adopted document are carried after a slash (EVS-EN ISO 9001:2015/A1:2024).", + examples: [ + { input: "EVS-EN ISO 9001:2015/A1:2024" }, + { input: "EVS-EN ISO/IEC 27017:2026" }, + { input: "EVS-EN ISO/IEC 27701:2025" }, + ], + }, +], + components: [ + { + name: "Publisher", + description: "EVS, with adoption chain prefixes EN, ISO, ISO/IEC", + dataType: "enum", + values: ["EVS", "EVS-EN", "EVS-EN ISO", "EVS-EN ISO/IEC"], + example: "EVS-EN ISO", + }, + { + name: "Document Type", + description: "Standard (default), TS, TR", + dataType: "enum", + values: ["", "TS", "TR"], + format: "Between publisher and number (EVS-TS 1)", + example: "TS", + }, + { + name: "Number", + description: "The document number", + attribute: "number", + dataType: "string", + example: "9001", + }, + { + name: "Year", + description: "Publication year after the colon", + attribute: "year", + dataType: "year", + example: "2015", + }, + { + name: "Supplement", + description: "Amendment/corrigendum of the adopted document, after a slash", + format: "A{n}:{year} (e.g. /A1:2024)", + example: "/A1:2024", + }, +], + algebra: [ + { type: "Adoption", description: "A national adoption of a European or international document", syntax: "EVS-EN ISO [Number]:[Year]", example: "EVS-EN ISO 9001:2015/A1:2024" }, + { type: "Amendment", description: "Amendment to the adopted document", syntax: "EVS [Number]:[Year]/A[n]:[Year]", example: "EVS-EN ISO 9001:2015/A1:2024" }, +], +} + +export default evs diff --git a/src/data/flavors/gb.ts b/src/data/flavors/gb.ts new file mode 100644 index 0000000..06d94d2 --- /dev/null +++ b/src/data/flavors/gb.ts @@ -0,0 +1,94 @@ +import type { Publisher } from '../types' + +export const gb: Publisher = { + flavor: "gb", + name: "GB", + fullName: "Chinese National Standards (Guobiao)", + category: "national", + description: "GB standards are China's national standards (Guobiao), issued by SAC (Standardization Administration of China). The prefix encodes the issuing body — GB (national), GB/T (recommended), GB/Z (guideline), GBn (formerly-prescribed internal), plus industry codes such as JB (machinery), HB (aviation), and social-group codes (T/{ORG}).", + website: "https://www.sac.gov.cn", + syntaxNotes: "Chinese standards follow: {Body}[/{Mandate}] {Number}[-{Part}]:-{Year}. The mandate letter after the slash states the legal force: T = recommended (推荐), Z = guideline (指导), none = mandatory (强制性). Social-group standards print the organization after T/ (T/GZAEPI 1-2020). Chinese typography uses an em-dash before the year (—2010); the ASCII hyphen is accepted.", + urnPattern: "urn:pubid:gb:std:{number}:pub-{body}:mandate-{mandate}:year-{year}", + relatedFlavors: ["jis", "iso", "iec"], + docTypes: [ + { + key: "standard", + title: "National Standard", + abbr: ["GB", "GB/T", "GB/Z", "GBn"], + description: "A national standard: GB (mandatory), GB/T (recommended), GB/Z (guideline), and GBn (internal, historical). The body letter and mandate are part of the identity — GB 20223-2006 and GBn 20223-2006 are two different documents.", + examples: [ + { input: "GB/T 20223-2006" }, + { input: "GB 1234-2010" }, + { input: "GB/Z 123-2008" }, + { input: "GBn 123-1990" }, + { input: "GB/T 5606.1-2004" }, + { input: "GB/T 5606 (all parts)" }, + ], + }, + { + key: "industry_standard", + title: "Industry Standard", + abbr: ["JB", "HB", "HB/Z"], + description: "Standards issued by an industry body rather than SAC: JB (machinery), HB (aviation), and other two-letter industry codes, with the same mandate syntax (JB/T 13368-2018).", + examples: [ + { input: "JB/T 13368-2018" }, + { input: "HB/Z 12-2010" }, + ], + }, + { + key: "social_group_standard", + title: "Social Group Standard", + abbr: ["T/{ORG}"], + description: "Standards issued by a social group (社会团体), printed with the organization code after T/ — e.g. T/GZAEPI 001-2018.", + examples: [ + { input: "T/GZAEPI 001-2018" }, + ], + }, +], + components: [ + { + name: "Issuing Body", + description: "The body code as printed: GB, JB, GBn, T/{ORG}…", + attribute: "publisher", + dataType: "enum", + values: ["GB", "GB/T", "GB/Z", "GBn", "JB", "HB", "T/{ORG}"], + format: "Part of the identity — the series distinguishes two documents with one number", + example: "GB/T", + }, + { + name: "Mandate", + description: "Legal force letter carried after the slash", + attribute: "mandate", + dataType: "enum", + values: ["T (recommended)", "Z (guideline)", "— (mandatory)"], + example: "T", + }, + { + name: "Number", + description: "Document number, possibly dotted with a part (5606.1)", + attribute: "number", + dataType: "string", + example: "20223", + }, + { + name: "Part", + description: "Part number, split from a dotted number on parse", + attribute: "part", + dataType: "string", + example: "1", + }, + { + name: "Year", + description: "Approval year, after an em-dash or hyphen", + attribute: "year", + dataType: "year", + example: "2006", + }, +], + algebra: [ + { type: "All Parts", description: "A reference to every part of a multi-part standard", syntax: "GB/T [Number] (all parts)", example: "GB/T 5606 (all parts)" }, + { type: "Part", description: "A part of a multi-part standard", syntax: "GB/T [Number].[Part]-[Year]", example: "GB/T 5606.1-2004" }, +], +} + +export default gb diff --git a/src/data/flavors/iec.ts b/src/data/flavors/iec.ts index fb283cb..83da48f 100644 --- a/src/data/flavors/iec.ts +++ b/src/data/flavors/iec.ts @@ -28,10 +28,23 @@ export const iec: Publisher = { key: "iecee_trf", title: "IEC EE Test Report Form", abbr: ["IECEE TRF"], - description: "Test Report Forms published under IEC EE (System for Certification to Standards Relating to Electrical Equipment). Format is \"IECEE TRF [base] [ edition]:[year]\"; semicolon separators are normalised to the ampersand form.", + description: "Test Report Forms published under IEC EE (System for Certification to Standards Relating to Electrical Equipment). Pattern: IECEE TRF {number-and-part}{trf-version}[{test-type}][:{year}]. Multiple parts join with & (the comma and semicolon separators parse and normalise to &). The version letter runs A, B, C… and can carry a string sub-version: F_II in \"IECEE TRF 61058-1F_II:2019\" — the full version is the whole string. An optional specialised test-type suffix follows: _EMC, _PS, _SOF (e.g. \"IECEE TRF 61000-3-33A_EMC:2019\").", examples: [ - { input: "IECEE TRF 60335-2-4;7F:2012" }, + { input: "IECEE TRF 60335-2-4;7F:2012", output: "IECEE TRF 60335-2-4&7F:2012" }, { input: "IECEE TRF 60065:2014" }, + { input: "IECEE TRF 60086-1&2A:2017" }, + { input: "IECEE TRF 61058-1F_II:2019" }, + { input: "IECEE TRF 61000-3-33A_EMC:2019" }, + ], + }, + { + key: "iecex_trf", + title: "IECEx Test Report Form", + abbr: ["IECEx TRF"], + description: "Test Report Forms published under IECEx (certification for explosive atmospheres). Pattern: IECEx TRF {number-and-part}{separator}{std-edition}{trf-version}. Multiple parts join with a comma. The separator between part and the standard's edition number is 'v' in the IEC Webstore (IECEx TRF 60079-15v1:2002) and '_' in the official IECEx identifier pattern — both parse. The version letter can carry a string sub-version (A_DS, Arev4_ds), the full version being the whole string.", + examples: [ + { input: "IECEx TRF 60079-15v1:2002" }, + { input: "IECEx TRF 60079-0_2A" }, ], }, { diff --git a/src/data/flavors/isbn.ts b/src/data/flavors/isbn.ts new file mode 100644 index 0000000..a712790 --- /dev/null +++ b/src/data/flavors/isbn.ts @@ -0,0 +1,71 @@ +import type { Publisher } from '../types' + +export const isbn: Publisher = { + flavor: "isbn", + name: "ISBN", + fullName: "International Standard Book Number", + category: "international", + description: "The ISBN identifies monographic publications (books). PubID parses both the legacy 10-digit form (with its check digit) and the current 13-digit form (EAN-13, prefixed 978 or 979), validating the check digit on parse.", + website: "https://www.isbn-international.org", + syntaxNotes: "ISBN identifiers follow: ISBN[][: ]{digits} with hyphens optional. The 10-digit form ends with a check character (0–9 or X); the 13-digit form (978/979 prefix) ends with a numeric check digit. At the flavor entry point (Pubid::Isbn.parse) a bare digit string parses without the \"ISBN\" token; global prefix routing requires the token.", + relatedFlavors: ["doi"], + docTypes: [ + { + key: "book", + title: "ISBN-10 (legacy)", + abbr: ["ISBN"], + description: "The 10-digit legacy form: group – registrant – publication – check character (0–9 or X). The check digit is validated on parse; a wrong one fails to parse.", + examples: [ + { input: "ISBN 0-306-40615-2" }, + { input: "ISBN: 0-306-40615-2" }, + { input: "0-306-40615-2", output: "ISBN 0-306-40615-2" }, + ], + }, + { + key: "book13", + title: "ISBN-13", + abbr: ["ISBN"], + description: "The current 13-digit form (EAN-13): 978/979 Bookland prefix – group – registrant – publication – numeric check digit. Bare 13-digit strings are accepted; hyphenation is preserved as given.", + examples: [ + { input: "ISBN 978-3-16-148410-0" }, + { input: "9783161484100", output: "ISBN 9783161484100" }, + ], + }, +], + components: [ + { + name: "Scheme", + description: "The \"ISBN\" token, optionally followed by a colon", + dataType: "enum", + values: ["ISBN", "ISBN:"], + format: "Optional for a bare 13-digit string", + example: "ISBN", + }, + { + name: "Group", + description: "The registration group (language area / country)", + dataType: "string", + format: "0, 1 (English); 3 (German); 978-… prefixes carry it after the Bookland prefix", + example: "978-3", + }, + { + name: "Registrant / Publication", + description: "The publisher and title allocations", + dataType: "string", + format: "Variable width; hyphenation is optional and preserved", + example: "16-148410", + }, + { + name: "Check character", + description: "The final check digit or letter", + dataType: "string", + format: "0–9 or X for ISBN-10; 0–9 for ISBN-13. Validated on parse", + example: "0", + }, +], + algebra: [ + { type: "Bare form", description: "A bare digit string parses as an ISBN (13 digits → ISBN-13)", syntax: "{13 digits}", example: "9783161484100" }, +], +} + +export default isbn diff --git a/src/data/flavors/iso.ts b/src/data/flavors/iso.ts index 949c757..986298c 100644 --- a/src/data/flavors/iso.ts +++ b/src/data/flavors/iso.ts @@ -73,22 +73,26 @@ export const iso: Publisher = { { key: "amendment", title: "Amendment", - abbr: ["Amd", "AM"], - description: "An Amendment modifies an existing International Standard by adding, changing, or deleting specified elements. It is published as a separate supplement to the base document and referenced using the base document ID.", + abbr: ["Amd", "AMD"], + description: "An Amendment modifies an existing International Standard by adding, changing, or deleting specified elements. It is published as a separate supplement to the base document and referenced using the base document ID. Draft amendments carry their own typed stages: DAM (40.xx, Draft Amendment) and FDAM (50.xx, Final Draft Amendment) — the historic long spellings \"DAmd\"/\"FDAmd\" parse but render canonically as DAM/FDAM.", examples: [ { input: "ISO 9001:2015/Amd 1:2024" }, { input: "ISO 14001:2015/Amd 1:2023" }, { input: "ISO/IEC 17031-1:2020/Amd 1:2022" }, + { input: "ISO 8601-1:2019/DAmd 1", output: "ISO 8601-1:2019/DAM 1" }, + { input: "ISO 1234:2019/FDAM 1" }, ], }, { key: "corrigendum", title: "Corrigendum", abbr: ["Cor", "COR"], - description: "A Corrigendum corrects technical or editorial errors in an already published document. Unlike amendments, corrigenda do not add new content but fix mistakes.", + description: "A Corrigendum corrects technical or editorial errors in an already published document. Unlike amendments, corrigenda do not add new content but fix mistakes. Draft corrigenda carry typed stages with genuine long/short faces: DCor (40.xx) and FDCor (50.xx) render the long face, DCOR/FDCOR the short one; the historic spellings \"DIS Cor\"/\"FDIS Cor\" and \"FCOR\" also parse (canonicalized to the short face).", examples: [ { input: "ISO 9001:2015/Cor 1:2016" }, { input: "ISO 14001:2015/Cor 1:2016" }, + { input: "ISO 1234:2019/DCor 1:2021" }, + { input: "ISO 1234:2019/FCOR 1:2021", output: "ISO 1234:2019/FDCOR 1:2021" }, ], }, { @@ -211,6 +215,13 @@ export const iso: Publisher = { { code: "90.93", abbr: "RVD", name: "Review Verified" }, { code: "95.20", abbr: "FPN", name: "Final Proposal for Withdrawal" }, { code: "95.99", abbr: "WTH", name: "Withdrawal of International Standard" }, +], + styles: [ + { key: "canonical", name: "Canonical (default)", description: "The surface form detected from the parsed spelling — parse and render round-trip byte-exactly", example: "ISO 1234:2019/Amd 1:2021" }, + { key: "short", name: "Short stage + single-letter language", description: "Rendering option lang_single: true — short stage abbreviations (AMD, DAM) and 1-letter language codes (E, F, R) when the identifier carries languages", example: "ISO 1234:2019/AMD 1:2021(E)" }, + { key: "long", name: "Long stage", description: "Rendering option stage_format_long: true — the long supplement abbreviations (Amd, DCor, FDCor)", example: "ISO 1234:2019/Amd 1:2021" }, + { key: "undated", name: "Undated", description: "Rendering option with_date: false — drops the copyright year (the historic :ref_undated format)", example: "ISO 1234/AMD 1" }, + { key: "edition", name: "With edition", description: "Rendering option with_edition: true — appends the edition portion when the identifier carries one (on the base document); the language face then always uses the 2-letter ISO 639-1 form", example: "ISO 1234:2019 ED2/Amd 1:2021" }, ], components: [ { @@ -290,11 +301,11 @@ export const iso: Publisher = { }, { name: "Typed Stage", - description: "Stage combined with document type (e.g., DTR for Draft Technical Report)", + description: "Stage combined with document type (e.g., DTR for Draft Technical Report). Supplements carry their own: DAM/FDAM for amendments, DCor/FDCor for corrigenda (long faces; short DCOR/FDCOR), WD Amd-style entries for the early stages", attribute: "typed_stage", dataType: "enum", - values: ["DTR", "DTS", "FDTR", "FDTS", "AWI", "NP", "PRF"], - format: "Optional. Combined stage+type abbreviation.", + values: ["WD", "CD", "DIS", "FDIS", "DTR", "DTS", "FDTR", "FDTS", "DAM", "FDAM", "DCor", "FDCor", "WD Amd", "PRF"], + format: "Optional. Combined stage+type abbreviation; looked up via Pubid::Iso.locate_stage.", example: "DTR", }, { diff --git a/src/data/flavors/omg.ts b/src/data/flavors/omg.ts new file mode 100644 index 0000000..d33d834 --- /dev/null +++ b/src/data/flavors/omg.ts @@ -0,0 +1,64 @@ +import type { Publisher } from '../types' + +export const omg: Publisher = { + flavor: "omg", + name: "OMG", + fullName: "Object Management Group", + category: "international", + description: "OMG is the consortium behind UML, SysML, CORBA, DDS and other modeling and interoperability specifications. OMG specifications are named by an acronym plus a free-form version string — e.g. \"OMG UML 2.5.1\", \"OMG DDS 5 beta 3\".", + website: "https://www.omg.org", + syntaxNotes: "OMG identifiers follow: OMG {Acronym}[ {Version}]. The version is free-form and stored verbatim (\"1.0\", \"2.5.1\", \"5 beta 3\", \"1.1\"); an identifier without a version (\"OMG CORBA\") names the specification as a whole.", + relatedFlavors: ["iso"], + docTypes: [ + { + key: "specification", + title: "Specification", + abbr: ["OMG"], + description: "An OMG specification, named by its acronym (UML, SysML, DDS, CORBA, AMI4CCM…) with an optional version. Adopted ISO/IEC versions carry the joint designation in the ISO flavor.", + examples: [ + { input: "OMG UML 2.5.1" }, + { input: "OMG SysML 1.6" }, + { input: "OMG DDS 5 beta 3" }, + { input: "OMG AMI4CCM 1.0" }, + { input: "OMG CORBA" }, + ], + }, +], + components: [ + { + name: "Publisher", + description: "The OMG token", + dataType: "enum", + values: ["OMG"], + example: "OMG", + }, + { + name: "Acronym", + description: "The specification's acronym", + attribute: "acronym", + dataType: "string", + example: "UML", + }, + { + name: "Version", + description: "Free-form version string, stored verbatim", + attribute: "version", + dataType: "string", + format: "Optional. \"1.0\", \"2.5.1\", \"5 beta 3\"", + example: "2.5.1", + }, + { + name: "Part", + description: "Volume or format segment after the version", + attribute: "part", + dataType: "string", + format: "Optional (PtC/PTC ballot volumes and formal/ documents)", + example: "formal", + }, +], + algebra: [ + { type: "Versionless", description: "A bare acronym names the specification without an edition", syntax: "OMG {Acronym}", example: "OMG CORBA" }, +], +} + +export default omg diff --git a/src/data/flavors/plateau.ts b/src/data/flavors/plateau.ts index e65a250..67ac183 100644 --- a/src/data/flavors/plateau.ts +++ b/src/data/flavors/plateau.ts @@ -8,18 +8,21 @@ export const plateau: Publisher = { category: "national", description: "PLATEAU is a project by Japan's Ministry of Land, Infrastructure, Transport and Tourism (MLIT) that promotes the use of 3D city models for urban planning and management. Legacy Latin references (e.g. \"PLATEAU Handbook #00 v1.0\") are parsed and normalised to the canonical Japanese form (第X.X版).", website: "https://www.mlit.go.jp/plateau", - syntaxNotes: "PLATEAU identifiers follow: PLATEAU [Type] #[Number] [Edition]. Types include Handbook, Technical Report, Annex. Japanese edition numbering is used (第X.X版). Legacy Latin references (\"v1.0\", \"Version 1.0\") are accepted and normalised.", + syntaxNotes: "PLATEAU identifiers follow: PLATEAU [Type] #[Number][-Annex] [Edition]. Types include Handbook, Technical Report, Annex. Japanese edition numbering is used (第X.X版). Legacy Latin references (\"v1.0\", \"Version 1.0\") are accepted and normalised. A \"Handbook #11\" reference with edition 1–3 and a part suffix ((民間活用編) / (公共活用編)) is rewritten to the handbook that actually existed for those editions: #06 and #04 respectively.", relatedFlavors: ["jis"], docTypes: [ { key: "handbook", title: "Handbook", abbr: ["Handbook"], - description: "PLATEAU Handbooks providing guidance on 3D city model usage. Legacy Latin edition forms are normalised to the canonical Japanese edition.", + description: "PLATEAU Handbooks providing guidance on 3D city model usage. Legacy Latin edition forms are normalised to the canonical Japanese edition. Handbook #11 combined two earlier handbooks: for editions 1–3, (民間活用編) was Handbook #06 and (公共活用編) was Handbook #04 — only from edition 4 (第4.0版) do the two combine into #11, so an edition-1–3 \"#11\" reference with a part suffix parses as the #06/#04 document.", examples: [ { input: "PLATEAU Handbook #00 第1.0版" }, { input: "PLATEAU Handbook #00 第2.0版" }, { input: "PLATEAU Handbook #00 v1.0" }, + { input: "PLATEAU Handbook #11 第1.0版(民間活用編)", output: "PLATEAU Handbook #06 第1.0版" }, + { input: "PLATEAU Handbook #11 第1.0版(公共活用編)", output: "PLATEAU Handbook #04 第1.0版" }, + { input: "PLATEAU Handbook #11 第4.0版" }, ], }, { diff --git a/src/data/flavors/un.ts b/src/data/flavors/un.ts new file mode 100644 index 0000000..e0bfc38 --- /dev/null +++ b/src/data/flavors/un.ts @@ -0,0 +1,55 @@ +import type { Publisher } from '../types' + +export const un: Publisher = { + flavor: "un", + name: "UN", + fullName: "United Nations documents", + category: "international", + description: "PubID parses United Nations document symbols — the slash-separated designations used across UN organs and bodies, such as General Assembly resolutions (\"A/RES/78/1\") and the UN/CEFACT trade documents (\"TRADE/CEFACT/2004/32\").", + website: "https://www.un.org", + syntaxNotes: "UN document symbols follow the UN Editorial Manual pattern {Series}/{Body}[/{Year}]/{Number}, e.g. A/RES/78/1 (General Assembly resolution 78/1). The \"UN \" prefix is optional and normalized away (\"UN A/RES/78/1\" renders \"A/RES/78/1\"). Working-party documents carry annex/recommendation letters (TRADE/WP.4/R.1068).", + relatedFlavors: ["iso"], + docTypes: [ + { + key: "document", + title: "UN Document", + abbr: ["UN"], + description: "A UN document symbol: the series letter(s) name the organ (A = General Assembly, E = ECOSOC, S = Security Council, TRADE = UN/CEFACT), followed by body, year and sequence elements.", + examples: [ + { input: "A/RES/78/1" }, + { input: "UN A/RES/78/1", output: "A/RES/78/1" }, + { input: "TRADE/CEFACT/2004/32" }, + { input: "TRADE/WP.4/1068" }, + { input: "TRADE/WP.4/R.1068" }, + ], + }, +], + components: [ + { + name: "Series", + description: "The organ or body series symbol", + dataType: "string", + format: "A, E, S, TD, TRADE… (first slash-separated element)", + example: "A", + }, + { + name: "Body / Subseries", + description: "Body and sub-body elements (RES, CEFACT, WP.4)", + dataType: "string", + format: "Middle slash-separated elements", + example: "RES", + }, + { + name: "Number", + description: "Session year and/or sequence number", + dataType: "string", + format: "Trailing elements (78/1, 2004/32, R.1068)", + example: "78/1", + }, +], + algebra: [ + { type: "Optional prefix", description: "The \"UN \" prefix is accepted and dropped on render", syntax: "UN {symbol}", example: "UN A/RES/78/1" }, +], +} + +export default un diff --git a/src/data/types.ts b/src/data/types.ts index 32b5670..1ce5afc 100644 --- a/src/data/types.ts +++ b/src/data/types.ts @@ -52,7 +52,9 @@ export interface Publisher { urnPattern?: string relatedFlavors?: string[] syntaxNotes?: string - logo: string + // Optional: the hero renders a two-letter badge when a flavor has no logo + // asset yet. + logo?: string logos?: string[] // For publishers with separate light/dark brand assets (rendered against // dark/light backgrounds). When set, the layout picks the variant that From 62794f8927a6d86bfdc1054d3379ec005ba191bd Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 8 Nov 2026 18:08:22 +0800 Subject: [PATCH 2/3] =?UTF-8?q?site:=20drop=20the=20scheme'd=20syntax=20pl?= =?UTF-8?q?aceholder=20from=20the=20DOI=20page=20=E2=80=94=20lychee=20woul?= =?UTF-8?q?d=20link-check=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/flavors/doi.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/flavors/doi.ts b/src/data/flavors/doi.ts index 1411ab9..1eb1a19 100644 --- a/src/data/flavors/doi.ts +++ b/src/data/flavors/doi.ts @@ -7,7 +7,7 @@ export const doi: Publisher = { category: "international", description: "The DOI system provides an actionable, persistent identifier for digital objects. PubID parses DOI names — including NIST's DOI-based standard designations such as \"doi:10.6028/NIST.SP.800-53r5\" — as identifiers alongside the SDO schemes.", website: "https://www.doi.org", - syntaxNotes: "DOI syntax is \"doi:10.{prefix}/{suffix}\" (case-insensitive scheme, normalized to lowercase). PubID accepts the canonical \"doi:\" form, the bare \"10.x/suffix\" name, the \"DOI:\" upper-scheme spelling, and the resolver URL \"https://doi.org/10.x/suffix\" — all render canonical.", + syntaxNotes: "DOI syntax is \"doi:10.{prefix}/{suffix}\" (case-insensitive scheme, normalized to lowercase). PubID accepts the canonical \"doi:\" form, the bare \"10.x/suffix\" name, the \"DOI:\" upper-scheme spelling, and the resolver-URL form on the doi.org host — all render canonical.", urnPattern: "urn:pubid:doi:resource:prefix-{10.NNNN}:suffix-{suffix}", relatedFlavors: ["nist", "isbn"], docTypes: [ @@ -51,7 +51,7 @@ export const doi: Publisher = { }, ], algebra: [ - { type: "URL form", description: "The resolver URL parses and renders as the canonical DOI name", syntax: "https://doi.org/10.{prefix}/{suffix}", example: "https://doi.org/10.1000/182" }, + { type: "URL form", description: "The resolver URL parses and renders as the canonical DOI name", syntax: "doi.org/10.{prefix}/{suffix}", example: "https://doi.org/10.1000/182" }, ], } From 43a0f3eb4b9468fa92713d3c9e879132e8394aa5 Mon Sep 17 00:00:00 2001 From: Ronald Tse Date: Sun, 8 Nov 2026 18:14:58 +0800 Subject: [PATCH 3/3] site: port the IEEE Type I/II pattern narrative and the AIEE/IRE predecessor docTypes to the IEEE page --- src/data/flavors/ieee.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/data/flavors/ieee.ts b/src/data/flavors/ieee.ts index d6d078e..80ea73a 100644 --- a/src/data/flavors/ieee.ts +++ b/src/data/flavors/ieee.ts @@ -8,7 +8,7 @@ export const ieee: Publisher = { category: "international", description: "IEEE is the world's largest technical professional organization for the advancement of technology. IEEE Standards Association (IEEE SA) develops standards across a wide range of industries including power and energy, telecommunications, computing, and more.", website: "https://standards.ieee.org", - syntaxNotes: "IEEE identifiers follow patterns like: IEEE Std [Number]-[Year], IEEE [Number]-[Year], IEEE [Draft Number]/D[DraftVersion]. Some include AIEE legacy identifiers. Both /Amd and /Cor separators are flexible — slash, hyphen, and space variants are accepted and normalised.", + syntaxNotes: "IEEE identifiers follow two historic pattern series: the old Type I — {publisher} {type} {series} {number}{part}.{subpart}{year} {edition}/{conform}/{correction}, e.g. \"IEEE Std 802.11-2020\" with optional Edition/Conformance/Cor tails — and the new Type II, whose part/subpart/year elements join with a mix of separators. Both parse; the monogem's docs/flavors/ieee.md carries the full pattern record. A relationship clause — \"(Revision of IEEE Std 1613-2009)\" — parses into the relationships collection (11 types, including reaffirmation_of and redesignation_of) but is deliberately not re-embedded in to_s, because consumers use to_s as the document number and output filename (to_mr_string/to_slug supply the machine form, supplements joining with _). Drafts spell the draft word \"Draft\" alone and use the P prefix with a /D{n} draft number: \"IEEE P802.11-2020/D1, 2020\". AIEE/IRE predecessor identifiers (1884–1963) parse as their own classes.", urnPattern: "urn:ieee:std:[number]:[year]", relatedFlavors: ["iec", "iso", "csa", "bsi"], docTypes: [ @@ -173,6 +173,26 @@ export const ieee: Publisher = { { input: "IEEE 1016-2009 (Revision of IEEE 1016-1998)" }, ], }, + { + key: "aiee", + title: "AIEE (predecessor, 1884–1963)", + abbr: ["AIEE No"], + description: "American Institute of Electrical Engineers identifiers, which merged into IEEE in 1963. Always uses \"No\"/\"No.\" (never \"Std\"); rendering profiles offer the short dash form and the long comma form with an optional month; the original parsed format is preserved by default and date_format: (:short|:long) overrides it. Transitional forms (IEEE-AIEE No. 56) parse as well.", + examples: [ + { input: "AIEE No. 552, November 1955" }, + { input: "AIEE No 18-1934 (ASA C55 1934)", output: "IEEE Std 18-1934 (ASA C55-1934)" }, + ], + }, + { + key: "ire", + title: "IRE (predecessor, 1912–1963)", + abbr: ["IRE"], + description: "Institute of Radio Engineers identifiers: year-first (unlike modern IEEE) with 2-digit years (12–63) expanded to 1912–1963 internally and rendered as parsed, and committee notation \"7.S2\" (committee 7, standard 2) or \"28 S1\". Compatible with relationship clauses.", + examples: [ + { input: "52 IRE 7.S2" }, + { input: "61 IRE 28 S1" }, + ], + }, ], components: [ {