@pubid/pubid 0.1.12 → 0.1.14

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.
@@ -284,6 +284,14 @@ class IeeeBuilder {
284
284
  determineIdentifierClass(attributes) {
285
285
  if (attributes["publisher"] === "AIEE")
286
286
  return AieeClass;
287
+ // A stage-bearing row is a joint development whatever its type
288
+ // code: the stage is the printed identity ("IEEE P15026.2/FDIS",
289
+ // pubid#203) and only the joint model carries iso_stage and the D=
290
+ // designator (the reference's chooser).
291
+ if (attributes["iso_stage"] !== undefined ||
292
+ (attributes["ieee_draft"] ?? "").startsWith("D=")) {
293
+ return JointDevelopmentClass;
294
+ }
287
295
  const typeCode = attributes["type"];
288
296
  if (typeCode === "P")
289
297
  return ProjectDraftIdentifierClass;
@@ -438,6 +446,33 @@ class IeeeBuilder {
438
446
  if (day !== undefined)
439
447
  attributes["day"] = day;
440
448
  this.handleDraft(parsed, attributes);
449
+ this.foldDraftDateTail(attributes);
450
+ // The bare-IEEE fdraft clause (pubid#203): a slash-led stage word
451
+ // after the code ("IEEE P15026.2/FDIS, August 2010") rides the D=
452
+ // designator with the IEEE date convention ("D=FDIS-201008"); the
453
+ // ISO face is not elected (the reference's fdraft rule).
454
+ const fdraft = parsed["fdraft"];
455
+ if (fdraft !== undefined && typeof fdraft === "object" && !Array.isArray(fdraft)) {
456
+ const fdStage = extractValue(fdraft["fdraft_stage"]);
457
+ if (fdStage !== undefined) {
458
+ const fdYear = extractValue(fdraft["year"]);
459
+ const fdMonth = extractValue(fdraft["month"]);
460
+ let date = "";
461
+ if (fdYear !== undefined) {
462
+ date = `-${fdYear}`;
463
+ const monthNum = fdMonth !== undefined
464
+ ? DRAFT_MONTH_NAMES[String(fdMonth).replace(/\.$/, "")]
465
+ : undefined;
466
+ if (monthNum !== undefined)
467
+ date += monthNum.padStart(2, "0");
468
+ }
469
+ attributes["publishers"] ??= [attributes["publisher"] ?? "IEEE"];
470
+ attributes["ieee_draft"] = `D=${fdStage}${date}`;
471
+ const fdRecord = locateStage(fdStage);
472
+ if (fdRecord !== undefined)
473
+ attributes["typed_stage"] = new IeeeTypedStage(fdRecord);
474
+ }
475
+ }
441
476
  this.applyDefaultDraftVersion(attributes, typeValue);
442
477
  this.handleKeyedHash(parsed, "corrigendum", attributes, "cor_number", "cor_year");
443
478
  this.handleKeyedHash(parsed, "amendment", attributes, "amd_number", "amd_year");
@@ -533,23 +568,39 @@ class IeeeBuilder {
533
568
  if (draftData["draft_version"] !== undefined) {
534
569
  const dv = draftData["draft_version"];
535
570
  version = Array.isArray(dv) ? dv.map((v) => extractValue(v) ?? "").join("") : extractValue(dv);
571
+ // The reference's designator normalization (gem#552): a leading
572
+ // hyphen drops, and a three-digit-plus zero-padded designator
573
+ // sheds its zero ("/013.0" -> "13.0" — the 2026-10-19 ruling;
574
+ // the strip is scoped to the dotted designator class — "D043"/
575
+ // "D09"/"D0.16" keep their conventional widths).
536
576
  if (version !== undefined)
537
- version = version.replace(/^-/, "");
577
+ version = version.replace(/^-/, "").replace(/^0(?=\d+\.\d)/, "");
538
578
  }
539
579
  revision = extractValue(draftData["revision"]);
540
580
  isoStage = extractValue(draftData["draft_iso_stage"]);
541
581
  isoIteration = extractValue(draftData["draft_iso_iteration"]);
542
582
  // FDIS/CDV/… are ISO/IEC stage tokens, not draft ordinals (C1
543
- // ruling, testsuite#2). "/DDIS" and "/DFDIS" are hallucinated
583
+ // ruling, testsuite#2). "/DDIS" and "/DFCD" are hallucinated
544
584
  // concatenations — the designator D does not merge onto a stage
545
585
  // word. The draft of a stage rides the iso_stage slot, which
546
586
  // Draft#to_s renders as the D= designator face ("…/D=DIS-2020").
547
- if (version !== undefined && /^(FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)$/.test(version)) {
548
- isoStage = version;
549
- version = undefined;
550
- }
587
+ // The glued month-name date ("-June2010", the 2026-10-19 FCD
588
+ // ruling) rides the designator's date slot with the month numeric.
551
589
  month = extractValue(draftData["month"]);
552
590
  year = extractValue(draftData["year"]);
591
+ if (version !== undefined) {
592
+ // The stage iteration rides glued onto the word ("DIS2" — the
593
+ // lossless face, pubid#477; the 2026-10-19 DIS2 ruling).
594
+ const stageDate = /^(FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)(\d+)?(?:-([A-Za-z]+)(\d{4}))?$/.exec(version);
595
+ if (stageDate !== null) {
596
+ isoStage = stageDate[1] + (stageDate[2] ?? "");
597
+ if (stageDate[3] !== undefined) {
598
+ month = DRAFT_MONTH_NAMES[stageDate[3]] !== undefined ? stageDate[3] : month;
599
+ year = stageDate[4];
600
+ }
601
+ version = undefined;
602
+ }
603
+ }
553
604
  // The apostrophe-year catalogue convention ("P1484.20.1/D7,
554
605
  // May 07"): the 2-digit year expands with the <30 pivot so the
555
606
  // hash carries an unambiguous year (the reference's builder).
@@ -598,6 +649,42 @@ class IeeeBuilder {
598
649
  attributes["draft"] = draftObj.render();
599
650
  }
600
651
  }
652
+ // A draft-date that routed to the identity slots (the grammar's
653
+ // month/year captures beside a bare draft ordinal) folds onto the
654
+ // draft designator: a REV-carrier embeds it numerically
655
+ // ("P1310Rev3/D2, February 2011" renders "D2-2011-02" — the
656
+ // reference's builder), a plain draft keeps the printed face routed
657
+ // through the draft's own parse so the month canonicalizes to the
658
+ // crawl abbreviation ("D08, September 2018" renders "D08, Sept,
659
+ // 2018"; a space-faced input stays space-faced).
660
+ foldDraftDateTail(attributes) {
661
+ const month = attributes["month"];
662
+ const year = attributes["year"];
663
+ const draft = attributes["draft"];
664
+ if (typeof month !== "string" || typeof year !== "string" ||
665
+ typeof draft !== "string" || !/^\/D[^ =,]+$/.test(draft)) {
666
+ return;
667
+ }
668
+ const draftNum = draft.slice(2);
669
+ if (attributes["revision"] !== undefined || attributes["rev_bare"] === true) {
670
+ const monthNum = DRAFT_MONTH_NAMES[String(month).replace(/\.$/, "")];
671
+ if (monthNum === undefined)
672
+ return;
673
+ attributes["draft"] =
674
+ new IeeeDraft({ version: `${draftNum}-${year}-${monthNum.padStart(2, "0")}` }).render();
675
+ }
676
+ else {
677
+ const escaped = month.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
678
+ const commaFace = this.originalInput !== "" &&
679
+ new RegExp(`,\\s+${escaped}`, "i").test(this.originalInput);
680
+ const reparsed = IeeeDraft.parse(`${draftNum}${commaFace ? ", " : " "}${month} ${year}`);
681
+ if (reparsed === undefined)
682
+ return;
683
+ attributes["draft"] = reparsed.render();
684
+ }
685
+ delete attributes["month"];
686
+ delete attributes["year"];
687
+ }
601
688
  applyDefaultDraftVersion(attributes, typeValue) {
602
689
  if (attributes["draft"] !== undefined)
603
690
  return;
@@ -885,6 +972,18 @@ class IeeeBuilder {
885
972
  if (attributes["month"] === undefined)
886
973
  attributes["month"] = extractValue(parsed["draft_month"]);
887
974
  }
975
+ // A status word ("Unapproved") marks the draft (pubid#318).
976
+ const jointDraftStatus = extractValue(parsed["draft_status"]);
977
+ if (jointDraftStatus !== undefined)
978
+ attributes["draft_status"] = jointDraftStatus;
979
+ // A dash_year tail ("/D-3-2007") is the identity year when the
980
+ // slots are otherwise empty — the ISO face prints it colon-joined
981
+ // ("ISO/IEC/IEEE FDIS P15288:2007", the reference's 9258 flow).
982
+ if (attributes["year"] === undefined && parsed["dash_year"] !== undefined) {
983
+ const dashYear = (extractValue(parsed["dash_year"]) ?? "").replace(/^[-:]/, "");
984
+ if (/^\d{4}$/.test(dashYear))
985
+ attributes["year"] = dashYear;
986
+ }
888
987
  // The IEEE-led tail word-stage ("IEEE/ISO/IEC P15026-3/CD,
889
988
  // December 2021") composes the D= designator with the glued
890
989
  // 6-digit date — the identity its D= twin spelling parses to (the
@@ -925,7 +1024,22 @@ class IeeeBuilder {
925
1024
  }
926
1025
  if (parsed["draft_version"] !== undefined) {
927
1026
  const draftVer = (extractValue(parsed["draft_version"]) ?? "").replace(/^D/, "");
928
- if (draftVer !== "") {
1027
+ // FCD is an ISO stage (the 2026-10-19 ruling): a glued month-name
1028
+ // date rides the designator's date slot with the month numeric —
1029
+ // the D= designator face, never the "DFCD" glue.
1030
+ const stageDate = /^(FDIS|FCD|CDV|DIS|CD|WD|PWI|NP)(\d+)?(?:-([A-Za-z]+)(\d{4}))?$/.exec(draftVer);
1031
+ if (stageDate !== null) {
1032
+ const monthNum = stageDate[3] !== undefined ? DRAFT_MONTH_NAMES[stageDate[3]] : undefined;
1033
+ const stageWord = stageDate[1] + (stageDate[2] ?? "");
1034
+ attributes["ieee_draft"] =
1035
+ stageDate[3] !== undefined && monthNum !== undefined
1036
+ ? `D=${stageWord}-${stageDate[4]}${monthNum.padStart(2, "0")}`
1037
+ : `D=${stageWord}`;
1038
+ delete attributes["draft"];
1039
+ delete attributes["year"];
1040
+ delete attributes["month"];
1041
+ }
1042
+ else if (draftVer !== "") {
929
1043
  // A date trailing the draft (", June 2010") belongs to the draft
930
1044
  // component (canonical month, comma form); the identity
931
1045
  // year/month the promotion filled from the same captures are
@@ -1115,11 +1229,45 @@ class IeeeBuilder {
1115
1229
  }
1116
1230
  return new StandardClass(printedAttrs);
1117
1231
  }
1232
+ // A stage word with a glued year tail ("/D=CD2-201802") rides the
1233
+ // D= designator with the IEEE lead (the reference's builder, the
1234
+ // pubid#203 class) — the tail's capture carries its separator.
1235
+ if (parsed["iso_stage"] !== undefined && parsed["draft_stage_year"] !== undefined) {
1236
+ const stageWord = extractValue(parsed["iso_stage"]) ?? "";
1237
+ const pubs = (attributes["publishers"] ?? []);
1238
+ if (pubs.length === 0) {
1239
+ attributes["publishers"] = ["IEEE"];
1240
+ attributes["publisher"] = "IEEE";
1241
+ attributes["copublisher"] = [];
1242
+ }
1243
+ attributes["ieee_draft"] = `D=${stageWord}${extractValue(parsed["draft_stage_year"]) ?? ""}`;
1244
+ const stageRecord = locateStage(stageWord);
1245
+ if (stageRecord !== undefined) {
1246
+ attributes["typed_stage"] = new IeeeTypedStage(stageRecord);
1247
+ }
1248
+ attributes["lead_party"] = "IEEE";
1249
+ delete attributes["year"];
1250
+ delete attributes["month"];
1251
+ }
1118
1252
  if (parsed["iso_stage"] !== undefined) {
1119
- attributes["lead_party"] = "ISO";
1120
- const stageAbbr = extractValue(parsed["iso_stage"]);
1121
- attributes["iso_stage"] = stageAbbr;
1122
- if (stageAbbr !== undefined) {
1253
+ // A bare-IEEE project carrying a stage never elects the ISO lead
1254
+ // (the reference's bare_ieee_project gate): the lead stays IEEE.
1255
+ const bareIeeeProject = parsed["project_marker"] !== undefined &&
1256
+ attributes["publisher"] === "IEEE" &&
1257
+ (attributes["copublisher"] ?? []).length === 0;
1258
+ if (!bareIeeeProject)
1259
+ attributes["lead_party"] = "ISO";
1260
+ let stageAbbr = extractValue(parsed["iso_stage"]);
1261
+ // The IEC "/V<version>" iteration rides glued onto the stage word
1262
+ // ("_CDV/V3" -> "CDV3") — the family's lossless spelling (the
1263
+ // reference's builder, pubid#477).
1264
+ const stageIteration = extractValue(parsed["iso_stage_iteration"]);
1265
+ if (stageAbbr !== undefined && stageIteration !== undefined) {
1266
+ stageAbbr += stageIteration;
1267
+ }
1268
+ if (!bareIeeeProject)
1269
+ attributes["iso_stage"] = stageAbbr;
1270
+ if (stageAbbr !== undefined && !bareIeeeProject) {
1123
1271
  const record = locateStage(stageAbbr);
1124
1272
  if (record !== undefined)
1125
1273
  attributes["typed_stage"] = new IeeeTypedStage(record);
@@ -1185,6 +1333,18 @@ class IeeeBuilder {
1185
1333
  }
1186
1334
  return new ProjectDraftIdentifierClass(projectAttrs);
1187
1335
  }
1336
+ // The iso_published joint rows normalize the publisher order to
1337
+ // ISO-first (the reference: "IEEE/ISO/IEC 8802-3:2021/Amd 4-2021"
1338
+ // builds the base [ISO, IEC, IEEE], lead ISO); the CD-stage rows and
1339
+ // the IEEE-led project rows preserve the printed order (P90003 stays
1340
+ // IEEE/ISO/IEC).
1341
+ if (parsed["iso_published"] !== undefined && parsed["iso_stage"] === undefined &&
1342
+ Array.isArray(attributes["publishers"])) {
1343
+ const pubs = attributes["publishers"];
1344
+ attributes["publishers"] = [...pubs.filter((p) => p !== "IEEE"), ...pubs.filter((p) => p === "IEEE")];
1345
+ attributes["publisher"] = attributes["publishers"][0];
1346
+ attributes["copublisher"] = attributes["publishers"].slice(1);
1347
+ }
1188
1348
  const joint = new JointDevelopmentClass(attributes);
1189
1349
  if (parsed["amd_number"] === undefined)
1190
1350
  return joint;
@@ -1114,8 +1114,9 @@ export const JointDevelopmentClass = ieeeClass({ kind: "joint-development" }, {
1114
1114
  iso_stage: { type: "string" },
1115
1115
  ieee_draft: { type: "string" },
1116
1116
  parenthetical_content: { type: "string" },
1117
+ draft_status: { type: "string" },
1117
1118
  },
1118
- extraMappings: keyValue({ wire: "publishers", to: "publishers" }, { wire: "lead_party", to: "lead_party" }, { wire: "iso_stage", to: "iso_stage" }, { wire: "ieee_draft", to: "ieee_draft" }, { wire: "parenthetical_content", to: "parenthetical_content" }),
1119
+ extraMappings: keyValue({ wire: "publishers", to: "publishers" }, { wire: "lead_party", to: "lead_party" }, { wire: "iso_stage", to: "iso_stage" }, { wire: "ieee_draft", to: "ieee_draft" }, { wire: "parenthetical_content", to: "parenthetical_content" }, { wire: "draft_status", to: "draft_status" }),
1119
1120
  wireDelete: { keys: ["publisher", "copublisher"] },
1120
1121
  render: (id) => {
1121
1122
  const publishers = id["publishers"];
@@ -1194,6 +1195,11 @@ export const JointDevelopmentClass = ieeeClass({ kind: "joint-development" }, {
1194
1195
  const parts = [];
1195
1196
  if (publishers !== undefined && publishers.length > 0)
1196
1197
  parts.push(publishers.join("/"));
1198
+ // A status word ("Unapproved") marks the draft (pubid#318) — it
1199
+ // prints after the publishers on the joint IEEE face.
1200
+ const draftStatus = id["draft_status"];
1201
+ if (draftStatus !== undefined)
1202
+ parts.push(draftStatus);
1197
1203
  // The P-state prints as spelled (P = project draft; no P =
1198
1204
  // standard). Never added or stripped.
1199
1205
  let codeStr = codeObj?.render() ?? "";
@@ -226,14 +226,11 @@ function normalizeRelatonSuffixes(input) {
226
226
  cleaned = `${m[1]}${date}${draftPart}/R-${m[3]}`;
227
227
  }
228
228
  }
229
- // /D-N drafts with a trailing numeric date: reposition onto the number.
230
- {
231
- const re = /^(.*)\/D-([0-9A-Za-z][0-9A-Za-z.+]*?)-((?:19|20)\d\d)(?:-(0[1-9]|1[0-2]))?$/;
232
- const m = re.exec(cleaned);
233
- if (m !== null) {
234
- cleaned = `${m[1]}-${m[3]}${m[4] !== undefined ? `-${m[4]}` : ""}/D${m[2]}`;
235
- }
236
- }
229
+ // The "/D-N-YYYY" draft-dash-date spelling is NOT repositioned: the
230
+ // artifact grammar reads it natively (draft + printed_dash_year) and
231
+ // the joint route renders the reference's dash-year face. The former
232
+ // reposition onto the number predated the artifact and broke the
233
+ // joint classification (the year left the designator's slot).
237
234
  // /E-N editions: "/E-2-2023-02" → "Edition 2.0 2023-02".
238
235
  {
239
236
  const re = /^(.*?)\/E-(\d+)(?:-((?:19|20)\d\d)(?:-(0[1-9]|1[0-2]))?)?$/;
@@ -255,10 +252,14 @@ function normalizeRelatonSuffixes(input) {
255
252
  }
256
253
  function normalizeRevisionNotation(input) {
257
254
  let cleaned = input;
258
- // Numbered revisions preserved, repositioned to "/R-<n>".
259
- cleaned = cleaned.replace(/(\/D-?[0-9A-Za-z.]+)\s+[Rr][Ee][Vv]\s*(\d+)/, "$1/R-$2");
260
- cleaned = cleaned.replace(/[-/_.]?\s?[Rr][Ee][Vv][-\s]?(\d+)(\/D-?[0-9A-Za-z.]+)/, "$2/R-$1");
261
- cleaned = cleaned.replace(/(\d)[-._]?\s?[Rr][Ee][Vv]\s*(\d+)\s*$/, "$1/R-$2");
255
+ // The reference's two glue-normalizing gsubs (parser.rb): a "REV.x"/
256
+ // "REV-x" after-word separator and a digit-glued "2rev1" normalize to
257
+ // a spaced boundary; the render keeps the glued face. The former
258
+ // /R-<n> reposition rules predate the artifact grammar and diverged
259
+ // from the reference (they ate "-rev1" spellings the artifact
260
+ // captures natively, which cost the glued-date rows their parse).
261
+ cleaned = cleaned.replace(/(REV|Rev|rev)[.\-](?=[A-Za-z])/g, "$1");
262
+ cleaned = cleaned.replace(/(\d)(REV|Rev|rev)/g, "$1 $2");
262
263
  // The joint IEEE/CSA catalogue spells the type word before the
263
264
  // project number ("IEEE/CSA Draft P844.1/D2, Sept, 2016"): the P
264
265
  // already carries the project semantics (docs/IEEE-DRAFT-STAGES.md
@@ -322,6 +323,12 @@ function normalizeJointStageSpellings(input) {
322
323
  // dash-date, and rewriting it onto the number breaks the parse.
323
324
  cleaned = cleaned.replace(new RegExp(`^${pubs}${num}/ ?${stage}(-(?:19|20)\\d\\d(?:-\\d\\d)?)`), "$1$3 $2$4");
324
325
  cleaned = cleaned.replace(new RegExp(`^${pubs}${stage} ${num}/? ?(D\\d+)\\b`), "$1$2 $3/$4");
326
+ // The dash-stage-with-part spelling ("ISO/IEC/IEEE P15288-DIS-1403",
327
+ // "… P12207-FDIS-1707, June 2017"): the stage glues between the number
328
+ // and the part. The reference renders the stage-first dot face
329
+ // ("DIS P15288.1403") — re-spell so the stage-first route parses it
330
+ // (a stage WITHOUT a trailing part, "P29119-2-DIS", routes elsewhere).
331
+ cleaned = cleaned.replace(new RegExp(`^${pubs}P?(\\d+)-(FDIS|DIS|CDV|CD|WD)(\\d*)-(\\d+(?:\\.\\d+)*)((?:, [A-Za-z]+ \\d{4})?)$`), "$1$3$4 P$2.$5$6");
325
332
  return cleaned;
326
333
  }
327
334
  // The Parser.parse cleaning pipeline, in Ruby order.
@@ -341,11 +348,19 @@ export function preprocessIeee(input) {
341
348
  cleaned = cleaned.replaceAll("&#x2019;", "'");
342
349
  cleaned = cleaned.replaceAll("&amp;amp;", "&");
343
350
  cleaned = cleaned.replaceAll("&amp;", "&");
344
- cleaned = cleaned.replace(/\s+(P&V)\s*$/, " ($1)");
351
+ // The "P&V" print tail is NOT parenthesized: the artifact captures it
352
+ // and the builder discards it (the reference renders "ANSI/IEEE
353
+ // 500-1984" bare) — the former wrap made the parenthetical print.
345
354
  cleaned = cleaned.replace(/(\d+\.\d+)\s+(\d+\.)/g, "$1$2");
346
355
  cleaned = cleaned.replace(/\b(1|2)\s+(\d{3})\b/g, "$1$2");
347
- cleaned = cleaned.replace(/\b(January|February|March|April|May|June|July|August|September|October|November|December)(\d{4})\b/g, "$1 $2");
348
- cleaned = cleaned.replace(/\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)(\d{4})\b/g, "$1 $2");
356
+ // The month-year glue split ("June2010" -> "June 2010") is NOT the
357
+ // reference's behavior — the reference keeps the glue (the FCD ruling:
358
+ // "FCD-June2010" is the stage + the glued month-name date), and the
359
+ // split cost the FCD rows their classification.
360
+ // The 8802-family IEEE-led joint prefix reorders to the ISO/IEC/IEEE
361
+ // label (the reference's Fix 2AA): "IEEE/ISO/IEC 8802-1Q-2020" parses
362
+ // under the ISO/IEC label and the amendment route renders ISO-first.
363
+ cleaned = cleaned.replace(/^IEEE\/ISO\/IEC\s+(8802[\w.-]+)/, "ISO/IEC/IEEE $1");
349
364
  cleaned = normalizeJointStageSpellings(cleaned);
350
365
  cleaned = cleaned.replace(/(IEC\s+\d+(?:-\d+)?(?::\d{4})?(?:\s+(?:First\s+)?[Ee]dition\s+\d+(?:\.\d+)?\s+\d{4}-\d{2})?)\s+(IEEE\s+Std\s+\S+|IEEE\s+\S+)/g, "$1; $2");
351
366
  cleaned = cleaned.replace(/^(IEC\s+\d+(?:-\d+)?):\d{4}(\s+(?:First\s+)?[Ee]dition\s+\d+(?:\.\d+)?\s+\d{4}-\d{2})/, "$1$2");
@@ -99,14 +99,12 @@ export const UPDATE_CODES = [
99
99
  { plain: "ISO/IEC/IEEE CD P26513/D1, August 2015", replacement: "ISO/IEC/IEEE P26513/D1, August 2015" },
100
100
  { plain: "IEEE Std P1073.1.3.4/D3.0", replacement: "IEEE Std P11073.00101" },
101
101
  { plain: "IEEE P802.1Qbu/03.0, July 2015", replacement: "IEEE P802.1Qbu/D-3.0-2015" },
102
- { plain: "IEEE P802.11aqTM/013.0 October 2017", replacement: "IEEE P802.11aqTM/D-13.0-2017" },
103
102
  { plain: "IEC/IEEE P62271-37-013:2015 D13.4", replacement: "IEC/IEEE P62271.37-013/D-13.4-2015" },
104
103
  { plain: "ISO/IEC/IEEE DIS P42020:201x(E), June 2017", replacement: "ISO/IEC/IEEE DIS P42020-2017-06" },
105
104
  { plain: "ISO/IEC/IEEE DIS P16326/201x, December 2018", replacement: "ISO/IEC/IEEE DIS P16326-2018-12" },
106
105
  { plain: "ISO/IEC/IEEE/P21839, 2019(E)", replacement: "ISO/IEC/IEEE P21839-2019" },
107
106
  { plain: "ISO/IEC/IEEE CD2 P12207-2: 201x(E), February 2019", replacement: "ISO/IEC/IEEE CD2 P12207.2-2019-02" },
108
107
  { plain: "ISO/IEC/IEEE P42010.WD4:2019(E)", replacement: "ISO/IEC/IEEE P42010/D-4-2019" },
109
- { plain: "IEC/IEEE P63195_CDV/V3, February 2020", replacement: "IEC/IEEE CDV P63195-2020-02" },
110
108
  { plain: "ISO/IEC/IEEE/P16085_DIS, March 2020", replacement: "ISO/IEC/IEEE DIS P16085-2020-03" },
111
109
  { plain: "ANSI/IEEE Std: Outdoor Apparatus Bushings", replacement: "ANSI/IEEE Std 21-1976-11" },
112
110
  { plain: "Draft National Electrical Safety Code, January 2016", replacement: "IEEE PC2-2016-01" },
@@ -185,6 +185,7 @@ const yearMapping = () => ({
185
185
  : new PubidDate({
186
186
  year: String(h["year"]),
187
187
  month: h["month"] === undefined || h["month"] === null ? undefined : String(h["month"]),
188
+ day: h["day"] === undefined || h["day"] === null ? undefined : String(h["day"]),
188
189
  }),
189
190
  });
190
191
  const monthMapping = () => ({
@@ -193,13 +194,21 @@ const monthMapping = () => ({
193
194
  toWire: (m) => m["date"]?.month,
194
195
  fromWire: () => undefined,
195
196
  });
197
+ // An Operational Bulletin's printed day ("15.III.2016") serializes flat,
198
+ // mirroring the reference's hash ("year"/"month"/"day" siblings).
199
+ const dayMapping = () => ({
200
+ wire: "day",
201
+ to: "date",
202
+ toWire: (m) => m["date"]?.day,
203
+ fromWire: () => undefined,
204
+ });
196
205
  /** StandardSerialization — the single-document whitelist. */
197
206
  const STANDARD_MAPPINGS = keyValue({ wire: "sector", to: "sector" }, { wire: "series", to: "series" }, codeKey("imp_marker", (c) => c.imp_marker), {
198
207
  wire: "number",
199
208
  to: "code",
200
209
  toWire: (m) => codeOf(m)?.number,
201
210
  fromWire: (h) => ituCodeFromHash(h),
202
- }, codeKey("series_suffix", (c) => c.series_suffix), codeKey("series_suffix_spaced", (c) => (c.series_suffix_spaced ? true : undefined)), codeKey("subseries", (c) => c.subseries), codeKey("parts", (c) => (c.parts.length > 0 ? c.parts : undefined)), codeKey("qualifier", (c) => c.qualifier), codeKey("qualifier_glued", (c) => (c.qualifier_glued ? true : undefined)), { wire: "series_word", to: "series_word" }, { wire: "series_dash", to: "series_dash" }, { wire: "attachment", to: "attachment" }, { wire: "range_end", to: "range_end" }, { wire: "version", to: "version" }, yearMapping(), monthMapping(), { wire: "language", to: "language" }, { wire: "common_text_twin", to: "common_text_twin" });
211
+ }, codeKey("series_suffix", (c) => c.series_suffix), codeKey("series_suffix_spaced", (c) => (c.series_suffix_spaced ? true : undefined)), codeKey("subseries", (c) => c.subseries), codeKey("parts", (c) => (c.parts.length > 0 ? c.parts : undefined)), codeKey("qualifier", (c) => c.qualifier), codeKey("qualifier_glued", (c) => (c.qualifier_glued ? true : undefined)), { wire: "series_word", to: "series_word" }, { wire: "series_dash", to: "series_dash" }, { wire: "attachment", to: "attachment" }, { wire: "range_end", to: "range_end" }, { wire: "version", to: "version" }, yearMapping(), monthMapping(), dayMapping(), { wire: "language", to: "language" }, { wire: "common_text_twin", to: "common_text_twin" });
203
212
  /**
204
213
  * The Supplement block: sector/series/series_word are emitted ONLY for
205
214
  * the base-less series-only form; with a base they are redundant
@@ -373,9 +373,10 @@ function cast(type, value, h, policy, series = "") {
373
373
  return undefined;
374
374
  const stripped = v.trim();
375
375
  const code = TRANSLATION_MAP[stripped.toLowerCase()] ?? stripped.toLowerCase();
376
- // The gem's translation attr is the component; its to_s prefixes
377
- // the space (" .uppl"), and the wire serializes that form.
378
- return { translation_component: new NistTranslation({ code }), translation: ` ${code}` };
376
+ // The gem's Translation#to_s (short) renders the parenthesised
377
+ // suffix — the 1.x corpus shape ("(spa)", pubid#497); the wire
378
+ // serializes that form.
379
+ return { translation_component: new NistTranslation({ code }), translation: `(${code})` };
379
380
  }
380
381
  case "version": {
381
382
  const v = s(value);
@@ -157,8 +157,10 @@ export class NistTranslation {
157
157
  toWire() {
158
158
  return { code: this.code };
159
159
  }
160
+ // The 1.x corpus shape: the trailing parenthesised suffix "(spa)"
161
+ // (pubid#497); the mr face keeps the dotted form (".spa").
160
162
  render(format = "short") {
161
- return format === "mr" ? `.${this.code}` : ` ${this.code}`;
163
+ return format === "mr" ? `.${this.code}` : `(${this.code})`;
162
164
  }
163
165
  }
164
166
  export class NistStage {
@@ -467,7 +469,7 @@ export class NistIdentifier extends BaseIdentifier {
467
469
  if (this.errata !== undefined)
468
470
  result += this.errata;
469
471
  if (this.index !== undefined)
470
- result += "index";
472
+ result += "indx";
471
473
  if (this.insert !== undefined)
472
474
  result += "insert";
473
475
  if (this.section !== undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubid/pubid",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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",