@pubid/pubid 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) 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 +31 -0
  80. package/dist/flavors/gb/index.js +159 -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 +697 -0
  107. package/dist/flavors/ieee/index.d.ts +3 -0
  108. package/dist/flavors/ieee/index.js +1258 -0
  109. package/dist/flavors/ieee/model.d.ts +144 -0
  110. package/dist/flavors/ieee/model.js +1237 -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 +197 -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 +206 -0
  155. package/dist/flavors/oiml/implementation.d.ts +9 -0
  156. package/dist/flavors/oiml/implementation.js +45 -0
  157. package/dist/flavors/oiml/model.d.ts +23 -0
  158. package/dist/flavors/oiml/model.js +592 -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/index.d.ts +1 -0
  174. package/dist/index.js +3 -0
  175. package/dist/model/attribute.d.ts +54 -0
  176. package/dist/model/attribute.js +53 -0
  177. package/dist/model/builder.d.ts +39 -0
  178. package/dist/model/builder.js +124 -0
  179. package/dist/model/component.d.ts +70 -0
  180. package/dist/model/component.js +170 -0
  181. package/dist/model/identifier.d.ts +84 -0
  182. package/dist/model/identifier.js +270 -0
  183. package/dist/model/urn-generator.d.ts +27 -0
  184. package/dist/model/urn-generator.js +91 -0
  185. package/package.json +1 -1
