@pubid/pubid 0.1.3 → 0.1.5
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/artifact/corpus.json.gz +0 -0
- package/dist/conformance/cli.js +5 -1
- package/dist/flavors/amca/grammar.d.ts +4 -0
- package/dist/flavors/amca/grammar.js +87 -0
- package/dist/flavors/amca/index.d.ts +4 -0
- package/dist/flavors/amca/index.js +76 -0
- package/dist/flavors/amca/model.d.ts +29 -0
- package/dist/flavors/amca/model.js +146 -0
- package/dist/flavors/ansi/index.d.ts +24 -0
- package/dist/flavors/ansi/index.js +147 -0
- package/dist/flavors/api/grammar.d.ts +4 -0
- package/dist/flavors/api/grammar.js +79 -0
- package/dist/flavors/api/index.d.ts +4 -0
- package/dist/flavors/api/index.js +71 -0
- package/dist/flavors/api/model.d.ts +27 -0
- package/dist/flavors/api/model.js +114 -0
- package/dist/flavors/ashrae/grammar.d.ts +2 -0
- package/dist/flavors/ashrae/grammar.js +667 -0
- package/dist/flavors/ashrae/index.d.ts +4 -0
- package/dist/flavors/ashrae/index.js +306 -0
- package/dist/flavors/ashrae/model.d.ts +87 -0
- package/dist/flavors/ashrae/model.js +335 -0
- package/dist/flavors/ashrae/preprocessor.d.ts +7 -0
- package/dist/flavors/ashrae/preprocessor.js +44 -0
- package/dist/flavors/asme/grammar.d.ts +2 -0
- package/dist/flavors/asme/grammar.js +200 -0
- package/dist/flavors/asme/index.d.ts +4 -0
- package/dist/flavors/asme/index.js +138 -0
- package/dist/flavors/asme/model.d.ts +34 -0
- package/dist/flavors/asme/model.js +123 -0
- package/dist/flavors/astm/grammar.d.ts +2 -0
- package/dist/flavors/astm/grammar.js +110 -0
- package/dist/flavors/astm/index.d.ts +2 -0
- package/dist/flavors/astm/index.js +127 -0
- package/dist/flavors/astm/model.d.ts +42 -0
- package/dist/flavors/astm/model.js +263 -0
- package/dist/flavors/bipm/index.d.ts +30 -0
- package/dist/flavors/bipm/index.js +434 -0
- package/dist/flavors/bsi/grammar.d.ts +4 -0
- package/dist/flavors/bsi/grammar.js +535 -0
- package/dist/flavors/bsi/index.d.ts +4 -0
- package/dist/flavors/bsi/index.js +941 -0
- package/dist/flavors/bsi/model.d.ts +127 -0
- package/dist/flavors/bsi/model.js +863 -0
- package/dist/flavors/calconnect/index.d.ts +21 -0
- package/dist/flavors/calconnect/index.js +113 -0
- package/dist/flavors/cen_cenelec/grammar.d.ts +4 -0
- package/dist/flavors/cen_cenelec/grammar.js +107 -0
- package/dist/flavors/cen_cenelec/index.d.ts +5 -0
- package/dist/flavors/cen_cenelec/index.js +263 -0
- package/dist/flavors/cen_cenelec/model.d.ts +45 -0
- package/dist/flavors/cen_cenelec/model.js +490 -0
- package/dist/flavors/cie/grammar.d.ts +5 -0
- package/dist/flavors/cie/grammar.js +127 -0
- package/dist/flavors/cie/index.d.ts +2 -0
- package/dist/flavors/cie/index.js +311 -0
- package/dist/flavors/cie/model.d.ts +122 -0
- package/dist/flavors/cie/model.js +464 -0
- package/dist/flavors/csa/grammar.d.ts +6 -0
- package/dist/flavors/csa/grammar.js +239 -0
- package/dist/flavors/csa/index.d.ts +2 -0
- package/dist/flavors/csa/index.js +634 -0
- package/dist/flavors/csa/model.d.ts +33 -0
- package/dist/flavors/csa/model.js +505 -0
- package/dist/flavors/doi/index.d.ts +13 -0
- package/dist/flavors/doi/index.js +65 -0
- package/dist/flavors/easc/index.d.ts +4 -0
- package/dist/flavors/easc/index.js +107 -0
- package/dist/flavors/ecma/index.d.ts +4 -0
- package/dist/flavors/ecma/index.js +117 -0
- package/dist/flavors/etsi/index.d.ts +14 -0
- package/dist/flavors/etsi/index.js +307 -0
- package/dist/flavors/evs/grammar.d.ts +2 -0
- package/dist/flavors/evs/grammar.js +22 -0
- package/dist/flavors/evs/index.d.ts +2 -0
- package/dist/flavors/evs/index.js +24 -0
- package/dist/flavors/evs/model.d.ts +19 -0
- package/dist/flavors/evs/model.js +36 -0
- package/dist/flavors/gb/index.d.ts +31 -0
- package/dist/flavors/gb/index.js +159 -0
- package/dist/flavors/gost/grammar.d.ts +2 -0
- package/dist/flavors/gost/grammar.js +46 -0
- package/dist/flavors/gost/index.d.ts +2 -0
- package/dist/flavors/gost/index.js +165 -0
- package/dist/flavors/gost/model.d.ts +61 -0
- package/dist/flavors/gost/model.js +147 -0
- package/dist/flavors/gost/prefix_routes.d.ts +15 -0
- package/dist/flavors/gost/prefix_routes.js +192 -0
- package/dist/flavors/iala/index.d.ts +32 -0
- package/dist/flavors/iala/index.js +300 -0
- package/dist/flavors/iana/index.d.ts +19 -0
- package/dist/flavors/iana/index.js +71 -0
- package/dist/flavors/idf/grammar.d.ts +2 -0
- package/dist/flavors/idf/grammar.js +71 -0
- package/dist/flavors/idf/index.d.ts +2 -0
- package/dist/flavors/idf/index.js +100 -0
- package/dist/flavors/idf/model.d.ts +54 -0
- package/dist/flavors/idf/model.js +359 -0
- package/dist/flavors/iec/grammar.d.ts +3 -0
- package/dist/flavors/iec/grammar.js +187 -0
- package/dist/flavors/iec/index.d.ts +4 -0
- package/dist/flavors/iec/index.js +507 -0
- package/dist/flavors/iec/model.d.ts +54 -0
- package/dist/flavors/iec/model.js +437 -0
- package/dist/flavors/ieee/grammar.d.ts +2 -0
- package/dist/flavors/ieee/grammar.js +697 -0
- package/dist/flavors/ieee/index.d.ts +3 -0
- package/dist/flavors/ieee/index.js +1258 -0
- package/dist/flavors/ieee/model.d.ts +144 -0
- package/dist/flavors/ieee/model.js +1237 -0
- package/dist/flavors/ieee/preprocessor.d.ts +8 -0
- package/dist/flavors/ieee/preprocessor.js +410 -0
- package/dist/flavors/ieee/update_codes.d.ts +8 -0
- package/dist/flavors/ieee/update_codes.js +197 -0
- package/dist/flavors/ietf/index.d.ts +30 -0
- package/dist/flavors/ietf/index.js +151 -0
- package/dist/flavors/iho/index.d.ts +4 -0
- package/dist/flavors/iho/index.js +126 -0
- package/dist/flavors/index.d.ts +9 -0
- package/dist/flavors/index.js +136 -0
- package/dist/flavors/isbn/index.d.ts +13 -0
- package/dist/flavors/isbn/index.js +97 -0
- package/dist/flavors/iso/grammar.d.ts +2 -0
- package/dist/flavors/iso/grammar.js +207 -0
- package/dist/flavors/iso/index.d.ts +2 -0
- package/dist/flavors/iso/index.js +343 -0
- package/dist/flavors/iso/model.d.ts +159 -0
- package/dist/flavors/iso/model.js +802 -0
- package/dist/flavors/iso/stages.generated.d.ts +13 -0
- package/dist/flavors/iso/stages.generated.js +391 -0
- package/dist/flavors/itu/grammar.d.ts +4 -0
- package/dist/flavors/itu/grammar.js +205 -0
- package/dist/flavors/itu/index.d.ts +2 -0
- package/dist/flavors/itu/index.js +224 -0
- package/dist/flavors/itu/model.d.ts +184 -0
- package/dist/flavors/itu/model.js +599 -0
- package/dist/flavors/jcgm/index.d.ts +7 -0
- package/dist/flavors/jcgm/index.js +300 -0
- package/dist/flavors/jis/index.d.ts +4 -0
- package/dist/flavors/jis/index.js +267 -0
- package/dist/flavors/nist/grammar.d.ts +2 -0
- package/dist/flavors/nist/grammar.js +287 -0
- package/dist/flavors/nist/index.d.ts +2 -0
- package/dist/flavors/nist/index.js +1244 -0
- package/dist/flavors/nist/model.d.ts +155 -0
- package/dist/flavors/nist/model.js +823 -0
- package/dist/flavors/nist/preprocessor.d.ts +11 -0
- package/dist/flavors/nist/preprocessor.js +281 -0
- package/dist/flavors/oasis/index.d.ts +23 -0
- package/dist/flavors/oasis/index.js +110 -0
- package/dist/flavors/ogc/index.d.ts +20 -0
- package/dist/flavors/ogc/index.js +75 -0
- package/dist/flavors/oiml/grammar.d.ts +2 -0
- package/dist/flavors/oiml/grammar.js +206 -0
- package/dist/flavors/oiml/implementation.d.ts +9 -0
- package/dist/flavors/oiml/implementation.js +45 -0
- package/dist/flavors/oiml/model.d.ts +23 -0
- package/dist/flavors/oiml/model.js +592 -0
- package/dist/flavors/omg/index.d.ts +14 -0
- package/dist/flavors/omg/index.js +73 -0
- package/dist/flavors/plateau/index.d.ts +4 -0
- package/dist/flavors/plateau/index.js +97 -0
- package/dist/flavors/tgpp/index.d.ts +4 -0
- package/dist/flavors/tgpp/index.js +122 -0
- package/dist/flavors/un/index.d.ts +13 -0
- package/dist/flavors/un/index.js +90 -0
- package/dist/flavors/w3c/index.d.ts +14 -0
- package/dist/flavors/w3c/index.js +148 -0
- package/dist/flavors/xsf/index.d.ts +18 -0
- package/dist/flavors/xsf/index.js +65 -0
- package/dist/grammar/engine.d.ts +78 -0
- package/dist/grammar/engine.js +351 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/model/attribute.d.ts +54 -0
- package/dist/model/attribute.js +53 -0
- package/dist/model/builder.d.ts +39 -0
- package/dist/model/builder.js +124 -0
- package/dist/model/component.d.ts +70 -0
- package/dist/model/component.js +170 -0
- package/dist/model/identifier.d.ts +84 -0
- package/dist/model/identifier.js +270 -0
- package/dist/model/urn-generator.d.ts +27 -0
- package/dist/model/urn-generator.js +91 -0
- package/package.json +1 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { match, str } from "../../grammar/engine.js";
|
|
2
|
+
import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
|
|
3
|
+
import { BaseIdentifier, registerType } from "../../model/identifier.js";
|
|
4
|
+
import { extendAttributes, keyValue } from "../../model/attribute.js";
|
|
5
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
6
|
+
import { BaseBuilder } from "../../model/builder.js";
|
|
7
|
+
const CYRILLIC_SERIES = { PMG: "ПМГ", RMG: "РМГ" };
|
|
8
|
+
function buildRules() {
|
|
9
|
+
const rules = {};
|
|
10
|
+
const rule = (name, build) => {
|
|
11
|
+
rules[name] = build();
|
|
12
|
+
};
|
|
13
|
+
const space = str(" ");
|
|
14
|
+
const digits = match("[0-9]").repeat(1, Infinity);
|
|
15
|
+
rule("year_sep", () => space
|
|
16
|
+
.maybe()
|
|
17
|
+
.then(str("-").or(str("—")).or(str("–")))
|
|
18
|
+
.then(space.maybe()));
|
|
19
|
+
rule("series_word", () => str("ПМГ")
|
|
20
|
+
.or(str("РМГ"))
|
|
21
|
+
.or(str("PMG"))
|
|
22
|
+
.or(str("RMG"))
|
|
23
|
+
.as("series")
|
|
24
|
+
.then(space));
|
|
25
|
+
rule("variant_word", () => str("В").or(str("V")).as("variant").then(space));
|
|
26
|
+
rule("number", () => digits.then(str(".").then(digits).repeat(0, Infinity)).as("number"));
|
|
27
|
+
rule("year", () => match("[0-9]").repeat(4, 4).or(match("[0-9]").repeat(2, 2)).as("year"));
|
|
28
|
+
rule("identifier", () => rules["series_word"]
|
|
29
|
+
.then(rules["variant_word"].maybe())
|
|
30
|
+
.then(rules["number"])
|
|
31
|
+
.then(rules["year_sep"].then(rules["year"]).maybe()));
|
|
32
|
+
rule("root", () => rules["identifier"]);
|
|
33
|
+
return rules;
|
|
34
|
+
}
|
|
35
|
+
export const eascGrammar = { rules: buildRules(), root: "root" };
|
|
36
|
+
function eascClass(series) {
|
|
37
|
+
class EascSeriesIdentifier extends BaseIdentifier {
|
|
38
|
+
static polymorphicName = `pubid:easc:${series.toLowerCase()}`;
|
|
39
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
40
|
+
series: { type: "string" },
|
|
41
|
+
variant: { type: "string" },
|
|
42
|
+
number: { type: "string" },
|
|
43
|
+
year: { type: "string" },
|
|
44
|
+
});
|
|
45
|
+
static mappings = keyValue({ wire: "series", to: "series" }, { wire: "variant", to: "variant" }, { wire: "number", to: "number" }, { wire: "year", to: "year" });
|
|
46
|
+
render() {
|
|
47
|
+
let result = CYRILLIC_SERIES[series];
|
|
48
|
+
if (this.variant === "V")
|
|
49
|
+
result += " В";
|
|
50
|
+
result += ` ${this.number}`;
|
|
51
|
+
if (this.year !== undefined)
|
|
52
|
+
result += `-${this.year}`;
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class EascUrnGenerator extends BaseUrnGenerator {
|
|
57
|
+
generate() {
|
|
58
|
+
const parts = ["urn", "easc", this.identifier.series.toLowerCase()];
|
|
59
|
+
if (this.identifier.variant !== undefined)
|
|
60
|
+
parts.push(this.identifier.variant.toLowerCase());
|
|
61
|
+
parts.push(this.identifier.number);
|
|
62
|
+
if (this.identifier.year !== undefined)
|
|
63
|
+
parts.push(this.identifier.year);
|
|
64
|
+
return parts.join(":");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
EascSeriesIdentifier.urnGenerator = EascUrnGenerator;
|
|
68
|
+
registerType(EascSeriesIdentifier);
|
|
69
|
+
return EascSeriesIdentifier;
|
|
70
|
+
}
|
|
71
|
+
const SERIES_CLASSES = {
|
|
72
|
+
PMG: eascClass("PMG"),
|
|
73
|
+
RMG: eascClass("RMG"),
|
|
74
|
+
};
|
|
75
|
+
class EascBuilder extends BaseBuilder {
|
|
76
|
+
selectClass(data) {
|
|
77
|
+
const raw = String(data["series"]).toUpperCase();
|
|
78
|
+
if (raw !== "ПМГ" && raw !== "PMG" && raw !== "РМГ" && raw !== "RMG") {
|
|
79
|
+
throw new ParseFailed(`EASC: unknown series ${raw}`, 0);
|
|
80
|
+
}
|
|
81
|
+
return SERIES_CLASSES[raw === "ПМГ" || raw === "PMG" ? "PMG" : "RMG"];
|
|
82
|
+
}
|
|
83
|
+
defaultIdentifierClass() {
|
|
84
|
+
return SERIES_CLASSES["PMG"];
|
|
85
|
+
}
|
|
86
|
+
cast(key, value) {
|
|
87
|
+
if (key === "series") {
|
|
88
|
+
const raw = String(value).toUpperCase();
|
|
89
|
+
return { series: raw === "ПМГ" || raw === "PMG" ? "PMG" : "RMG" };
|
|
90
|
+
}
|
|
91
|
+
if (key === "variant")
|
|
92
|
+
return { variant: "V" };
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export function eascGrammarImplementation() {
|
|
97
|
+
const builder = new EascBuilder();
|
|
98
|
+
return {
|
|
99
|
+
parse(input) {
|
|
100
|
+
const tree = parseGrammar(eascGrammar, input);
|
|
101
|
+
if (typeof tree !== "object" || tree === null || Array.isArray(tree)) {
|
|
102
|
+
throw new ParseFailed("EASC: unexpected parse tree", 0);
|
|
103
|
+
}
|
|
104
|
+
return builder.build(tree);
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { match, str } from "../../grammar/engine.js";
|
|
2
|
+
import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
|
|
3
|
+
import { BaseIdentifier, registerType } from "../../model/identifier.js";
|
|
4
|
+
import { extendAttributes, keyValue } from "../../model/attribute.js";
|
|
5
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
6
|
+
import { BaseBuilder } from "../../model/builder.js";
|
|
7
|
+
const KIND_PREFIXES = {
|
|
8
|
+
standard: undefined,
|
|
9
|
+
"technical-report": "TR",
|
|
10
|
+
memento: "MEM",
|
|
11
|
+
};
|
|
12
|
+
function buildRules() {
|
|
13
|
+
const rules = {};
|
|
14
|
+
const rule = (name, build) => {
|
|
15
|
+
rules[name] = build();
|
|
16
|
+
};
|
|
17
|
+
const digits = match("[0-9]").repeat(1, Infinity);
|
|
18
|
+
rule("prefix", () => str("ECMA"));
|
|
19
|
+
rule("tr", () => str(" TR/").then(digits.as("tr_number")));
|
|
20
|
+
rule("mem", () => str(" MEM/").then(digits.as("mem_number")));
|
|
21
|
+
rule("standard_separator", () => str("-").or(str(" ")));
|
|
22
|
+
rule("standard", () => rules["standard_separator"]
|
|
23
|
+
.then(digits.as("number"))
|
|
24
|
+
.then(str("-").then(digits.as("part")).maybe()));
|
|
25
|
+
rule("edition", () => str(" ed")
|
|
26
|
+
.then(digits.then(str(".").then(digits).repeat(0, Infinity)).as("edition")));
|
|
27
|
+
rule("volume", () => str(" vol").then(digits.as("volume")));
|
|
28
|
+
rule("identifier", () => rules["prefix"]
|
|
29
|
+
.then(rules["tr"].or(rules["mem"], rules["standard"]))
|
|
30
|
+
.then(rules["edition"].maybe())
|
|
31
|
+
.then(rules["volume"].maybe()));
|
|
32
|
+
rule("root", () => rules["identifier"]);
|
|
33
|
+
return rules;
|
|
34
|
+
}
|
|
35
|
+
export const ecmaGrammar = { rules: buildRules(), root: "root" };
|
|
36
|
+
function ecmaClass(kind) {
|
|
37
|
+
const prefix = KIND_PREFIXES[kind];
|
|
38
|
+
class EcmaKindIdentifier extends BaseIdentifier {
|
|
39
|
+
static polymorphicName = `pubid:ecma:${kind}`;
|
|
40
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
41
|
+
number: { type: "string" },
|
|
42
|
+
part: { type: "string" },
|
|
43
|
+
edition: { type: "string" },
|
|
44
|
+
volume: { type: "string" },
|
|
45
|
+
});
|
|
46
|
+
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "edition", to: "edition" }, { wire: "volume", to: "volume" });
|
|
47
|
+
render() {
|
|
48
|
+
let core;
|
|
49
|
+
if (prefix === undefined) {
|
|
50
|
+
core = `ECMA-${this.number}${this.part === undefined ? "" : `-${this.part}`}`;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
core = `ECMA ${prefix}/${this.number}`;
|
|
54
|
+
}
|
|
55
|
+
if (this.edition !== undefined)
|
|
56
|
+
core += ` ed${this.edition}`;
|
|
57
|
+
if (this.volume !== undefined)
|
|
58
|
+
core += ` vol${this.volume}`;
|
|
59
|
+
return core;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
class EcmaUrnGenerator extends BaseUrnGenerator {
|
|
63
|
+
generate() {
|
|
64
|
+
const parts = ["urn", "ecma"];
|
|
65
|
+
if (prefix !== undefined)
|
|
66
|
+
parts.push(prefix.toLowerCase());
|
|
67
|
+
parts.push(this.identifier.number);
|
|
68
|
+
if (this.identifier.part !== undefined)
|
|
69
|
+
parts.push(`part-${this.identifier.part}`);
|
|
70
|
+
if (this.identifier.edition !== undefined)
|
|
71
|
+
parts.push(`ed-${this.identifier.edition}`);
|
|
72
|
+
if (this.identifier.volume !== undefined)
|
|
73
|
+
parts.push(`vol-${this.identifier.volume}`);
|
|
74
|
+
return parts.join(":");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
EcmaKindIdentifier.urnGenerator = EcmaUrnGenerator;
|
|
78
|
+
registerType(EcmaKindIdentifier);
|
|
79
|
+
return EcmaKindIdentifier;
|
|
80
|
+
}
|
|
81
|
+
const KIND_CLASSES = {
|
|
82
|
+
standard: ecmaClass("standard"),
|
|
83
|
+
"technical-report": ecmaClass("technical-report"),
|
|
84
|
+
memento: ecmaClass("memento"),
|
|
85
|
+
};
|
|
86
|
+
class EcmaBuilder extends BaseBuilder {
|
|
87
|
+
selectClass(data) {
|
|
88
|
+
if (data["tr_number"] !== undefined && data["tr_number"] !== null) {
|
|
89
|
+
return KIND_CLASSES["technical-report"];
|
|
90
|
+
}
|
|
91
|
+
if (data["mem_number"] !== undefined && data["mem_number"] !== null) {
|
|
92
|
+
return KIND_CLASSES["memento"];
|
|
93
|
+
}
|
|
94
|
+
return KIND_CLASSES["standard"];
|
|
95
|
+
}
|
|
96
|
+
defaultIdentifierClass() {
|
|
97
|
+
return KIND_CLASSES["standard"];
|
|
98
|
+
}
|
|
99
|
+
cast(key, value) {
|
|
100
|
+
if (key === "tr_number" || key === "mem_number") {
|
|
101
|
+
return { number: String(value) };
|
|
102
|
+
}
|
|
103
|
+
return value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export function ecmaGrammarImplementation() {
|
|
107
|
+
const builder = new EcmaBuilder();
|
|
108
|
+
return {
|
|
109
|
+
parse(input) {
|
|
110
|
+
const tree = parseGrammar(ecmaGrammar, input);
|
|
111
|
+
if (typeof tree !== "object" || tree === null || Array.isArray(tree)) {
|
|
112
|
+
throw new ParseFailed("ECMA: unexpected parse tree", 0);
|
|
113
|
+
}
|
|
114
|
+
return builder.build(tree);
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Grammar } from "../../grammar/engine.js";
|
|
2
|
+
import type { FlavorImplementation } from "../../conformance/implementation.js";
|
|
3
|
+
import { Component } from "../../model/component.js";
|
|
4
|
+
export declare const etsiGrammar: Grammar;
|
|
5
|
+
/** ETSI Version component: "V1.2.3" or "ed.1". */
|
|
6
|
+
export declare class EtsiVersion extends Component {
|
|
7
|
+
readonly version: string;
|
|
8
|
+
readonly is_edition: boolean;
|
|
9
|
+
constructor(attrs: Record<string, unknown>);
|
|
10
|
+
render(): string;
|
|
11
|
+
toWire(): Record<string, unknown>;
|
|
12
|
+
protected fieldIsDefaulted(name: string, value: unknown): boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function etsiGrammarImplementation(): FlavorImplementation;
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { match, str } from "../../grammar/engine.js";
|
|
2
|
+
import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
|
|
3
|
+
import { BaseIdentifier, registerType } from "../../model/identifier.js";
|
|
4
|
+
import { extendAttributes, keyValue } from "../../model/attribute.js";
|
|
5
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
6
|
+
import { Component, PubidDate } from "../../model/component.js";
|
|
7
|
+
/**
|
|
8
|
+
* Port of lib/pubid/etsi/ on the unified model — the largest corpus
|
|
9
|
+
* (24,724 rows). "ETSI TYPE CODE[-parts][/A…/C…] [V…|ed.N] (YYYY-MM)".
|
|
10
|
+
* Compact serialization: number/parts/minor flat, Version flattens to a
|
|
11
|
+
* scalar + is_edition flag (omitted when false), Date to year/month.
|
|
12
|
+
* The publisher constant is never serialized. Supplements carry only
|
|
13
|
+
* their ordinal and the nested base; their URN is the BASE's URN —
|
|
14
|
+
* Ruby's generator string-matches "SupplementIdentifier" on the class
|
|
15
|
+
* NAME, which "Amendment" doesn't contain, so supplements fall through
|
|
16
|
+
* to the base branch (corpus-recorded behavior, mirrored exactly).
|
|
17
|
+
*/
|
|
18
|
+
const TYPES = ["I-ETS", "TCRTR", "GTS", "ETR", "ETS", "TBR", "NET", "EN", "ES", "EG", "TS", "GR", "GS", "SR", "TR"];
|
|
19
|
+
function buildRules() {
|
|
20
|
+
const rules = {};
|
|
21
|
+
const rule = (name, build) => {
|
|
22
|
+
rules[name] = build();
|
|
23
|
+
};
|
|
24
|
+
const space = str(" ");
|
|
25
|
+
const digits = match("[0-9]").repeat(1, Infinity);
|
|
26
|
+
const alnum = match("[A-Za-z0-9]");
|
|
27
|
+
const alnums = alnum.repeat(1, Infinity);
|
|
28
|
+
const dot = str(".");
|
|
29
|
+
rule("etsi_prefix", () => str("ETSI").then(space));
|
|
30
|
+
rule("type", () => TYPES.reduce((acc, t) => acc.or(str(t)), str(TYPES[0])).as("type"));
|
|
31
|
+
// parslet capture() converted to named captures; the builder composes
|
|
32
|
+
// the number string from whichever keys matched.
|
|
33
|
+
rule("gsm_with_space_number", () => str("GSM").as("gsm_prefix").then(space, digits.as("main"), dot, digits.as("sub")));
|
|
34
|
+
rule("dotted_number", () => digits.as("main").then(dot, digits.as("sub")));
|
|
35
|
+
rule("complex_number", () => alnums.as("prefix1")
|
|
36
|
+
.then(str("-").then(alnums.as("prefix2")).maybe())
|
|
37
|
+
.then(space, digits.as("num")));
|
|
38
|
+
rule("simple_number", () => digits.as("num"));
|
|
39
|
+
rule("number", () => rules["gsm_with_space_number"]
|
|
40
|
+
.or(rules["dotted_number"])
|
|
41
|
+
.or(rules["complex_number"])
|
|
42
|
+
.or(rules["simple_number"])
|
|
43
|
+
.as("number"));
|
|
44
|
+
rule("minor", () => space.then(digits.as("minor")));
|
|
45
|
+
rule("part", () => str("-").then(alnums.as("part")));
|
|
46
|
+
rule("parts", () => rules["part"].repeat(0, Infinity).as("parts"));
|
|
47
|
+
rule("version", () => str("V").then(match("[0-9.]").repeat(1, Infinity).as("version")));
|
|
48
|
+
rule("edition", () => str("ed.").then(digits.as("edition")));
|
|
49
|
+
rule("version_or_edition", () => rules["version"].or(rules["edition"]));
|
|
50
|
+
rule("date_part", () => str("(")
|
|
51
|
+
.then(match("[0-9]").repeat(4, 4).as("year"), str("-"), match("[0-9]").repeat(2, 2).as("month"), str(")")));
|
|
52
|
+
rule("amendment", () => str("/A").then(digits.as("number")));
|
|
53
|
+
rule("corrigendum", () => str("/C").then(digits.as("number")));
|
|
54
|
+
rule("supplements", () => rules["amendment"].as("amendment").or(rules["corrigendum"].as("corrigendum")).repeat(1, Infinity).as("supplements"));
|
|
55
|
+
rule("identifier", () => rules["etsi_prefix"]
|
|
56
|
+
.then(rules["type"], space, rules["number"])
|
|
57
|
+
.then(rules["minor"].maybe())
|
|
58
|
+
.then(rules["parts"])
|
|
59
|
+
.then(rules["supplements"].maybe())
|
|
60
|
+
.then(space.then(rules["version_or_edition"]).maybe())
|
|
61
|
+
.then(space.then(rules["date_part"]).maybe()));
|
|
62
|
+
rule("root", () => rules["identifier"]);
|
|
63
|
+
return rules;
|
|
64
|
+
}
|
|
65
|
+
export const etsiGrammar = { rules: buildRules(), root: "root" };
|
|
66
|
+
/** ETSI Version component: "V1.2.3" or "ed.1". */
|
|
67
|
+
export class EtsiVersion extends Component {
|
|
68
|
+
version;
|
|
69
|
+
is_edition;
|
|
70
|
+
constructor(attrs) {
|
|
71
|
+
super();
|
|
72
|
+
this.version = attrs["version"];
|
|
73
|
+
this.is_edition = attrs["is_edition"] ?? false;
|
|
74
|
+
}
|
|
75
|
+
render() {
|
|
76
|
+
return this.is_edition ? `ed.${this.version}` : `V${this.version}`;
|
|
77
|
+
}
|
|
78
|
+
toWire() {
|
|
79
|
+
return { version: this.version, is_edition: this.is_edition };
|
|
80
|
+
}
|
|
81
|
+
fieldIsDefaulted(name, value) {
|
|
82
|
+
return name === "is_edition" && value === false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
class EtsiIdentifier extends BaseIdentifier {
|
|
86
|
+
/** Components::Code#to_s: number, minor after a space, parts dashed. */
|
|
87
|
+
code() {
|
|
88
|
+
if (this.number === undefined)
|
|
89
|
+
return undefined;
|
|
90
|
+
let result = this.number;
|
|
91
|
+
if (this.minor !== undefined)
|
|
92
|
+
result += ` ${this.minor}`;
|
|
93
|
+
if (this.parts !== undefined)
|
|
94
|
+
result += this.parts.map((p) => `-${p}`).join("");
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
supplementNotation() {
|
|
98
|
+
return "";
|
|
99
|
+
}
|
|
100
|
+
actualBase() {
|
|
101
|
+
let current = this.base ?? this;
|
|
102
|
+
while (current.base !== undefined)
|
|
103
|
+
current = current.base;
|
|
104
|
+
return current;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Version and Date flatten through converter mappings (Ruby's with: hooks).
|
|
108
|
+
const ETSI_MAPPINGS = keyValue({ wire: "type", to: "type" }, { wire: "number", to: "number" }, { wire: "parts", to: "parts" }, { wire: "minor", to: "minor" }, {
|
|
109
|
+
wire: "version",
|
|
110
|
+
to: "version",
|
|
111
|
+
toWire: (m) => m["version"]?.version,
|
|
112
|
+
fromWire: (h) => h["version"] === undefined || h["version"] === null
|
|
113
|
+
? undefined
|
|
114
|
+
: new EtsiVersion({ version: String(h["version"]), is_edition: h["is_edition"] === true }),
|
|
115
|
+
}, {
|
|
116
|
+
wire: "is_edition",
|
|
117
|
+
to: "version",
|
|
118
|
+
toWire: (m) => (m["version"]?.is_edition ? true : undefined),
|
|
119
|
+
fromWire: () => undefined,
|
|
120
|
+
}, {
|
|
121
|
+
wire: "year",
|
|
122
|
+
to: "date",
|
|
123
|
+
toWire: (m) => m["date"]?.year,
|
|
124
|
+
fromWire: (h) => h["year"] === undefined || h["year"] === null
|
|
125
|
+
? undefined
|
|
126
|
+
: new PubidDate({ year: String(h["year"]), month: h["month"] }),
|
|
127
|
+
}, { wire: "month", to: "date", toWire: (m) => m["date"]?.month, fromWire: () => undefined });
|
|
128
|
+
function etsiClass(kind) {
|
|
129
|
+
const isStandard = kind === "etsi-standard";
|
|
130
|
+
class EtsiKindIdentifier extends EtsiIdentifier {
|
|
131
|
+
static polymorphicName = `pubid:etsi:${kind}`;
|
|
132
|
+
static attributes = isStandard
|
|
133
|
+
? extendAttributes(BaseIdentifier, {
|
|
134
|
+
type: { type: "string" },
|
|
135
|
+
number: { type: "string" },
|
|
136
|
+
minor: { type: "string" },
|
|
137
|
+
parts: { type: "string", collection: true, initializeEmpty: true },
|
|
138
|
+
version: { type: EtsiVersion },
|
|
139
|
+
})
|
|
140
|
+
: extendAttributes(BaseIdentifier, {
|
|
141
|
+
base: { type: EtsiIdentifier },
|
|
142
|
+
number: { type: "integer" },
|
|
143
|
+
});
|
|
144
|
+
static mappings = isStandard
|
|
145
|
+
? ETSI_MAPPINGS
|
|
146
|
+
: keyValue({ wire: "number", to: "number" }, { wire: "base", to: "base" });
|
|
147
|
+
render() {
|
|
148
|
+
if (isStandard) {
|
|
149
|
+
let result = `ETSI ${this.type} ${this.code()}`;
|
|
150
|
+
if (this.version !== undefined)
|
|
151
|
+
result += ` ${this.version.render()}`;
|
|
152
|
+
const d = this.date?.render();
|
|
153
|
+
if (d !== undefined && d !== "")
|
|
154
|
+
result += ` (${d})`;
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
// render_supplement: the innermost base code plus the chain of
|
|
158
|
+
// notations ("/A1/C2"), innermost first.
|
|
159
|
+
const notations = [];
|
|
160
|
+
const collect = (id) => {
|
|
161
|
+
if (id.base !== undefined) {
|
|
162
|
+
notations.unshift(`/${id.supplementNotation()}`);
|
|
163
|
+
collect(id.base);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
collect(this);
|
|
167
|
+
const actualBase = this.actualBase();
|
|
168
|
+
let result = `ETSI ${actualBase.type} ${actualBase.code()}${notations.join("")}`;
|
|
169
|
+
if (actualBase.version !== undefined)
|
|
170
|
+
result += ` ${actualBase.version.render()}`;
|
|
171
|
+
const d = actualBase.date?.render();
|
|
172
|
+
if (d !== undefined && d !== "")
|
|
173
|
+
result += ` (${d})`;
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
176
|
+
supplementNotation() {
|
|
177
|
+
const prefix = kind === "corrigendum" ? "C" : "A";
|
|
178
|
+
return `${prefix}${this.number ?? ""}`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
registerType(EtsiKindIdentifier);
|
|
182
|
+
return EtsiKindIdentifier;
|
|
183
|
+
}
|
|
184
|
+
const KIND_CLASSES = {
|
|
185
|
+
"etsi-standard": etsiClass("etsi-standard"),
|
|
186
|
+
amendment: etsiClass("amendment"),
|
|
187
|
+
corrigendum: etsiClass("corrigendum"),
|
|
188
|
+
};
|
|
189
|
+
class EtsiUrnGenerator extends BaseUrnGenerator {
|
|
190
|
+
generate() {
|
|
191
|
+
// Ruby's generator string-matches "SupplementIdentifier" on the
|
|
192
|
+
// class NAME; "Amendment"/"Corrigendum" don't contain it, so every
|
|
193
|
+
// supplement produces the BASE urn — mirrored exactly. The base's
|
|
194
|
+
// type/code/version/date delegate transitively down a supplement
|
|
195
|
+
// chain, i.e. they are the innermost standard's.
|
|
196
|
+
const id = this.identifier.actualBase();
|
|
197
|
+
const parts = ["urn", "etsi"];
|
|
198
|
+
if (id.type !== undefined)
|
|
199
|
+
parts.push(id.type.toLowerCase());
|
|
200
|
+
const code = id.code();
|
|
201
|
+
if (code !== undefined)
|
|
202
|
+
parts.push(code);
|
|
203
|
+
if (id.version !== undefined)
|
|
204
|
+
parts.push(id.version.render().toLowerCase());
|
|
205
|
+
const d = id.date?.render();
|
|
206
|
+
if (d !== undefined && d !== "")
|
|
207
|
+
parts.push(d);
|
|
208
|
+
return parts.join(":");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
for (const klass of Object.values(KIND_CLASSES)) {
|
|
212
|
+
klass.urnGenerator = EtsiUrnGenerator;
|
|
213
|
+
}
|
|
214
|
+
function isObj(v) {
|
|
215
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
216
|
+
}
|
|
217
|
+
function composeNumber(numberData) {
|
|
218
|
+
if (isObj(numberData)) {
|
|
219
|
+
const main = numberData["main"];
|
|
220
|
+
const sub = numberData["sub"];
|
|
221
|
+
if (numberData["gsm_prefix"] !== undefined)
|
|
222
|
+
return `GSM ${String(main)}.${String(sub)}`;
|
|
223
|
+
if (main !== undefined && sub !== undefined)
|
|
224
|
+
return `${String(main)}.${String(sub)}`;
|
|
225
|
+
if (numberData["prefix1"] !== undefined) {
|
|
226
|
+
let prefix = String(numberData["prefix1"]);
|
|
227
|
+
if (numberData["prefix2"] !== undefined)
|
|
228
|
+
prefix += `-${String(numberData["prefix2"])}`;
|
|
229
|
+
return `${prefix} ${String(numberData["num"])}`;
|
|
230
|
+
}
|
|
231
|
+
return String(numberData["num"]);
|
|
232
|
+
}
|
|
233
|
+
return String(numberData);
|
|
234
|
+
}
|
|
235
|
+
function extractParts(partsData) {
|
|
236
|
+
if (Array.isArray(partsData)) {
|
|
237
|
+
return partsData.map((p) => (isObj(p) ? String(p["part"]) : String(p)));
|
|
238
|
+
}
|
|
239
|
+
if (isObj(partsData) && partsData["part"] !== undefined)
|
|
240
|
+
return [String(partsData["part"])];
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
class EtsiBuilder {
|
|
244
|
+
build(data) {
|
|
245
|
+
const tree = (Array.isArray(data) ? Object.assign({}, ...data) : data);
|
|
246
|
+
if (tree["supplements"] !== undefined)
|
|
247
|
+
return this.buildWithSupplements(tree);
|
|
248
|
+
return this.buildStandard(tree);
|
|
249
|
+
}
|
|
250
|
+
buildStandard(data) {
|
|
251
|
+
const attrs = {
|
|
252
|
+
type: String(data["type"]),
|
|
253
|
+
number: composeNumber(data["number"]),
|
|
254
|
+
parts: extractParts(data["parts"]),
|
|
255
|
+
};
|
|
256
|
+
if (data["minor"] !== undefined && data["minor"] !== null) {
|
|
257
|
+
attrs["minor"] = String(data["minor"]);
|
|
258
|
+
}
|
|
259
|
+
if (data["version"] !== undefined && data["version"] !== null) {
|
|
260
|
+
attrs["version"] = new EtsiVersion({ version: String(data["version"]), is_edition: false });
|
|
261
|
+
}
|
|
262
|
+
else if (data["edition"] !== undefined && data["edition"] !== null) {
|
|
263
|
+
attrs["version"] = new EtsiVersion({ version: String(data["edition"]), is_edition: true });
|
|
264
|
+
}
|
|
265
|
+
if (data["year"] !== undefined && data["year"] !== null) {
|
|
266
|
+
attrs["date"] = new PubidDate({ year: String(data["year"]), month: data["month"] === undefined || data["month"] === null ? undefined : String(data["month"]) });
|
|
267
|
+
}
|
|
268
|
+
return new KIND_CLASSES["etsi-standard"](attrs);
|
|
269
|
+
}
|
|
270
|
+
buildWithSupplements(data) {
|
|
271
|
+
const baseData = { ...data };
|
|
272
|
+
delete baseData["supplements"];
|
|
273
|
+
let base = this.buildStandard(baseData);
|
|
274
|
+
const supplements = Array.isArray(data["supplements"]) ? data["supplements"] : [data["supplements"]];
|
|
275
|
+
for (const supp of supplements) {
|
|
276
|
+
if (!isObj(supp))
|
|
277
|
+
continue;
|
|
278
|
+
if (supp["amendment"] !== undefined) {
|
|
279
|
+
const inner = isObj(supp["amendment"]) ? supp["amendment"] : undefined;
|
|
280
|
+
base = new KIND_CLASSES["amendment"]({
|
|
281
|
+
base,
|
|
282
|
+
number: Number(inner === undefined ? supp["amendment"] : inner["number"]),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
else if (supp["corrigendum"] !== undefined) {
|
|
286
|
+
const inner = isObj(supp["corrigendum"]) ? supp["corrigendum"] : undefined;
|
|
287
|
+
base = new KIND_CLASSES["corrigendum"]({
|
|
288
|
+
base,
|
|
289
|
+
number: Number(inner === undefined ? supp["corrigendum"] : inner["number"]),
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return base;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
export function etsiGrammarImplementation() {
|
|
297
|
+
const builder = new EtsiBuilder();
|
|
298
|
+
return {
|
|
299
|
+
parse(input) {
|
|
300
|
+
const tree = parseGrammar(etsiGrammar, input);
|
|
301
|
+
if (typeof tree !== "object" || tree === null) {
|
|
302
|
+
throw new ParseFailed("ETSI: unexpected parse tree", 0);
|
|
303
|
+
}
|
|
304
|
+
return builder.build(tree);
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ref, str } from "../../grammar/engine.js";
|
|
2
|
+
import { cenCenelecGrammar } from "../cen_cenelec/grammar.js";
|
|
3
|
+
/**
|
|
4
|
+
* Port of lib/pubid/evs/parser.rb — "EVS" ("-" | " ") <CEN identifier>,
|
|
5
|
+
* with the CEN/CENELEC grammar's identifier rule embedded as an atom
|
|
6
|
+
* (the adopted subtree goes straight to the CEN builder as data, no
|
|
7
|
+
* string re-serialization).
|
|
8
|
+
*/
|
|
9
|
+
function buildRules() {
|
|
10
|
+
const rules = {};
|
|
11
|
+
const rule = (name, build) => {
|
|
12
|
+
rules[name] = build();
|
|
13
|
+
};
|
|
14
|
+
rule("root", () => str("EVS")
|
|
15
|
+
.then(str("-").or(str(" ")).as("evs_separator"))
|
|
16
|
+
.then(ref(cenCenelecGrammar.rules, "identifier").as("adopted")));
|
|
17
|
+
return rules;
|
|
18
|
+
}
|
|
19
|
+
export const evsGrammar = {
|
|
20
|
+
rules: buildRules(),
|
|
21
|
+
root: "root",
|
|
22
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { parseGrammar } from "../../grammar/engine.js";
|
|
2
|
+
import { evsGrammar } from "./grammar.js";
|
|
3
|
+
import { buildCenIdentifier } from "../cen_cenelec/index.js";
|
|
4
|
+
import { NationalAdoption } from "./model.js";
|
|
5
|
+
/**
|
|
6
|
+
* Port of lib/pubid/evs/builder.rb — the :adopted subtree (from the
|
|
7
|
+
* embedded CEN grammar) goes to the CEN/CENELEC builder as data.
|
|
8
|
+
*/
|
|
9
|
+
function buildIdentifier(tree) {
|
|
10
|
+
const d = Array.isArray(tree)
|
|
11
|
+
? Object.assign({}, ...tree)
|
|
12
|
+
: tree;
|
|
13
|
+
const separator = d["evs_separator"] !== undefined ? String(d["evs_separator"]) : "-";
|
|
14
|
+
const base = buildCenIdentifier(d["adopted"]);
|
|
15
|
+
return new NationalAdoption({ base, separator });
|
|
16
|
+
}
|
|
17
|
+
export function evsGrammarImplementation() {
|
|
18
|
+
return {
|
|
19
|
+
parse(input) {
|
|
20
|
+
const tree = parseGrammar(evsGrammar, input);
|
|
21
|
+
return buildIdentifier(tree);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
2
|
+
import { type FieldMapping } from "../../model/attribute.js";
|
|
3
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
4
|
+
import { CenIdentifier } from "../cen_cenelec/model.js";
|
|
5
|
+
declare class EvsUrnGenerator extends BaseUrnGenerator<EvsIdentifier> {
|
|
6
|
+
generate(): string;
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class EvsIdentifier extends BaseIdentifier {
|
|
9
|
+
readonly base: CenIdentifier | undefined;
|
|
10
|
+
readonly separator: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export declare class NationalAdoption extends EvsIdentifier {
|
|
13
|
+
static polymorphicName: string;
|
|
14
|
+
static urnGenerator: typeof EvsUrnGenerator;
|
|
15
|
+
static attributes: import("../../model/attribute.js").AttributeTable;
|
|
16
|
+
static mappings: FieldMapping[];
|
|
17
|
+
render(): string;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BaseIdentifier, registerType } from "../../model/identifier.js";
|
|
2
|
+
import { keyValue, extendAttributes } from "../../model/attribute.js";
|
|
3
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
4
|
+
import { CenIdentifier } from "../cen_cenelec/model.js";
|
|
5
|
+
/**
|
|
6
|
+
* Port of lib/pubid/evs: the national-adoption identifier wrapping a
|
|
7
|
+
* CEN/CENELEC base, the renderer ("EVS" + separator + adopted) and the
|
|
8
|
+
* URN generator (the adopted CEN URN with urn:cen: swapped for
|
|
9
|
+
* urn:evs:).
|
|
10
|
+
*/
|
|
11
|
+
const CEN_NAMESPACE = "urn:cen:";
|
|
12
|
+
const EVS_NAMESPACE = "urn:evs:";
|
|
13
|
+
class EvsUrnGenerator extends BaseUrnGenerator {
|
|
14
|
+
generate() {
|
|
15
|
+
const adoptedUrn = this.identifier.base?.toUrn() ?? "";
|
|
16
|
+
if (!adoptedUrn.startsWith(CEN_NAMESPACE)) {
|
|
17
|
+
throw new Error(`expected adopted CEN URN, got ${JSON.stringify(adoptedUrn)}`);
|
|
18
|
+
}
|
|
19
|
+
return EVS_NAMESPACE + adoptedUrn.slice(CEN_NAMESPACE.length);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class EvsIdentifier extends BaseIdentifier {
|
|
23
|
+
}
|
|
24
|
+
export class NationalAdoption extends EvsIdentifier {
|
|
25
|
+
static polymorphicName = "pubid:evs:national-adoption";
|
|
26
|
+
static urnGenerator = EvsUrnGenerator;
|
|
27
|
+
static attributes = extendAttributes(EvsIdentifier, {
|
|
28
|
+
base: { type: CenIdentifier },
|
|
29
|
+
separator: { type: "string", default: "-" },
|
|
30
|
+
});
|
|
31
|
+
static mappings = keyValue({ wire: "base", to: "base" }, { wire: "separator", to: "separator" });
|
|
32
|
+
render() {
|
|
33
|
+
return `EVS${this.separator ?? "-"}${this.base?.toHuman() ?? ""}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
registerType(NationalAdoption);
|