@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.
Files changed (183) hide show
  1. package/artifact/corpus.json.gz +0 -0
  2. package/dist/conformance/cli.js +5 -1
  3. package/dist/flavors/amca/grammar.d.ts +4 -0
  4. package/dist/flavors/amca/grammar.js +87 -0
  5. package/dist/flavors/amca/index.d.ts +4 -0
  6. package/dist/flavors/amca/index.js +76 -0
  7. package/dist/flavors/amca/model.d.ts +29 -0
  8. package/dist/flavors/amca/model.js +146 -0
  9. package/dist/flavors/ansi/index.d.ts +24 -0
  10. package/dist/flavors/ansi/index.js +147 -0
  11. package/dist/flavors/api/grammar.d.ts +4 -0
  12. package/dist/flavors/api/grammar.js +79 -0
  13. package/dist/flavors/api/index.d.ts +4 -0
  14. package/dist/flavors/api/index.js +71 -0
  15. package/dist/flavors/api/model.d.ts +27 -0
  16. package/dist/flavors/api/model.js +114 -0
  17. package/dist/flavors/ashrae/grammar.d.ts +2 -0
  18. package/dist/flavors/ashrae/grammar.js +667 -0
  19. package/dist/flavors/ashrae/index.d.ts +4 -0
  20. package/dist/flavors/ashrae/index.js +306 -0
  21. package/dist/flavors/ashrae/model.d.ts +87 -0
  22. package/dist/flavors/ashrae/model.js +335 -0
  23. package/dist/flavors/ashrae/preprocessor.d.ts +7 -0
  24. package/dist/flavors/ashrae/preprocessor.js +44 -0
  25. package/dist/flavors/asme/grammar.d.ts +2 -0
  26. package/dist/flavors/asme/grammar.js +200 -0
  27. package/dist/flavors/asme/index.d.ts +4 -0
  28. package/dist/flavors/asme/index.js +138 -0
  29. package/dist/flavors/asme/model.d.ts +34 -0
  30. package/dist/flavors/asme/model.js +123 -0
  31. package/dist/flavors/astm/grammar.d.ts +2 -0
  32. package/dist/flavors/astm/grammar.js +110 -0
  33. package/dist/flavors/astm/index.d.ts +2 -0
  34. package/dist/flavors/astm/index.js +127 -0
  35. package/dist/flavors/astm/model.d.ts +42 -0
  36. package/dist/flavors/astm/model.js +263 -0
  37. package/dist/flavors/bipm/index.d.ts +30 -0
  38. package/dist/flavors/bipm/index.js +434 -0
  39. package/dist/flavors/bsi/grammar.d.ts +4 -0
  40. package/dist/flavors/bsi/grammar.js +535 -0
  41. package/dist/flavors/bsi/index.d.ts +4 -0
  42. package/dist/flavors/bsi/index.js +941 -0
  43. package/dist/flavors/bsi/model.d.ts +127 -0
  44. package/dist/flavors/bsi/model.js +863 -0
  45. package/dist/flavors/calconnect/index.d.ts +21 -0
  46. package/dist/flavors/calconnect/index.js +113 -0
  47. package/dist/flavors/cen_cenelec/grammar.d.ts +4 -0
  48. package/dist/flavors/cen_cenelec/grammar.js +107 -0
  49. package/dist/flavors/cen_cenelec/index.d.ts +5 -0
  50. package/dist/flavors/cen_cenelec/index.js +263 -0
  51. package/dist/flavors/cen_cenelec/model.d.ts +45 -0
  52. package/dist/flavors/cen_cenelec/model.js +490 -0
  53. package/dist/flavors/cie/grammar.d.ts +5 -0
  54. package/dist/flavors/cie/grammar.js +127 -0
  55. package/dist/flavors/cie/index.d.ts +2 -0
  56. package/dist/flavors/cie/index.js +311 -0
  57. package/dist/flavors/cie/model.d.ts +122 -0
  58. package/dist/flavors/cie/model.js +464 -0
  59. package/dist/flavors/csa/grammar.d.ts +6 -0
  60. package/dist/flavors/csa/grammar.js +239 -0
  61. package/dist/flavors/csa/index.d.ts +2 -0
  62. package/dist/flavors/csa/index.js +634 -0
  63. package/dist/flavors/csa/model.d.ts +33 -0
  64. package/dist/flavors/csa/model.js +505 -0
  65. package/dist/flavors/doi/index.d.ts +13 -0
  66. package/dist/flavors/doi/index.js +65 -0
  67. package/dist/flavors/easc/index.d.ts +4 -0
  68. package/dist/flavors/easc/index.js +107 -0
  69. package/dist/flavors/ecma/index.d.ts +4 -0
  70. package/dist/flavors/ecma/index.js +117 -0
  71. package/dist/flavors/etsi/index.d.ts +14 -0
  72. package/dist/flavors/etsi/index.js +307 -0
  73. package/dist/flavors/evs/grammar.d.ts +2 -0
  74. package/dist/flavors/evs/grammar.js +22 -0
  75. package/dist/flavors/evs/index.d.ts +2 -0
  76. package/dist/flavors/evs/index.js +24 -0
  77. package/dist/flavors/evs/model.d.ts +19 -0
  78. package/dist/flavors/evs/model.js +36 -0
  79. package/dist/flavors/gb/index.d.ts +24 -0
  80. package/dist/flavors/gb/index.js +111 -0
  81. package/dist/flavors/gost/grammar.d.ts +2 -0
  82. package/dist/flavors/gost/grammar.js +46 -0
  83. package/dist/flavors/gost/index.d.ts +2 -0
  84. package/dist/flavors/gost/index.js +165 -0
  85. package/dist/flavors/gost/model.d.ts +61 -0
  86. package/dist/flavors/gost/model.js +147 -0
  87. package/dist/flavors/gost/prefix_routes.d.ts +15 -0
  88. package/dist/flavors/gost/prefix_routes.js +192 -0
  89. package/dist/flavors/iala/index.d.ts +32 -0
  90. package/dist/flavors/iala/index.js +300 -0
  91. package/dist/flavors/iana/index.d.ts +19 -0
  92. package/dist/flavors/iana/index.js +71 -0
  93. package/dist/flavors/idf/grammar.d.ts +2 -0
  94. package/dist/flavors/idf/grammar.js +71 -0
  95. package/dist/flavors/idf/index.d.ts +2 -0
  96. package/dist/flavors/idf/index.js +100 -0
  97. package/dist/flavors/idf/model.d.ts +54 -0
  98. package/dist/flavors/idf/model.js +359 -0
  99. package/dist/flavors/iec/grammar.d.ts +3 -0
  100. package/dist/flavors/iec/grammar.js +187 -0
  101. package/dist/flavors/iec/index.d.ts +4 -0
  102. package/dist/flavors/iec/index.js +507 -0
  103. package/dist/flavors/iec/model.d.ts +54 -0
  104. package/dist/flavors/iec/model.js +437 -0
  105. package/dist/flavors/ieee/grammar.d.ts +2 -0
  106. package/dist/flavors/ieee/grammar.js +626 -0
  107. package/dist/flavors/ieee/index.d.ts +3 -0
  108. package/dist/flavors/ieee/index.js +1186 -0
  109. package/dist/flavors/ieee/model.d.ts +142 -0
  110. package/dist/flavors/ieee/model.js +1148 -0
  111. package/dist/flavors/ieee/preprocessor.d.ts +8 -0
  112. package/dist/flavors/ieee/preprocessor.js +410 -0
  113. package/dist/flavors/ieee/update_codes.d.ts +8 -0
  114. package/dist/flavors/ieee/update_codes.js +191 -0
  115. package/dist/flavors/ietf/index.d.ts +30 -0
  116. package/dist/flavors/ietf/index.js +151 -0
  117. package/dist/flavors/iho/index.d.ts +4 -0
  118. package/dist/flavors/iho/index.js +126 -0
  119. package/dist/flavors/index.d.ts +9 -0
  120. package/dist/flavors/index.js +136 -0
  121. package/dist/flavors/isbn/index.d.ts +13 -0
  122. package/dist/flavors/isbn/index.js +97 -0
  123. package/dist/flavors/iso/grammar.d.ts +2 -0
  124. package/dist/flavors/iso/grammar.js +207 -0
  125. package/dist/flavors/iso/index.d.ts +2 -0
  126. package/dist/flavors/iso/index.js +343 -0
  127. package/dist/flavors/iso/model.d.ts +159 -0
  128. package/dist/flavors/iso/model.js +802 -0
  129. package/dist/flavors/iso/stages.generated.d.ts +13 -0
  130. package/dist/flavors/iso/stages.generated.js +391 -0
  131. package/dist/flavors/itu/grammar.d.ts +4 -0
  132. package/dist/flavors/itu/grammar.js +205 -0
  133. package/dist/flavors/itu/index.d.ts +2 -0
  134. package/dist/flavors/itu/index.js +224 -0
  135. package/dist/flavors/itu/model.d.ts +184 -0
  136. package/dist/flavors/itu/model.js +599 -0
  137. package/dist/flavors/jcgm/index.d.ts +7 -0
  138. package/dist/flavors/jcgm/index.js +300 -0
  139. package/dist/flavors/jis/index.d.ts +4 -0
  140. package/dist/flavors/jis/index.js +267 -0
  141. package/dist/flavors/nist/grammar.d.ts +2 -0
  142. package/dist/flavors/nist/grammar.js +287 -0
  143. package/dist/flavors/nist/index.d.ts +2 -0
  144. package/dist/flavors/nist/index.js +1244 -0
  145. package/dist/flavors/nist/model.d.ts +155 -0
  146. package/dist/flavors/nist/model.js +823 -0
  147. package/dist/flavors/nist/preprocessor.d.ts +11 -0
  148. package/dist/flavors/nist/preprocessor.js +281 -0
  149. package/dist/flavors/oasis/index.d.ts +23 -0
  150. package/dist/flavors/oasis/index.js +110 -0
  151. package/dist/flavors/ogc/index.d.ts +20 -0
  152. package/dist/flavors/ogc/index.js +75 -0
  153. package/dist/flavors/oiml/grammar.d.ts +2 -0
  154. package/dist/flavors/oiml/grammar.js +180 -0
  155. package/dist/flavors/oiml/implementation.d.ts +9 -0
  156. package/dist/flavors/oiml/implementation.js +16 -0
  157. package/dist/flavors/oiml/model.d.ts +4 -0
  158. package/dist/flavors/oiml/model.js +462 -0
  159. package/dist/flavors/omg/index.d.ts +14 -0
  160. package/dist/flavors/omg/index.js +73 -0
  161. package/dist/flavors/plateau/index.d.ts +4 -0
  162. package/dist/flavors/plateau/index.js +97 -0
  163. package/dist/flavors/tgpp/index.d.ts +4 -0
  164. package/dist/flavors/tgpp/index.js +122 -0
  165. package/dist/flavors/un/index.d.ts +13 -0
  166. package/dist/flavors/un/index.js +90 -0
  167. package/dist/flavors/w3c/index.d.ts +14 -0
  168. package/dist/flavors/w3c/index.js +148 -0
  169. package/dist/flavors/xsf/index.d.ts +18 -0
  170. package/dist/flavors/xsf/index.js +65 -0
  171. package/dist/grammar/engine.d.ts +78 -0
  172. package/dist/grammar/engine.js +351 -0
  173. package/dist/model/attribute.d.ts +54 -0
  174. package/dist/model/attribute.js +53 -0
  175. package/dist/model/builder.d.ts +39 -0
  176. package/dist/model/builder.js +124 -0
  177. package/dist/model/component.d.ts +70 -0
  178. package/dist/model/component.js +170 -0
  179. package/dist/model/identifier.d.ts +83 -0
  180. package/dist/model/identifier.js +270 -0
  181. package/dist/model/urn-generator.d.ts +27 -0
  182. package/dist/model/urn-generator.js +91 -0
  183. package/package.json +1 -1
