@pubid/pubid 0.1.3 → 0.1.4
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 +24 -0
- package/dist/flavors/gb/index.js +111 -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 +626 -0
- package/dist/flavors/ieee/index.d.ts +3 -0
- package/dist/flavors/ieee/index.js +1186 -0
- package/dist/flavors/ieee/model.d.ts +142 -0
- package/dist/flavors/ieee/model.js +1148 -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 +191 -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 +180 -0
- package/dist/flavors/oiml/implementation.d.ts +9 -0
- package/dist/flavors/oiml/implementation.js +16 -0
- package/dist/flavors/oiml/model.d.ts +4 -0
- package/dist/flavors/oiml/model.js +462 -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/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 +83 -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,24 @@
|
|
|
1
|
+
import { Grammar } from "../../grammar/engine.js";
|
|
2
|
+
import type { FlavorImplementation } from "../../conformance/implementation.js";
|
|
3
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
4
|
+
import { PubidDate, Publisher } from "../../model/component.js";
|
|
5
|
+
export declare const gbGrammar: Grammar;
|
|
6
|
+
export declare class GbIdentifier extends BaseIdentifier {
|
|
7
|
+
static polymorphicName: string;
|
|
8
|
+
static attributes: import("../../model/attribute.js").AttributeTable;
|
|
9
|
+
static mappings: import("../../model/attribute.js").FieldMapping[];
|
|
10
|
+
static flatScalarComponents: {
|
|
11
|
+
publisher: string;
|
|
12
|
+
};
|
|
13
|
+
static flatScalarFields: {
|
|
14
|
+
publisher: string;
|
|
15
|
+
};
|
|
16
|
+
readonly publisher: Publisher;
|
|
17
|
+
readonly mandate: string | undefined;
|
|
18
|
+
readonly number: string;
|
|
19
|
+
readonly part: string | undefined;
|
|
20
|
+
readonly date: PubidDate | undefined;
|
|
21
|
+
readonly all_parts: boolean | undefined;
|
|
22
|
+
render(): string;
|
|
23
|
+
}
|
|
24
|
+
export declare function gbGrammarImplementation(): FlavorImplementation;
|
|
@@ -0,0 +1,111 @@
|
|
|
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 { BaseBuilder } from "../../model/builder.js";
|
|
6
|
+
import { PubidDate, Publisher } from "../../model/component.js";
|
|
7
|
+
/**
|
|
8
|
+
* Port of lib/pubid/gb/ on the unified model. The series code lives in
|
|
9
|
+
* the inherited `publisher` (a Publisher, flattened to its body scalar
|
|
10
|
+
* via the class's flat-scalar entry — the CEN precedent); mandate is
|
|
11
|
+
* split out of an inline /T|/Z; date flattens to `year` (shared rule).
|
|
12
|
+
* No Ruby urn_generator: the BASE template derives
|
|
13
|
+
* urn:gb:<publisher>:<number>[:-part][:year] with the publisher
|
|
14
|
+
* lowercased.
|
|
15
|
+
*/
|
|
16
|
+
function buildRules() {
|
|
17
|
+
const rules = {};
|
|
18
|
+
const rule = (name, build) => {
|
|
19
|
+
rules[name] = build();
|
|
20
|
+
};
|
|
21
|
+
const space = str(" ");
|
|
22
|
+
const digits = match("[0-9]").repeat(1, Infinity);
|
|
23
|
+
rule("publisher_code", () =>
|
|
24
|
+
// Social-group form: "T/" then uppercase letters/digits.
|
|
25
|
+
(str("T/").then(match("[A-Z0-9]").repeat(1, Infinity))).as("publisher_code")
|
|
26
|
+
// Confidential national series: lowercase "n" is part of the code
|
|
27
|
+
// (PEG: before the uppercase-only branch, no backtracking).
|
|
28
|
+
.or((str("GBn").then(str("/T").or(str("/Z")).maybe())).as("publisher_code"))
|
|
29
|
+
// Standard form: 1-3 letters, optional slash-T-or-Z.
|
|
30
|
+
.or((match("[A-Z]")
|
|
31
|
+
.repeat(1, 3)
|
|
32
|
+
.then(str("/T").or(str("/Z")).maybe()).as("publisher_code"))));
|
|
33
|
+
rule("mandate_suffix", () => str("/").then(str("T").or(str("Z")).as("mandate")).maybe());
|
|
34
|
+
rule("number", () => digits.as("number"));
|
|
35
|
+
rule("part", () => str(".").then(digits.as("part")));
|
|
36
|
+
rule("year", () => str("-").or(str("—")).then(match("[0-9]").repeat(4, 4).as("year")).maybe());
|
|
37
|
+
rule("all_parts_flag", () => space.then(str("("), str("all parts"), str(")")).as("all_parts"));
|
|
38
|
+
rule("identifier", () => rules["publisher_code"]
|
|
39
|
+
.then(space)
|
|
40
|
+
.then(rules["mandate_suffix"])
|
|
41
|
+
.then(rules["number"])
|
|
42
|
+
.then(rules["part"].maybe())
|
|
43
|
+
.then(rules["year"])
|
|
44
|
+
.then(rules["all_parts_flag"].maybe()));
|
|
45
|
+
rule("root", () => rules["identifier"]);
|
|
46
|
+
return rules;
|
|
47
|
+
}
|
|
48
|
+
export const gbGrammar = { rules: buildRules(), root: "root" };
|
|
49
|
+
export class GbIdentifier extends BaseIdentifier {
|
|
50
|
+
static polymorphicName = "pubid:gb:standard";
|
|
51
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
52
|
+
publisher: { type: Publisher },
|
|
53
|
+
mandate: { type: "string" },
|
|
54
|
+
number: { type: "string" },
|
|
55
|
+
part: { type: "string" },
|
|
56
|
+
all_parts: { type: "boolean", default: false },
|
|
57
|
+
});
|
|
58
|
+
static mappings = keyValue({ wire: "publisher", to: "publisher" }, { wire: "mandate", to: "mandate" }, { wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "date", to: "date" }, { wire: "all_parts", to: "all_parts" });
|
|
59
|
+
// GB's flat-scalar entry: the Publisher serializes as its body scalar.
|
|
60
|
+
static flatScalarComponents = { publisher: "publisher" };
|
|
61
|
+
static flatScalarFields = { publisher: "body" };
|
|
62
|
+
render() {
|
|
63
|
+
let code = this.publisher.body;
|
|
64
|
+
if (this.mandate !== undefined && !code.includes("/"))
|
|
65
|
+
code += `/${this.mandate}`;
|
|
66
|
+
let numberPortion = this.number;
|
|
67
|
+
if (this.part !== undefined)
|
|
68
|
+
numberPortion += `.${this.part}`;
|
|
69
|
+
if (this.date !== undefined && this.date.year !== undefined)
|
|
70
|
+
numberPortion += `-${this.date.year}`;
|
|
71
|
+
let result = `${code} ${numberPortion}`;
|
|
72
|
+
if (this.all_parts)
|
|
73
|
+
result += " (all parts)";
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
registerType(GbIdentifier);
|
|
78
|
+
class GbBuilder extends BaseBuilder {
|
|
79
|
+
defaultIdentifierClass() {
|
|
80
|
+
return GbIdentifier;
|
|
81
|
+
}
|
|
82
|
+
cast(key, value) {
|
|
83
|
+
if (key === "publisher_code") {
|
|
84
|
+
// Split an inline /T or /Z mandate out of the code.
|
|
85
|
+
const code = String(value);
|
|
86
|
+
const m = /^(.*?)\/(T|Z)$/.exec(code);
|
|
87
|
+
return m
|
|
88
|
+
? { publisher: new Publisher({ body: m[1] }), mandate: m[2] }
|
|
89
|
+
: { publisher: new Publisher({ body: code }) };
|
|
90
|
+
}
|
|
91
|
+
if (key === "year") {
|
|
92
|
+
return value === undefined || value === null ? null : { date: new PubidDate({ year: String(value) }) };
|
|
93
|
+
}
|
|
94
|
+
if (key === "all_parts") {
|
|
95
|
+
return { all_parts: String(value) !== "" };
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export function gbGrammarImplementation() {
|
|
101
|
+
const builder = new GbBuilder();
|
|
102
|
+
return {
|
|
103
|
+
parse(input) {
|
|
104
|
+
const tree = parseGrammar(gbGrammar, input);
|
|
105
|
+
if (typeof tree !== "object" || tree === null || Array.isArray(tree)) {
|
|
106
|
+
throw new ParseFailed("GB: unexpected parse tree", 0);
|
|
107
|
+
}
|
|
108
|
+
return builder.build(tree);
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { match, ref, str } from "../../grammar/engine.js";
|
|
2
|
+
/**
|
|
3
|
+
* Port of lib/pubid/gost/parser.rb — 1:1. Root: identifier.
|
|
4
|
+
*
|
|
5
|
+
* Captures: gost_word, scope_r (R/Р), prefix_text (copublisher/subtype
|
|
6
|
+
* as a raw string, split by the builder), raw (number+year digit runs),
|
|
7
|
+
* adopted_raw (the foreign id after "/"), adopted_reference_raw (the
|
|
8
|
+
* parenthesized harmonization list).
|
|
9
|
+
*/
|
|
10
|
+
function buildRules() {
|
|
11
|
+
const rules = {};
|
|
12
|
+
const rule = (name, build) => {
|
|
13
|
+
rules[name] = build();
|
|
14
|
+
};
|
|
15
|
+
const space = str(" ");
|
|
16
|
+
rule("dot", () => str("."));
|
|
17
|
+
rule("year_sep", () => str("-").or(str("—")).or(str("–")).then(space.maybe()));
|
|
18
|
+
rule("gost_word", () => str("GOST").or(str("ГОСТ")).as("gost_word").then(space));
|
|
19
|
+
rule("scope_r", () => match("R").or(match("Р")).as("scope_r").then(space));
|
|
20
|
+
rule("prefix_text", () => match("[A-ZА-Яa-zа-я/]")
|
|
21
|
+
.repeat(1, Infinity)
|
|
22
|
+
.then(space.then(match("[A-ZА-Яa-zа-я/]").repeat(1, Infinity)).repeat(0, Infinity))
|
|
23
|
+
.as("prefix_text")
|
|
24
|
+
.then(space));
|
|
25
|
+
rule("digits", () => match("[0-9]").repeat(1, Infinity));
|
|
26
|
+
rule("raw_body", () => ref(rules, "digits")
|
|
27
|
+
.then(ref(rules, "dot").or(ref(rules, "year_sep")).then(ref(rules, "digits")).repeat(0, Infinity))
|
|
28
|
+
.as("raw"));
|
|
29
|
+
rule("adopted_part", () => space.maybe().then(str("/")).then(match(".").repeat(1, Infinity).as("adopted_raw")));
|
|
30
|
+
rule("adopted_reference_part", () => space
|
|
31
|
+
.maybe()
|
|
32
|
+
.then(str("("))
|
|
33
|
+
.then(match("[^)]").repeat(1, Infinity).as("adopted_reference_raw"))
|
|
34
|
+
.then(str(")")));
|
|
35
|
+
rule("identifier", () => ref(rules, "gost_word")
|
|
36
|
+
.then(ref(rules, "scope_r").maybe())
|
|
37
|
+
.then(ref(rules, "prefix_text").maybe())
|
|
38
|
+
.then(ref(rules, "raw_body"))
|
|
39
|
+
.then(ref(rules, "adopted_part").maybe())
|
|
40
|
+
.then(ref(rules, "adopted_reference_part").maybe()));
|
|
41
|
+
return rules;
|
|
42
|
+
}
|
|
43
|
+
export const gostGrammar = {
|
|
44
|
+
rules: buildRules(),
|
|
45
|
+
root: "identifier",
|
|
46
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { parseGrammar } from "../../grammar/engine.js";
|
|
2
|
+
import { grammarImplementation } from "../index.js";
|
|
3
|
+
import { gostGrammar } from "./grammar.js";
|
|
4
|
+
import { PREFIX_ROUTES } from "./prefix_routes.js";
|
|
5
|
+
import { ForeignReference, Harmonized, IdenticalAdoption, InterstateStandard, NationalStandard, } from "./model.js";
|
|
6
|
+
const COPUBLISHER_MAP = {
|
|
7
|
+
"ИСО": "ISO", "МЭК": "IEC", "ЕН": "EN",
|
|
8
|
+
"ИСО/МЭК": "ISO/IEC", "МЭК/ИСО": "IEC/ISO",
|
|
9
|
+
"ИСО/ТУ": "ISO/TS", "ИСО/ТО": "ISO/TR",
|
|
10
|
+
"МЭК/ТУ": "IEC/TS", "МЭК/ТО": "IEC/TR",
|
|
11
|
+
"ИСО/МЭК/ТУ": "ISO/IEC/TS",
|
|
12
|
+
};
|
|
13
|
+
const SUBTYPE_MAP = {
|
|
14
|
+
"МФС": "ISP", "ТУ": "TS", "ТО": "TR",
|
|
15
|
+
};
|
|
16
|
+
const NUMBER_YEAR_SPLIT = /^(.+?)\s*[-—–]\s*(\d{2}|\d{4})$/;
|
|
17
|
+
// The gem's try_each_flavor iterates Registry.flavor_names.sort; this
|
|
18
|
+
// mirrors that order over the TS grammar registry (the still-unported
|
|
19
|
+
// flavors are skipped). bsi's grammar parses bare ISO/IEC/IEEE
|
|
20
|
+
// references (adopted inside BS documents) and returns the owning
|
|
21
|
+
// flavor's identifier — that is what routes the joint "ISO/IEC …"
|
|
22
|
+
// adoptions to the ISO flavor ahead of IEC.
|
|
23
|
+
const FOREIGN_FALLBACK_ORDER = [
|
|
24
|
+
"tgpp", "ansi", "astm", "bipm",
|
|
25
|
+
"bsi",
|
|
26
|
+
"calconnect", "ccsds", "cen_cenelec", "cie", "doi", "easc", "ecma",
|
|
27
|
+
"etsi", "gb", "iala", "iana", "idf", "iec", "ieee", "ietf", "iho",
|
|
28
|
+
"isbn", "iso", "itu", "jcgm", "jis", "nist", "oasis", "ogc", "oiml",
|
|
29
|
+
"omg", "plateau", "un", "w3c", "xsf",
|
|
30
|
+
];
|
|
31
|
+
const str = (v) => {
|
|
32
|
+
if (v === undefined || v === null)
|
|
33
|
+
return undefined;
|
|
34
|
+
const s = Array.isArray(v) ? v.map(String).join("") : String(v);
|
|
35
|
+
return s.length > 0 ? s : undefined;
|
|
36
|
+
};
|
|
37
|
+
function flatten(tree) {
|
|
38
|
+
return Array.isArray(tree) ? Object.assign({}, ...tree) : tree;
|
|
39
|
+
}
|
|
40
|
+
function buildIdentifier(tree) {
|
|
41
|
+
return buildFrom(flatten(tree));
|
|
42
|
+
}
|
|
43
|
+
function buildFrom(d) {
|
|
44
|
+
const russian = d["scope_r"] !== undefined;
|
|
45
|
+
const [copublisher, subtype] = splitPrefix(str(d["prefix_text"]));
|
|
46
|
+
const [number, year] = splitNumberYear(str(d["raw"]));
|
|
47
|
+
const klass = (russian ? NationalStandard : InterstateStandard);
|
|
48
|
+
const base = new klass({
|
|
49
|
+
...(copublisher !== undefined ? { copublisher } : {}),
|
|
50
|
+
...(subtype !== undefined ? { subtype } : {}),
|
|
51
|
+
...(number !== undefined ? { number } : {}),
|
|
52
|
+
...(year !== undefined ? { year } : {}),
|
|
53
|
+
});
|
|
54
|
+
return withForeignAdoption(base, d);
|
|
55
|
+
}
|
|
56
|
+
function withForeignAdoption(base, d) {
|
|
57
|
+
return wrapHarmonized(wrapIdenticalAdoption(base, d), d);
|
|
58
|
+
}
|
|
59
|
+
function wrapIdenticalAdoption(base, d) {
|
|
60
|
+
const adoptedRaw = str(d["adopted_raw"]);
|
|
61
|
+
if (adoptedRaw === undefined)
|
|
62
|
+
return base;
|
|
63
|
+
const adopted = parseForeign(adoptedRaw);
|
|
64
|
+
const klass = IdenticalAdoption;
|
|
65
|
+
return new klass({ base, adopted });
|
|
66
|
+
}
|
|
67
|
+
function wrapHarmonized(base, d) {
|
|
68
|
+
const referenceRaw = str(d["adopted_reference_raw"]);
|
|
69
|
+
if (referenceRaw === undefined)
|
|
70
|
+
return base;
|
|
71
|
+
const adopted = referenceRaw
|
|
72
|
+
.split(",")
|
|
73
|
+
.map((s) => s.trim())
|
|
74
|
+
.map(normalizeForeign)
|
|
75
|
+
.map(adoptIdentifier);
|
|
76
|
+
const klass = Harmonized;
|
|
77
|
+
return new klass({ base, adoptedIdentifiers: adopted });
|
|
78
|
+
}
|
|
79
|
+
function adoptIdentifier(raw) {
|
|
80
|
+
return parseForeign(raw) ?? new ForeignReference({ raw });
|
|
81
|
+
}
|
|
82
|
+
function splitPrefix(text) {
|
|
83
|
+
if (text === undefined)
|
|
84
|
+
return [undefined, undefined];
|
|
85
|
+
const tokens = text.trim().split(/\s+/);
|
|
86
|
+
const copublisher = normalizeCopublisher(tokens[0]);
|
|
87
|
+
const subtype = tokens.length > 1 ? normalizeSubtype(tokens.slice(1).join(" ")) : undefined;
|
|
88
|
+
return [copublisher, subtype];
|
|
89
|
+
}
|
|
90
|
+
function normalizeCopublisher(raw) {
|
|
91
|
+
if (raw === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
return COPUBLISHER_MAP[raw] ?? raw.toUpperCase();
|
|
94
|
+
}
|
|
95
|
+
function normalizeSubtype(raw) {
|
|
96
|
+
if (raw === undefined)
|
|
97
|
+
return undefined;
|
|
98
|
+
return SUBTYPE_MAP[raw] ?? raw;
|
|
99
|
+
}
|
|
100
|
+
// Longest keys first so "ИСО/МЭК" wins over "ИСО".
|
|
101
|
+
function normalizeForeign(raw) {
|
|
102
|
+
for (const cyr of Object.keys(COPUBLISHER_MAP).sort((a, b) => b.length - a.length)) {
|
|
103
|
+
if (raw.startsWith(cyr + " ") || raw.startsWith(cyr + "/")) {
|
|
104
|
+
return COPUBLISHER_MAP[cyr] + raw.slice(cyr.length);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return raw;
|
|
108
|
+
}
|
|
109
|
+
function splitNumberYear(raw) {
|
|
110
|
+
if (raw === undefined)
|
|
111
|
+
return [undefined, undefined];
|
|
112
|
+
const m = NUMBER_YEAR_SPLIT.exec(raw);
|
|
113
|
+
if (m === null)
|
|
114
|
+
return [raw, undefined];
|
|
115
|
+
return [m[1], m[2]];
|
|
116
|
+
}
|
|
117
|
+
// The single flavor owning the longest matching prefix; undefined when
|
|
118
|
+
// no prefix matches or the prefix is jointly owned (joint forms go
|
|
119
|
+
// through the fallback loop).
|
|
120
|
+
function prefixOwner(raw) {
|
|
121
|
+
let match;
|
|
122
|
+
for (const prefix of Object.keys(PREFIX_ROUTES)) {
|
|
123
|
+
if ((raw.startsWith(`${prefix} `) || raw === prefix) &&
|
|
124
|
+
(match === undefined || prefix.length > match.length)) {
|
|
125
|
+
match = prefix;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (match === undefined)
|
|
129
|
+
return undefined;
|
|
130
|
+
const owners = PREFIX_ROUTES[match] ?? [];
|
|
131
|
+
return owners.length === 1 ? owners[0] : undefined;
|
|
132
|
+
}
|
|
133
|
+
function tryParse(flavor, raw) {
|
|
134
|
+
const impl = grammarImplementation(flavor);
|
|
135
|
+
if (impl === undefined)
|
|
136
|
+
return undefined;
|
|
137
|
+
try {
|
|
138
|
+
return impl.parse(raw);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function parseForeign(raw) {
|
|
145
|
+
const owner = prefixOwner(raw);
|
|
146
|
+
if (owner !== undefined) {
|
|
147
|
+
const parsed = tryParse(owner, raw);
|
|
148
|
+
if (parsed !== undefined)
|
|
149
|
+
return parsed;
|
|
150
|
+
}
|
|
151
|
+
for (const flavor of FOREIGN_FALLBACK_ORDER) {
|
|
152
|
+
const parsed = tryParse(flavor, raw);
|
|
153
|
+
if (parsed !== undefined)
|
|
154
|
+
return parsed;
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
export function gostGrammarImplementation() {
|
|
159
|
+
return {
|
|
160
|
+
parse(input) {
|
|
161
|
+
const tree = parseGrammar(gostGrammar, input.trim());
|
|
162
|
+
return buildIdentifier(tree);
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { BaseIdentifier } from "../../model/identifier.js";
|
|
2
|
+
import { type FieldMapping } from "../../model/attribute.js";
|
|
3
|
+
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
4
|
+
/**
|
|
5
|
+
* Port of lib/pubid/gost: the five identifier classes
|
|
6
|
+
* (interstate/national standard, identical adoption, harmonized,
|
|
7
|
+
* foreign reference), the renderer and the URN generator.
|
|
8
|
+
*
|
|
9
|
+
* GOST shape: GOST [R ][<copublisher> ][<subtype> ]<number>[-<year>]
|
|
10
|
+
* [/<adopted>][(<refs>)]
|
|
11
|
+
*/
|
|
12
|
+
export declare abstract class GostIdentifier extends BaseIdentifier {
|
|
13
|
+
static attributes: import("../../model/attribute.js").AttributeTable;
|
|
14
|
+
static mappings: FieldMapping[];
|
|
15
|
+
readonly copublisher: string | undefined;
|
|
16
|
+
readonly subtype: string | undefined;
|
|
17
|
+
readonly number: string | undefined;
|
|
18
|
+
readonly year: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
declare class GostUrnGenerator extends BaseUrnGenerator<GostIdentifier> {
|
|
21
|
+
generate(): string;
|
|
22
|
+
}
|
|
23
|
+
export declare class InterstateStandard extends GostIdentifier {
|
|
24
|
+
static polymorphicName: string;
|
|
25
|
+
static urnGenerator: typeof GostUrnGenerator;
|
|
26
|
+
render(): string;
|
|
27
|
+
}
|
|
28
|
+
export declare class NationalStandard extends GostIdentifier {
|
|
29
|
+
static polymorphicName: string;
|
|
30
|
+
static urnGenerator: typeof GostUrnGenerator;
|
|
31
|
+
render(): string;
|
|
32
|
+
}
|
|
33
|
+
export declare class ForeignReference extends GostIdentifier {
|
|
34
|
+
static polymorphicName: string;
|
|
35
|
+
static get attributes(): import("../../model/attribute.js").AttributeTable;
|
|
36
|
+
static mappings: FieldMapping[];
|
|
37
|
+
readonly raw: string | undefined;
|
|
38
|
+
render(): string;
|
|
39
|
+
}
|
|
40
|
+
declare function delegatedWireFields(model: BaseIdentifier, hash: Record<string, unknown>): void;
|
|
41
|
+
export declare class Harmonized extends GostIdentifier {
|
|
42
|
+
static polymorphicName: string;
|
|
43
|
+
static urnGenerator: typeof GostUrnGenerator;
|
|
44
|
+
static get attributes(): import("../../model/attribute.js").AttributeTable;
|
|
45
|
+
static mappings: FieldMapping[];
|
|
46
|
+
static compactHash: typeof delegatedWireFields;
|
|
47
|
+
readonly base: GostIdentifier | undefined;
|
|
48
|
+
readonly adoptedIdentifiers: GostIdentifier[] | undefined;
|
|
49
|
+
render(): string;
|
|
50
|
+
}
|
|
51
|
+
export declare class IdenticalAdoption extends GostIdentifier {
|
|
52
|
+
static polymorphicName: string;
|
|
53
|
+
static urnGenerator: typeof GostUrnGenerator;
|
|
54
|
+
static get attributes(): import("../../model/attribute.js").AttributeTable;
|
|
55
|
+
static mappings: FieldMapping[];
|
|
56
|
+
static compactHash: typeof delegatedWireFields;
|
|
57
|
+
readonly base: GostIdentifier | undefined;
|
|
58
|
+
readonly adopted: GostIdentifier | undefined;
|
|
59
|
+
render(): string;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
/**
|
|
5
|
+
* Port of lib/pubid/gost: the five identifier classes
|
|
6
|
+
* (interstate/national standard, identical adoption, harmonized,
|
|
7
|
+
* foreign reference), the renderer and the URN generator.
|
|
8
|
+
*
|
|
9
|
+
* GOST shape: GOST [R ][<copublisher> ][<subtype> ]<number>[-<year>]
|
|
10
|
+
* [/<adopted>][(<refs>)]
|
|
11
|
+
*/
|
|
12
|
+
export class GostIdentifier extends BaseIdentifier {
|
|
13
|
+
static attributes = extendAttributes(BaseIdentifier, {
|
|
14
|
+
copublisher: { type: "string" },
|
|
15
|
+
subtype: { type: "string" },
|
|
16
|
+
number: { type: "string" },
|
|
17
|
+
year: { type: "string" },
|
|
18
|
+
});
|
|
19
|
+
// publisher defaults to "GOST" but is deliberately NOT mapped: the
|
|
20
|
+
// Ruby class declares it without a key_value entry, so it never
|
|
21
|
+
// serializes (a default-equal scalar would drop anyway).
|
|
22
|
+
static mappings = keyValue({ wire: "copublisher", to: "copublisher" }, { wire: "subtype", to: "subtype" }, { wire: "number", to: "number" }, { wire: "year", to: "year" });
|
|
23
|
+
}
|
|
24
|
+
function renderStandard(id) {
|
|
25
|
+
const national = id instanceof NationalStandard;
|
|
26
|
+
return [
|
|
27
|
+
"GOST",
|
|
28
|
+
national ? " R" : "",
|
|
29
|
+
id.copublisher !== undefined ? ` ${id.copublisher}` : "",
|
|
30
|
+
id.subtype !== undefined ? ` ${id.subtype}` : "",
|
|
31
|
+
` ${id.number ?? ""}`,
|
|
32
|
+
id.year !== undefined ? `-${id.year}` : "",
|
|
33
|
+
].join("");
|
|
34
|
+
}
|
|
35
|
+
class GostUrnGenerator extends BaseUrnGenerator {
|
|
36
|
+
generate() {
|
|
37
|
+
let id = this.identifier;
|
|
38
|
+
while (id instanceof Harmonized || id instanceof IdenticalAdoption) {
|
|
39
|
+
id = id.base;
|
|
40
|
+
}
|
|
41
|
+
if (!(id instanceof InterstateStandard) && !(id instanceof NationalStandard)) {
|
|
42
|
+
throw new Error(`Unknown GOST identifier class: ${id.constructor.name}`);
|
|
43
|
+
}
|
|
44
|
+
const parts = ["urn", "gost", "std"];
|
|
45
|
+
if (id instanceof NationalStandard)
|
|
46
|
+
parts.push("r");
|
|
47
|
+
if (id.number !== undefined)
|
|
48
|
+
parts.push(id.number);
|
|
49
|
+
if (id.year !== undefined)
|
|
50
|
+
parts.push(id.year);
|
|
51
|
+
return parts.join(":");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class InterstateStandard extends GostIdentifier {
|
|
55
|
+
static polymorphicName = "pubid:gost:interstate-standard";
|
|
56
|
+
static urnGenerator = GostUrnGenerator;
|
|
57
|
+
render() {
|
|
58
|
+
return renderStandard(this);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class NationalStandard extends GostIdentifier {
|
|
62
|
+
static polymorphicName = "pubid:gost:national-standard";
|
|
63
|
+
static urnGenerator = GostUrnGenerator;
|
|
64
|
+
render() {
|
|
65
|
+
return renderStandard(this);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class ForeignReference extends GostIdentifier {
|
|
69
|
+
static polymorphicName = "pubid:gost:foreign-reference";
|
|
70
|
+
// raw is instance-only: never declared on the wire (the Ruby class
|
|
71
|
+
// declares the attribute without a key_value mapping).
|
|
72
|
+
static get attributes() {
|
|
73
|
+
return extendAttributes(GostIdentifier, { raw: { type: "string" } });
|
|
74
|
+
}
|
|
75
|
+
static mappings = [];
|
|
76
|
+
render() {
|
|
77
|
+
return this.raw ?? "";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// number/year/copublisher/subtype delegate to base (Ruby overrides the
|
|
81
|
+
// getters); the delegated values ride along on the wire via the
|
|
82
|
+
// compactHash hook.
|
|
83
|
+
function delegatedWireFields(model, hash) {
|
|
84
|
+
const base = model.base;
|
|
85
|
+
if (base === undefined)
|
|
86
|
+
return;
|
|
87
|
+
if (base.number !== undefined)
|
|
88
|
+
hash["number"] = base.number;
|
|
89
|
+
if (base.year !== undefined)
|
|
90
|
+
hash["year"] = base.year;
|
|
91
|
+
if (base.copublisher !== undefined)
|
|
92
|
+
hash["copublisher"] = base.copublisher;
|
|
93
|
+
if (base.subtype !== undefined)
|
|
94
|
+
hash["subtype"] = base.subtype;
|
|
95
|
+
}
|
|
96
|
+
export class Harmonized extends GostIdentifier {
|
|
97
|
+
static polymorphicName = "pubid:gost:harmonized";
|
|
98
|
+
static urnGenerator = GostUrnGenerator;
|
|
99
|
+
static get attributes() {
|
|
100
|
+
return extendAttributes(GostIdentifier, {
|
|
101
|
+
base: { type: GostIdentifier },
|
|
102
|
+
adoptedIdentifiers: {
|
|
103
|
+
type: GostIdentifier,
|
|
104
|
+
collection: true,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
static mappings = [
|
|
109
|
+
...GostIdentifier.mappings,
|
|
110
|
+
{ wire: "base", to: "base" },
|
|
111
|
+
{ wire: "adopted_identifiers", to: "adoptedIdentifiers" },
|
|
112
|
+
];
|
|
113
|
+
static compactHash = delegatedWireFields;
|
|
114
|
+
render() {
|
|
115
|
+
const base = this.base?.toHuman() ?? "";
|
|
116
|
+
const adopted = (this.adoptedIdentifiers ?? []).map((a) => a.toHuman()).join(", ");
|
|
117
|
+
return adopted === "" ? base : `${base} (${adopted})`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export class IdenticalAdoption extends GostIdentifier {
|
|
121
|
+
static polymorphicName = "pubid:gost:identical-adoption";
|
|
122
|
+
static urnGenerator = GostUrnGenerator;
|
|
123
|
+
static get attributes() {
|
|
124
|
+
return extendAttributes(GostIdentifier, {
|
|
125
|
+
base: { type: GostIdentifier },
|
|
126
|
+
adopted: { type: GostIdentifier },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
static mappings = [
|
|
130
|
+
...GostIdentifier.mappings,
|
|
131
|
+
{ wire: "base", to: "base" },
|
|
132
|
+
{ wire: "adopted", to: "adopted" },
|
|
133
|
+
];
|
|
134
|
+
static compactHash = delegatedWireFields;
|
|
135
|
+
render() {
|
|
136
|
+
return `${this.base?.toHuman() ?? ""}/${this.adopted?.toHuman() ?? ""}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
for (const klass of [
|
|
140
|
+
InterstateStandard,
|
|
141
|
+
NationalStandard,
|
|
142
|
+
ForeignReference,
|
|
143
|
+
Harmonized,
|
|
144
|
+
IdenticalAdoption,
|
|
145
|
+
]) {
|
|
146
|
+
registerType(klass);
|
|
147
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATED from the Ruby gem (Pubid.prefix_flavors after
|
|
3
|
+
* eager_load_flavors!) — the prefix→flavors routing table. Regenerate
|
|
4
|
+
* from the pubid gem with:
|
|
5
|
+
* bundle exec ruby -e 'require "pubid"; Pubid.eager_load_flavors!;
|
|
6
|
+
* require "json"; puts JSON.pretty_generate(Pubid.prefix_flavors
|
|
7
|
+
* .transform_values { |v| v.sort })'
|
|
8
|
+
* Do not edit by hand.
|
|
9
|
+
*
|
|
10
|
+
* Used by the gost flavor's foreign-adoption routing (prefix_owner in
|
|
11
|
+
* index.ts): the single flavor owning the longest matching prefix parses
|
|
12
|
+
* the adopted identifier; joint prefixes (ISO/IEC, ANSI/AMCA…) fall
|
|
13
|
+
* through to the sorted fallback loop.
|
|
14
|
+
*/
|
|
15
|
+
export declare const PREFIX_ROUTES: Record<string, string[]>;
|