@pubid/pubid 0.1.20 → 0.1.21
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.
- package/dist/urn/parsers.js +156 -50
- package/package.json +1 -1
package/dist/urn/parsers.js
CHANGED
|
@@ -1,10 +1,36 @@
|
|
|
1
1
|
import { splitParts, withYear } from "./types.js";
|
|
2
|
-
import { grammarImplementation } from "../flavors/index.js";
|
|
3
|
-
import { NationalAdoption } from "../flavors/evs/model.js";
|
|
4
2
|
// Each parser mirrors lib/pubid/<flavor>/urn_parser.rb#parse_urn exactly —
|
|
5
3
|
// including its lossiness (dropped segments stay dropped) and its error
|
|
6
4
|
// cases. The parity fixture (test/fixtures/urn-parity.json) pins the
|
|
7
5
|
// reference behavior for ~93k corpus URNs.
|
|
6
|
+
// The adopted CEN spine consumes its own supplements; their urn segments
|
|
7
|
+
// carry the consolidation marker ("plus" = "+A1", its absence = "/A1") —
|
|
8
|
+
// cen_cenelec/urn_generator.rb's supplement_segments.
|
|
9
|
+
function adoptedSupplement(parts) {
|
|
10
|
+
let faces = "";
|
|
11
|
+
while (["plus", "slash", "amd", "cor", "frag"].includes(parts[0] ?? "")) {
|
|
12
|
+
let sep = "/";
|
|
13
|
+
let kind;
|
|
14
|
+
if (parts[0] === "plus" || parts[0] === "slash") {
|
|
15
|
+
sep = parts.shift() === "plus" ? "+" : "/";
|
|
16
|
+
kind = parts.shift();
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
kind = parts.shift();
|
|
20
|
+
}
|
|
21
|
+
const num = /^\d{1,3}$/.test(parts[0] ?? "") ? parts.shift() : undefined;
|
|
22
|
+
const yr = /^\d{4}$/.test(parts[0] ?? "") ? parts.shift() : undefined;
|
|
23
|
+
const iter = num ?? "";
|
|
24
|
+
const year = yr !== undefined ? `:${yr}` : "";
|
|
25
|
+
if (kind === "amd")
|
|
26
|
+
faces += `${sep}A${iter}${year}`;
|
|
27
|
+
else if (kind === "cor")
|
|
28
|
+
faces += `${sep}AC${iter}${year}`;
|
|
29
|
+
else
|
|
30
|
+
faces += ` FRAG${iter}`;
|
|
31
|
+
}
|
|
32
|
+
return faces;
|
|
33
|
+
}
|
|
8
34
|
export const URN_PARSERS = {
|
|
9
35
|
// lib/pubid/ansi/urn_parser.rb
|
|
10
36
|
ansi: {
|
|
@@ -459,64 +485,111 @@ export const URN_PARSERS = {
|
|
|
459
485
|
return parse(text);
|
|
460
486
|
},
|
|
461
487
|
},
|
|
462
|
-
// lib/pubid/evs/urn_parser.rb —
|
|
463
|
-
//
|
|
488
|
+
// lib/pubid/evs/urn_parser.rb — reassemble the identifier's human text
|
|
489
|
+
// (draft prefix, adopted org, national annex, language, supplements)
|
|
490
|
+
// and delegate to the EVS grammar, exactly like the reference.
|
|
464
491
|
evs: {
|
|
465
492
|
prefix: "urn:evs:",
|
|
466
493
|
reconstruct(body, parse) {
|
|
467
494
|
const parts = splitParts(body);
|
|
468
|
-
const
|
|
469
|
-
if (
|
|
470
|
-
|
|
471
|
-
}
|
|
495
|
+
const draft = parts[0] === "pr";
|
|
496
|
+
if (draft)
|
|
497
|
+
parts.shift();
|
|
472
498
|
const orgs = {
|
|
473
499
|
iso: "ISO",
|
|
474
500
|
"iso-iec": "ISO/IEC",
|
|
475
501
|
iec: "IEC",
|
|
476
502
|
cispr: "CISPR",
|
|
477
503
|
};
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
504
|
+
const types = { ts: "TS", tr: "TR" };
|
|
505
|
+
// The trailing language and the EVS-level national annex peel
|
|
506
|
+
// before the body dispatch — both are EVS-level, never CEN
|
|
507
|
+
// segments.
|
|
508
|
+
let language;
|
|
509
|
+
const last = parts[parts.length - 1];
|
|
510
|
+
if (parts.length > 1 && last !== undefined && /^(et-en|et|en)$/.test(last)) {
|
|
511
|
+
language = parts.pop();
|
|
481
512
|
}
|
|
482
|
-
let
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
513
|
+
let naPresent = false;
|
|
514
|
+
let naYear;
|
|
515
|
+
const naIndex = parts.indexOf("na");
|
|
516
|
+
if (naIndex >= 0) {
|
|
517
|
+
parts.splice(naIndex, 1);
|
|
518
|
+
naPresent = true;
|
|
519
|
+
if (/^\d{4}$/.test(parts[naIndex] ?? ""))
|
|
520
|
+
naYear = parts.splice(naIndex, 1)[0];
|
|
488
521
|
}
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
522
|
+
const head = parts.shift() ?? "";
|
|
523
|
+
let text = draft ? "prEVS" : "EVS";
|
|
524
|
+
if (/^en$/i.test(head)) {
|
|
525
|
+
text += "-EN";
|
|
526
|
+
const orgParts = [];
|
|
527
|
+
while (parts[0] !== undefined && /^[a-z][a-z-]*$/.test(parts[0])) {
|
|
528
|
+
orgParts.push(parts.shift());
|
|
529
|
+
}
|
|
530
|
+
if (orgParts.length) {
|
|
531
|
+
const org = orgs[orgParts.join("-")];
|
|
532
|
+
if (org === undefined)
|
|
533
|
+
throw new Error(`unknown adopted org in ${JSON.stringify(body)}`);
|
|
534
|
+
text += ` ${org}`;
|
|
502
535
|
}
|
|
503
|
-
|
|
504
|
-
|
|
536
|
+
const number = parts.shift();
|
|
537
|
+
if (number === undefined)
|
|
538
|
+
throw new Error(`missing number in ${JSON.stringify(body)}`);
|
|
539
|
+
text += ` ${number}`;
|
|
540
|
+
const year = parts.shift();
|
|
541
|
+
if (year !== undefined)
|
|
542
|
+
text += `:${year}`;
|
|
543
|
+
text += adoptedSupplement(parts);
|
|
544
|
+
}
|
|
545
|
+
else if (head === "ts" || head === "tr") {
|
|
546
|
+
const number = parts.shift();
|
|
547
|
+
const year = parts.shift();
|
|
548
|
+
text += `-${types[head]} ${number}`;
|
|
549
|
+
if (year !== undefined)
|
|
550
|
+
text += `:${year}`;
|
|
551
|
+
}
|
|
552
|
+
else if (head === "iso" || head === "iec" || head === "cispr") {
|
|
553
|
+
const org = orgs[head];
|
|
554
|
+
const number = parts.shift();
|
|
555
|
+
let part;
|
|
556
|
+
if (/^\d+$/.test(parts[0] ?? "") && !/^\d{4}$/.test(parts[0]) && parts.length > 1) {
|
|
557
|
+
part = parts.shift();
|
|
505
558
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
559
|
+
const year = parts.shift();
|
|
560
|
+
text += `-${org} ${number}`;
|
|
561
|
+
if (part !== undefined)
|
|
562
|
+
text += `-${part}`;
|
|
563
|
+
if (year !== undefined)
|
|
564
|
+
text += `:${year}`;
|
|
509
565
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
566
|
+
else if (/^\d/.test(head)) {
|
|
567
|
+
const number = head;
|
|
568
|
+
const year = parts.shift();
|
|
569
|
+
text += ` ${number}`;
|
|
570
|
+
if (year !== undefined)
|
|
571
|
+
text += `:${year}`;
|
|
572
|
+
}
|
|
573
|
+
else {
|
|
574
|
+
throw new Error(`unsupported shape in ${JSON.stringify(body)}`);
|
|
575
|
+
}
|
|
576
|
+
if (parts.length)
|
|
577
|
+
throw new Error(`trailing segments in ${JSON.stringify(body)}`);
|
|
578
|
+
if (naPresent) {
|
|
579
|
+
text += "+NA";
|
|
580
|
+
if (naYear !== undefined)
|
|
581
|
+
text += `:${naYear}`;
|
|
582
|
+
}
|
|
583
|
+
if (language !== undefined)
|
|
584
|
+
text += ` ${language}`;
|
|
585
|
+
return parse(text);
|
|
516
586
|
},
|
|
517
587
|
},
|
|
518
|
-
// lib/pubid/nist/urn_parser.rb —
|
|
519
|
-
//
|
|
588
|
+
// lib/pubid/nist/urn_parser.rb — the generator dot-joins the identity's
|
|
589
|
+
// fields after the number; reassemble the human spelling the flavor
|
|
590
|
+
// parser reads (edition/volume glue onto the number, a revision glues
|
|
591
|
+
// after them, ".supp" marks supplementability, ".supp-<v>" is "sup<v>",
|
|
592
|
+
// insert/errata glue).
|
|
520
593
|
nist: {
|
|
521
594
|
prefix: "urn:nist:",
|
|
522
595
|
reconstruct(body, parse) {
|
|
@@ -530,17 +603,50 @@ export const URN_PARSERS = {
|
|
|
530
603
|
const typeToken = parts[0] ?? "";
|
|
531
604
|
const payload = parts[1] ?? "";
|
|
532
605
|
const label = typeUpcase[typeToken.toLowerCase()] ?? typeToken.toUpperCase();
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
606
|
+
const tokens = payload.split(".");
|
|
607
|
+
// Ruby's String#split drops trailing empty fields; JS keeps them, and a
|
|
608
|
+
// trailing "." would become a stray marker that the flavor rejects.
|
|
609
|
+
while (tokens.length > 0 && tokens[tokens.length - 1] === "")
|
|
610
|
+
tokens.pop();
|
|
611
|
+
const code = tokens.shift() ?? "";
|
|
612
|
+
let marker = "";
|
|
613
|
+
let part;
|
|
614
|
+
let edition;
|
|
615
|
+
let volume;
|
|
616
|
+
let revision;
|
|
617
|
+
let supplement;
|
|
618
|
+
for (const token of tokens) {
|
|
619
|
+
const supp = /^supp-(.+)$/is.exec(token);
|
|
620
|
+
if (/^r\d+$/i.test(token))
|
|
621
|
+
revision = token;
|
|
622
|
+
else if (/^e\w+$/i.test(token))
|
|
623
|
+
edition = token;
|
|
624
|
+
else if (/^v\d+$/i.test(token))
|
|
625
|
+
volume = token;
|
|
626
|
+
else if (supp !== null)
|
|
627
|
+
supplement = supp[1];
|
|
628
|
+
else if (token === "supp")
|
|
629
|
+
continue; // supplementability marker
|
|
630
|
+
else if (token === "insert" || token === "errata")
|
|
631
|
+
marker += token;
|
|
632
|
+
else if (/^[A-Z]$/.test(token))
|
|
633
|
+
part = token; // part letter, glued to the code
|
|
634
|
+
else if (/^[A-Z][a-z]+\d{4}$/.test(token)) {
|
|
635
|
+
// the edition's additional text ("13e2.June1908")
|
|
636
|
+
if (edition !== undefined)
|
|
637
|
+
edition = `${edition}.${token}`;
|
|
638
|
+
}
|
|
639
|
+
else
|
|
640
|
+
marker += `.${token}`;
|
|
641
|
+
}
|
|
537
642
|
// The NBS-era series carry the NBS imprint in their canonical form
|
|
538
643
|
// ("NBS CSM 1") — the rebuild names the publisher the document
|
|
539
644
|
// carries, or the flavor parse rejects its own URN's rebuild.
|
|
540
645
|
const publisher = typeToken.toLowerCase() === "csm" ? "NBS" : "NIST";
|
|
541
|
-
let text = `${publisher} ${label} ${code}`;
|
|
542
|
-
|
|
543
|
-
|
|
646
|
+
let text = `${publisher} ${label} ${code}${part ?? ""}${marker}`;
|
|
647
|
+
text += `${edition ?? ""}${volume ?? ""}${revision ?? ""}`;
|
|
648
|
+
if (supplement !== undefined)
|
|
649
|
+
text += `sup${supplement}`;
|
|
544
650
|
return parse(text);
|
|
545
651
|
},
|
|
546
652
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pubid/pubid",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
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",
|