@@ -0,0 +1,490 @@
1
+ import { BaseIdentifier, registerType } from "../../model/identifier.js";
2
+ import { extendAttributes, keyValue } from "../../model/attribute.js";
3
+ import { PubidDate } from "../../model/component.js";
4
+ import { BaseUrnGenerator } from "../../model/urn-generator.js";
5
+ export const TYPED_STAGES = [
6
+ { code: "pwien", stage_code: "preliminary", type_code: "en", abbr: "pWI EN" },
7
+ { code: "pren", stage_code: "proposal", type_code: "en", abbr: "prEN" },
8
+ { code: "fpren", stage_code: "final_proposal", type_code: "en", abbr: "FprEN" },
9
+ { code: "fven", stage_code: "formal_vote", type_code: "en", abbr: "FV prEN" },
10
+ { code: "ven", stage_code: "vote", type_code: "en", abbr: "vEN" },
11
+ { code: "puben", stage_code: "published", type_code: "en", abbr: "EN" },
12
+ { code: "rven", stage_code: "review", type_code: "en", abbr: "rvEN" },
13
+ { code: "racen", stage_code: "reactivation", type_code: "en", abbr: "racEN" },
14
+ { code: "wden", stage_code: "withdrawn", type_code: "en", abbr: "wdEN" },
15
+ { code: "pubts", stage_code: "published", type_code: "ts", abbr: "TS" },
16
+ { code: "prts", stage_code: "proposal", type_code: "ts", abbr: "prTS" },
17
+ { code: "pubtr", stage_code: "published", type_code: "tr", abbr: "TR" },
18
+ { code: "pubcwa", stage_code: "published", type_code: "cwa", abbr: "CWA" },
19
+ { code: "pubguide", stage_code: "published", type_code: "guide", abbr: "Guide" },
20
+ { code: "pubhd", stage_code: "published", type_code: "hd", abbr: "HD" },
21
+ { code: "pubes", stage_code: "published", type_code: "es", abbr: "ES" },
22
+ { code: "pubcr", stage_code: "published", type_code: "cr", abbr: "CR" },
23
+ { code: "pubenv", stage_code: "published", type_code: "env", abbr: "ENV" },
24
+ ];
25
+ export const DEFAULT_TYPED_STAGE = TYPED_STAGES.find((s) => s.code === "puben");
26
+ export function locateStage(abbr) {
27
+ const up = abbr.toUpperCase();
28
+ return TYPED_STAGES.find((s) => s.abbr.toUpperCase() === up);
29
+ }
30
+ export function locateStageByCode(code) {
31
+ return TYPED_STAGES.find((s) => s.code === code);
32
+ }
33
+ export const PUBLISHER_TYPES = ["CWA", "HD", "ES", "CR", "ENV"];
34
+ const TYPE_CLASS = {
35
+ en: "european-norm",
36
+ ts: "technical-specification",
37
+ tr: "technical-report",
38
+ guide: "guide",
39
+ cwa: "cen-workshop-agreement",
40
+ hd: "harmonization-document",
41
+ es: "european-specification",
42
+ cr: "cen-report",
43
+ env: "european-prestandard",
44
+ };
45
+ const CLASS_SHORT = {
46
+ "european-norm": "EN",
47
+ "technical-specification": "TS",
48
+ "technical-report": "TR",
49
+ guide: "Guide",
50
+ "cen-workshop-agreement": "CWA",
51
+ "harmonization-document": "HD",
52
+ "cenelec-harmonization-document": "HD",
53
+ "european-specification": "ES",
54
+ "cen-report": "CR",
55
+ "european-prestandard": "ENV",
56
+ };
57
+ export function locateTypeClass(typeCode) {
58
+ const key = typeCode;
59
+ return key in TYPE_CLASS ? key : undefined;
60
+ }
61
+ const dateWire = {
62
+ wire: "date",
63
+ to: "date",
64
+ toWire: (model) => {
65
+ const date = model["date"];
66
+ if (date === undefined)
67
+ return undefined;
68
+ // The shared flattener turns a single-field date into the bare "year"
69
+ // scalar; a month keeps the nested form.
70
+ if (date.month === undefined)
71
+ return { year: date.year };
72
+ return { year: date.year, month: date.month };
73
+ },
74
+ fromWire: (hash) => {
75
+ const nested = hash["date"];
76
+ if (nested !== undefined && typeof nested === "object" && nested !== null) {
77
+ const rec = nested;
78
+ return new PubidDate({ year: rec["year"], month: rec["month"] });
79
+ }
80
+ const year = hash["year"];
81
+ if (typeof year === "string" && year !== "")
82
+ return new PubidDate({ year });
83
+ return undefined;
84
+ },
85
+ };
86
+ const stageWire = {
87
+ wire: "stage",
88
+ to: "typedStage",
89
+ toWire: (model) => {
90
+ const stage = model["typedStage"];
91
+ return stage?.code;
92
+ },
93
+ fromWire: (hash) => {
94
+ const code = hash["stage"];
95
+ if (typeof code !== "string")
96
+ return undefined;
97
+ return locateStageByCode(code);
98
+ },
99
+ };
100
+ export class CenIdentifier extends BaseIdentifier {
101
+ static attributes = extendAttributes(BaseIdentifier, {
102
+ number: { type: "string" },
103
+ part: { type: "string" },
104
+ date: { type: PubidDate },
105
+ typedStage: { type: "string" },
106
+ type: { type: "string" },
107
+ publisher: { type: "string" },
108
+ copublishers: { type: "string", collection: true },
109
+ });
110
+ static mappings = keyValue({ wire: "number", to: "number" }, { wire: "part", to: "part" }, dateWire, stageWire, { wire: "publisher", to: "publisher" }, { wire: "copublishers", to: "copublishers" });
111
+ root() {
112
+ return this;
113
+ }
114
+ toUrn() {
115
+ return new CenUrnGenerator(this).generate();
116
+ }
117
+ }
118
+ function draftAbbr(id) {
119
+ const abbr = id.typedStage?.abbr;
120
+ return abbr === "prEN" || abbr === "FprEN" ? abbr : undefined;
121
+ }
122
+ function supplementToken(id) {
123
+ const self = id;
124
+ const prefix = self["_supplement"] === "corrigendum" ? "AC" : "A";
125
+ let result = `${prefix}${self["number"] ?? ""}`;
126
+ if (self["year"] !== undefined && self["year"] !== "") {
127
+ result += `:${self["year"]}`;
128
+ if (self["month"] !== undefined && self["month"] !== "")
129
+ result += `-${self["month"]}`;
130
+ }
131
+ return result;
132
+ }
133
+ function renderSingle(id, classShort) {
134
+ const parts = [];
135
+ const isDraftStage = draftAbbr(id) !== undefined;
136
+ const typeShort = isDraftStage
137
+ ? id.typedStage.type_code.toUpperCase()
138
+ : id.type ?? classShort ?? "EN";
139
+ let useSlashBeforeType = false;
140
+ if (PUBLISHER_TYPES.includes(typeShort)) {
141
+ parts.push(id.typedStage !== undefined && id.typedStage.abbr !== typeShort
142
+ ? id.typedStage.abbr
143
+ : typeShort);
144
+ }
145
+ else if (isDraftStage) {
146
+ parts.push(id.typedStage.abbr);
147
+ }
148
+ else if (id.publisher !== undefined) {
149
+ parts.push(id.publisher);
150
+ useSlashBeforeType = true;
151
+ }
152
+ if (id.copublishers !== undefined && id.copublishers.length > 0) {
153
+ const copubStr = id.copublishers.join("/");
154
+ if (parts.length > 0) {
155
+ parts[parts.length - 1] = `${parts[parts.length - 1]}/${copubStr}`;
156
+ }
157
+ else {
158
+ parts.push(copubStr);
159
+ }
160
+ }
161
+ if (typeShort !== "EN" && typeShort !== "Guide" && !PUBLISHER_TYPES.includes(typeShort)) {
162
+ parts.push(useSlashBeforeType && parts.length > 0 ? `/${typeShort}` : typeShort);
163
+ }
164
+ else if (typeShort === "Guide") {
165
+ parts.push("Guide");
166
+ }
167
+ if (id.number !== undefined) {
168
+ parts.push(id.part !== undefined ? `${id.number}-${id.part}` : id.number);
169
+ }
170
+ let result = "";
171
+ parts.forEach((part, idx) => {
172
+ if (idx > 0 && !part.startsWith("/"))
173
+ result += " ";
174
+ result += part;
175
+ });
176
+ if (id.date !== undefined && id.date.present()) {
177
+ result += `:${id.date.render()}`;
178
+ }
179
+ return result;
180
+ }
181
+ function renderAdopted(id) {
182
+ const publishers = [id.publisher, ...(id.copublishers ?? [])].filter((p) => p !== undefined && p !== "");
183
+ const draft = draftAbbr(id);
184
+ if (draft !== undefined && publishers.length > 0)
185
+ publishers[0] = draft;
186
+ let result = publishers.join("/");
187
+ const adopted = id["adopted"];
188
+ if (adopted !== undefined)
189
+ result += ` ${adopted.toHuman()}`;
190
+ return result;
191
+ }
192
+ const ISO_TYPE_SEGMENT = {
193
+ "technical-specification": "ts",
194
+ "technical-report": "tr",
195
+ guide: "guide",
196
+ };
197
+ /** MR string of a nested iso/iec document read from its wire hash. */
198
+ function mrFromHash(hash) {
199
+ const type = String(hash["_type"] ?? "");
200
+ const segments = [];
201
+ if (type.startsWith("pubid:iso")) {
202
+ // ISO's MR string dashes copublishers onto the publisher slot
203
+ // ("iso-iec.27017.2026"); IEC's does not.
204
+ const copubs = Array.isArray(hash["copublishers"]) ? hash["copublishers"].map(String) : [];
205
+ segments.push(["iso", ...copubs.map((c) => c.toLowerCase())].join("-"));
206
+ }
207
+ else if (type.startsWith("pubid:iec")) {
208
+ segments.push("iec");
209
+ }
210
+ const typeSuffix = type.split(":")[2] ?? "";
211
+ const typeSegment = ISO_TYPE_SEGMENT[typeSuffix];
212
+ if (typeSegment !== undefined)
213
+ segments.push(typeSegment);
214
+ const identity = [hash["number"], hash["part"], hash["subpart"]]
215
+ .map((s) => String(s ?? ""))
216
+ .filter((s) => s !== "")
217
+ .join("-");
218
+ if (identity !== "")
219
+ segments.push(identity);
220
+ if (hash["year"] !== undefined)
221
+ segments.push(String(hash["year"]));
222
+ const base = hash["base"];
223
+ if (base !== undefined && typeof base === "object" && !Array.isArray(base)) {
224
+ const baseMr = mrFromHash(base);
225
+ const suffixTag = typeSuffix.endsWith("amendment") ? "amd" : "cor";
226
+ const suffix = [hash["number"], hash["year"]]
227
+ .map((s) => String(s ?? ""))
228
+ .filter((s) => s !== "")
229
+ .map((s, i) => (i === 0 ? s : `.${s}`))
230
+ .join("");
231
+ return suffix === "" ? `${baseMr}_${suffixTag}` : `${baseMr}_${suffixTag}.${suffix}`;
232
+ }
233
+ return segments.join(".");
234
+ }
235
+ // --- URN -----------------------------------------------------------------------
236
+ class CenUrnGenerator extends BaseUrnGenerator {
237
+ generate() {
238
+ return this.urnFor(this.identifier);
239
+ }
240
+ urnFor(id) {
241
+ const self = id;
242
+ const base = self["base"];
243
+ if (base !== undefined) {
244
+ return [this.urnFor(base), ...this.supplementSegments(id)].join(":");
245
+ }
246
+ const identifiers = self["identifiers"];
247
+ if (identifiers !== undefined) {
248
+ const [first, ...rest] = identifiers;
249
+ return [
250
+ this.urnFor(first),
251
+ ...rest.flatMap((s) => ["plus", ...this.supplementSegments(s)]),
252
+ ].join(":");
253
+ }
254
+ const adopted = self["adopted"];
255
+ if (adopted !== undefined) {
256
+ const body = [this.mrPublisher(id), this.adoptedMr(adopted)]
257
+ .filter((s) => s !== undefined && s !== "")
258
+ .join(".")
259
+ .replaceAll(".", ":")
260
+ .replaceAll("_", ":");
261
+ const segments = [`urn:cen:${body}`];
262
+ const code = id.typedStage?.stage_code;
263
+ if (code !== undefined && code !== "published")
264
+ segments.push(`stage.${code}`);
265
+ return segments.join(":");
266
+ }
267
+ return this.baseUrn(id);
268
+ }
269
+ supplementSegments(id) {
270
+ const self = id;
271
+ if (self["_supplement"] === "fragment") {
272
+ return ["frag", String(self["number"] ?? "")].filter((s) => s !== "");
273
+ }
274
+ if (self["_supplement"] === "corrigendum") {
275
+ const segments = ["cor", self["number"], this.supplementDate(self)];
276
+ return segments.map((s) => String(s ?? "")).filter((s) => s !== "");
277
+ }
278
+ const segments = ["amd", self["number"], self["year"]];
279
+ return segments.map((s) => String(s ?? "")).filter((s) => s !== "");
280
+ }
281
+ supplementDate(id) {
282
+ const year = id["year"];
283
+ if (year === undefined || year === "")
284
+ return undefined;
285
+ return [year, id["month"]].filter((m) => m !== undefined && m !== "").join("-");
286
+ }
287
+ mrPublisher(id) {
288
+ return (id.publisher ?? "en").toLowerCase();
289
+ }
290
+ /** The adopted document's MR string (iso/iec supplement recursion). */
291
+ adoptedMr(adopted) {
292
+ return mrFromHash(adopted.toHash());
293
+ }
294
+ baseUrn(id) {
295
+ const parts = ["urn", "cen", (id.publisher ?? "en").toLowerCase()];
296
+ if (id.copublishers !== undefined && id.copublishers.length > 0) {
297
+ parts[2] = `${parts[2]}-${id.copublishers.map((c) => c.toLowerCase()).join("-")}`;
298
+ }
299
+ if (id.number !== undefined) {
300
+ parts.push(id.part !== undefined ? `${id.number}-${id.part}` : id.number);
301
+ }
302
+ if (id.date !== undefined && id.date.present()) {
303
+ parts.push(id.date.render("urn"));
304
+ }
305
+ const code = id.typedStage?.stage_code;
306
+ if (code !== undefined && code !== "published")
307
+ parts.push(`stage.${code}`);
308
+ return parts.join(":");
309
+ }
310
+ }
311
+ function documentClass(spec) {
312
+ const defaultPublisher = PUBLISHER_TYPES.includes(spec.short) ? spec.short : "EN";
313
+ class CenConcrete extends CenIdentifier {
314
+ static polymorphicName = `pubid:cencenelec:${spec.kind}`;
315
+ static urnGenerator = CenUrnGenerator;
316
+ static get attributes() {
317
+ return extendAttributes(CenIdentifier, {
318
+ publisher: { type: "string", default: defaultPublisher },
319
+ type: { type: "string", default: spec.short },
320
+ adopted: { type: CenIdentifier },
321
+ });
322
+ }
323
+ static get mappings() {
324
+ return [...CenIdentifier.mappings, { wire: "adopted", to: "adopted" }];
325
+ }
326
+ constructor(attrs = {}) {
327
+ super(attrs);
328
+ // An ENV adoption renders "ENV <adopted>" (render_european_prestandard).
329
+ if (this.adopted !== undefined) {
330
+ this["_envAdopted"] = true;
331
+ }
332
+ }
333
+ render() {
334
+ if (this.adopted !== undefined) {
335
+ return `${spec.short} ${this.adopted.toHuman()}`;
336
+ }
337
+ return renderSingle(this, spec.short);
338
+ }
339
+ root() {
340
+ return this;
341
+ }
342
+ toUrn() {
343
+ if (this.adopted !== undefined) {
344
+ const body = [spec.short.toLowerCase(), this.adoptedUrnBody()]
345
+ .filter((s) => s !== "")
346
+ .join(":");
347
+ return `urn:cen:${body}`;
348
+ }
349
+ return super.toUrn();
350
+ }
351
+ adoptedUrnBody() {
352
+ const adopted = this.adopted;
353
+ const rawPub = adopted["publisher"];
354
+ const pub = (typeof rawPub === "string"
355
+ ? rawPub
356
+ : typeof rawPub?.publisher === "string"
357
+ ? rawPub.publisher
358
+ : String(rawPub?.body ?? "")).toLowerCase();
359
+ const rawType = adopted["type"];
360
+ const typeKey = this.adopted.constructor.typeKey;
361
+ const type = typeKey !== undefined && typeKey !== "is"
362
+ ? typeKey
363
+ : (typeof rawType === "string" ? rawType : String(rawType?.abbr ?? "")).toLowerCase();
364
+ const segments = [
365
+ pub,
366
+ type,
367
+ [adopted["number"], adopted["part"], adopted["subpart"]]
368
+ .map((s) => String(s ?? ""))
369
+ .filter((s) => s !== "")
370
+ .join("-"),
371
+ adopted["year"] ?? this.adopted?.date?.year,
372
+ ]
373
+ .map((s) => String(s ?? ""))
374
+ .filter((s) => s !== "");
375
+ return segments.join(":");
376
+ }
377
+ }
378
+ registerType(CenConcrete);
379
+ return CenConcrete;
380
+ }
381
+ export const CEN_DOCUMENT_CLASSES = new Map(Object.keys(TYPE_CLASS).map((key) => [
382
+ key,
383
+ documentClass({ kind: TYPE_CLASS[key], short: CLASS_SHORT[TYPE_CLASS[key]] }),
384
+ ]));
385
+ // The CENELEC HD variant has its own _type; the builder routes plain HD to
386
+ // the CEN class (Ruby's registry finds it first).
387
+ export const CenelecHarmonizationDocumentClass = documentClass({
388
+ kind: "cenelec-harmonization-document",
389
+ short: "HD",
390
+ });
391
+ function supplementClass(kind, extraDefs) {
392
+ class CenSupplement extends CenIdentifier {
393
+ static polymorphicName = `pubid:cencenelec:${kind}`;
394
+ static urnGenerator = CenUrnGenerator;
395
+ static get attributes() {
396
+ return extendAttributes(CenIdentifier, {
397
+ base: { type: CenIdentifier },
398
+ ...extraDefs,
399
+ });
400
+ }
401
+ static get mappings() {
402
+ return [
403
+ ...CenIdentifier.mappings.filter((m) => m.wire !== "publisher" && m.wire !== "copublishers" && m.wire !== "date" && m.wire !== "stage" && m.wire !== "type"),
404
+ { wire: "base", to: "base" },
405
+ { wire: "number", to: "number" },
406
+ { wire: "year", to: "year" },
407
+ ...(kind === "corrigendum" ? [{ wire: "month", to: "month" }] : []),
408
+ ];
409
+ }
410
+ constructor(attrs = {}) {
411
+ super(attrs);
412
+ this["_supplement"] = kind;
413
+ }
414
+ render() {
415
+ const self = this;
416
+ self["_supplement"] = kind;
417
+ if (kind === "fragment") {
418
+ return `${this.base?.toHuman() ?? ""} FRAG${this.number ?? ""}`;
419
+ }
420
+ return `${this.base?.toHuman() ?? ""}/${supplementToken(this)}`;
421
+ }
422
+ root() {
423
+ return this.base?.root() ?? this;
424
+ }
425
+ }
426
+ registerType(CenSupplement);
427
+ return CenSupplement;
428
+ }
429
+ export const AmendmentClass = supplementClass("amendment", { year: { type: "string" } });
430
+ export const CorrigendumClass = supplementClass("corrigendum", { year: { type: "string" }, month: { type: "string" } });
431
+ export const FragmentClass = supplementClass("fragment", {});
432
+ export const ConsolidatedIdentifierClass = (() => {
433
+ class CenConsolidated extends CenIdentifier {
434
+ static polymorphicName = "pubid:cencenelec:consolidated-identifier";
435
+ static urnGenerator = CenUrnGenerator;
436
+ static get attributes() {
437
+ return extendAttributes(CenIdentifier, {
438
+ identifiers: { type: CenIdentifier, collection: true },
439
+ });
440
+ }
441
+ static get mappings() {
442
+ return [{ wire: "identifiers", to: "identifiers" }];
443
+ }
444
+ render() {
445
+ const [base, ...supplements] = this.identifiers;
446
+ return ((base?.toHuman() ?? "") +
447
+ supplements
448
+ .map((sub) => {
449
+ const self = sub;
450
+ return `+${supplementToken(sub)}`;
451
+ })
452
+ .join(""));
453
+ }
454
+ root() {
455
+ return this.identifiers[0]?.root() ?? this;
456
+ }
457
+ }
458
+ registerType(CenConsolidated);
459
+ return CenConsolidated;
460
+ })();
461
+ export const AdoptedEuropeanNormClass = (() => {
462
+ class CenAdopted extends CenIdentifier {
463
+ static polymorphicName = "pubid:cencenelec:adopted-european-norm";
464
+ static urnGenerator = CenUrnGenerator;
465
+ static get attributes() {
466
+ return extendAttributes(CenIdentifier, {
467
+ publisher: { type: "string", default: "EN" },
468
+ adopted: { type: CenIdentifier },
469
+ });
470
+ }
471
+ static get mappings() {
472
+ return [
473
+ { wire: "publisher", to: "publisher" },
474
+ { wire: "copublishers", to: "copublishers" },
475
+ stageWire,
476
+ { wire: "adopted", to: "adopted" },
477
+ ];
478
+ }
479
+ render() {
480
+ return renderAdopted(this);
481
+ }
482
+ root() {
483
+ return this;
484
+ }
485
+ }
486
+ registerType(CenAdopted);
487
+ return CenAdopted;
488
+ })();
489
+ export const EuropeanPrestandardClass = CEN_DOCUMENT_CLASSES.get("env");
490
+ export const EuropeanNormClass = CEN_DOCUMENT_CLASSES.get("en");
@@ -0,0 +1,5 @@
1
+ import { Grammar } from "../../grammar/engine.js";
2
+ /** Parser.parse's preprocessing: strip, drop #-comments, collapse runs,
3
+ * insert the missing colon in "/E2007"-style language-years. */
4
+ export declare function preprocessCie(input: string): string;
5
+ export declare const cieGrammar: Grammar;
@@ -0,0 +1,127 @@
1
+ import { match, ref, str } from "../../grammar/engine.js";
2
+ /**
3
+ * Port of lib/pubid/cie/parser.rb — the CIE grammar's 17 alternatives,
4
+ * longest/most-specific first (PEG ordered choice). The dual-style date
5
+ * era is carried by WHICH rule matched (current_date ":" vs
6
+ * legacy_date "-"), wrapped in its own marker so the builder can derive
7
+ * `style` without a separate separator field.
8
+ */
9
+ function buildRules() {
10
+ const rules = {};
11
+ const rule = (name, build) => {
12
+ rules[name] = build();
13
+ };
14
+ const digit = match("[0-9]");
15
+ const digits = digit.repeat(1, Infinity);
16
+ const upper = match("[A-Z]");
17
+ const space = str(" ");
18
+ const dash = str("-");
19
+ const colon = str(":");
20
+ const dot = str(".");
21
+ const slash = str("/");
22
+ const comma = str(",");
23
+ // Year 1900-2099.
24
+ const yearDigits = str("19").or(str("20")).then(digit.repeat(2, 2));
25
+ rule("legacy_date", () => dash.then(yearDigits.as("year")).as("legacy_date"));
26
+ rule("current_date", () => colon.then(yearDigits.as("year")).as("current_date"));
27
+ rule("date", () => rules["current_date"].or(rules["legacy_date"]));
28
+ // Legacy NUMBER-YEAR ("001-1980"), optionally with "(RU-2020)".
29
+ rule("legacy_code_with_year", () => str("CIE").then(space, digits.as("number"), dash, yearDigits.as("year"), ref(rules, "language_paren_year").or(str("")).as("trailing_lang")));
30
+ rule("code_with_iteration", () => digits.as("number").then(dot, digits.as("iteration")));
31
+ rule("code_with_part_and_iteration_slash", () => digits.as("number").then(slash, digits.as("part"), dot, digits.as("iteration")));
32
+ rule("code_with_part_and_iteration_dash", () => digits
33
+ .as("number")
34
+ .then(dash, digits.as("part"), dot, digits.as("iteration"), dash.absent()));
35
+ // Part 1-3 digits, never a 4-digit year and never dot/dash after.
36
+ rule("code_with_part_dash", () => digits
37
+ .as("number")
38
+ .then(dash, digit.repeat(1, 3).as("part"), str("").as("dash_sep"), str("19").or(str("20")).absent(), dot.or(dash).absent()));
39
+ rule("code_with_part_slash", () => digits
40
+ .as("number")
41
+ .then(slash, digits.as("part"), str("").as("slash_sep"), dot.absent()));
42
+ rule("code_simple", () => digits.as("number"));
43
+ rule("code", () => rules["code_with_part_and_iteration_slash"]
44
+ .or(rules["code_with_part_and_iteration_dash"], rules["code_with_iteration"], rules["code_with_part_slash"], rules["code_with_part_dash"], rules["code_simple"]));
45
+ // Language formats: /E (slash), (DE) (paren), (RU-2021) (paren_year).
46
+ rule("language_slash", () => slash.then(upper.as("lang_code")).as("language_slash"));
47
+ rule("language_paren", () => str("(")
48
+ .then(match("[A-Za-z]").repeat(1, 3).as("lang_code"), str(")"))
49
+ .as("language_paren"));
50
+ rule("language_paren_year", () => space
51
+ .maybe()
52
+ .then(str("("), upper.repeat(2, Infinity).as("lang_code"), dash, yearDigits.as("trans_year"), str(")"))
53
+ .as("language_paren_year"));
54
+ rule("language", () => rules["language_paren_year"]
55
+ .or(rules["language_paren"], rules["language_slash"]));
56
+ const sPrefix = () => str("S").then(space);
57
+ const stageCode = () => str("FDIS").or(str("DIS"), str("DS")).then(space);
58
+ const docType = () => str("TR").or(str("TS"));
59
+ // Proceedings paper code: 1+ uppercase letters (OP, PO, PP, …).
60
+ const paperCode = () => match("[A-Z]").repeat(1, Infinity).as("paper_code");
61
+ // Techstreet opaque conference variant; lowercase-alnum so it cannot
62
+ // collide with /E, /Cor or /IEC tails.
63
+ rule("variant_slug", () => slash.then(match("[0-9a-z]").repeat(1, Infinity).as("variant")));
64
+ // "CIE x043-OP01".
65
+ rule("conf_proceedings", () => str("CIE").then(space, str("x"), digits.as("conf_number"), dash, paperCode(), digits.as("paper_number")));
66
+ // "CIE OP02 1-5".
67
+ rule("standalone_proceedings", () => str("CIE").then(space, paperCode(), digits.as("paper_number"), space, digits.then(dash, digits).as("page_range")));
68
+ rule("d_series_identifier", () => str("CIE").then(space, str("D").as("d_prefix"), digits.as("number"), rules["date"]));
69
+ // "CIE S 007/1998" — narrow number(+iteration) so part_slash can't
70
+ // greedily eat the /year.
71
+ rule("standard_with_slash_year", () => str("CIE").then(space, stageCode().maybe().as("stage"), sPrefix().maybe().as("s_prefix"), digits.as("number").then(dot.then(digits.as("iteration")).maybe()), slash, yearDigits.as("slash_year")));
72
+ // "DIS 025-SP1/E:2019".
73
+ rule("dis_with_supplement", () => str("CIE").then(space, stageCode().as("stage"), sPrefix().maybe().as("s_prefix"), digits.as("base_number"), dash, str("SP"), digits.as("supplement_number"), dot.then(digits.as("supplement_part")).maybe(), slash, upper.as("lang_code"), colon.as("lang_colon"), yearDigits.as("year")));
74
+ rule("standard_identifier", () => str("CIE").then(space, stageCode().maybe().as("stage"), sPrefix().maybe().as("s_prefix"), rules["code"], rules["language"]
75
+ .then(rules["date"])
76
+ .as("lang_before")
77
+ .or(rules["date"].then(rules["language"].maybe()).as("date_then_lang"), rules["date"])
78
+ .maybe()));
79
+ // "CIE S 014-4/E:2007" (language/year, no ISO reference).
80
+ rule("standard_with_language_year", () => str("CIE").then(space, stageCode().maybe().as("stage"), sPrefix().maybe().as("s_prefix"), rules["code"], slash, upper.as("lang_code"), colon.as("lang_colon"), yearDigits.as("year")));
81
+ rule("conference_identifier", () => str("CIE").then(space, str("x").as("conference"), digits.as("conf_number"), rules["date"], space.then(str("Amendment"), space, digits.as("amd_number")).maybe(), rules["variant_slug"].maybe()));
82
+ // "CIE ISO 11664-1:2019(E)" — the absent-guard rejects "ISO/CIE".
83
+ rule("joint_with_iso", () => str("CIE")
84
+ .then(space, str("ISO").as("copublisher"), slash.then(str("CIE")).absent(), space, docType().then(space).maybe().as("doc_type"), stageCode().maybe().as("stage"), digits.as("number"), dash.then(digits.as("part")).maybe(), colon.or(dash).then(yearDigits.as("year")).maybe(), rules["language"].maybe()));
85
+ rule("joint_with_iso_cie", () => str("CIE").then(space, str("ISO/CIE").as("copublisher"), space, docType().then(space).maybe().as("doc_type"), digits.as("number"), colon.then(yearDigits.as("year")).maybe(), rules["language"].maybe()));
86
+ // IEC always joins the part with a dot and the year with a dash.
87
+ rule("joint_with_iec", () => str("CIE").then(space, str("IEC").as("copublisher"), space, digits.as("number"), dot, digits.as("part"), dash, yearDigits.as("year")));
88
+ // "CIE S 008/E:2001 (ISO 8995-1:2002(E))".
89
+ rule("identical_with_iso", () => str("CIE").then(space, sPrefix().maybe().as("s_prefix"), digits.as("number"), dot.then(digits.as("iteration"))
90
+ .or(dash.then(digits.as("part")))
91
+ .maybe(), slash
92
+ .then(upper
93
+ .as("lang_code")
94
+ .then(colon.as("lang_colon"), yearDigits.as("year"))
95
+ .or(upper.as("lang_code").then(yearDigits.as("year")), yearDigits.as("slash_year")))
96
+ .maybe(), space, str("(ISO"), space, match("[0-9A-Z:/.\\-]")
97
+ .repeat(1, Infinity)
98
+ .then(str("(").then(match("[A-Z]").repeat(1, Infinity), str(")")).maybe())
99
+ .as("iso_reference"), str(")")));
100
+ // "CIE S 009:2002/IEC 62471:2006".
101
+ rule("dual_with_iec", () => str("CIE").then(space, sPrefix().maybe().as("s_prefix"), digits.as("number"), colon, yearDigits.as("year"), slash, str("IEC"), space, match("[^)\\n]").repeat(1, Infinity).as("iec_identifier")));
102
+ rule("supplement_identifier", () => str("CIE").then(space, digits.as("base_number"), dash, str("SP"), digits.as("supplement_number"), dot.then(digits.as("supplement_part")).maybe(), colon, yearDigits.as("year")));
103
+ rule("corrigendum_identifier", () => str("CIE")
104
+ .then(space, digits.as("base_number"), dash
105
+ .then(str("SP"), digits.as("base_supplement"), dot.then(digits.as("base_supplement_part")).maybe())
106
+ .maybe(), colon, yearDigits.as("base_year"), slash, str("Cor"), digits.as("cor_number"), colon, yearDigits.as("cor_year")));
107
+ rule("bundle_identifier", () => str("CIE").then(space, digits.as("first_number"), dash, str("SP"), digits, dot, digits, colon, yearDigits, comma.then(digits.as("next_number"), dash, str("SP"), digits, dot, digits, colon, yearDigits)
108
+ .repeat(1, Infinity)
109
+ .as("bundle_items")));
110
+ rule("tutorial_bundle", () => str("CIE Tutorials Bundle").then(space, digits.as("bundle_number")));
111
+ rule("identifier", () => rules["tutorial_bundle"]
112
+ .or(rules["bundle_identifier"], rules["joint_with_iso_cie"], rules["joint_with_iso"], rules["joint_with_iec"], rules["identical_with_iso"], rules["dis_with_supplement"], rules["dual_with_iec"], rules["corrigendum_identifier"], rules["supplement_identifier"], rules["standard_with_language_year"], rules["standard_with_slash_year"], rules["d_series_identifier"], rules["conf_proceedings"], rules["standalone_proceedings"],
113
+ // Before standard_identifier, to catch the 001-1980 pattern.
114
+ rules["legacy_code_with_year"], rules["conference_identifier"], rules["standard_identifier"]));
115
+ rule("root", () => rules["identifier"]);
116
+ return rules;
117
+ }
118
+ /** Parser.parse's preprocessing: strip, drop #-comments, collapse runs,
119
+ * insert the missing colon in "/E2007"-style language-years. */
120
+ export function preprocessCie(input) {
121
+ return input
122
+ .trim()
123
+ .replace(/\s*#.*$/, "")
124
+ .replace(/\s+/g, " ")
125
+ .replace(/\/(E|F|G|DE|ES|CN|RU|FR)(\d{4})/g, "/$1:$2");
126
+ }
127
+ export const cieGrammar = { rules: buildRules(), root: "root" };
@@ -0,0 +1,2 @@
1
+ import type { FlavorImplementation } from "../../conformance/implementation.js";
2
+ export declare function cieGrammarImplementation(): FlavorImplementation;