@pubid/pubid 0.1.18 → 0.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,13 @@ export function checkCase(testCase, impl) {
29
29
  for (const alias of testCase.nonNormalizedAliases ?? []) {
30
30
  try {
31
31
  const aliased = impl.parse(alias.spelling);
32
- if (aliased.toHuman() !== testCase.representations.human) {
32
+ // A multi-round chain walks another render/parse round per
33
+ // recorded `rounds` until it reaches the canonical human.
34
+ let human = aliased.toHuman();
35
+ for (let round = 1; round < (alias.rounds ?? 1); round += 1) {
36
+ human = impl.parse(human).toHuman();
37
+ }
38
+ if (human !== testCase.representations.human) {
33
39
  mismatches.push(`${label} alias ${alias.spelling}`);
34
40
  }
35
41
  }
@@ -13,6 +13,9 @@ export interface Representations {
13
13
  export interface Spelling {
14
14
  spelling: string;
15
15
  style?: string;
16
+ /** Render/parse rounds the spelling needs to reach the canonical human;
17
+ * present only above 1 (a documented multi-round normalization chain). */
18
+ rounds?: number;
16
19
  }
17
20
  export interface ErrorExpectation {
18
21
  /** Neutral rejection code - never an implementation class. */
@@ -501,11 +501,15 @@ class IeeeBuilder {
501
501
  // An explicit joint stage on the draft ("=DFDIS.3") IS the stage —
502
502
  // it outranks the ordinal ladder, which classifies IEEE-internal
503
503
  // drafts only (docs/IEEE-DRAFT-STAGES.md §1.3).
504
+ let draftVersionPresent = false;
504
505
  if (parsed["draft"] !== undefined) {
505
506
  let draftData = parsed["draft"];
506
507
  if (Array.isArray(draftData)) {
507
508
  draftData = mergeParsedArray(draftData);
508
509
  }
510
+ // A verbatim draft designator string is a version in itself.
511
+ if (!isObj(draftData))
512
+ draftVersionPresent = true;
509
513
  if (isObj(draftData) && draftData["draft_iso_stage"] !== undefined) {
510
514
  const stage = extractValue(draftData["draft_iso_stage"]) ?? "";
511
515
  // The doubled-D alias ("DFDIS") carries its own draft marker;
@@ -516,9 +520,21 @@ class IeeeBuilder {
516
520
  : stage;
517
521
  }
518
522
  if (isObj(draftData) && draftData["draft_version"] !== undefined) {
523
+ draftVersionPresent = true;
519
524
  const dv = draftData["draft_version"];
520
525
  const version = Array.isArray(dv) ? dv.map((v) => extractValue(v) ?? "").join("") : extractValue(dv);
521
526
  if (version !== undefined) {
527
+ // A draft version that IS an ISO stage token ("/DCD1" and
528
+ // "/CD1" both capture "CD1") takes that stage on a plain IEEE
529
+ // document (docs/IEEE-DRAFT-STAGES.md §1.3, "DDIS" = DIS); a
530
+ // joint ISO-led stage-word draft keeps its generic project
531
+ // stage.
532
+ const pubs = parsed["publishers"];
533
+ const copubs = isObj(pubs) ? pubs["copublishers"] : undefined;
534
+ const joint = copubs !== undefined && (Array.isArray(copubs) ? copubs.length > 0 : true);
535
+ const stageMatch = /^(FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)\d*$/.exec(version);
536
+ if (!joint && stageMatch !== null)
537
+ return stageMatch[1];
522
538
  // relaton's hyphenated "/D-2" spelling captures the dash inside
523
539
  // draft_version; the stage registry keys on "D2". A date-embedded
524
540
  // version ("3-2017-07") keys on its leading ordinal.
@@ -534,7 +550,7 @@ class IeeeBuilder {
534
550
  return "P";
535
551
  if (typeValue === "Std")
536
552
  return "Std";
537
- if (typeValue === "Draft Std" && parsed["draft"] === undefined && parsed["digit_draft"] === undefined) {
553
+ if (typeValue === "Draft Std" && !draftVersionPresent && parsed["digit_draft"] === undefined) {
538
554
  return "D1";
539
555
  }
540
556
  if (/^No\.?$/.test(typeValue))
@@ -315,6 +315,19 @@ const STAGE_MONTH_NUMBERS = {
315
315
  };
316
316
  function normalizeJointStageSpellings(input) {
317
317
  let cleaned = input;
318
+ // The three-way ISO-led list glues the language marker to the year
319
+ // ("ISO/IEC/IEEE 8802-1AC-2018(E)"): the spaced spelling is the
320
+ // language marker and drops, the glued one prints as parenthetical
321
+ // content. Re-spell the boundary (Ruby: Parser.normalize_input).
322
+ // Scoped to the three-way lists - the two-way IEC/IEEE copublished
323
+ // form keeps its glued (E) as identity ("…:2012(E)").
324
+ cleaned = cleaned.replace(/^((?:ISO\/IEC\/IEEE|IEEE\/ISO\/IEC|IEEE\/IEC\/ISO)\s+[^()]+?[^()\s])\(E\)$/, "$1 (E)");
325
+ // The stage-first joint draft's dash part carries a bare comma year
326
+ // ("IEC/IEEE FDIS P60780-323, 2015"): the joint route takes no bare
327
+ // year there and the copublished route claims the string, so drop
328
+ // the non-identity year and let the stage-first route read the dash
329
+ // part (the rendered face is the dot human).
330
+ cleaned = cleaned.replace(/^((?:IEC\/IEEE|IEEE\/IEC)\s+(?:FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)\s+P\d+(?:-\d+)+),\s+\d{4}$/, "$1");
318
331
  // The IEEE-led joint list's dash/underscore stage tail carries a
319
332
  // month-name date ("IEEE/ISO/IEC P29119-2-DIS, December 2011"): the
320
333
  // generic route answers with a slashed copublisher, so re-spell the
@@ -104,7 +104,7 @@ export const UPDATE_CODES = [
104
104
  { plain: "ISO/IEC/IEEE DIS P16326/201x, December 2018", replacement: "ISO/IEC/IEEE DIS P16326-2018-12" },
105
105
  { plain: "ISO/IEC/IEEE/P21839, 2019(E)", replacement: "ISO/IEC/IEEE P21839-2019" },
106
106
  { plain: "ISO/IEC/IEEE CD2 P12207-2: 201x(E), February 2019", replacement: "ISO/IEC/IEEE CD2 P12207.2-2019-02" },
107
- { plain: "ISO/IEC/IEEE P42010.WD4:2019(E)", replacement: "ISO/IEC/IEEE P42010/D-4-2019" },
107
+ { plain: "ISO/IEC/IEEE P42010.WD4:2019(E)", replacement: "ISO/IEC/IEEE P42010.2019/D4" },
108
108
  { plain: "ISO/IEC/IEEE/P16085_DIS, March 2020", replacement: "ISO/IEC/IEEE DIS P16085-2020-03" },
109
109
  { plain: "ANSI/IEEE Std: Outdoor Apparatus Bushings", replacement: "ANSI/IEEE Std 21-1976-11" },
110
110
  { plain: "Draft National Electrical Safety Code, January 2016", replacement: "IEEE PC2-2016-01" },
@@ -1259,6 +1259,15 @@ export function nistGrammarImplementation() {
1259
1259
  const { cleaned, format } = preprocessNist(input);
1260
1260
  const base = cleaned.replace(/\s*\(all parts\)\s*$/, "");
1261
1261
  const tree = toBuilderTree(cachedRuntime(base));
1262
+ // The reference's language_code rule captures its separator with
1263
+ // the code (".uppl"); the baked nist artifact labels the code
1264
+ // alone. Restore the reference's slice for a trailing dot-prefixed
1265
+ // code, so the renderer prints the canonical double-dot face
1266
+ // ("NIST.SP.955-S..uppl").
1267
+ const dotted = /\.([a-z]{2,4})$/.exec(base);
1268
+ if (dotted && tree["translation"] === dotted[1]) {
1269
+ tree["translation"] = `.${dotted[1]}`;
1270
+ }
1262
1271
  const marked = base !== cleaned
1263
1272
  ? { ...tree, all_parts: true }
1264
1273
  : tree;
@@ -266,8 +266,11 @@ export function preprocessNist(input) {
266
266
  // normalize_legacy_corpus_spellings: catalogue forms the grammar and
267
267
  // the renderers cannot round-trip on their own. Each rule mirrors an
268
268
  // already-parseable spelling of the same document.
269
- // The mr renderer prints the translation code with its own leading
270
- // dot ("955-S..uppl"); the grammar wants one dot.
269
+ // The reference's mr translation CODE keeps its own leading dot
270
+ // (".uppl") and its renderer adds the separator dot, so the canonical
271
+ // face is "955-S..uppl". The grammar reads the collapsed single-dot
272
+ // spelling and captures the dot with the code; collapse here so a
273
+ // double-dot input reads the same way.
271
274
  cleaned = cleaned.replace(/\.\./g, ".");
272
275
  // Trailing-dot addendum spelling; the canonical render comes from
273
276
  // the lowercase ".add" parse ("NBS.TN.467pt1.Add." -> ".add").
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubid/pubid",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "TypeScript PubID identifiers: resolution, normalization and canonical hashes for every identifier in the pubid-testsuite corpus (41 standards bodies), with a conformance runner mirroring the Ruby reference",
5
5
  "type": "module",
6
6
  "license": "MIT",