@@ -0,0 +1,1244 @@
1
+ import { ParseFailed, parseGrammar } from "../../grammar/engine.js";
2
+ import { nistGrammar } from "./grammar.js";
3
+ import { preprocessNist } from "./preprocessor.js";
4
+ import { NIST_CLASSES, NistCode, NistEdition, NistPart, NistStage, NistSupplement, NistTranslation, NistUpdate, NistVersion, NistVolume, SP_SUBSERIES, seriesPolicy, } from "./model.js";
5
+ /**
6
+ * Port of lib/pubid/nist/{builder,caster,router,parser_output_normalizer,
7
+ * circular_supplement_builder}.rb — the parse-tree → identifier pipeline.
8
+ */
9
+ const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
10
+ const s = (v) => {
11
+ if (v === undefined || v === null)
12
+ return undefined;
13
+ const str = Array.isArray(v) ? v.join("") : String(v);
14
+ return str.length > 0 ? str : undefined;
15
+ };
16
+ function flatten(data) {
17
+ return Array.isArray(data) ? Object.assign({}, ...data) : data;
18
+ }
19
+ const TRANSLATION_MAP = {
20
+ es: "spa", sp: "spa", pt: "por", id: "ind",
21
+ chi: "zho", viet: "vie", port: "por", esp: "spa",
22
+ };
23
+ const MONTH_NUMBERS = {
24
+ Jan: 1, January: 1, Feb: 2, February: 2, Mar: 3, March: 3,
25
+ Apr: 4, April: 4, May: 5, Jun: 6, June: 6, Jul: 7, July: 7,
26
+ Aug: 8, August: 8, Sep: 9, Sept: 9, September: 9, Oct: 10, October: 10,
27
+ Nov: 11, November: 11, Dec: 12, December: 12,
28
+ };
29
+ const VALID_YEAR_RANGE_MIN = 1901;
30
+ const VALID_YEAR_RANGE_MAX = 2026;
31
+ const DASH_YEAR_AS_EDITION_SERIES = new Set(["HB", "CS", "FIPS"]);
32
+ // --- Parser output normalizer ---------------------------------------------------
33
+ function normalizeParsedHash(h) {
34
+ mergeEditionEIntoUpdate(h);
35
+ extractEmbeddedEditionWithYear(h);
36
+ extractEmbeddedEditionWithoutDashYear(h);
37
+ splitSecondNumberEditionYear(h);
38
+ splitFipsMonthYearAfterPart(h);
39
+ disambiguateIrCompoundVsEdition(h);
40
+ disambiguateDashYear(h);
41
+ }
42
+ function mergeEditionEIntoUpdate(h) {
43
+ const updatePrefix = h["update_prefix"];
44
+ const update = h["update"];
45
+ const editionE = h["edition_e"];
46
+ if (updatePrefix === undefined || update === undefined || !isObj(editionE))
47
+ return;
48
+ const editionId = editionE["edition_id"];
49
+ h["update"] = { ...(isObj(update) ? update : {}), update_year: editionId };
50
+ delete h["edition_e"];
51
+ }
52
+ function editionFromParts(type, id, additionalText) {
53
+ return { __edition: true, type, id, ...(additionalText !== undefined ? { additional_text: additionalText } : {}) };
54
+ }
55
+ function extractEmbeddedEditionWithYear(h) {
56
+ const first = s(h["first_number"]);
57
+ if (first !== undefined && /^[0-9]+[a-zA-Z]\d+$/.test(first) && h["edition_dash_year"] !== undefined) {
58
+ const m = /^(\d+)([a-zA-Z])(\d+)$/.exec(first);
59
+ if (m !== null) {
60
+ h["first_number"] = m[1];
61
+ h["edition_with_year"] = editionFromParts(m[2].toLowerCase(), m[3], s(isObj(h["edition_dash_year"]) ? h["edition_dash_year"]["dash_year"] : undefined));
62
+ delete h["edition_dash_year"];
63
+ return;
64
+ }
65
+ }
66
+ // Same shape embedded in the SECOND number ("53e1988" of "260-53e1988").
67
+ const second = s(h["second_number"]);
68
+ if (second !== undefined && /^[0-9]+[a-zA-Z]\d+$/.test(second)) {
69
+ const m = /^(\d+)([a-zA-Z])(\d+)$/.exec(second);
70
+ if (m !== null) {
71
+ h["second_number"] = m[1];
72
+ h["edition_with_year"] = editionFromParts(m[2].toLowerCase(), m[3]);
73
+ }
74
+ }
75
+ }
76
+ function extractEmbeddedEditionWithoutDashYear(h) {
77
+ if (h["second_number"] !== undefined)
78
+ return;
79
+ const first = s(h["first_number"]);
80
+ if (first === undefined || !/^[0-9]+[a-zA-Z]\d+$/.test(first))
81
+ return;
82
+ if (h["edition_dash_year"] !== undefined)
83
+ return;
84
+ const m = /^(\d+)([a-zA-Z])(\d+)$/.exec(first);
85
+ if (m === null)
86
+ return;
87
+ h["first_number"] = m[1];
88
+ h["edition_with_year"] = editionFromParts(m[2].toLowerCase(), m[3]);
89
+ }
90
+ function splitSecondNumberEditionYear(h) {
91
+ const combined = h["second_number_edition_year"];
92
+ if (!isObj(combined))
93
+ return;
94
+ h["second_number"] = combined["second_number"];
95
+ const dashYear = s(combined["dash_year"]);
96
+ const isHandbook = s(h["series"]) === "HB";
97
+ if (isHandbook && dashYear !== undefined && /^\d{4}$/.test(dashYear)) {
98
+ h["edition_from_year"] = editionFromParts("e", dashYear);
99
+ }
100
+ else {
101
+ h["edition_dash_year"] = { dash_year: dashYear };
102
+ }
103
+ delete h["second_number_edition_year"];
104
+ }
105
+ function splitFipsMonthYearAfterPart(h) {
106
+ const combined = h["fips_month_year_after_part"];
107
+ if (!isObj(combined))
108
+ return;
109
+ h["second_number"] = combined["second_number"];
110
+ const monthStr = s(combined["edition_month"]) ?? "";
111
+ const yearStr = s(combined["edition_year"]) ?? "";
112
+ const monthNum = MONTH_NUMBERS[monthStr];
113
+ const editionId = monthNum !== undefined ? `${yearStr}${String(monthNum).padStart(2, "0")}` : yearStr;
114
+ h["edition_from_year"] = editionFromParts("e", editionId);
115
+ delete h["fips_month_year_after_part"];
116
+ }
117
+ function disambiguateIrCompoundVsEdition(h) {
118
+ if (s(h["series"]) !== "IR")
119
+ return;
120
+ if (h["first_number"] === undefined || h["edition_dash_year"] === undefined)
121
+ return;
122
+ const firstNum = s(h["first_number"]) ?? "";
123
+ const dashYear = s(isObj(h["edition_dash_year"]) ? h["edition_dash_year"]["dash_year"] : undefined) ?? "";
124
+ if (!/^\d{2}$/.test(firstNum) || !/^\d{4}$/.test(dashYear))
125
+ return;
126
+ const isValidYear = Number(dashYear) >= VALID_YEAR_RANGE_MIN && Number(dashYear) <= VALID_YEAR_RANGE_MAX;
127
+ const hasEmbeddedEdition = h["edition_e"] !== undefined;
128
+ if (isValidYear && !hasEmbeddedEdition) {
129
+ h["edition"] = editionFromParts("e", dashYear);
130
+ }
131
+ else {
132
+ h["first_number"] = `${firstNum}-${dashYear}`;
133
+ }
134
+ delete h["edition_dash_year"];
135
+ }
136
+ function disambiguateDashYear(h) {
137
+ if (h["first_number"] === undefined || h["edition_dash_year"] === undefined)
138
+ return;
139
+ const first = s(h["first_number"]) ?? "";
140
+ if (/^[0-9]+[a-zA-Z]\d+$/.test(first))
141
+ return;
142
+ const dashYear = s(isObj(h["edition_dash_year"]) ? h["edition_dash_year"]["dash_year"] : undefined) ?? "";
143
+ const series = s(h["series"]) ?? "";
144
+ const dashYearNum = Number(dashYear);
145
+ const isValidYear = dashYearNum >= VALID_YEAR_RANGE_MIN && dashYearNum <= VALID_YEAR_RANGE_MAX;
146
+ if (series === "RPT") {
147
+ h["first_number"] = `${first}-${dashYear}`;
148
+ delete h["edition_dash_year"];
149
+ }
150
+ else if (series === "GCR") {
151
+ h["edition_from_year"] = editionFromParts("e", dashYear);
152
+ delete h["edition_dash_year"];
153
+ }
154
+ else if (series === "IR" && isValidYear) {
155
+ h["edition_from_year"] = editionFromParts("e", dashYear);
156
+ delete h["edition_dash_year"];
157
+ }
158
+ else if (DASH_YEAR_AS_EDITION_SERIES.has(series) && isValidYear) {
159
+ h["edition_from_year"] = editionFromParts("e", dashYear);
160
+ delete h["edition_dash_year"];
161
+ }
162
+ else if (dashYearNum < 1900) {
163
+ h["second_number"] = dashYear;
164
+ delete h["edition_dash_year"];
165
+ }
166
+ }
167
+ function classForKind(kind) {
168
+ return NIST_CLASSES.get(kind);
169
+ }
170
+ function locateIdentifierKlass(h) {
171
+ if (h["dated_date"] !== undefined && h["dated_seq"] !== undefined) {
172
+ return classForKind("dated-document");
173
+ }
174
+ let series = s(h["series"]);
175
+ if (series?.startsWith("NBS ") === true || series?.startsWith("NBS.") === true || series?.startsWith("NIST.") === true) {
176
+ const separator = series.startsWith("NBS ") ? "NBS " : series.startsWith("NBS.") ? "NBS." : "NIST.";
177
+ let simple = series.slice(separator.length);
178
+ if (simple === "LCIRC" || simple === "CIRC") {
179
+ return hasSupplement(h) ? classForKind("circular-supplement") : simple === "CIRC" ? classForKind("circular") : classForKind("letter-circular");
180
+ }
181
+ series = simple;
182
+ }
183
+ if (series === "CS") {
184
+ const firstNum = h["first_number"];
185
+ if (isObj(firstNum) && firstNum["volume_number"] !== undefined && firstNum["issue_number"] !== undefined) {
186
+ return classForKind("commercial-standards-monthly");
187
+ }
188
+ const firstNumStr = s(firstNum) ?? "";
189
+ if (/^e\d{3,}/.test(firstNumStr)) {
190
+ return classForKind("commercial-standard-emergency");
191
+ }
192
+ }
193
+ // Series token → type via the typed-stage abbreviation table.
194
+ const kind = SERIES_TO_KIND[series ?? ""];
195
+ return kind !== undefined ? classForKind(kind) : classForKind("identifier");
196
+ }
197
+ const SERIES_TO_KIND = {
198
+ SP: "special-publication",
199
+ "NIST SP": "special-publication",
200
+ "NBS SP": "special-publication",
201
+ FIPS: "federal-information-processing-standards",
202
+ "NIST FIPS": "federal-information-processing-standards",
203
+ IR: "interagency-report",
204
+ "NIST IR": "interagency-report",
205
+ "NBS IR": "interagency-report",
206
+ HB: "handbook",
207
+ "NIST HB": "handbook",
208
+ "NBS HB": "handbook",
209
+ TN: "technical-note",
210
+ "NIST TN": "technical-note",
211
+ "NBS TN": "technical-note",
212
+ CIRC: "circular",
213
+ "NBS CIRC": "circular",
214
+ CRPL: "crpl-report",
215
+ "NBS CRPL": "crpl-report",
216
+ "CRPL-F-B": "crpl-report",
217
+ "CRPL-F-A": "crpl-report",
218
+ "NBS CRPL-F-B": "crpl-report",
219
+ "NBS CRPL-F-A": "crpl-report",
220
+ "NBS BRPD-CRPL-D": "crpl-report",
221
+ RPT: "report",
222
+ "NBS RPT": "report",
223
+ MONO: "monograph",
224
+ "NBS MONO": "monograph",
225
+ "NIST MONO": "monograph",
226
+ MP: "miscellaneous-publication",
227
+ "NBS MP": "miscellaneous-publication",
228
+ GCR: "grant-contractor-report",
229
+ "NIST GCR": "grant-contractor-report",
230
+ NCSTAR: "ncstar",
231
+ "NIST NCSTAR": "ncstar",
232
+ OWMWP: "owmwp",
233
+ "NIST OWMWP": "owmwp",
234
+ NSRDS: "nsrds",
235
+ "NBS NSRDS": "nsrds",
236
+ "NSRDS-NBS": "nsrds",
237
+ LCIRC: "letter-circular",
238
+ CS: "commercial-standard",
239
+ "NBS CS": "commercial-standard",
240
+ "NBS CS-E": "commercial-standard-emergency",
241
+ "CS-E": "commercial-standard-emergency",
242
+ CSM: "commercial-standards-monthly",
243
+ "NBS CSM": "commercial-standards-monthly",
244
+ "NIST PS": "identifier",
245
+ "NIST DCI": "identifier",
246
+ "NIST Other": "identifier",
247
+ "ITL Bulletin": "identifier",
248
+ "CSRC Building Block": "identifier",
249
+ "CSRC Use Case": "identifier",
250
+ "CSRC Book": "identifier",
251
+ };
252
+ function hasSupplement(h) {
253
+ return (h["supplement"] !== undefined ||
254
+ h["supplement_date_range"] !== undefined ||
255
+ h["supplement_date"] !== undefined ||
256
+ h["supplement_slash_year"] !== undefined ||
257
+ h["supplement_with_rev"] !== undefined);
258
+ }
259
+ function editionCast(type, id, additionalText, originalPrefix) {
260
+ const edition = new NistEdition({ type, id, ...(additionalText !== undefined ? { additional_text: additionalText } : {}), ...(originalPrefix !== undefined ? { original_prefix: originalPrefix } : {}) });
261
+ return { edition };
262
+ }
263
+ function cast(type, value, h, policy, series = "") {
264
+ switch (type) {
265
+ case "publisher":
266
+ return s(value);
267
+ case "dated_date": {
268
+ if (!isObj(value))
269
+ return undefined;
270
+ return { date_year: s(value["date_year"]), date_month: s(value["date_month"]), date_day: s(value["date_day"]) };
271
+ }
272
+ case "dated_seq":
273
+ return s(value) === undefined ? undefined : { dated_seq: s(value) };
274
+ case "year":
275
+ return s(value) === undefined ? undefined : Number(s(value));
276
+ case "series": {
277
+ const strValue = s(value);
278
+ if (strValue === undefined)
279
+ return undefined;
280
+ const m = /^(NBS|NIST) (.+)$/.exec(strValue);
281
+ if (m !== null) {
282
+ return { publisher: m[1], series: new NistCode({ value: m[2] }) };
283
+ }
284
+ return { series: new NistCode({ value: strValue }) };
285
+ }
286
+ case "volume_number": {
287
+ const v = s(value);
288
+ return v === undefined ? undefined : { volume: new NistVolume({ value: v }) };
289
+ }
290
+ case "issue_number": {
291
+ const v = s(value);
292
+ return v === undefined ? undefined : { part: new NistPart({ type: "n", value: v }) };
293
+ }
294
+ case "part_number":
295
+ return s(value);
296
+ case "letter_number": {
297
+ if (!isObj(value))
298
+ return undefined;
299
+ const suffix = s(value["letter_suffix"]) ?? "";
300
+ const extra = s(value["letter_suffix_extra"]) ?? "";
301
+ const full = extra === "" ? suffix : `${suffix}${extra}`;
302
+ if (full === "")
303
+ return undefined;
304
+ // IR keeps R/Ur for the compound handler; NCSTAR/MONO keep any
305
+ // letter in the number (Ruby Series class dispatch).
306
+ if (series === "IR" && (full === "R" || full === "Ur"))
307
+ return { letter_number: { ...value, letter_suffix: full } };
308
+ if (series === "NCSTAR" || series === "MONO") {
309
+ return { letter_number: { ...value, letter_suffix: full } };
310
+ }
311
+ return { part: new NistPart({ type: "", value: full.toUpperCase() }) };
312
+ }
313
+ case "fips_part": {
314
+ const v = s(value);
315
+ return v === undefined ? undefined : { part: new NistPart({ type: "pt", value: v }) };
316
+ }
317
+ case "owmwp_date_number": {
318
+ if (value === undefined)
319
+ return undefined;
320
+ const v = isObj(value) ? value : {};
321
+ const owmwp = isObj(v["owmwp_date_number"]) ? v["owmwp_date_number"] : v;
322
+ const numberPart = `${s(owmwp["owmwp_month"])}-${s(owmwp["owmwp_day"])}`;
323
+ return { first_number: numberPart, edition: new NistEdition({ type: "e", id: s(owmwp["owmwp_year"]) }) };
324
+ }
325
+ case "first_number":
326
+ case "second_number":
327
+ return castNumber(type, value, h, policy);
328
+ case "crpl_range": {
329
+ const strValue = s(value);
330
+ if (strValue === undefined)
331
+ return undefined;
332
+ let m = /^(\d+)_(\d+-\d+)([A-Z])$/.exec(strValue);
333
+ if (m !== null) {
334
+ return {
335
+ second_number: m[1],
336
+ part: new NistPart({ type: "pt", value: m[2] }),
337
+ supplement: m[3],
338
+ };
339
+ }
340
+ m = /^(\d+)_(\d+-\d+)$/.exec(strValue);
341
+ if (m !== null) {
342
+ return { second_number: m[1], part: new NistPart({ type: "pt", value: m[2] }) };
343
+ }
344
+ return { second_number: strValue };
345
+ }
346
+ case "stage": {
347
+ if (!isObj(value))
348
+ return undefined;
349
+ const stageId = (s(value["stage_id"]) ?? "").toLowerCase();
350
+ const stageType = (s(value["stage_type"]) ?? "").toLowerCase();
351
+ if (stageId === "" || stageType === "")
352
+ return undefined;
353
+ return { stage: new NistStage({ id: stageId, type: stageType }) };
354
+ }
355
+ case "stage_id":
356
+ case "stage_type":
357
+ return undefined;
358
+ case "parsed_format": {
359
+ const v = s(value);
360
+ return v === "short" || v === undefined ? undefined : v;
361
+ }
362
+ case "translation": {
363
+ const v = s(value);
364
+ if (v === undefined)
365
+ return undefined;
366
+ const stripped = v.trim();
367
+ const code = TRANSLATION_MAP[stripped.toLowerCase()] ?? stripped.toLowerCase();
368
+ // The gem's translation attr is the component; its to_s prefixes
369
+ // the space (" .uppl"), and the wire serializes that form.
370
+ return { translation_component: new NistTranslation({ code }), translation: ` ${code}` };
371
+ }
372
+ case "version": {
373
+ const v = s(value);
374
+ return v === undefined ? undefined : { version_component: new NistVersion({ value: v }) };
375
+ }
376
+ case "update": {
377
+ if (isObj(value)) {
378
+ // Ruby's prefix lookup reads the INNER hash where update_prefix
379
+ // never lands — the prefix is always the slash form.
380
+ const update = new NistUpdate({
381
+ number: s(value["update_number"]),
382
+ year: s(value["update_year"]),
383
+ month: s(value["update_month"]),
384
+ prefix: "slash",
385
+ });
386
+ return { update, update_component: update };
387
+ }
388
+ // Bare "-upd"/"/upd" with no details: Ruby's empty-string branch reads
389
+ // the OUTER hash prefix, so the dash/slash distinction survives here.
390
+ const barePrefix = (s(h["update_prefix"]) ?? "").includes("-") ? "dash" : "slash";
391
+ const update = new NistUpdate({ number: "1", prefix: barePrefix });
392
+ return { update, update_component: update };
393
+ }
394
+ case "update_prefix":
395
+ case "update_number":
396
+ case "update_year":
397
+ case "update_month":
398
+ return undefined;
399
+ case "volume": {
400
+ const v = s(value);
401
+ return v === undefined ? undefined : { volume: new NistVolume({ value: v }) };
402
+ }
403
+ case "section":
404
+ case "appendix":
405
+ case "errata":
406
+ case "index":
407
+ case "insert":
408
+ return s(value);
409
+ case "revision": {
410
+ if (isObj(value) && value["revision_prefix"] !== undefined) {
411
+ const prefix = s(value["revision_prefix"]) ?? "";
412
+ let id = (s(value["revision_id"]) ?? "").trim();
413
+ if (id === "" || id === "r" || id === "R")
414
+ id = "1";
415
+ else {
416
+ const m = /^(\d+[a-z]?)$/.exec(id);
417
+ if (m !== null)
418
+ id = m[1];
419
+ }
420
+ return editionCast("r", id, undefined, prefix);
421
+ }
422
+ const strValue = (s(value) ?? "").trim();
423
+ let revisionId;
424
+ if (strValue === "" || strValue === "r" || strValue === "R")
425
+ revisionId = "1";
426
+ else {
427
+ const m = /^[rR]?(\d+[a-z]?)$/.exec(strValue);
428
+ revisionId = m !== null ? m[1] : strValue;
429
+ }
430
+ return editionCast("r", revisionId);
431
+ }
432
+ case "revision_year": {
433
+ const yearValue = (s(value) ?? "").trim();
434
+ if (h["revision_month"] !== undefined)
435
+ return yearValue;
436
+ return editionCast("r", yearValue);
437
+ }
438
+ case "revision_month":
439
+ return s(value);
440
+ case "supplement_year":
441
+ return s(value) === undefined ? undefined : { supplement: s(value) };
442
+ case "supplement": {
443
+ if (value === undefined)
444
+ return undefined;
445
+ if (Array.isArray(value) && value.length === 0)
446
+ return { supplement: "" };
447
+ const strValue = (s(value) ?? "").trim();
448
+ return strValue === "" ? undefined : { supplement: strValue };
449
+ }
450
+ case "supplement_date_range": {
451
+ if (!isObj(value))
452
+ return undefined;
453
+ const ms = s(value["supp_month_start"]);
454
+ const ys = s(value["supp_year_start"]);
455
+ const me = s(value["supp_month_end"]);
456
+ const ye = s(value["supp_year_end"]);
457
+ return {
458
+ supplement_date_range_start: ms !== undefined && ys !== undefined ? `${ms}${ys}` : undefined,
459
+ supplement_date_range_end: me !== undefined && ye !== undefined ? `${me}${ye}` : undefined,
460
+ };
461
+ }
462
+ case "supplement_date": {
463
+ if (!isObj(value))
464
+ return undefined;
465
+ const month = s(value["supp_month"]);
466
+ const year = s(value["supp_year"]);
467
+ return month !== undefined && year !== undefined ? { supplement: `${month}${year}` } : undefined;
468
+ }
469
+ case "supplement_slash_year": {
470
+ if (!isObj(value))
471
+ return undefined;
472
+ const number = s(value["supp_number"]);
473
+ const year = s(value["supp_year"]);
474
+ return number !== undefined && year !== undefined ? { supplement: `${number}/${year}` } : undefined;
475
+ }
476
+ case "supplement_with_rev":
477
+ return { supplement: "", supplement_has_revision: true };
478
+ case "supp_year":
479
+ return { supplement: s(value) ?? "" };
480
+ case "edition_e":
481
+ return isObj(value) ? editionCast("e", s(value["edition_id"])) : undefined;
482
+ case "edition_r":
483
+ case "edition_r_no_space":
484
+ case "edition_rev":
485
+ return isObj(value) ? editionCast("r", s(value["edition_id"])) : undefined;
486
+ case "edition_r_letter":
487
+ if (!isObj(value))
488
+ return undefined;
489
+ return editionCast("r", s(value["edition_id"]), (s(value["edition_letter"]) ?? "").toLowerCase());
490
+ case "edition_r_letter_only":
491
+ if (!isObj(value))
492
+ return undefined;
493
+ return editionCast("r", (s(value["edition_letter"]) ?? "").toLowerCase());
494
+ case "edition_historical":
495
+ return isObj(value) ? editionCast("-", s(value["edition_id"])) : undefined;
496
+ case "edition_r_with_space_letter":
497
+ case "edition_r_no_space_letter": {
498
+ if (!isObj(value))
499
+ return undefined;
500
+ const id = s(value["edition_id"]);
501
+ const editionLetter = (s(value["edition_letter"]) ?? "").toUpperCase();
502
+ const hasUpdate = h["update_prefix"] !== undefined || h["update"] !== undefined;
503
+ if (hasUpdate)
504
+ return editionCast("r", id, editionLetter);
505
+ return editionCast("r", id, editionLetter, " r");
506
+ }
507
+ case "edition_r_with_space": {
508
+ if (!isObj(value))
509
+ return undefined;
510
+ const id = s(value["edition_id"]);
511
+ const hasUpdate = h["update_prefix"] !== undefined || h["update"] !== undefined;
512
+ if (hasUpdate)
513
+ return editionCast("r", id);
514
+ return editionCast("r", id, undefined, " r");
515
+ }
516
+ case "edition_id":
517
+ case "edition_date":
518
+ case "legacy_edition":
519
+ case "edition_month":
520
+ return undefined;
521
+ case "edition_day":
522
+ return s(value);
523
+ case "edition_dash_year":
524
+ return undefined;
525
+ case "edition_year": {
526
+ const yearValue = s(value);
527
+ if (yearValue === undefined)
528
+ return undefined;
529
+ const monthStr = s(h["edition_month"]);
530
+ const policyHere = seriesPolicy(s(h["series"]));
531
+ if (monthStr !== undefined) {
532
+ const monthNum = MONTH_NUMBERS[monthStr];
533
+ if (monthNum !== undefined) {
534
+ if (!policyHere.modernEditionDate &&
535
+ /^[A-Z][a-z]+/.test(monthStr) &&
536
+ /^\d{4}$/.test(yearValue)) {
537
+ return {
538
+ edition: new NistEdition({ type: "-", id: "", additional_text: `${monthStr}${yearValue}` }),
539
+ edition_year: yearValue,
540
+ };
541
+ }
542
+ let editionId = `${yearValue}${String(monthNum).padStart(2, "0")}`;
543
+ if (policyHere.modernEditionDate && h["edition_day"] !== undefined) {
544
+ const dayNum = Number(s(h["edition_day"]));
545
+ if (dayNum > 0 && dayNum <= 31)
546
+ editionId += `${String(dayNum).padStart(2, "0")}`;
547
+ }
548
+ return { edition: new NistEdition({ type: "e", id: editionId }), edition_year: yearValue };
549
+ }
550
+ }
551
+ return { edition: new NistEdition({ type: "e", id: yearValue }), edition_year: yearValue };
552
+ }
553
+ case "edition_e_date":
554
+ return isObj(value) ? editionCast("e", s(value["edition_date"])) : undefined;
555
+ case "part": {
556
+ const strValue = (s(value) ?? "").trim();
557
+ if (strValue === "")
558
+ return undefined;
559
+ let m = /^(\d+)add(e\d+)$/.exec(strValue);
560
+ if (m !== null)
561
+ return { part: new NistPart({ type: "pt", value: m[1] }), addendum: "true" };
562
+ m = /^(\d+)add/.exec(strValue);
563
+ if (m !== null)
564
+ return { part: new NistPart({ type: "pt", value: m[1] }), addendum: "true" };
565
+ return { part: new NistPart({ type: "pt", value: strValue }) };
566
+ }
567
+ case "edition_letter":
568
+ return s(value);
569
+ case "public_draft":
570
+ return s(value);
571
+ case "draft": {
572
+ const strValue = (s(value) ?? "").trim();
573
+ if (strValue === "")
574
+ return undefined;
575
+ let m = /^\s*-draft\s+(\d+)$/.exec(strValue);
576
+ if (m !== null)
577
+ return { draft_number: m[1] };
578
+ m = /^\s*(\d+)pd$/.exec(strValue);
579
+ if (m !== null)
580
+ return { public_draft: m[1] };
581
+ return strValue;
582
+ }
583
+ case "addendum": {
584
+ if (isObj(value)) {
585
+ const addendumNum = (s(value["addendum_number"]) ?? "").trim();
586
+ return addendumNum !== "" ? { addendum_number: addendumNum } : { addendum: "true" };
587
+ }
588
+ const strValue = (s(value) ?? "").trim();
589
+ return strValue === "" ? { addendum: "true" } : { addendum_number: strValue };
590
+ }
591
+ case "addendum_number":
592
+ return s(value);
593
+ case "supplement_suffix":
594
+ return { supplement: s(value) ?? "" };
595
+ case "date": {
596
+ if (!isObj(value))
597
+ return undefined;
598
+ const month = s(value["date_month"]);
599
+ const year = s(value["date_year"]);
600
+ if (month !== undefined && year !== undefined && value["date_day"] === undefined && /^[A-Za-z]+$/.test(month)) {
601
+ return editionCast("-", undefined, `${month}${year}`);
602
+ }
603
+ return undefined;
604
+ }
605
+ default:
606
+ return isObj(value) ? value : undefined;
607
+ }
608
+ }
609
+ function castNumber(type, value, h, policy) {
610
+ if (value === undefined || (s(value) === undefined && !isObj(value)))
611
+ return undefined;
612
+ if (isObj(value)) {
613
+ if (value["owmwp_date_number"] !== undefined) {
614
+ const owmwp = value["owmwp_date_number"];
615
+ const numberPart = `${s(owmwp["owmwp_month"])}-${s(owmwp["owmwp_day"])}`;
616
+ return { [type]: numberPart, edition: new NistEdition({ type: "e", id: s(owmwp["owmwp_year"]) }) };
617
+ }
618
+ if (type === "second_number" && value["number_only"] !== undefined && value["edition_id"] !== undefined) {
619
+ return { second_number: value };
620
+ }
621
+ if (type === "second_number" && value["revision_letter"] !== undefined) {
622
+ const revisionData = value["revision_letter"];
623
+ const numberOnly = s(revisionData["number_only"]) ?? "";
624
+ const letter = (s(revisionData["letter"]) ?? "").toUpperCase();
625
+ return { second_number: `${numberOnly}r${letter}` };
626
+ }
627
+ if (value["volume_number"] !== undefined && value["issue_number"] !== undefined) {
628
+ return {
629
+ volume: new NistVolume({ value: s(value["volume_number"]) }),
630
+ part: new NistPart({ type: "n", value: s(value["issue_number"]) }),
631
+ };
632
+ }
633
+ if (type === "first_number" && value["number_with_rev_year"] !== undefined) {
634
+ const data = value["number_with_rev_year"];
635
+ return {
636
+ first_number: s(data["number"]),
637
+ edition: new NistEdition({ type: "rv", id: s(data["revision_year"]) }),
638
+ };
639
+ }
640
+ if (type === "first_number" && value["number_with_letter_revision"] !== undefined) {
641
+ const data = value["number_with_letter_revision"];
642
+ return {
643
+ first_number: s(data["number"]),
644
+ part: new NistPart({ type: "", value: (s(data["letter_suffix"]) ?? "").toUpperCase() }),
645
+ edition: new NistEdition({ type: "r", id: s(data["revision_id"]) }),
646
+ };
647
+ }
648
+ if (type === "first_number" && value["number"] !== undefined && value["language_code"] !== undefined) {
649
+ const languageCode = (s(value["language_code"]) ?? "").toLowerCase();
650
+ return {
651
+ first_number: s(value["number"]),
652
+ translation_component: new NistTranslation({ code: TRANSLATION_MAP[languageCode] ?? languageCode }),
653
+ };
654
+ }
655
+ if (type === "first_number" && value["number"] !== undefined && value["part_number"] !== undefined && value["edition_year"] !== undefined) {
656
+ return {
657
+ first_number: s(value["number"]),
658
+ part: new NistPart({ type: "pt", value: s(value["part_number"]) }),
659
+ edition: new NistEdition({ type: "e", id: s(value["edition_year"]) }),
660
+ };
661
+ }
662
+ if (type === "first_number" && value["number_with_volume"] !== undefined) {
663
+ const data = value["number_with_volume"];
664
+ if (data["volume_suffix"] !== undefined) {
665
+ return {
666
+ first_number: s(data["number"]),
667
+ volume: new NistVolume({ value: s(data["volume_suffix"]) }),
668
+ };
669
+ }
670
+ }
671
+ }
672
+ const strValue = s(value) ?? "";
673
+ if (type === "first_number") {
674
+ let m;
675
+ if (h["edition_year_separate"] !== undefined && /^(\d+)e(\d+)$/.test(strValue)) {
676
+ m = /^(\d+)e(\d+)$/.exec(strValue);
677
+ return {
678
+ first_number: m[1],
679
+ edition: new NistEdition({ type: "e", id: m[2], additional_text: s(h["edition_year_separate"]) }),
680
+ };
681
+ }
682
+ if (h["historical_month"] !== undefined && h["historical_year"] !== undefined) {
683
+ const monthPart = s(h["historical_month"]);
684
+ const yearPart = s(h["historical_year"]);
685
+ if (/^\d+$/.test(strValue)) {
686
+ return { first_number: strValue, edition: new NistEdition({ type: "-", additional_text: `${monthPart}${yearPart}` }) };
687
+ }
688
+ return { edition: new NistEdition({ type: "-", additional_text: `${monthPart}${yearPart}` }) };
689
+ }
690
+ if ((m = /^(\d+)supp?$/.exec(strValue)) !== null) {
691
+ if (h["supplement_year"] !== undefined) {
692
+ return { first_number: m[1], supplement: s(h["supplement_year"]) };
693
+ }
694
+ return { first_number: m[1], supplement: "" };
695
+ }
696
+ if ((m = /^(\d+)supprev$/.exec(strValue)) !== null) {
697
+ return { first_number: m[1], supplement: "", supplement_has_revision: true };
698
+ }
699
+ if ((m = /^(\d+)e(\d+)-(\d{4})$/.exec(strValue)) !== null) {
700
+ return { first_number: m[1], edition: new NistEdition({ type: "e", id: m[2], additional_text: m[3] }) };
701
+ }
702
+ if ((m = /^-([A-Za-z]{3,9})(\d{4})$/.exec(strValue)) !== null) {
703
+ return { edition: new NistEdition({ type: "-", additional_text: `${m[1]}${m[2]}` }) };
704
+ }
705
+ if (/^e(\d{3,})$/.test(strValue)) {
706
+ return { first_number: h["second_number"] !== undefined ? strValue : strValue.slice(1) };
707
+ }
708
+ if ((m = /^(\d+)e(\d+)$/.exec(strValue)) !== null && h["second_number"] === undefined && h["edition_dash_year"] === undefined) {
709
+ return { first_number: m[1], edition: new NistEdition({ type: "e", id: m[2] }) };
710
+ }
711
+ // "150-1Ae2009" / "53ar1": letter between the compound number and an
712
+ // edition/revision splits off as a Part component.
713
+ if ((m = /^(\d+-\d+)([A-Z])e(\d{4})$/.exec(strValue)) !== null) {
714
+ return {
715
+ first_number: m[1],
716
+ part: new NistPart({ type: "", value: m[2] }),
717
+ edition: new NistEdition({ type: "e", id: m[3] }),
718
+ };
719
+ }
720
+ if ((m = /^e(\d{1,2})$/.exec(strValue)) !== null) {
721
+ return { edition: new NistEdition({ type: "e", id: m[1] }) };
722
+ }
723
+ if ((m = /^(\d+)e(\d+)rev(\d{4})$/.exec(strValue)) !== null) {
724
+ return { first_number: m[1], edition: new NistEdition({ type: "e", id: m[2], additional_text: m[3] }) };
725
+ }
726
+ if ((m = /^(\d+)e(\d+)(rev.+)$/.exec(strValue)) !== null) {
727
+ return { first_number: m[1], edition: new NistEdition({ type: "e", id: m[2], additional_text: m[3].replace(/^rev/, "") }) };
728
+ }
729
+ if ((m = /^(\d+)supp([A-Za-z]{3,9})(\d{4})$/.exec(strValue)) !== null) {
730
+ return { first_number: m[1], supplement: `${m[2]}${m[3]}` };
731
+ }
732
+ if ((m = /^(\d+)supp(\d{4})$/.exec(strValue)) !== null) {
733
+ return { first_number: m[1], supplement: m[2] };
734
+ }
735
+ if ((m = /^(\d+)supp-(\d{4})$/.exec(strValue)) !== null) {
736
+ return { first_number: m[1], supplement: m[2] };
737
+ }
738
+ if ((m = /^(\d+)e(\d+)supp$/.exec(strValue)) !== null) {
739
+ return { first_number: m[1], edition: new NistEdition({ type: "e", id: m[2] }), supplement: "" };
740
+ }
741
+ }
742
+ else if (type === "second_number" && isObj(value) && value["first_number"] !== undefined) {
743
+ const v = value;
744
+ return {
745
+ first_number: s(v["first_number"]),
746
+ part: new NistPart({ value: s(v["part_value"]) ?? "" }),
747
+ edition: new NistEdition({ type: "r", id: s(v["revision_value"]) }),
748
+ };
749
+ }
750
+ let m;
751
+ if ((m = /^(.+?)pt(\d+)r(\d+[a-z]?)$/.exec(strValue)) !== null) {
752
+ return { [type]: m[1], part: new NistPart({ type: "pt", value: m[2] }), edition: new NistEdition({ type: "r", id: m[3] }) };
753
+ }
754
+ if ((m = /^(.+?)pt(\d+)$/.exec(strValue)) !== null) {
755
+ return { [type]: m[1], part: new NistPart({ type: "pt", value: m[2] }) };
756
+ }
757
+ if ((m = /^(\d+)v(\d+)$/.exec(strValue)) !== null) {
758
+ return { [type]: m[1], volume: m[2] };
759
+ }
760
+ if ((m = /^(.+?)(r\d+\/\d{4})$/i.exec(strValue)) !== null) {
761
+ const rm = /^r(\d+)\/(\d{4})$/.exec(m[2]);
762
+ if (rm !== null) {
763
+ return { [type]: m[1], edition: new NistEdition({ type: "r", id: rm[1], additional_text: rm[2] }) };
764
+ }
765
+ }
766
+ if ((m = /^(.*\d)(r\d{4})$/i.exec(strValue)) !== null) {
767
+ return { [type]: m[1], edition: new NistEdition({ type: "r", id: m[2].slice(1) }) };
768
+ }
769
+ if ((m = /^(.+?)(r[A-Za-z]{3,9}\d{4})$/i.exec(strValue)) !== null) {
770
+ const rm = /^r([A-Za-z]{3,9})(\d{4})$/.exec(m[2]);
771
+ if (rm !== null) {
772
+ return { [type]: m[1], edition: new NistEdition({ type: "r", id: `${rm[1]}${rm[2]}` }) };
773
+ }
774
+ }
775
+ if ((m = /^(.*\d)(r\d+[a-z]?)$/i.exec(strValue)) !== null) {
776
+ return { [type]: m[1], edition: new NistEdition({ type: "r", id: m[2].slice(1) }) };
777
+ }
778
+ if ((m = /^(.+?)(?<![a-zA-Z])(r)$/i.exec(strValue)) !== null) {
779
+ return { [type]: m[1], edition: new NistEdition({ type: "r", id: "1" }) };
780
+ }
781
+ if ((m = /^(.+?)([A-Z])(r\d+[a-z]?)$/.exec(strValue)) !== null) {
782
+ return {
783
+ [type]: m[1],
784
+ part: new NistPart({ type: "", value: m[2] }),
785
+ edition: new NistEdition({ type: "r", id: m[3].slice(1) }),
786
+ };
787
+ }
788
+ if ((m = /^(.+?)([A-Z])$/.exec(strValue)) !== null) {
789
+ if (policy.preserveLetterSuffix) {
790
+ return { [type]: strValue };
791
+ }
792
+ return { [type]: m[1], part: new NistPart({ type: "", value: m[2] }) };
793
+ }
794
+ return { [type]: strValue };
795
+ }
796
+ class NistBuilder {
797
+ build(data, parsedFormat) {
798
+ const h = { ...flatten(data) };
799
+ h["parsed_format"] = parsedFormat;
800
+ normalizeParsedHash(h);
801
+ if (h["supplement_date_range"] !== undefined ||
802
+ h["supplement_slash_year"] !== undefined ||
803
+ h["supplement_month_year"] !== undefined ||
804
+ h["supplement_year"] !== undefined ||
805
+ h["supplement"] !== undefined ||
806
+ h["base_portion"] !== undefined) {
807
+ return this.buildCircularSupplement(h);
808
+ }
809
+ const klass = locateIdentifierKlass(h);
810
+ const policy = seriesPolicy(s(h["series"]));
811
+ const identifier = new klass();
812
+ const attrs = {};
813
+ const toEdition = (v) => {
814
+ if (isObj(v)) {
815
+ return new NistEdition({
816
+ type: s(v["type"]),
817
+ id: s(v["id"]),
818
+ additional_text: s(v["additional_text"]),
819
+ });
820
+ }
821
+ return undefined;
822
+ };
823
+ for (const editionKey of ["edition_with_year", "edition_from_year", "edition"]) {
824
+ if (h[editionKey] !== undefined) {
825
+ const edition = toEdition(h[editionKey]);
826
+ if (edition !== undefined)
827
+ attrs["edition"] = edition;
828
+ delete h[editionKey];
829
+ }
830
+ }
831
+ const supp = { value: undefined, hasRevision: false, rangeStart: undefined, rangeEnd: undefined, present: false };
832
+ const captureSupplement = (key, value) => {
833
+ if (key === "supplement") {
834
+ supp.value = value === undefined || value === null ? undefined : String(value);
835
+ }
836
+ else if (key === "supplement_has_revision") {
837
+ supp.hasRevision = value === true;
838
+ }
839
+ else if (key === "supplement_date_range_start") {
840
+ supp.rangeStart = s(value);
841
+ }
842
+ else if (key === "supplement_date_range_end") {
843
+ supp.rangeEnd = s(value);
844
+ }
845
+ else {
846
+ return false;
847
+ }
848
+ supp.present = true;
849
+ return true;
850
+ };
851
+ let firstNum;
852
+ let secondNum;
853
+ let secondNumRaw;
854
+ let decimalNum;
855
+ let letterNum;
856
+ let partNum;
857
+ let extractedRevision;
858
+ const seriesToken = s(h["series"]);
859
+ for (const [key, rawValue] of Object.entries(h)) {
860
+ const realized = cast(key, rawValue, h, policy, seriesToken);
861
+ if (realized === undefined)
862
+ continue;
863
+ if (typeof realized !== "object" || realized === null) {
864
+ if (captureSupplement(key, realized))
865
+ continue;
866
+ }
867
+ if (key === "first_number" && typeof realized === "string") {
868
+ firstNum = realized;
869
+ continue;
870
+ }
871
+ if (key === "second_number" && typeof realized === "string") {
872
+ secondNum = realized;
873
+ continue;
874
+ }
875
+ if (key === "second_number" && isObj(realized) && isObj(realized["second_number"])) {
876
+ secondNumRaw = realized["second_number"];
877
+ continue;
878
+ }
879
+ if (key === "crpl_range" && typeof realized === "object" && realized !== null && !Array.isArray(realized)) {
880
+ const rec = realized;
881
+ const sn = rec["second_number"];
882
+ if (typeof sn === "string")
883
+ secondNum = sn;
884
+ if (rec["part"] !== undefined)
885
+ attrs["part"] = rec["part"];
886
+ if (rec["supplement"] !== undefined)
887
+ captureSupplement("supplement", rec["supplement"]);
888
+ continue;
889
+ }
890
+ if (key === "part_number") {
891
+ partNum = s(rawValue);
892
+ continue;
893
+ }
894
+ if (key === "decimal_number" && isObj(rawValue)) {
895
+ decimalNum = rawValue;
896
+ continue;
897
+ }
898
+ if (key === "letter_number" && isObj(rawValue)) {
899
+ letterNum = rawValue;
900
+ if (isObj(realized) && "part" in realized)
901
+ attrs["part"] = realized["part"];
902
+ continue;
903
+ }
904
+ if (isObj(realized)) {
905
+ for (const [subKey, subValue] of Object.entries(realized)) {
906
+ if (subKey === "first_number" && typeof subValue === "string") {
907
+ firstNum = subValue;
908
+ continue;
909
+ }
910
+ if (subKey === "second_number" && typeof subValue === "string") {
911
+ secondNum = subValue;
912
+ continue;
913
+ }
914
+ if (subKey === "second_number" && isObj(subValue)) {
915
+ secondNumRaw = subValue;
916
+ extractedRevision = "r";
917
+ continue;
918
+ }
919
+ if (subKey === "revision") {
920
+ extractedRevision = s(subValue);
921
+ continue;
922
+ }
923
+ if (captureSupplement(subKey, subValue))
924
+ continue;
925
+ attrs[subKey] = subValue;
926
+ }
927
+ }
928
+ else {
929
+ attrs[key] = realized;
930
+ }
931
+ }
932
+ Object.assign(identifier, attrs);
933
+ // Compound number construction.
934
+ const self = identifier;
935
+ if (firstNum !== undefined && self["number"] === undefined) {
936
+ if (self["volume"] !== undefined && self["issue_number"] !== undefined) {
937
+ // v#n# handled as Part during cast.
938
+ }
939
+ else if (decimalNum !== undefined) {
940
+ self["number"] = `${firstNum}-${s(decimalNum["decimal_base"])}.${s(decimalNum["decimal_suffix"])}`;
941
+ }
942
+ else if (letterNum !== undefined) {
943
+ const letterBase = s(letterNum["letter_base"]) ?? "";
944
+ const letterSuffix = s(letterNum["letter_suffix"]) ?? "";
945
+ const extra = s(letterNum["letter_suffix_extra"]) ?? "";
946
+ const full = extra === "" ? letterSuffix : `${letterSuffix}${extra}`;
947
+ if (policy === seriesPolicy("IR") && full === "R") {
948
+ self["number"] = `${firstNum}-${letterBase}`;
949
+ self["edition"] = new NistEdition({ type: "r", id: "1" });
950
+ }
951
+ else if (self["part"] !== undefined) {
952
+ self["number"] = `${firstNum}-${letterBase}`;
953
+ }
954
+ else {
955
+ self["number"] = `${firstNum}-${letterBase}${full}`;
956
+ }
957
+ }
958
+ else if (secondNumRaw !== undefined && secondNumRaw["number_only"] !== undefined && secondNumRaw["edition_id"] !== undefined) {
959
+ self["number"] = `${firstNum}-${s(secondNumRaw["number_only"])}`;
960
+ self["edition"] = new NistEdition({ type: "r", id: s(secondNumRaw["edition_id"]) });
961
+ }
962
+ else if (secondNum !== undefined) {
963
+ let m = /^e(\d{3})$/.exec(firstNum);
964
+ if (m !== null && /^\d{2}$/.test(secondNum)) {
965
+ self["number"] = m[1];
966
+ self["edition"] = new NistEdition({ type: "e", id: `19${secondNum}` });
967
+ }
968
+ else if ((m = /^(\d+)e(\d+)$/.exec(firstNum)) !== null && /^\d{2,4}$/.test(secondNum)) {
969
+ let yearPart = secondNum;
970
+ if (yearPart.length === 2)
971
+ yearPart = `19${yearPart}`;
972
+ self["number"] = m[1];
973
+ self["edition"] = new NistEdition({ type: "e", id: m[2], additional_text: yearPart });
974
+ }
975
+ else if ((m = /^(\d+)supp?$/.exec(firstNum)) !== null && /^\d{4}$/.test(secondNum)) {
976
+ self["number"] = m[1];
977
+ supp.value = secondNum;
978
+ supp.present = true;
979
+ }
980
+ else if ((m = /^(\d+)supp?$/.exec(secondNum)) !== null) {
981
+ self["number"] = `${firstNum}-${m[1]}`;
982
+ supp.value = "";
983
+ supp.present = true;
984
+ }
985
+ else if (klass === classForKind("technical-note") && /^(19|20)\d{2}$/.test(secondNum)) {
986
+ self["number"] = firstNum;
987
+ self["edition"] = new NistEdition({ type: "e", id: secondNum });
988
+ self["edition_year"] = secondNum;
989
+ }
990
+ else if (partNum !== undefined && policy.partNumAsComponent) {
991
+ self["part"] = new NistPart({ type: "pt", value: partNum });
992
+ self["number"] = `${firstNum}-${secondNum}`;
993
+ }
994
+ else {
995
+ let compound = `${firstNum}-${secondNum}`;
996
+ if (partNum !== undefined)
997
+ compound += `-${partNum}`;
998
+ self["number"] = compound;
999
+ }
1000
+ }
1001
+ else {
1002
+ self["number"] = firstNum;
1003
+ }
1004
+ }
1005
+ if (extractedRevision !== undefined && extractedRevision !== "r" && self["edition"] === undefined) {
1006
+ self["edition"] = new NistEdition({ type: "r", id: extractedRevision });
1007
+ }
1008
+ // SP subseries metadata.
1009
+ if (klass === classForKind("special-publication") && firstNum !== undefined) {
1010
+ if (SP_SUBSERIES.includes(firstNum)) {
1011
+ self["subseries"] = new NistCode({ value: firstNum });
1012
+ }
1013
+ else {
1014
+ const m = /^(\d+GB)\b/.exec(firstNum);
1015
+ if (m !== null && SP_SUBSERIES.includes(m[1])) {
1016
+ self["subseries"] = new NistCode({ value: m[1] });
1017
+ }
1018
+ }
1019
+ }
1020
+ // IR reverses the "84e2946" form back to "84-2946".
1021
+ if (policy === seriesPolicy("IR") && typeof self["number"] === "string") {
1022
+ const m = /^(\d+)e(\d{4})$/.exec(self["number"]);
1023
+ if (m !== null) {
1024
+ self["number"] = `${m[1]}-${m[2]}`;
1025
+ self["edition"] = undefined;
1026
+ }
1027
+ }
1028
+ if (self["publisher"] === undefined) {
1029
+ self["publisher_was_parsed"] = false;
1030
+ }
1031
+ // TN bare "-upd": Ruby's TechnicalNote#update_component= replaces a
1032
+ // dash-prefix update that has no year with the default Feb-2021 update.
1033
+ const tnSeriesCode = identifier.seriesCode?.();
1034
+ const bareUpdate = self["update_component"] instanceof NistUpdate ? self["update_component"] : undefined;
1035
+ if (tnSeriesCode === "TN" && bareUpdate !== undefined && bareUpdate.prefix === "dash" && bareUpdate.year === undefined) {
1036
+ const defaulted = new NistUpdate({
1037
+ number: bareUpdate.number ?? "1",
1038
+ year: "2021",
1039
+ month: "02",
1040
+ prefix: "slash",
1041
+ });
1042
+ self["update_component"] = defaulted;
1043
+ self["update"] = defaulted;
1044
+ }
1045
+ // Revision with month+year → update component (V1 compatibility).
1046
+ if (h["revision_month"] !== undefined && h["revision_year"] !== undefined) {
1047
+ const monthNum = MONTH_NUMBERS[s(h["revision_month"]) ?? ""] ?? 1;
1048
+ const update = new NistUpdate({
1049
+ number: "1",
1050
+ year: s(h["revision_year"]),
1051
+ month: String(monthNum).padStart(2, "0"),
1052
+ prefix: "slash",
1053
+ });
1054
+ self["update_component"] = update;
1055
+ self["update"] = update;
1056
+ self["revision_year"] = undefined;
1057
+ self["revision_month"] = undefined;
1058
+ }
1059
+ if ((supp.present || supp.hasRevision) && "supplement" in identifier.constructor.attributes) {
1060
+ if (supp.rangeStart !== undefined || supp.rangeEnd !== undefined) {
1061
+ const component = {};
1062
+ const sm = /^([A-Za-z]{3,9})?(\d{4})$/.exec(supp.rangeStart ?? "");
1063
+ if (supp.rangeStart !== undefined && sm !== null) {
1064
+ component["month"] = sm[1];
1065
+ component["year"] = sm[2];
1066
+ }
1067
+ const em = /^([A-Za-z]{3,9})?(\d{4})$/.exec(supp.rangeEnd ?? "");
1068
+ if (supp.rangeEnd !== undefined && em !== null) {
1069
+ component["month_end"] = em[1];
1070
+ component["year_end"] = em[2];
1071
+ }
1072
+ self["supplement"] = new NistSupplement(component);
1073
+ }
1074
+ else {
1075
+ self["supplement"] = NistSupplement.fromRaw(supp.value, supp.hasRevision);
1076
+ }
1077
+ }
1078
+ // Default publisher for series-less renders.
1079
+ if (self["publisher"] === undefined) {
1080
+ const defaultPublisher = identifier.constructor.nistDefaultPublisher;
1081
+ if (defaultPublisher !== undefined && defaultPublisher !== "") {
1082
+ self["publisher"] = defaultPublisher;
1083
+ if (self["publisher_was_parsed"] === false) {
1084
+ // Circular/RPT/MP render "NBS <series>" through the default even
1085
+ // when not parsed — keep the flag false (Ruby publisher_was_parsed
1086
+ // stays false but to_short_style uses default only via series).
1087
+ }
1088
+ }
1089
+ }
1090
+ return identifier;
1091
+ }
1092
+ buildCircularSupplement(h) {
1093
+ if (isObj(h["supplement_slash_year"]) || isObj(h["implicit_supplement"])) {
1094
+ return this.buildCircularSupplementWrapper(h);
1095
+ }
1096
+ const seriesValue = isObj(h["circ_series"]) ? s(h["circ_series"]["series"]) : s(h["series"]);
1097
+ if (isObj(h["supplement_date_range"])) {
1098
+ const range = h["supplement_date_range"];
1099
+ const identifier = this.build({ series: seriesValue }, "short");
1100
+ const ms = s(range["supp_month_start"]);
1101
+ const ys = s(range["supp_year_start"]);
1102
+ const me = s(range["supp_month_end"]);
1103
+ const ye = s(range["supp_year_end"]);
1104
+ identifier["supplement"] = new NistSupplement({
1105
+ month: ms,
1106
+ year: ys,
1107
+ month_end: me,
1108
+ year_end: ye,
1109
+ });
1110
+ return identifier;
1111
+ }
1112
+ const base = this.buildCircularSupplementBase(h, seriesValue);
1113
+ const self = base;
1114
+ const raw = h["supplement_month_year"] !== undefined
1115
+ ? s(h["supplement_month_year"])
1116
+ : h["supplement_year"] !== undefined
1117
+ ? s(h["supplement_year"])
1118
+ : "";
1119
+ self["supplement"] = NistSupplement.fromRaw(raw);
1120
+ return base;
1121
+ }
1122
+ buildCircularSupplementBase(h, seriesValue) {
1123
+ const basePortion = h["base_portion"];
1124
+ if (!isObj(basePortion)) {
1125
+ return this.build({
1126
+ publisher: s(h["publisher"]),
1127
+ series: seriesValue ?? s(h["series"]),
1128
+ first_number: s(h["first_number"]),
1129
+ }, s(h["parsed_format"]) ?? "short");
1130
+ }
1131
+ const baseNumber = s(basePortion["simple_number"]) ?? s(basePortion["base_number"]);
1132
+ let letterSuffix;
1133
+ if (basePortion["letter_suffix"] !== undefined) {
1134
+ letterSuffix = (s(basePortion["letter_suffix"]) ?? "").toUpperCase();
1135
+ }
1136
+ let publisherValue;
1137
+ if (isObj(h["circ_series"]) && h["circ_series"]["series"] !== undefined) {
1138
+ const seriesStr = s(h["circ_series"]["series"]) ?? "";
1139
+ if (seriesStr.includes(" "))
1140
+ publisherValue = seriesStr.split(" ")[0];
1141
+ }
1142
+ const hasEdition = basePortion["edition_number"] !== undefined;
1143
+ let baseNumberWithSuffix = baseNumber ?? "";
1144
+ if (letterSuffix !== undefined)
1145
+ baseNumberWithSuffix += letterSuffix;
1146
+ if (hasEdition)
1147
+ baseNumberWithSuffix += `e${s(basePortion["edition_number"])}`;
1148
+ const baseHash = {
1149
+ series: seriesValue,
1150
+ first_number: baseNumberWithSuffix,
1151
+ };
1152
+ if (publisherValue !== undefined)
1153
+ baseHash["publisher"] = publisherValue;
1154
+ if (hasEdition)
1155
+ baseHash["edition_e"] = { edition_id: s(basePortion["edition_number"]) };
1156
+ return this.build(baseHash, s(h["parsed_format"]) ?? "short");
1157
+ }
1158
+ buildCircularSupplementWrapper(h) {
1159
+ const klass = classForKind("circular-supplement");
1160
+ const supplement = new klass();
1161
+ const self = supplement;
1162
+ const seriesValue = isObj(h["circ_series"])
1163
+ ? s(h["circ_series"]["series"])
1164
+ : s(h["series"]);
1165
+ if (isObj(h["supplement_date_range"])) {
1166
+ const range = h["supplement_date_range"];
1167
+ const ms = s(range["supp_month_start"]);
1168
+ const ys = s(range["supp_year_start"]);
1169
+ const me = s(range["supp_month_end"]);
1170
+ const ye = s(range["supp_year_end"]);
1171
+ if (ms !== undefined && ys !== undefined)
1172
+ self["supplement_date_range_start"] = `${ms}${ys}`;
1173
+ if (me !== undefined && ye !== undefined)
1174
+ self["supplement_date_range_end"] = `${me}${ye}`;
1175
+ return supplement;
1176
+ }
1177
+ if (isObj(h["base_portion"])) {
1178
+ const basePortion = h["base_portion"];
1179
+ const baseNumber = s(basePortion["simple_number"]) ?? s(basePortion["base_number"]);
1180
+ let letterSuffix;
1181
+ if (basePortion["letter_suffix"] !== undefined) {
1182
+ letterSuffix = (s(basePortion["letter_suffix"]) ?? "").toUpperCase();
1183
+ }
1184
+ let publisherValue;
1185
+ if (isObj(h["circ_series"]) && h["circ_series"]["series"] !== undefined) {
1186
+ const seriesStr = s(h["circ_series"]["series"]) ?? "";
1187
+ if (seriesStr.includes(" "))
1188
+ publisherValue = seriesStr.split(" ")[0];
1189
+ }
1190
+ const hasRevision = basePortion["revision_letter"] !== undefined && basePortion["revision_number"] !== undefined;
1191
+ const hasEdition = basePortion["edition_number"] !== undefined;
1192
+ let baseNumberWithSuffix = baseNumber ?? "";
1193
+ if (letterSuffix !== undefined)
1194
+ baseNumberWithSuffix += letterSuffix;
1195
+ if (hasEdition)
1196
+ baseNumberWithSuffix += `e${s(basePortion["edition_number"])}`;
1197
+ const baseHash = { series: seriesValue, first_number: baseNumberWithSuffix };
1198
+ if (publisherValue !== undefined)
1199
+ baseHash["publisher"] = publisherValue;
1200
+ if (hasEdition)
1201
+ baseHash["edition_e"] = { edition_id: s(basePortion["edition_number"]) };
1202
+ self["base"] = this.build(baseHash, s(h["parsed_format"]) ?? "short");
1203
+ if (hasRevision && isObj(h["implicit_supplement"])) {
1204
+ const revisionNumber = s(basePortion["revision_number"]) ?? "";
1205
+ const supplementYear = s(h["implicit_supplement"]["implicit_supplement_year"]) ?? "";
1206
+ self["update"] = new NistUpdate({ number: "1", year: `${supplementYear}${revisionNumber}`, prefix: "slash" });
1207
+ self["implicit_supplement"] = true;
1208
+ }
1209
+ }
1210
+ else if (h["first_number"] !== undefined) {
1211
+ self["base"] = this.build({
1212
+ publisher: s(h["publisher"]),
1213
+ series: seriesValue ?? s(h["series"]),
1214
+ first_number: s(h["first_number"]),
1215
+ }, s(h["parsed_format"]) ?? "short");
1216
+ }
1217
+ if (h["supplement_month_year"] !== undefined) {
1218
+ self["edition"] = new NistEdition({ type: "s", id: s(h["supplement_month_year"]) });
1219
+ }
1220
+ else if (h["supplement_year"] !== undefined) {
1221
+ self["edition"] = new NistEdition({ type: "s", id: s(h["supplement_year"]) });
1222
+ }
1223
+ else if (isObj(h["supplement_slash_year"])) {
1224
+ const suppHash = h["supplement_slash_year"];
1225
+ const suppNumber = s(suppHash["supp_number"]) ?? "";
1226
+ const suppYear = s(suppHash["supp_year"]) ?? "";
1227
+ self["update"] = new NistUpdate({ number: "1", year: `${suppYear}${suppNumber.padStart(2, "0")}`, prefix: "slash" });
1228
+ }
1229
+ return supplement;
1230
+ }
1231
+ }
1232
+ export function nistGrammarImplementation() {
1233
+ const builder = new NistBuilder();
1234
+ return {
1235
+ parse(input) {
1236
+ const { cleaned, format } = preprocessNist(input);
1237
+ const tree = parseGrammar(nistGrammar, cleaned);
1238
+ if (typeof tree !== "object" || tree === null) {
1239
+ throw new ParseFailed("NIST: unexpected parse tree", 0);
1240
+ }
1241
+ return builder.build(tree, format);
1242
+ },
1243
+ };
1244
+ }