@pubid/pubid 0.1.14 → 0.1.16

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.
Binary file
@@ -576,7 +576,10 @@ class IeeeBuilder {
576
576
  if (version !== undefined)
577
577
  version = version.replace(/^-/, "").replace(/^0(?=\d+\.\d)/, "");
578
578
  }
579
- revision = extractValue(draftData["revision"]);
579
+ // The revision capture carries its own leading dot (".1" — the
580
+ // #471 separator-in-capture rule); Draft#to_s prints the dot
581
+ // itself, so keep only the tail or "D6..1" renders doubled.
582
+ revision = extractValue(draftData["revision"])?.replace(/^\./, "");
580
583
  isoStage = extractValue(draftData["draft_iso_stage"]);
581
584
  isoIteration = extractValue(draftData["draft_iso_iteration"]);
582
585
  // FDIS/CDV/… are ISO/IEC stage tokens, not draft ordinals (C1
@@ -1231,9 +1234,16 @@ class IeeeBuilder {
1231
1234
  }
1232
1235
  // A stage word with a glued year tail ("/D=CD2-201802") rides the
1233
1236
  // 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
+ // pubid#203 class) — the tail's capture carries its separator. The
1238
+ // date-less form ("/D=CD1") elects the same face only when the input
1239
+ // spells the designator (the reference's original_input gate) — the
1240
+ // stage-first rows keep their ISO rendering.
1241
+ const dEqInput = this.originalInput.includes("/D=");
1242
+ const stageWordForGate = extractValue(parsed["iso_stage"]) ?? "";
1243
+ if (parsed["iso_stage"] !== undefined &&
1244
+ (parsed["draft_stage_year"] !== undefined ||
1245
+ (dEqInput && /\d$/.test(stageWordForGate)))) {
1246
+ const stageWord = stageWordForGate;
1237
1247
  const pubs = (attributes["publishers"] ?? []);
1238
1248
  if (pubs.length === 0) {
1239
1249
  attributes["publishers"] = ["IEEE"];
@@ -190,13 +190,16 @@ export class IeeeDraft {
190
190
  // ordinal stays short (one or two digits): a glued D= date
191
191
  // ("-201908", the pubid#203 convention) must ride the version body
192
192
  // verbatim, not fold into the iteration slot.
193
- const compound = /=D?(PWI|NP|WD|CDV|FDIS|DIS|CD)(?:[.-]?(\d{1,2}[a-z]?))?$/;
193
+ const compound = /=D?(PWI|NP|WD|CDV|FDIS|DIS|CD)(\d{1,2}[a-z]?)?(?:[.-](\d{1,2}[a-z]?))?$/;
194
194
  let isoStage;
195
195
  let isoIteration;
196
196
  const cm = compound.exec(body);
197
197
  if (cm !== null) {
198
- isoStage = cm[1];
199
- isoIteration = cm[2];
198
+ // The glued digits ride the STAGE ("D=CD1", matching the reference's
199
+ // "D=#{stage}" composition); only an explicit separator opens the
200
+ // ".iter" slot ("=DIS.3").
201
+ isoStage = cm[1] + (cm[2] ?? "");
202
+ isoIteration = cm[3];
200
203
  body = body.slice(0, cm.index);
201
204
  }
202
205
  const monthAlt = Object.keys(DRAFT_MONTH_NAMES)
@@ -312,7 +312,10 @@ function normalizeJointStageSpellings(input) {
312
312
  const stage = "(FDIS|FCD|CDV|DIS\\d?|CD\\d?|WD|PWI|NP)";
313
313
  const num = "(P?\\d+(?:[.-]\\d+)?)";
314
314
  const tail = "(,? (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)[a-z]* \\d{4}|$|-\\d{4}(?:-\\d\\d)?)";
315
- cleaned = cleaned.replace(/(\d)_D(\d)/g, "$1/D$2");
315
+ // The "(\d)_D(\d)" underscore rewrite is NOT here: the reference's
316
+ // Fix 2G/2S are ANCHORED to their publisher shapes and cannot span a
317
+ // slash ("PC37.60/DP62271-111_D6.1" keeps its underscore) — the
318
+ // unscoped rule rewrote rows the reference leaves verbatim.
316
319
  cleaned = cleaned.replace(new RegExp(`^${pubs}${num}/(D[\\d.]+)/ ?${stage}\\b`), "$1$4 $2/$3");
317
320
  cleaned = cleaned.replace(new RegExp(`^${pubs}${num}/ ${stage}${tail}`), "$1$3 $2$4");
318
321
  cleaned = cleaned.replace(new RegExp(`^${pubs}${num}/${stage} (\\d{4})\\b`), "$1$3 $2-$4");
@@ -116,7 +116,10 @@ function buildRules() {
116
116
  .then(str("r"), letter.as("letter"))
117
117
  .as("revision_letter"), str("r").then(letter).as("revision_letter_suffix"), str("r")
118
118
  .then(digits.then(dot.then(digits).maybe()).as("edition_id"))
119
- .as("revision_simple"), str("NCNR"), str("PERMIS"), str("BFRL"), upperLetter.repeat(1, 3), rules["digits_with_suffix"]
119
+ .as("revision_simple"), str("NCNR"), str("PERMIS"), str("BFRL"),
120
+ // A trailing lowercase means a capitalized word ("Suppl"),
121
+ // not a second number.
122
+ upperLetter.repeat(1, 3).then(lowerLetter.absent()), rules["digits_with_suffix"]
120
123
  .then(dash.then(monthAbbrev(), digits, slash).absent()), lowerLetter.then(digit.absent()))).as("second_number")));
121
124
  const dottedDigits = () => digits.then(dot.then(digits).maybe());
122
125
  const editionInner = () => space
@@ -241,9 +244,15 @@ function buildRules() {
241
244
  rule("addendum", () => (str("-add").or(str(".add")).or(str(" Add.")))
242
245
  .then((space.or(dash)).maybe(), digits.or(str("")).as("addendum_number"))
243
246
  .as("addendum"));
247
+ // The capitalized supplement words a letter slot must yield to.
248
+ rule("supplement_word", () => str("Suppl").or(str("Supp")).or(str("Sup")));
244
249
  rule("supplement", () => space
245
250
  .maybe()
246
- .then(str("supp").or(str("sup")))
251
+ .then(dot.maybe())
252
+ .then(
253
+ // The capitalized catalogue spellings ("NIST.SP.955.Suppl") ride
254
+ // the same slot; longest-first so "Suppl" is not "Sup"+"p".
255
+ str("Suppl").or(str("Supp")).or(str("Sup")).or(str("supp")).or(str("sup")))
247
256
  .then(str("rev")
248
257
  .as("supplement_with_rev")
249
258
  .or(monthAbbrev()
@@ -271,7 +280,9 @@ function buildRules() {
271
280
  .then(rules["publisher"], dot, rules["simple_series"],
272
281
  // The catalogue also lists bare series identities with no
273
282
  // report number ("NBS.CIRC").
274
- dot.maybe(), rules["report_number"].maybe(), str("_").then(digits.as("edition_year")).maybe(), upperLetter.maybe(), rules["edition"].maybe(), rules["update"].maybe(), dot.then(digits.or(upperLetter)).repeat(0, 3), rules["parts"].repeat(0, Infinity), rules["draft"].maybe()));
283
+ dot.maybe(), rules["report_number"].maybe(), str("_").then(digits.as("edition_year")).maybe(), rules["supplement_word"].absent().then(upperLetter.maybe()), rules["edition"].maybe(), rules["update"].maybe(), dot
284
+ .then(digits.or(rules["supplement_word"].absent().then(upperLetter)))
285
+ .repeat(0, 3), rules["parts"].repeat(0, Infinity), rules["draft"].maybe()));
275
286
  rule("identifier", () => ref(rules, "circ_supplement_identifier").or(rules["dated_identifier"], rules["mr_identifier"], rules["research_library_identifier"], rules["compound_series"]
276
287
  .then(space.or(dot), rules["old_stage"].maybe(), rules["report_number"].maybe(), rules["fips_date"].maybe(), rules["parts"].repeat(0, Infinity), rules["draft"].maybe(), rules["translation"].maybe(), rules["new_stage"].maybe()), rules["publisher"]
277
288
  .then(space.or(dot), rules["simple_series"], rules["old_stage"].maybe(), space.or(dot), rules["report_number"].maybe(), rules["fips_date"].maybe(), rules["parts"].repeat(0, Infinity), rules["draft"].maybe(), rules["translation"].maybe(), rules["new_stage"].maybe()), rules["simple_series"]
@@ -627,6 +627,16 @@ function castNumber(type, value, h, policy) {
627
627
  if (type === "second_number" && value["number_only"] !== undefined && value["edition_id"] !== undefined) {
628
628
  return { second_number: value };
629
629
  }
630
+ // The dotted revision spelling ("8278.r1"): fold to the edition,
631
+ // matching the glued "8278r1" canonical — the raw hash must not
632
+ // stringify into the number.
633
+ if (type === "second_number" && value["revision_simple"] !== undefined) {
634
+ const simple = value["revision_simple"];
635
+ const editionId = s(simple["edition_id"]) ?? "";
636
+ return {
637
+ edition: new NistEdition({ type: "r", id: editionId }),
638
+ };
639
+ }
630
640
  if (type === "second_number" && value["revision_letter"] !== undefined) {
631
641
  const revisionData = value["revision_letter"];
632
642
  const numberOnly = s(revisionData["number_only"]) ?? "";
@@ -49,7 +49,6 @@ const UPDATE_CODE_LINES = {
49
49
  "NBS.CIRC.supJun1925-Jun1926": "NBS CIRC 24e7sup2",
50
50
  "NBS.CIRC.supJun1925-Jun1927": "NBS CIRC 24e7sup3",
51
51
  "NIST.TN.2150-upd": "NIST.TN.2150-upd1-202102",
52
- "NIST.SP.955.Suppl": "NIST.SP.955-S.uppl",
53
52
  "NIST SP 782-1995-96": "NIST SP 782-1995",
54
53
  "NIST FIPS 54-Jan15": "NIST FIPS 54",
55
54
  "NBS report ;": "NBS RPT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pubid/pubid",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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",