@pubid/pubid 0.1.33 → 0.1.35
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/flavors/api/grammar.js +9 -2
- package/dist/flavors/api/index.js +31 -2
- package/dist/flavors/bipm/index.js +89 -14
- package/dist/flavors/bsi/model.js +13 -9
- package/dist/flavors/iec/index.js +15 -4
- package/dist/flavors/iec/model.js +26 -10
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/model/identifier.d.ts +3 -0
- package/dist/model/identifier.js +6 -0
- package/dist/pubid-urn/decoder.d.ts +27 -0
- package/dist/pubid-urn/decoder.js +165 -0
- package/dist/pubid-urn/encoder.d.ts +30 -0
- package/dist/pubid-urn/encoder.js +197 -0
- package/dist/pubid-urn/errors.d.ts +15 -0
- package/dist/pubid-urn/errors.js +21 -0
- package/dist/pubid-urn/escape.d.ts +19 -0
- package/dist/pubid-urn/escape.js +65 -0
- package/dist/pubid-urn/index.d.ts +65 -0
- package/dist/pubid-urn/index.js +216 -0
- package/dist/pubid-urn/paths.d.ts +26 -0
- package/dist/pubid-urn/paths.js +129 -0
- package/dist/pubid-urn/schema.d.ts +88 -0
- package/dist/pubid-urn/schema.js +186 -0
- package/dist/pubid-urn/schemas/adobe.d.ts +8 -0
- package/dist/pubid-urn/schemas/adobe.js +15 -0
- package/dist/pubid-urn/schemas/amca.d.ts +8 -0
- package/dist/pubid-urn/schemas/amca.js +19 -0
- package/dist/pubid-urn/schemas/ansi.d.ts +8 -0
- package/dist/pubid-urn/schemas/ansi.js +30 -0
- package/dist/pubid-urn/schemas/api.d.ts +8 -0
- package/dist/pubid-urn/schemas/api.js +25 -0
- package/dist/pubid-urn/schemas/ashrae.d.ts +8 -0
- package/dist/pubid-urn/schemas/ashrae.js +28 -0
- package/dist/pubid-urn/schemas/asme.d.ts +8 -0
- package/dist/pubid-urn/schemas/asme.js +26 -0
- package/dist/pubid-urn/schemas/astm.d.ts +8 -0
- package/dist/pubid-urn/schemas/astm.js +38 -0
- package/dist/pubid-urn/schemas/bipm.d.ts +8 -0
- package/dist/pubid-urn/schemas/bipm.js +34 -0
- package/dist/pubid-urn/schemas/bsi.d.ts +8 -0
- package/dist/pubid-urn/schemas/bsi.js +98 -0
- package/dist/pubid-urn/schemas/calconnect.d.ts +8 -0
- package/dist/pubid-urn/schemas/calconnect.js +16 -0
- package/dist/pubid-urn/schemas/ccsds.d.ts +8 -0
- package/dist/pubid-urn/schemas/ccsds.js +18 -0
- package/dist/pubid-urn/schemas/cencenelec.d.ts +8 -0
- package/dist/pubid-urn/schemas/cencenelec.js +39 -0
- package/dist/pubid-urn/schemas/cie.d.ts +8 -0
- package/dist/pubid-urn/schemas/cie.js +41 -0
- package/dist/pubid-urn/schemas/csa.d.ts +8 -0
- package/dist/pubid-urn/schemas/csa.js +45 -0
- package/dist/pubid-urn/schemas/doi.d.ts +8 -0
- package/dist/pubid-urn/schemas/doi.js +14 -0
- package/dist/pubid-urn/schemas/easc.d.ts +8 -0
- package/dist/pubid-urn/schemas/easc.js +16 -0
- package/dist/pubid-urn/schemas/ecma.d.ts +8 -0
- package/dist/pubid-urn/schemas/ecma.js +17 -0
- package/dist/pubid-urn/schemas/etsi.d.ts +8 -0
- package/dist/pubid-urn/schemas/etsi.js +23 -0
- package/dist/pubid-urn/schemas/evs.d.ts +8 -0
- package/dist/pubid-urn/schemas/evs.js +30 -0
- package/dist/pubid-urn/schemas/gb.d.ts +8 -0
- package/dist/pubid-urn/schemas/gb.js +18 -0
- package/dist/pubid-urn/schemas/gost.d.ts +8 -0
- package/dist/pubid-urn/schemas/gost.js +23 -0
- package/dist/pubid-urn/schemas/iala.d.ts +8 -0
- package/dist/pubid-urn/schemas/iala.js +26 -0
- package/dist/pubid-urn/schemas/iana.d.ts +8 -0
- package/dist/pubid-urn/schemas/iana.js +13 -0
- package/dist/pubid-urn/schemas/idf.d.ts +8 -0
- package/dist/pubid-urn/schemas/idf.js +34 -0
- package/dist/pubid-urn/schemas/iec.d.ts +8 -0
- package/dist/pubid-urn/schemas/iec.js +61 -0
- package/dist/pubid-urn/schemas/ieee.d.ts +8 -0
- package/dist/pubid-urn/schemas/ieee.js +116 -0
- package/dist/pubid-urn/schemas/ietf.d.ts +8 -0
- package/dist/pubid-urn/schemas/ietf.js +17 -0
- package/dist/pubid-urn/schemas/iho.d.ts +8 -0
- package/dist/pubid-urn/schemas/iho.js +35 -0
- package/dist/pubid-urn/schemas/index.d.ts +9 -0
- package/dist/pubid-urn/schemas/index.js +97 -0
- package/dist/pubid-urn/schemas/isbn.d.ts +8 -0
- package/dist/pubid-urn/schemas/isbn.js +14 -0
- package/dist/pubid-urn/schemas/iso.d.ts +8 -0
- package/dist/pubid-urn/schemas/iso.js +46 -0
- package/dist/pubid-urn/schemas/itu.d.ts +8 -0
- package/dist/pubid-urn/schemas/itu.js +61 -0
- package/dist/pubid-urn/schemas/jcgm.d.ts +8 -0
- package/dist/pubid-urn/schemas/jcgm.js +21 -0
- package/dist/pubid-urn/schemas/jis.d.ts +8 -0
- package/dist/pubid-urn/schemas/jis.js +24 -0
- package/dist/pubid-urn/schemas/nist.d.ts +8 -0
- package/dist/pubid-urn/schemas/nist.js +90 -0
- package/dist/pubid-urn/schemas/oasis.d.ts +8 -0
- package/dist/pubid-urn/schemas/oasis.js +17 -0
- package/dist/pubid-urn/schemas/ogc.d.ts +8 -0
- package/dist/pubid-urn/schemas/ogc.js +14 -0
- package/dist/pubid-urn/schemas/oiml.d.ts +8 -0
- package/dist/pubid-urn/schemas/oiml.js +39 -0
- package/dist/pubid-urn/schemas/omg.d.ts +8 -0
- package/dist/pubid-urn/schemas/omg.js +15 -0
- package/dist/pubid-urn/schemas/plateau.d.ts +8 -0
- package/dist/pubid-urn/schemas/plateau.js +20 -0
- package/dist/pubid-urn/schemas/sae.d.ts +8 -0
- package/dist/pubid-urn/schemas/sae.js +18 -0
- package/dist/pubid-urn/schemas/tgpp.d.ts +8 -0
- package/dist/pubid-urn/schemas/tgpp.js +17 -0
- package/dist/pubid-urn/schemas/un.d.ts +8 -0
- package/dist/pubid-urn/schemas/un.js +13 -0
- package/dist/pubid-urn/schemas/w3c.d.ts +8 -0
- package/dist/pubid-urn/schemas/w3c.js +23 -0
- package/dist/pubid-urn/schemas/xsf.d.ts +8 -0
- package/dist/pubid-urn/schemas/xsf.js +12 -0
- package/dist/pubid-urn/slug.d.ts +20 -0
- package/dist/pubid-urn/slug.js +47 -0
- package/package.json +2 -1
package/artifact/corpus.json.gz
CHANGED
|
Binary file
|
|
@@ -53,9 +53,16 @@ function buildRules() {
|
|
|
53
53
|
rule("typeless_identifier", () => ref(rules, "publisher")
|
|
54
54
|
.then(docTail())
|
|
55
55
|
.then(ref(rules, "reaffirmation").maybe()));
|
|
56
|
-
rule("second_identifier", () =>
|
|
56
|
+
rule("second_identifier", () =>
|
|
57
|
+
// The reference's grammar binds the second side's captures under
|
|
58
|
+
// second_* names (second_type/second_number/second_part), so the
|
|
59
|
+
// builder can fold first+second and let the second side overwrite.
|
|
60
|
+
ref(rules, "doc_type").as("second_type")
|
|
57
61
|
.then(ref(rules, "space"))
|
|
58
|
-
.then(
|
|
62
|
+
.then(match("[0-9A-Z]").repeat(1, Infinity).as("second_number")
|
|
63
|
+
.then((ref(rules, "dash").then((match("[0-9A-Z]").repeat(1, Infinity)).as("second_part"))).maybe())
|
|
64
|
+
.then(ref(rules, "part_notation").maybe())
|
|
65
|
+
.then(ref(rules, "date_dash").or(ref(rules, "date_colon")).maybe())));
|
|
59
66
|
rule("combined_identifier", () => ref(rules, "mpms_identifier")
|
|
60
67
|
.or(ref(rules, "typed_identifier"))
|
|
61
68
|
.or(ref(rules, "typeless_identifier"))
|
|
@@ -52,13 +52,42 @@ function assignAttributes(identifier, data) {
|
|
|
52
52
|
self[key] = realized;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
+
// The bound grammar wraps the combined form's first/second identifiers
|
|
56
|
+
// under "first"/"second"; the reference folds both wrappers into one
|
|
57
|
+
// hash in parse order, so the second side's number/part/year overwrite
|
|
58
|
+
// the first's — and select_class saw no top-level :type, so a combined
|
|
59
|
+
// form always built the typeless class. Reproduce both.
|
|
60
|
+
const SECOND_KEYS = ["second_number", "second_part", "second_year", "second_type"];
|
|
61
|
+
const SECOND_OVERWRITES = {
|
|
62
|
+
second_number: "number",
|
|
63
|
+
second_part: "part",
|
|
64
|
+
second_year: "year",
|
|
65
|
+
};
|
|
66
|
+
function unflattenCombined(data) {
|
|
67
|
+
if (!SECOND_KEYS.some((k) => k in data))
|
|
68
|
+
return data;
|
|
69
|
+
const out = { ...data };
|
|
70
|
+
delete out["type"];
|
|
71
|
+
for (const [source, target] of Object.entries(SECOND_OVERWRITES)) {
|
|
72
|
+
if (source in out)
|
|
73
|
+
out[target] = out[source];
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
55
77
|
export function buildApiIdentifier(tree) {
|
|
56
78
|
const data = flatten(tree);
|
|
57
|
-
const
|
|
79
|
+
const first = isObj(data["first"]) ? data["first"] : undefined;
|
|
80
|
+
const second = isObj(data["second"]) ? data["second"] : undefined;
|
|
81
|
+
// Parse-order fold: the first side's captures, then the second's.
|
|
82
|
+
const combined = { ...data, ...(first ?? {}), ...(second ?? {}) };
|
|
83
|
+
delete combined["first"];
|
|
84
|
+
delete combined["second"];
|
|
85
|
+
const unflattened = unflattenCombined(combined);
|
|
86
|
+
const typeStr = strv(unflattened["type"]);
|
|
58
87
|
const klass = (typeStr !== undefined ? TYPE_CLASS_MAP[typeStr] : undefined)
|
|
59
88
|
?? TypelessStandardClass;
|
|
60
89
|
const identifier = new klass();
|
|
61
|
-
assignAttributes(identifier,
|
|
90
|
+
assignAttributes(identifier, unflattened);
|
|
62
91
|
return identifier;
|
|
63
92
|
}
|
|
64
93
|
export function apiGrammarImplementation() {
|
|
@@ -4,13 +4,14 @@ import { BaseIdentifier, registerType } from "../../model/identifier.js";
|
|
|
4
4
|
import { extendAttributes, keyValue } from "../../model/attribute.js";
|
|
5
5
|
import { BaseUrnGenerator } from "../../model/urn-generator.js";
|
|
6
6
|
/**
|
|
7
|
-
* 1:1 port of lib/pubid/bipm/ —
|
|
7
|
+
* 1:1 port of lib/pubid/bipm/ — corpus rows across six families:
|
|
8
8
|
* committee documents (REC/RES/DECN/ACT/DECL in short and long EN/FR
|
|
9
|
-
* forms
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
9
|
+
* forms, with the appendix/annex complement and the publisher-prefixed
|
|
10
|
+
* decision spelling), meetings (EN ordinals and double-dash forms, FR
|
|
11
|
+
* <sup>e</sup> réunion), the Metrologia journal, the SI Brochure and
|
|
12
|
+
* its derived products, mises en pratique (incl. Rapport BIPM-YYYY/NN),
|
|
13
|
+
* and CC guides. Historic group names (CCDS) and two-letter language
|
|
14
|
+
* codes (EN/FR) normalize at parse time and never reach the hash.
|
|
14
15
|
*/
|
|
15
16
|
const GROUPS = [
|
|
16
17
|
"CIPM", "CGPM", "JCRB", "CCTF", "CCQM", "CCT", "CCL", "CCAUV",
|
|
@@ -66,15 +67,31 @@ function buildRules() {
|
|
|
66
67
|
const connective = () => str("de la").or(str("du"));
|
|
67
68
|
const ordinal = () => str("st").or(str("nd"), str("rd"), str("th"));
|
|
68
69
|
const meetingWordFr = () => str("Réunion").or(str("réunion"));
|
|
69
|
-
// The trailing date is optional so "CCTF REC 2" parses date-less.
|
|
70
|
+
// The trailing date is optional so "CCTF REC 2" parses date-less. The
|
|
71
|
+
// appendix/annex complement tail ("CIPM DECN 112 A3.5 (2022, F)")
|
|
72
|
+
// rides the number: "A" + digits [ "." + digits ].
|
|
70
73
|
rule("committee_short", () => rules["group"]
|
|
71
|
-
.then(space, rules["type_abbrev"], space.then(rules["number"]
|
|
74
|
+
.then(space, rules["type_abbrev"], space.then(rules["number"], space
|
|
75
|
+
.then(str("A").then(digits.as("appendix"), str(".").then(digits.as("annex")).maybe()))
|
|
76
|
+
.maybe()).maybe(), space.then(rules["year_paren"]).maybe())
|
|
72
77
|
.as("committee_short"));
|
|
78
|
+
// The publisher-prefixed decision form the corpus cites
|
|
79
|
+
// ("BIPM DECN CIPM/101-1 (2012, EN)"): a literal BIPM publisher lead,
|
|
80
|
+
// the outcome type, then the body-qualified resource-specific-id
|
|
81
|
+
// "{body}/{id}". It names the same document as the unprefixed
|
|
82
|
+
// "<body> <TYPE> <id>" form, so the builder dispatches it through the
|
|
83
|
+
// same CommitteeDocument path.
|
|
84
|
+
rule("committee_prefixed", () => str("BIPM")
|
|
85
|
+
.then(space, rules["type_abbrev"], space, rules["group"], str("/"), rules["number"], space.then(rules["year_paren"]).maybe())
|
|
86
|
+
.as("committee_prefixed"));
|
|
73
87
|
rule("committee_long_en", () => rules["group"]
|
|
74
88
|
.then(space, rules["type_name_en"], space.then(rules["number"]).maybe(), space.then(rules["year_paren_nolang"]).maybe())
|
|
75
89
|
.as("committee_long_en"));
|
|
76
90
|
rule("committee_long_fr", () => rules["type_name_fr"]
|
|
77
91
|
.then(space.then(rules["number"]).maybe(), space, connective(), space, rules["group"], space.then(rules["year_paren_nolang"]).maybe())
|
|
92
|
+
// The corpus-cited French body-qualified form ("Décision
|
|
93
|
+
// CIPM/101-1 (2012)").
|
|
94
|
+
.or(rules["type_name_fr"].then(space, rules["group"], str("/"), rules["number"], space.then(rules["year_paren_nolang"]).maybe()))
|
|
78
95
|
.as("committee_long_fr"));
|
|
79
96
|
// Loose consumer form: a French type name in group-leading order.
|
|
80
97
|
rule("committee_group_fr", () => rules["group"]
|
|
@@ -87,6 +104,9 @@ function buildRules() {
|
|
|
87
104
|
rule("meeting_en", () => rules["group"]
|
|
88
105
|
.then(space, rules["number"], ordinal(), space, str("Meeting"), space.then(rules["year_paren"]).maybe())
|
|
89
106
|
.or(rules["group"].then(space, str("Meeting"), space, rules["number"], space.then(rules["year_paren"]).maybe()))
|
|
107
|
+
// The double-dash consumer spellings ("CGPM -- Meeting 1 (1889)").
|
|
108
|
+
.or(rules["group"].then(space, str("--"), space, rules["number"], ordinal(), space, str("Meeting"), space.then(rules["year_paren"]).maybe()))
|
|
109
|
+
.or(rules["group"].then(space, str("--"), space, str("Meeting"), space, rules["number"], space.then(rules["year_paren"]).maybe()))
|
|
90
110
|
.as("meeting_en"));
|
|
91
111
|
rule("meeting_fr", () => rules["group"]
|
|
92
112
|
.then(space, rules["number"], str("<sup>e</sup>"), space, str("réunion"), space.then(rules["year_paren"]).maybe())
|
|
@@ -102,10 +122,23 @@ function buildRules() {
|
|
|
102
122
|
rule("edition", () => digits.then(str("e")).as("edition"));
|
|
103
123
|
rule("version", () => str("v").then(match("[0-9.]").repeat(1, Infinity)).as("version"));
|
|
104
124
|
rule("years", () => digits.then(str("/").then(digits).maybe()).as("years"));
|
|
105
|
-
rule("si_brochure", () =>
|
|
125
|
+
rule("si_brochure", () =>
|
|
126
|
+
// The guide's French publication identifier leads with
|
|
127
|
+
// "Brochure sur le SI" instead of the English brochure name; all
|
|
128
|
+
// three leads render in the canonical "BIPM SI Brochure …" spelling.
|
|
129
|
+
str("BIPM SI Brochure")
|
|
106
130
|
.or(str("SI Brochure"))
|
|
107
|
-
.
|
|
131
|
+
.or(str("Brochure sur le SI"))
|
|
132
|
+
.then(space, str("sur le SI").then(space).maybe(), rules["edition"],
|
|
133
|
+
// The version and the language code are optional (the guide's
|
|
134
|
+
// language-independent form, "SI Brochure 9e (2019, E)").
|
|
135
|
+
space.then(rules["version"]).maybe(), space, str("("), rules["years"], str(", ").then(lang()).maybe(), str(")"))
|
|
108
136
|
.as("si_brochure"));
|
|
137
|
+
// The corpus-cited ordinal-edition spelling ("SI Brochure 9th edition
|
|
138
|
+
// (2019)") normalizes the English ordinal onto the canonical "9e".
|
|
139
|
+
rule("si_brochure_ordinal_edition", () => str("SI Brochure")
|
|
140
|
+
.then(space, digits.then(ordinal()).as("ordinal_edition"), space, str("edition"), space.then(rules["year_paren"]).maybe())
|
|
141
|
+
.as("si_brochure_ordinal_edition"));
|
|
109
142
|
rule("brochure_variant", () => str("Appendix")
|
|
110
143
|
.then(space, digits)
|
|
111
144
|
.or(str("Concise"), str("FAQ"))
|
|
@@ -134,7 +167,7 @@ function buildRules() {
|
|
|
134
167
|
.or(guideBody())
|
|
135
168
|
.as("guide"));
|
|
136
169
|
rule("group_leading", () => rules["meeting_en"].or(rules["meeting_fr"], rules["committee_short"], rules["committee_long_en"], rules["committee_group_fr"], rules["committee_bare"]));
|
|
137
|
-
rule("identifier", () => rules["metrologia"].or(rules["si_brochure"], rules["si_brochure_variant"], rules["si_brochure_section"], rules["mep"], rules["guide"], rules["committee_long_fr"], rules["group_leading"]));
|
|
170
|
+
rule("identifier", () => rules["metrologia"].or(rules["si_brochure"], rules["si_brochure_variant"], rules["si_brochure_section"], rules["si_brochure_ordinal_edition"], rules["mep"], rules["guide"], rules["committee_long_fr"], rules["committee_prefixed"], rules["group_leading"]));
|
|
138
171
|
rule("root", () => rules["identifier"]);
|
|
139
172
|
return rules;
|
|
140
173
|
}
|
|
@@ -183,6 +216,16 @@ function ordinalSuffix(number) {
|
|
|
183
216
|
function frenchConnective(group) {
|
|
184
217
|
return group === "CGPM" ? "de la" : "du";
|
|
185
218
|
}
|
|
219
|
+
/** The appendix/annex complement folds into the identifier segment as
|
|
220
|
+
* "-a{appendix}[.{annex}]" (ruby committee_number_segment); a bare
|
|
221
|
+
* number is digits and dashes only, so the suffix is unambiguous. */
|
|
222
|
+
function committeeNumberSegment(id) {
|
|
223
|
+
if (id.number === undefined)
|
|
224
|
+
return "";
|
|
225
|
+
if (id.appendix === undefined)
|
|
226
|
+
return id.number;
|
|
227
|
+
return `${id.number}-a${id.appendix}${id.annex === undefined ? "" : `.${id.annex}`}`;
|
|
228
|
+
}
|
|
186
229
|
export class BipmIdentifier extends BaseIdentifier {
|
|
187
230
|
static attributes = extendAttributes(BaseIdentifier, BIPM_ATTRS);
|
|
188
231
|
static mappings = BIPM_MAPPINGS;
|
|
@@ -205,7 +248,7 @@ class BipmUrnGenerator extends BaseUrnGenerator {
|
|
|
205
248
|
if (id instanceof BipmCommitteeDocument) {
|
|
206
249
|
const parts = [
|
|
207
250
|
"urn", "bipm", id.group?.toLowerCase() ?? "",
|
|
208
|
-
id.type_code?.toLowerCase() ?? "", id
|
|
251
|
+
id.type_code?.toLowerCase() ?? "", committeeNumberSegment(id),
|
|
209
252
|
];
|
|
210
253
|
if (id.year !== undefined)
|
|
211
254
|
parts.push(String(id.year));
|
|
@@ -248,7 +291,15 @@ class BipmCommitteeDocument extends BipmIdentifier {
|
|
|
248
291
|
}
|
|
249
292
|
const lang = this.language === undefined ? "" : `, ${this.language}`;
|
|
250
293
|
const type = this.type_code === undefined ? "" : ` ${this.type_code}`;
|
|
251
|
-
return `${this.group ?? ""}${type}${this.numberSegment()}${this.yearSegment(lang)}`;
|
|
294
|
+
return `${this.group ?? ""}${type}${this.numberSegment()}${this.appendixSegment()}${this.yearSegment(lang)}`;
|
|
295
|
+
}
|
|
296
|
+
/** The complement tail "{identifier} A{appendix}[.{annex}]" — parts and
|
|
297
|
+
* subparts are dash forms inside `number` itself; appendices and
|
|
298
|
+
* annexes are this separate tail. */
|
|
299
|
+
appendixSegment() {
|
|
300
|
+
if (this.appendix === undefined)
|
|
301
|
+
return "";
|
|
302
|
+
return ` A${this.appendix}${this.annex === undefined ? "" : `.${this.annex}`}`;
|
|
252
303
|
}
|
|
253
304
|
}
|
|
254
305
|
registerType(BipmCommitteeDocument);
|
|
@@ -294,7 +345,8 @@ class BipmSiBrochure extends BipmIdentifier {
|
|
|
294
345
|
}
|
|
295
346
|
const phrase = this.language === "F" ? "sur le SI " : "";
|
|
296
347
|
const lang = this.language === undefined ? "" : `, ${this.language}`;
|
|
297
|
-
|
|
348
|
+
const version = this.version === undefined ? "" : ` ${this.version}`;
|
|
349
|
+
return `BIPM SI Brochure ${phrase}${this.edition}${version} (${this.years}${lang})`;
|
|
298
350
|
}
|
|
299
351
|
}
|
|
300
352
|
registerType(BipmSiBrochure);
|
|
@@ -336,6 +388,12 @@ class BipmBuilder {
|
|
|
336
388
|
};
|
|
337
389
|
if (tree["committee_short"] !== undefined)
|
|
338
390
|
return this.buildCommittee(node("committee_short"), "short");
|
|
391
|
+
if (tree["committee_prefixed"] !== undefined) {
|
|
392
|
+
// The corpus's "BIPM DECN CIPM/101-1 (2012, EN)" publisher-prefixed
|
|
393
|
+
// spelling names the same document as the unprefixed short form; the
|
|
394
|
+
// BIPM lead is the constant publisher, so it carries no attribute.
|
|
395
|
+
return this.buildCommittee(node("committee_prefixed"), "short");
|
|
396
|
+
}
|
|
339
397
|
if (tree["committee_long_en"] !== undefined)
|
|
340
398
|
return this.buildCommittee(node("committee_long_en"), "long", "E");
|
|
341
399
|
if (tree["committee_bare"] !== undefined)
|
|
@@ -373,6 +431,21 @@ class BipmBuilder {
|
|
|
373
431
|
const n = node("si_brochure_section");
|
|
374
432
|
return new BipmSiBrochure({ part: s(n["part"]) });
|
|
375
433
|
}
|
|
434
|
+
if (tree["si_brochure_ordinal_edition"] !== undefined) {
|
|
435
|
+
// The guide prints the edition "9e"; the corpus-cited "9th edition"
|
|
436
|
+
// spelling is the English ordinal form of the same edition,
|
|
437
|
+
// normalized onto the canonical "9e" value. The version is absent
|
|
438
|
+
// from the input, so it stays nil and drops from the hash.
|
|
439
|
+
const n = node("si_brochure_ordinal_edition");
|
|
440
|
+
const edition = `${(s(n["ordinal_edition"]) ?? "").replace(/(st|nd|rd|th)$/, "")}e`;
|
|
441
|
+
const year = s(n["year"]);
|
|
442
|
+
return new BipmSiBrochure({
|
|
443
|
+
number: edition,
|
|
444
|
+
edition,
|
|
445
|
+
...(year !== undefined ? { years: year } : {}),
|
|
446
|
+
language: normalizeLanguage(s(n["language"])),
|
|
447
|
+
});
|
|
448
|
+
}
|
|
376
449
|
if (tree["mep"] !== undefined) {
|
|
377
450
|
const n = node("mep");
|
|
378
451
|
const mepCode = s(n["mep_code"]);
|
|
@@ -407,6 +480,8 @@ class BipmBuilder {
|
|
|
407
480
|
...(year !== undefined ? { year: Number(year) } : {}),
|
|
408
481
|
language: normalizeLanguage(language ?? s(n["language"])),
|
|
409
482
|
form,
|
|
483
|
+
appendix: s(n["appendix"]),
|
|
484
|
+
annex: s(n["annex"]),
|
|
410
485
|
});
|
|
411
486
|
}
|
|
412
487
|
buildMeeting(n, language) {
|
|
@@ -776,15 +776,6 @@ export const ConsolidatedIdentifierClass = bsiClassWith({ kind: "consolidated-id
|
|
|
776
776
|
.replace(/ - TC$/, "")
|
|
777
777
|
.replace(/ PDF$/, "")
|
|
778
778
|
.replace(/ \([A-Z][a-z]+\)$/, "");
|
|
779
|
-
// The gem serializes only the `type` block onto the consolidated
|
|
780
|
-
// wrapper (the members carry their own full typed-stage trio).
|
|
781
|
-
ConsolidatedIdentifierClass.compactHash =
|
|
782
|
-
(model, hash) => {
|
|
783
|
-
const ts = model.typedStage;
|
|
784
|
-
if (ts === undefined || ts.code === "pubbs")
|
|
785
|
-
return;
|
|
786
|
-
hash["type"] = { name: ts.name, abbr: ts.abbr[0], type_code: ts.typeCode };
|
|
787
|
-
};
|
|
788
779
|
for (const supp of supplements) {
|
|
789
780
|
if (supp instanceof Amendment) {
|
|
790
781
|
if (supp.amd_suffix_form) {
|
|
@@ -838,6 +829,19 @@ export const ConsolidatedIdentifierClass = bsiClassWith({ kind: "consolidated-id
|
|
|
838
829
|
result += ` (R${ry})`;
|
|
839
830
|
return result;
|
|
840
831
|
});
|
|
832
|
+
// The gem serializes only the `type` block onto the consolidated
|
|
833
|
+
// wrapper (the members carry their own full typed-stage trio). This
|
|
834
|
+
// override is installed at load time, NOT inside the render closure: a
|
|
835
|
+
// fromHash -> toHash of a not-yet-rendered consolidated id must
|
|
836
|
+
// serialize identically (the pubid-scheme URN's canonical gate,
|
|
837
|
+
// pubid-ts#161, depends on it).
|
|
838
|
+
ConsolidatedIdentifierClass.compactHash =
|
|
839
|
+
(model, hash) => {
|
|
840
|
+
const ts = model.typedStage;
|
|
841
|
+
if (ts === undefined || ts.code === "pubbs")
|
|
842
|
+
return;
|
|
843
|
+
hash["type"] = { name: ts.name, abbr: ts.abbr[0], type_code: ts.typeCode };
|
|
844
|
+
};
|
|
841
845
|
// ConsolidatedIdentifier#to_urn override (renderer.rb class method):
|
|
842
846
|
// base URN plus per-supplement segments.
|
|
843
847
|
export function consolidatedUrn(id) {
|
|
@@ -133,12 +133,16 @@ class IecBuilder {
|
|
|
133
133
|
const kind = typedStage.typeCode === "wp" ? "is" : typedStage.typeCode;
|
|
134
134
|
const attrs = {};
|
|
135
135
|
this.commonAttrs(tree, attrs);
|
|
136
|
-
// TRF with an embedded CISPR document: the
|
|
137
|
-
//
|
|
136
|
+
// TRF with an embedded CISPR document: the form itself has no number
|
|
137
|
+
// — the document it reports on is built as the nested identifier and
|
|
138
|
+
// serializes under `cispr_identifier` (pubid#687). The runtime
|
|
139
|
+
// columns stay for the renderer and the official-URN slot.
|
|
138
140
|
if (tree["trf_org"] !== undefined) {
|
|
139
141
|
const cisprComponents = numberComponents(tree["number_with_part"]);
|
|
140
142
|
attrs["cispr_number"] = cisprComponents["number"];
|
|
141
143
|
attrs["cispr_part"] = cisprComponents["part"];
|
|
144
|
+
const cispr = new IEC_CLASSES.KIND_CLASSES["is"]({ publisher: "CISPR", ...cisprComponents });
|
|
145
|
+
attrs["cispr_identifier"] = cispr;
|
|
142
146
|
delete attrs["number"];
|
|
143
147
|
delete attrs["part"];
|
|
144
148
|
delete attrs["subpart"];
|
|
@@ -300,9 +304,14 @@ class IecBuilder {
|
|
|
300
304
|
});
|
|
301
305
|
}
|
|
302
306
|
buildWorkingProgramme(tree) {
|
|
307
|
+
// The grammar captures a trailing space on wp_type ("PWI TR 100-36"
|
|
308
|
+
// -> "TR "); ruby strips it at the construction site (its builder's
|
|
309
|
+
// :wp_type cast) so the stored value — and the hash and URN — is the
|
|
310
|
+
// stripped form, an all-space value dropping out entirely.
|
|
311
|
+
const wpTypeRaw = s(tree["wp_type"])?.replace(/\s+$/, "");
|
|
303
312
|
const attrs = {
|
|
304
313
|
wp_stage: s(tree["wp_stage"]),
|
|
305
|
-
wp_type:
|
|
314
|
+
wp_type: wpTypeRaw === "" ? undefined : wpTypeRaw,
|
|
306
315
|
};
|
|
307
316
|
if (tree["wp_raw"] !== undefined) {
|
|
308
317
|
// TC-style name ("SyCCOMM-1"): split the trailing number as the part.
|
|
@@ -329,7 +338,9 @@ class IecBuilder {
|
|
|
329
338
|
buildWorkingDocument(tree) {
|
|
330
339
|
const attrs = {
|
|
331
340
|
technical_committee: s(tree["technical_committee"]),
|
|
332
|
-
|
|
341
|
+
// The document number is the inherited `number` (pubid#687: the
|
|
342
|
+
// parallel wd_number slot used to serialize nowhere).
|
|
343
|
+
number: s(tree["wd_number"]),
|
|
333
344
|
wd_language: s(tree["wd_language"]),
|
|
334
345
|
wd_stage: s(tree["wd_stage"]),
|
|
335
346
|
};
|
|
@@ -190,7 +190,10 @@ function iecSingleClass(kind) {
|
|
|
190
190
|
registerType(IecSingleIdentifier);
|
|
191
191
|
return IecSingleIdentifier;
|
|
192
192
|
}
|
|
193
|
-
/** Working documents: the
|
|
193
|
+
/** Working documents: the committee/number/language/stage identity
|
|
194
|
+
* serializes (pubid#687) — the document number is the inherited
|
|
195
|
+
* `number`, and the five plain strings sit in the hash under their own
|
|
196
|
+
* keys (ruby WorkingDocument's key_value block, render_default off). */
|
|
194
197
|
function iecWorkingDocumentClass() {
|
|
195
198
|
class IecWorkingDocument extends IecIdentifier {
|
|
196
199
|
static polymorphicName = "pubid:iec:working-document";
|
|
@@ -199,11 +202,10 @@ function iecWorkingDocumentClass() {
|
|
|
199
202
|
wp_stage: { type: "string" },
|
|
200
203
|
wp_type: { type: "string" },
|
|
201
204
|
technical_committee: { type: "string" },
|
|
202
|
-
wd_number: { type: "string" },
|
|
203
205
|
wd_language: { type: "string" },
|
|
204
206
|
wd_stage: { type: "string" },
|
|
205
207
|
});
|
|
206
|
-
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "subpart", to: "subpart" }, { wire: "edition", to: "edition" });
|
|
208
|
+
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "subpart", to: "subpart" }, { wire: "edition", to: "edition" }, { wire: "technical_committee", to: "technical_committee" }, { wire: "wd_language", to: "wd_language" }, { wire: "wd_stage", to: "wd_stage" }, { wire: "wp_stage", to: "wp_stage" }, { wire: "wp_type", to: "wp_type" });
|
|
207
209
|
render() {
|
|
208
210
|
if (this.wp_stage !== undefined) {
|
|
209
211
|
const parts = [this.wp_stage];
|
|
@@ -225,8 +227,8 @@ function iecWorkingDocumentClass() {
|
|
|
225
227
|
const parts = [];
|
|
226
228
|
if (this.technical_committee !== undefined)
|
|
227
229
|
parts.push(this.technical_committee);
|
|
228
|
-
if (this.
|
|
229
|
-
let numPart = this.
|
|
230
|
+
if (this.number !== undefined) {
|
|
231
|
+
let numPart = String(this.number);
|
|
230
232
|
if (this.wd_language !== undefined)
|
|
231
233
|
numPart += `(${this.wd_language})`;
|
|
232
234
|
parts.push(numPart);
|
|
@@ -249,6 +251,10 @@ function iecTrfClass() {
|
|
|
249
251
|
cispr_number: { type: "string" },
|
|
250
252
|
cispr_part: { type: "string" },
|
|
251
253
|
trf_info: { type: "string" },
|
|
254
|
+
// The embedded CISPR document is part of the form's identity and
|
|
255
|
+
// serializes under its own key (pubid#687), dispatching on the
|
|
256
|
+
// nested _type like ruby's polymorphic attribute.
|
|
257
|
+
cispr_identifier: { type: IecIdentifier },
|
|
252
258
|
});
|
|
253
259
|
static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, { wire: "subpart", to: "subpart" }, { wire: "year", to: "date", toWire: (m) => m["date"]?.year, fromWire: yearFromWire }, { wire: "month", to: "date", toWire: (m) => m["date"]?.month, fromWire: () => undefined }, { wire: "day", to: "date", toWire: (m) => m["date"]?.day, fromWire: () => undefined }, {
|
|
254
260
|
wire: "publisher",
|
|
@@ -263,13 +269,23 @@ function iecTrfClass() {
|
|
|
263
269
|
return list !== undefined && list.length > 0 ? list : undefined;
|
|
264
270
|
},
|
|
265
271
|
fromWire: (h) => (Array.isArray(h["copublishers"]) ? h["copublishers"].map(String) : undefined),
|
|
266
|
-
});
|
|
272
|
+
}, { wire: "cispr_identifier", to: "cispr_identifier" });
|
|
267
273
|
render() {
|
|
268
274
|
let result = this.publisherPortion() + " ";
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
275
|
+
// The parse path carries the CISPR document in the runtime columns;
|
|
276
|
+
// a fromHash-built form carries the nested identifier — read the
|
|
277
|
+
// document from whichever one is set (ruby renderer).
|
|
278
|
+
const cispr = this.cispr_identifier;
|
|
279
|
+
const cisprNumber = this.cispr_number ??
|
|
280
|
+
(cispr?.number === undefined || cispr.number === null ? undefined : String(cispr.number));
|
|
281
|
+
const cisprPart = this.cispr_part ??
|
|
282
|
+
(cispr?.part === undefined || cispr.part === null || cispr.part === ""
|
|
283
|
+
? undefined
|
|
284
|
+
: String(cispr.part));
|
|
285
|
+
if (cisprNumber !== undefined) {
|
|
286
|
+
let numStr = cisprNumber;
|
|
287
|
+
if (cisprPart !== undefined && cisprPart !== "")
|
|
288
|
+
numStr += `-${cisprPart}`;
|
|
273
289
|
let cisprStr = `CISPR ${numStr}`;
|
|
274
290
|
if (this.date !== undefined)
|
|
275
291
|
cisprStr += `:${this.date.render()}`;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export { checkCase, runFlavor, runCorpus } from "./conformance/runner.js";
|
|
|
8
8
|
export { corpusModeImplementation, CorpusBackedIdentifier } from "./implementations/corpus-mode.js";
|
|
9
9
|
export { grammarImplementation } from "./flavors/index.js";
|
|
10
10
|
export { subsetMatch, valueMatch, exactMatch } from "./model/subset-match.js";
|
|
11
|
+
export * as PubidUrn from "./pubid-urn/index.js";
|
package/dist/index.js
CHANGED
|
@@ -8,3 +8,5 @@ export { corpusModeImplementation, CorpusBackedIdentifier } from "./implementati
|
|
|
8
8
|
// (pubid/pubid-ts#63 - the estate serving codec).
|
|
9
9
|
export { grammarImplementation } from "./flavors/index.js";
|
|
10
10
|
export { subsetMatch, valueMatch, exactMatch } from "./model/subset-match.js";
|
|
11
|
+
// The pubid-scheme URN codec and the slug form (pubid-ts#161).
|
|
12
|
+
export * as PubidUrn from "./pubid-urn/index.js";
|
|
@@ -58,6 +58,9 @@ export declare abstract class BaseIdentifier {
|
|
|
58
58
|
toString(): string;
|
|
59
59
|
/** Ruby to_urn: the flavor's UrnGenerator, else the base template. */
|
|
60
60
|
toUrn(): string;
|
|
61
|
+
/** The pubid-scheme URN (pubid#656): the lossless form,
|
|
62
|
+
* `PubidUrn.parse(this.toPubidUrn()).toHash == this.toHash`. */
|
|
63
|
+
toPubidUrn(): string;
|
|
61
64
|
fromHash(hash: Record<string, unknown>): BaseIdentifier;
|
|
62
65
|
/** SubsetMatch: an all-parts reference skips the part attributes. */
|
|
63
66
|
subsetAllPartsWildcard(): boolean;
|
package/dist/model/identifier.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import { FLAT_SCALAR_COMPONENTS, FLAT_SCALAR_FIELDS, BASE_ATTRIBUTES } from "./attribute.js";
|
|
21
21
|
import { Component } from "./component.js";
|
|
22
22
|
import { BaseUrnGenerator } from "./urn-generator.js";
|
|
23
|
+
import { generate as generatePubidUrn } from "../pubid-urn/index.js";
|
|
23
24
|
const TYPE_REGISTRY = new Map();
|
|
24
25
|
/** Register a concrete identifier class under its polymorphic _type (the *_TYPE_MAP role). */
|
|
25
26
|
export function registerType(klass) {
|
|
@@ -107,6 +108,11 @@ export class BaseIdentifier {
|
|
|
107
108
|
const Generator = this.constructor.urnGenerator ?? BaseUrnGenerator;
|
|
108
109
|
return new Generator(this).generate();
|
|
109
110
|
}
|
|
111
|
+
/** The pubid-scheme URN (pubid#656): the lossless form,
|
|
112
|
+
* `PubidUrn.parse(this.toPubidUrn()).toHash == this.toHash`. */
|
|
113
|
+
toPubidUrn() {
|
|
114
|
+
return generatePubidUrn(this.toHash());
|
|
115
|
+
}
|
|
110
116
|
fromHash(hash) {
|
|
111
117
|
return this.constructor.fromHash(hash);
|
|
112
118
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads the body of a pubid-scheme URN back into an identifier hash —
|
|
3
|
+
* the TS mirror of Pubid::PubidUrn::Decoder (lib/pubid/pubid_urn/decoder.rb).
|
|
4
|
+
* The inverse of Encoder; strict about structure. Canonical form (atom
|
|
5
|
+
* order, escapes, trailing "~") is checked by PubidUrn.parse, which
|
|
6
|
+
* re-encodes the result and compares.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Decoder {
|
|
9
|
+
private readonly body;
|
|
10
|
+
constructor(body: string);
|
|
11
|
+
/** The identifier hash (`_type` and `base` included). */
|
|
12
|
+
toIdentifierHash(): Record<string, unknown>;
|
|
13
|
+
private decodeIdentifier;
|
|
14
|
+
private nodeSchema;
|
|
15
|
+
private decodeCore;
|
|
16
|
+
private coreValue;
|
|
17
|
+
private decodeAtoms;
|
|
18
|
+
private atomValue;
|
|
19
|
+
/** The value of a :string, :bool or list atom, or of a facet. */
|
|
20
|
+
private slotValue;
|
|
21
|
+
private scalar;
|
|
22
|
+
private boolean;
|
|
23
|
+
private integer;
|
|
24
|
+
private nested;
|
|
25
|
+
private decodeFacets;
|
|
26
|
+
private fail;
|
|
27
|
+
}
|