@pubid/pubid 0.1.33 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/artifact/corpus.json.gz +0 -0
  2. package/dist/flavors/api/grammar.js +9 -2
  3. package/dist/flavors/api/index.js +31 -2
  4. package/dist/flavors/bipm/index.js +89 -14
  5. package/dist/flavors/bsi/model.js +13 -9
  6. package/dist/flavors/iec/index.js +15 -4
  7. package/dist/flavors/iec/model.js +26 -10
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +2 -0
  10. package/dist/model/identifier.d.ts +3 -0
  11. package/dist/model/identifier.js +6 -0
  12. package/dist/pubid-urn/decoder.d.ts +27 -0
  13. package/dist/pubid-urn/decoder.js +165 -0
  14. package/dist/pubid-urn/encoder.d.ts +30 -0
  15. package/dist/pubid-urn/encoder.js +197 -0
  16. package/dist/pubid-urn/errors.d.ts +15 -0
  17. package/dist/pubid-urn/errors.js +21 -0
  18. package/dist/pubid-urn/escape.d.ts +19 -0
  19. package/dist/pubid-urn/escape.js +65 -0
  20. package/dist/pubid-urn/index.d.ts +65 -0
  21. package/dist/pubid-urn/index.js +216 -0
  22. package/dist/pubid-urn/paths.d.ts +26 -0
  23. package/dist/pubid-urn/paths.js +129 -0
  24. package/dist/pubid-urn/schema.d.ts +88 -0
  25. package/dist/pubid-urn/schema.js +186 -0
  26. package/dist/pubid-urn/schemas/adobe.d.ts +8 -0
  27. package/dist/pubid-urn/schemas/adobe.js +15 -0
  28. package/dist/pubid-urn/schemas/amca.d.ts +8 -0
  29. package/dist/pubid-urn/schemas/amca.js +19 -0
  30. package/dist/pubid-urn/schemas/ansi.d.ts +8 -0
  31. package/dist/pubid-urn/schemas/ansi.js +30 -0
  32. package/dist/pubid-urn/schemas/api.d.ts +8 -0
  33. package/dist/pubid-urn/schemas/api.js +25 -0
  34. package/dist/pubid-urn/schemas/ashrae.d.ts +8 -0
  35. package/dist/pubid-urn/schemas/ashrae.js +28 -0
  36. package/dist/pubid-urn/schemas/asme.d.ts +8 -0
  37. package/dist/pubid-urn/schemas/asme.js +26 -0
  38. package/dist/pubid-urn/schemas/astm.d.ts +8 -0
  39. package/dist/pubid-urn/schemas/astm.js +38 -0
  40. package/dist/pubid-urn/schemas/bipm.d.ts +8 -0
  41. package/dist/pubid-urn/schemas/bipm.js +34 -0
  42. package/dist/pubid-urn/schemas/bsi.d.ts +8 -0
  43. package/dist/pubid-urn/schemas/bsi.js +98 -0
  44. package/dist/pubid-urn/schemas/calconnect.d.ts +8 -0
  45. package/dist/pubid-urn/schemas/calconnect.js +16 -0
  46. package/dist/pubid-urn/schemas/ccsds.d.ts +8 -0
  47. package/dist/pubid-urn/schemas/ccsds.js +18 -0
  48. package/dist/pubid-urn/schemas/cencenelec.d.ts +8 -0
  49. package/dist/pubid-urn/schemas/cencenelec.js +39 -0
  50. package/dist/pubid-urn/schemas/cie.d.ts +8 -0
  51. package/dist/pubid-urn/schemas/cie.js +41 -0
  52. package/dist/pubid-urn/schemas/csa.d.ts +8 -0
  53. package/dist/pubid-urn/schemas/csa.js +45 -0
  54. package/dist/pubid-urn/schemas/doi.d.ts +8 -0
  55. package/dist/pubid-urn/schemas/doi.js +14 -0
  56. package/dist/pubid-urn/schemas/easc.d.ts +8 -0
  57. package/dist/pubid-urn/schemas/easc.js +16 -0
  58. package/dist/pubid-urn/schemas/ecma.d.ts +8 -0
  59. package/dist/pubid-urn/schemas/ecma.js +17 -0
  60. package/dist/pubid-urn/schemas/etsi.d.ts +8 -0
  61. package/dist/pubid-urn/schemas/etsi.js +23 -0
  62. package/dist/pubid-urn/schemas/evs.d.ts +8 -0
  63. package/dist/pubid-urn/schemas/evs.js +30 -0
  64. package/dist/pubid-urn/schemas/gb.d.ts +8 -0
  65. package/dist/pubid-urn/schemas/gb.js +18 -0
  66. package/dist/pubid-urn/schemas/gost.d.ts +8 -0
  67. package/dist/pubid-urn/schemas/gost.js +23 -0
  68. package/dist/pubid-urn/schemas/iala.d.ts +8 -0
  69. package/dist/pubid-urn/schemas/iala.js +26 -0
  70. package/dist/pubid-urn/schemas/iana.d.ts +8 -0
  71. package/dist/pubid-urn/schemas/iana.js +13 -0
  72. package/dist/pubid-urn/schemas/idf.d.ts +8 -0
  73. package/dist/pubid-urn/schemas/idf.js +34 -0
  74. package/dist/pubid-urn/schemas/iec.d.ts +8 -0
  75. package/dist/pubid-urn/schemas/iec.js +61 -0
  76. package/dist/pubid-urn/schemas/ieee.d.ts +8 -0
  77. package/dist/pubid-urn/schemas/ieee.js +116 -0
  78. package/dist/pubid-urn/schemas/ietf.d.ts +8 -0
  79. package/dist/pubid-urn/schemas/ietf.js +17 -0
  80. package/dist/pubid-urn/schemas/iho.d.ts +8 -0
  81. package/dist/pubid-urn/schemas/iho.js +35 -0
  82. package/dist/pubid-urn/schemas/index.d.ts +9 -0
  83. package/dist/pubid-urn/schemas/index.js +97 -0
  84. package/dist/pubid-urn/schemas/isbn.d.ts +8 -0
  85. package/dist/pubid-urn/schemas/isbn.js +14 -0
  86. package/dist/pubid-urn/schemas/iso.d.ts +8 -0
  87. package/dist/pubid-urn/schemas/iso.js +46 -0
  88. package/dist/pubid-urn/schemas/itu.d.ts +8 -0
  89. package/dist/pubid-urn/schemas/itu.js +61 -0
  90. package/dist/pubid-urn/schemas/jcgm.d.ts +8 -0
  91. package/dist/pubid-urn/schemas/jcgm.js +21 -0
  92. package/dist/pubid-urn/schemas/jis.d.ts +8 -0
  93. package/dist/pubid-urn/schemas/jis.js +24 -0
  94. package/dist/pubid-urn/schemas/nist.d.ts +8 -0
  95. package/dist/pubid-urn/schemas/nist.js +90 -0
  96. package/dist/pubid-urn/schemas/oasis.d.ts +8 -0
  97. package/dist/pubid-urn/schemas/oasis.js +17 -0
  98. package/dist/pubid-urn/schemas/ogc.d.ts +8 -0
  99. package/dist/pubid-urn/schemas/ogc.js +14 -0
  100. package/dist/pubid-urn/schemas/oiml.d.ts +8 -0
  101. package/dist/pubid-urn/schemas/oiml.js +39 -0
  102. package/dist/pubid-urn/schemas/omg.d.ts +8 -0
  103. package/dist/pubid-urn/schemas/omg.js +15 -0
  104. package/dist/pubid-urn/schemas/plateau.d.ts +8 -0
  105. package/dist/pubid-urn/schemas/plateau.js +20 -0
  106. package/dist/pubid-urn/schemas/sae.d.ts +8 -0
  107. package/dist/pubid-urn/schemas/sae.js +18 -0
  108. package/dist/pubid-urn/schemas/tgpp.d.ts +8 -0
  109. package/dist/pubid-urn/schemas/tgpp.js +17 -0
  110. package/dist/pubid-urn/schemas/un.d.ts +8 -0
  111. package/dist/pubid-urn/schemas/un.js +13 -0
  112. package/dist/pubid-urn/schemas/w3c.d.ts +8 -0
  113. package/dist/pubid-urn/schemas/w3c.js +23 -0
  114. package/dist/pubid-urn/schemas/xsf.d.ts +8 -0
  115. package/dist/pubid-urn/schemas/xsf.js +12 -0
  116. package/dist/pubid-urn/slug.d.ts +20 -0
  117. package/dist/pubid-urn/slug.js +47 -0
  118. package/package.json +2 -1
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Reads the body of a pubid-scheme URN back into an identifier hash —
3
+ * the TS mirror of Pubid::PubidUrn::Decoder (lib/pubid/pubid_urn/decoder.rb).
4
+ * The inverse of Encoder; strict about structure. Canonical form (atom
5
+ * order, escapes, trailing "~") is checked by PubidUrn.parse, which
6
+ * re-encodes the result and compares.
7
+ */
8
+ import { decode as escape, exactMatch } from "./escape.js";
9
+ import { UrnParseError } from "./errors.js";
10
+ import { put } from "./paths.js";
11
+ import { Decoder as _Decoder } from "./decoder.js";
12
+ import { flavorSchema, splitToken, splitTop, structural } from "./index.js";
13
+ const FACET = /^(\d+)\.([a-z0-9]+)-(.*)/;
14
+ const INTEGER = /^-?[0-9]+$/;
15
+ const NULL = "~";
16
+ export class Decoder {
17
+ body;
18
+ constructor(body) {
19
+ this.body = body;
20
+ }
21
+ /** The identifier hash (`_type` and `base` included). */
22
+ toIdentifierHash() {
23
+ const sep = this.body.indexOf(":");
24
+ const flavor = sep === -1 ? undefined : this.body.slice(0, sep);
25
+ const rest = sep === -1 ? undefined : this.body.slice(sep + 1);
26
+ if (rest === undefined || rest === "")
27
+ this.fail("no type segment");
28
+ return this.decodeIdentifier(rest, flavor);
29
+ }
30
+ decodeIdentifier(body, flavor) {
31
+ const tokens = splitTop(body, ":");
32
+ const nodes = [];
33
+ const schemas = [];
34
+ let i = 0;
35
+ while (i < tokens.length) {
36
+ const token = tokens[i];
37
+ if (token === "f") {
38
+ this.decodeFacets(tokens.slice(i + 1), nodes, schemas);
39
+ break;
40
+ }
41
+ const schema = this.nodeSchema(token, flavor);
42
+ const node = { _type: schema.type };
43
+ i = this.decodeCore(tokens, i + 1, node, schema);
44
+ i = this.decodeAtoms(tokens, i, node, schema);
45
+ nodes.push(node);
46
+ schemas.push(schema);
47
+ }
48
+ if (nodes.length === 0)
49
+ this.fail("no identifier");
50
+ for (let k = 1; k < nodes.length; k++) {
51
+ nodes[k]["base"] = nodes[k - 1];
52
+ }
53
+ return nodes[nodes.length - 1];
54
+ }
55
+ nodeSchema(token, flavor) {
56
+ if (token === "type") {
57
+ this.fail("type-metadata URN names a type, not a document");
58
+ }
59
+ let schema;
60
+ if (structural(token)) {
61
+ const [owner, name] = splitToken(token, flavor);
62
+ schema = flavorSchema(owner)?.forToken(name);
63
+ }
64
+ if (schema === undefined) {
65
+ this.fail(`unknown type or segment ${JSON.stringify(token)}`);
66
+ }
67
+ return schema;
68
+ }
69
+ decodeCore(tokens, index, node, schema) {
70
+ let i = index;
71
+ for (let ci = 0; ci < schema.core.length; ci++) {
72
+ if (i >= tokens.length)
73
+ break;
74
+ const token = tokens[i];
75
+ if (structural(token))
76
+ break;
77
+ if (token !== NULL) {
78
+ put(node, schema.core[ci], this.coreValue(token, schema.coreKinds[ci]));
79
+ }
80
+ i += 1;
81
+ }
82
+ return i;
83
+ }
84
+ coreValue(token, kind) {
85
+ return kind === "int" ? this.integer(token) : escape(token);
86
+ }
87
+ decodeAtoms(tokens, index, node, schema) {
88
+ let last = -1;
89
+ let i = index;
90
+ while (i < tokens.length) {
91
+ const token = tokens[i];
92
+ const position = schema.atomPosition(token);
93
+ if (position === undefined)
94
+ break;
95
+ if (position <= last) {
96
+ this.fail(`atom ${JSON.stringify(token)} is out of order or repeated`);
97
+ }
98
+ last = position;
99
+ const atom = schema.atoms[position];
100
+ const dash = token.indexOf("-");
101
+ const raw = dash === -1 ? "" : token.slice(dash + 1);
102
+ put(node, atom.parts, this.atomValue(raw, atom));
103
+ i += 1;
104
+ }
105
+ return i;
106
+ }
107
+ atomValue(raw, atom) {
108
+ if (atom.kind === "int")
109
+ return this.integer(raw);
110
+ if (atom.kind === "id")
111
+ return this.nested(raw);
112
+ if (atom.kind === "ids")
113
+ return splitTop(raw, ",").map((item) => this.nested(item));
114
+ return this.slotValue(raw, atom);
115
+ }
116
+ /** The value of a :string, :bool or list atom, or of a facet. */
117
+ slotValue(raw, slot) {
118
+ if (!slot.list)
119
+ return this.scalar(raw, slot);
120
+ return splitTop(raw, ",", true).map((item) => item === NULL ? null : this.scalar(item, slot));
121
+ }
122
+ scalar(raw, slot) {
123
+ return slot.element === "bool" ? this.boolean(raw) : escape(raw);
124
+ }
125
+ boolean(raw) {
126
+ if (raw === "true")
127
+ return true;
128
+ if (raw === "false")
129
+ return false;
130
+ this.fail(`${JSON.stringify(raw)} is not a boolean`);
131
+ }
132
+ integer(raw) {
133
+ const m = INTEGER.exec(raw);
134
+ if (m === null || m[0].length !== raw.length) {
135
+ this.fail(`${JSON.stringify(raw)} is not an integer`);
136
+ }
137
+ return Number(raw);
138
+ }
139
+ nested(raw) {
140
+ if (!raw.startsWith("(") || !raw.endsWith(")")) {
141
+ this.fail(`nested identifier ${JSON.stringify(raw)} needs brackets`);
142
+ }
143
+ return new _Decoder(raw.slice(1, -1)).toIdentifierHash();
144
+ }
145
+ decodeFacets(tokens, nodes, schemas) {
146
+ if (tokens.length === 0)
147
+ this.fail("empty facet group");
148
+ for (const token of tokens) {
149
+ const match = exactMatch(FACET, token);
150
+ if (match === null)
151
+ this.fail(`bad facet ${JSON.stringify(token)}`);
152
+ const index = Number(match[1]);
153
+ const schema = schemas[index];
154
+ if (schema === undefined)
155
+ this.fail(`facet ${JSON.stringify(token)} has no node`);
156
+ const facet = schema.facet(match[2]);
157
+ if (facet === undefined)
158
+ this.fail(`bad facet ${JSON.stringify(token)}`);
159
+ put(nodes[index], facet.parts, this.slotValue(match[3], facet));
160
+ }
161
+ }
162
+ fail(message) {
163
+ throw new UrnParseError(`pubid URN ${JSON.stringify(this.body)}: ${message}`);
164
+ }
165
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Writes the pubid-scheme URN of an identifier hash — the TS mirror of
3
+ * Pubid::PubidUrn::Encoder (lib/pubid/pubid_urn/encoder.rb).
4
+ *
5
+ * urn:pubid:<flavor>:<node>[:<node>…][:f:<facet>…]
6
+ * <node> = <type>[:<core>…][:<prefix>-<value>…]
7
+ *
8
+ * The nodes go root first, along `base`: "ISO 9001:2015/Amd 1:2016" is
9
+ * "is:9001:year-2015:amd:1:year-2016", so every supplement of a document
10
+ * shares its slug prefix. A core value is positional; an absent one in
11
+ * the middle is "~", and absent ones at the end are dropped. An optional
12
+ * value is a prefixed atom, so it leaves no segment when it is absent.
13
+ */
14
+ export declare class Encoder {
15
+ private readonly hash;
16
+ constructor(hash: Record<string, unknown>);
17
+ /** The body after "urn:pubid:". */
18
+ body(): string;
19
+ private encodeIdentifier;
20
+ /** The nodes from the root document to `hash` itself. */
21
+ private chain;
22
+ private token;
23
+ private coreSegments;
24
+ private atomSegments;
25
+ private atomValue;
26
+ /** The value of a :string, :bool or list atom, or of a facet. */
27
+ private slotValue;
28
+ private scalar;
29
+ private facetSegments;
30
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Writes the pubid-scheme URN of an identifier hash — the TS mirror of
3
+ * Pubid::PubidUrn::Encoder (lib/pubid/pubid_urn/encoder.rb).
4
+ *
5
+ * urn:pubid:<flavor>:<node>[:<node>…][:f:<facet>…]
6
+ * <node> = <type>[:<core>…][:<prefix>-<value>…]
7
+ *
8
+ * The nodes go root first, along `base`: "ISO 9001:2015/Amd 1:2016" is
9
+ * "is:9001:year-2015:amd:1:year-2016", so every supplement of a document
10
+ * shares its slug prefix. A core value is positional; an absent one in
11
+ * the middle is "~", and absent ones at the end are dropped. An optional
12
+ * value is a prefixed atom, so it leaves no segment when it is absent.
13
+ */
14
+ import { typeSchemaOrThrow, structural } from "./index.js";
15
+ import { InvalidInputError } from "./errors.js";
16
+ import { encode as escapeEncode, force } from "./escape.js";
17
+ import { blank, deepDup, isNil, take } from "./paths.js";
18
+ import { Encoder as _Encoder } from "./encoder.js";
19
+ /** ruby's PubidUrn::NULL: the absent-slot marker. */
20
+ const NULL = "~";
21
+ export class Encoder {
22
+ hash;
23
+ constructor(hash) {
24
+ this.hash = hash;
25
+ }
26
+ /** The body after "urn:pubid:". */
27
+ body() {
28
+ const flavor = flavorSegmentOf(this.hash["_type"]);
29
+ return `${flavor}:${this.encodeIdentifier(this.hash, flavor)}`;
30
+ }
31
+ encodeIdentifier(hash, flavor) {
32
+ const segments = [];
33
+ const facets = [];
34
+ this.chain(hash).forEach((node, index) => {
35
+ const schema = typeSchemaOrThrow(node["_type"]);
36
+ const copy = deepDup(node);
37
+ delete copy["_type"];
38
+ segments.push(this.token(schema, flavor));
39
+ segments.push(...this.coreSegments(copy, schema));
40
+ segments.push(...this.atomSegments(copy, schema));
41
+ facets.push(...this.facetSegments(copy, schema, index));
42
+ checkPlaced(copy, schema);
43
+ });
44
+ if (facets.length > 0)
45
+ segments.push("f", ...facets);
46
+ return segments.join(":");
47
+ }
48
+ /** The nodes from the root document to `hash` itself. */
49
+ chain(hash) {
50
+ const nodes = [];
51
+ let node = hash;
52
+ while (node !== undefined && node !== null) {
53
+ if (typeof node !== "object" ||
54
+ Array.isArray(node) ||
55
+ typeof node["_type"] !== "string") {
56
+ throw new InvalidInputError(`a pubid URN base must be an identifier hash, got ${inspect(node)}`);
57
+ }
58
+ const copy = { ...node };
59
+ delete copy["base"];
60
+ nodes.unshift(copy);
61
+ node = node["base"];
62
+ }
63
+ return nodes;
64
+ }
65
+ token(schema, flavor) {
66
+ return schema.flavor === flavor ? schema.token : `${schema.flavor}.${schema.token}`;
67
+ }
68
+ coreSegments(node, schema) {
69
+ const values = schema.core.map((parts) => take(node, parts));
70
+ while (values.length > 0 && isNil(values[values.length - 1]))
71
+ values.pop();
72
+ return values.map((value, i) => {
73
+ if (isNil(value))
74
+ return NULL;
75
+ if (schema.coreKinds[i] === "int")
76
+ return coreInteger(value, schema, i);
77
+ return coreValue(value, schema);
78
+ });
79
+ }
80
+ atomSegments(node, schema) {
81
+ const segments = [];
82
+ for (const atom of schema.atoms) {
83
+ const value = take(node, atom.parts);
84
+ if (isNil(value))
85
+ continue;
86
+ segments.push(`${atom.prefix}-${this.atomValue(value, atom, schema)}`);
87
+ }
88
+ return segments;
89
+ }
90
+ atomValue(value, atom, schema) {
91
+ if (atom.kind === "int")
92
+ return String(typed(value, "number", atom, schema));
93
+ if (atom.kind === "id") {
94
+ return nested(typed(value, "hash", atom, schema));
95
+ }
96
+ if (atom.kind === "ids") {
97
+ return typed(value, "array", atom, schema)
98
+ .map((hash) => nested(hash))
99
+ .join(",");
100
+ }
101
+ return this.slotValue(value, atom, schema);
102
+ }
103
+ /** The value of a :string, :bool or list atom, or of a facet. */
104
+ slotValue(value, slot, schema) {
105
+ if (!slot.list)
106
+ return this.scalar(value, slot, schema);
107
+ return typed(value, "array", slot, schema)
108
+ .map((item) => (isNil(item) ? NULL : this.scalar(item, slot, schema)))
109
+ .join(",");
110
+ }
111
+ scalar(value, slot, schema) {
112
+ if (slot.element === "bool") {
113
+ if (value !== true && value !== false) {
114
+ throw typeError(value, slot, schema);
115
+ }
116
+ return String(value);
117
+ }
118
+ return escapeEncode(typed(value, "string", slot, schema));
119
+ }
120
+ facetSegments(node, schema, index) {
121
+ const segments = [];
122
+ for (const facet of schema.facets) {
123
+ const value = take(node, facet.parts);
124
+ if (isNil(value))
125
+ continue;
126
+ segments.push(`${index}.${facet.name}-${this.slotValue(value, facet, schema)}`);
127
+ }
128
+ return segments;
129
+ }
130
+ }
131
+ function nested(hash) {
132
+ return `(${new _Encoder(hash).body()})`;
133
+ }
134
+ /** A decimal never has the shape of structure, so it needs no escape. */
135
+ function coreInteger(value, schema, index) {
136
+ if (typeof value === "number" && Number.isInteger(value))
137
+ return String(value);
138
+ throw new InvalidInputError(`${schema.type} ${schema.core[index]?.join(".")}: ${inspect(value)} is not int`);
139
+ }
140
+ /** A core value that has the shape of structure gets its first character
141
+ * escaped. */
142
+ function coreValue(value, schema) {
143
+ if (typeof value !== "string" || value.length === 0) {
144
+ throw new InvalidInputError(`core value of ${schema.type} must be a non-empty String, got ${inspect(value)}`);
145
+ }
146
+ const encoded = escapeEncode(value);
147
+ if (!structural(encoded))
148
+ return encoded;
149
+ const chars = Array.from(value);
150
+ return force(chars[0]) + escapeEncode(chars.slice(1).join(""));
151
+ }
152
+ function typed(value, expected, slot, schema) {
153
+ let ok;
154
+ switch (expected) {
155
+ case "number":
156
+ ok = typeof value === "number" && Number.isInteger(value);
157
+ break;
158
+ case "string":
159
+ ok = typeof value === "string";
160
+ break;
161
+ case "hash":
162
+ ok = value !== null && typeof value === "object" && !Array.isArray(value);
163
+ break;
164
+ case "array":
165
+ ok = Array.isArray(value);
166
+ break;
167
+ }
168
+ if (ok)
169
+ return value;
170
+ throw typeError(value, slot, schema);
171
+ }
172
+ function typeError(value, slot, schema) {
173
+ return new InvalidInputError(`${schema.type} ${slot.path}: ${inspect(value)} is not ${slot.kind}`);
174
+ }
175
+ function checkPlaced(node, schema) {
176
+ const left = {};
177
+ for (const [key, value] of Object.entries(node)) {
178
+ if (!blank(value))
179
+ left[key] = value;
180
+ }
181
+ if (Object.keys(left).length > 0) {
182
+ throw new InvalidInputError(`the pubid URN schema of ${schema.type} does not place ${inspect(left)}`);
183
+ }
184
+ }
185
+ function inspect(value) {
186
+ if (typeof value === "string")
187
+ return JSON.stringify(value);
188
+ try {
189
+ return JSON.stringify(value) ?? String(value);
190
+ }
191
+ catch {
192
+ return String(value);
193
+ }
194
+ }
195
+ function flavorSegmentOf(type) {
196
+ return typeof type === "string" ? type.split(":")[1] : undefined;
197
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The error classes of the pubid-scheme URN codec — the TS mirror of
3
+ * Pubid::Errors::UrnParseError and Pubid::Errors::InvalidInputError
4
+ * (lib/pubid/errors.rb). A decode failure is ALWAYS one of these two,
5
+ * never a parser-internal error (pubid#670 item 3).
6
+ */
7
+ /** A malformed, unknown or non-canonical pubid-scheme URN (ruby:
8
+ * UrnParseError < StandardError). */
9
+ export declare class UrnParseError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /** A non-String or over-long input (ruby: InvalidInputError < ArgumentError). */
13
+ export declare class InvalidInputError extends Error {
14
+ constructor(message: string);
15
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The error classes of the pubid-scheme URN codec — the TS mirror of
3
+ * Pubid::Errors::UrnParseError and Pubid::Errors::InvalidInputError
4
+ * (lib/pubid/errors.rb). A decode failure is ALWAYS one of these two,
5
+ * never a parser-internal error (pubid#670 item 3).
6
+ */
7
+ /** A malformed, unknown or non-canonical pubid-scheme URN (ruby:
8
+ * UrnParseError < StandardError). */
9
+ export class UrnParseError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "UrnParseError";
13
+ }
14
+ }
15
+ /** A non-String or over-long input (ruby: InvalidInputError < ArgumentError). */
16
+ export class InvalidInputError extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "InvalidInputError";
20
+ }
21
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The value escape of the pubid-scheme URN — the TS mirror of
3
+ * Pubid::PubidUrn::Escape (lib/pubid/pubid_urn/escape.rb).
4
+ *
5
+ * The URN is also an HTTP path slug (relaton/relaton#441), so it cannot
6
+ * use "%": servers and proxies decode "%2F" before they route, or they
7
+ * refuse it. "~XX" (uppercase hex of one UTF-8 byte) has no meaning in a
8
+ * path, and "~" is an RFC 3986 unreserved character. Every byte outside
9
+ * SAFE is escaped, so ":" "," "(" ")" "~" never occur raw in a value and
10
+ * stay free for the structure of the URN.
11
+ */
12
+ export declare function encode(value: string): string;
13
+ /** Escape every byte of `char`. The codec uses it on the first character
14
+ * of a core value that would otherwise read as structure ("amd" -> "~61md"). */
15
+ export declare function force(char: string): string;
16
+ export declare function decode(token: string): string;
17
+ /** A pattern whose ruby twin ends in \z: the match must cover the whole
18
+ * string (JS `$` alone would also match before one trailing "\n"). */
19
+ export declare function exactMatch(re: RegExp, token: string): RegExpExecArray | null;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The value escape of the pubid-scheme URN — the TS mirror of
3
+ * Pubid::PubidUrn::Escape (lib/pubid/pubid_urn/escape.rb).
4
+ *
5
+ * The URN is also an HTTP path slug (relaton/relaton#441), so it cannot
6
+ * use "%": servers and proxies decode "%2F" before they route, or they
7
+ * refuse it. "~XX" (uppercase hex of one UTF-8 byte) has no meaning in a
8
+ * path, and "~" is an RFC 3986 unreserved character. Every byte outside
9
+ * SAFE is escaped, so ":" "," "(" ")" "~" never occur raw in a value and
10
+ * stay free for the structure of the URN.
11
+ */
12
+ import { UrnParseError } from "./errors.js";
13
+ const SAFE = /^[A-Za-z0-9._-]$/;
14
+ const RAW_TOKEN = /^(?:[A-Za-z0-9._-]|~[0-9A-F]{2})*$/;
15
+ const ESCAPE = /~([0-9A-F]{2})/g;
16
+ export function encode(value) {
17
+ let out = "";
18
+ // ruby each_char walks code points, not UTF-16 units.
19
+ for (const char of String(value)) {
20
+ out += SAFE.test(char) ? char : force(char);
21
+ }
22
+ return out;
23
+ }
24
+ /** Escape every byte of `char`. The codec uses it on the first character
25
+ * of a core value that would otherwise read as structure ("amd" -> "~61md"). */
26
+ export function force(char) {
27
+ const bytes = new TextEncoder().encode(char);
28
+ let out = "";
29
+ for (const byte of bytes) {
30
+ out += `~${byte.toString(16).toUpperCase().padStart(2, "0")}`;
31
+ }
32
+ return out;
33
+ }
34
+ export function decode(token) {
35
+ const raw = RAW_TOKEN.exec(token);
36
+ if (raw === null || raw[0].length !== token.length) {
37
+ throw new UrnParseError(`bad character or escape in pubid URN value ${JSON.stringify(token)}`);
38
+ }
39
+ const bytes = [];
40
+ let i = 0;
41
+ while (i < token.length) {
42
+ if (token[i] === "~") {
43
+ bytes.push(parseInt(token.slice(i + 1, i + 3), 16));
44
+ i += 3;
45
+ }
46
+ else {
47
+ bytes.push(token.charCodeAt(i));
48
+ i += 1;
49
+ }
50
+ }
51
+ let decoded;
52
+ try {
53
+ decoded = new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(bytes));
54
+ }
55
+ catch {
56
+ throw new UrnParseError(`escape in pubid URN value ${JSON.stringify(token)} is not UTF-8`);
57
+ }
58
+ return decoded;
59
+ }
60
+ /** A pattern whose ruby twin ends in \z: the match must cover the whole
61
+ * string (JS `$` alone would also match before one trailing "\n"). */
62
+ export function exactMatch(re, token) {
63
+ const m = re.exec(token);
64
+ return m !== null && m[0].length === token.length ? m : null;
65
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The pubid-scheme URN (pubid#656) — the TS mirror of
3
+ * Pubid::PubidUrn (lib/pubid/pubid_urn.rb). Pubid's own URN namespace,
4
+ * which carries the full typed model and always round-trips —
5
+ * `parsePubidUrn(generate(id.toHash())).toHash == id.toHash`.
6
+ *
7
+ * It is a different thing from the official URN (`toUrn()`, e.g.
8
+ * "urn:iso:std:…"), which follows the registered grammar of the
9
+ * organization and may be lossy. relaton uses the pubid-scheme URN as
10
+ * its HTTP path slug (relaton/relaton#441), so the form is also
11
+ * path-safe: no "/", "%", "?", "#" and no empty segment.
12
+ *
13
+ * urn:pubid:iso:is:9001:year-2015:amd:1:year-2016
14
+ *
15
+ * The shape of each identifier type is declared in its flavor's schema
16
+ * (src/pubid-urn/schemas/<flavor>.ts), through {@linkcode define}. A
17
+ * flavor with no schema cannot emit one yet.
18
+ */
19
+ import { FlavorSchema, type TypeSchema } from "./schema.js";
20
+ import { exactMatch } from "./escape.js";
21
+ import type { BaseIdentifier } from "../model/identifier.js";
22
+ import { toSlug, fromSlug } from "./slug.js";
23
+ export { toSlug, fromSlug };
24
+ export { Atom, Facet, FlavorSchema, TypeSchema } from "./schema.js";
25
+ export type { AtomKind, FacetKind, CoreKind, CoreSpec, AtomSpec, FacetSpec, TypeDeclaration } from "./schema.js";
26
+ export { Encoder } from "./encoder.js";
27
+ export { Decoder } from "./decoder.js";
28
+ export { UrnParseError, InvalidInputError } from "./errors.js";
29
+ export { encode, force, decode } from "./escape.js";
30
+ export { take, put, blank } from "./paths.js";
31
+ export declare const PREFIX = "urn:pubid:";
32
+ /** An absent core value in the middle of the core, or an absent element
33
+ * of a list. Distinct from an escape, which always has two hex digits. */
34
+ export declare const NULL = "~";
35
+ /** Pubid::MAX_INPUT_LENGTH (1000) — the printed-identifier guard. */
36
+ export declare const MAX_INPUT_LENGTH = 1000;
37
+ export declare const MAX_LENGTH: number;
38
+ /** Declare the schema of a flavor. `flavor` is the flavor segment of its
39
+ * `_type` ("iso", "cencenelec", "3gpp"). The callback plays ruby's
40
+ * instance_eval: it receives the schema and declares atoms/facets/types. */
41
+ export declare function define(flavor: string, body: (schema: FlavorSchema) => void): FlavorSchema;
42
+ /** The pubid-scheme URN of an identifier hash (ruby generate(identifier):
43
+ * the hash is Identifier#to_hash's output). */
44
+ export declare function generate(hash: Record<string, unknown>): string;
45
+ /** Parse a pubid-scheme URN back into an identifier, through the
46
+ * canonical gate: the decoded identifier must re-encode to exactly the
47
+ * input, so one identifier has exactly one URN. */
48
+ export declare function parse(urn: string): BaseIdentifier;
49
+ /** True for "urn:pubid:…" (the NID is case-insensitive, RFC 8141). */
50
+ export declare function isPubidUrn(urn: string): boolean;
51
+ /** "iso" in "pubid:iso:technical-report". */
52
+ export declare function flavorSegment(type: unknown): string | undefined;
53
+ export declare function flavorSchema(flavor: string): FlavorSchema | undefined;
54
+ /** @raise InvalidInputError when no schema places `type`. */
55
+ export declare function typeSchemaOrThrow(type: unknown): TypeSchema;
56
+ /** Does `token` (a raw, still escaped segment) read as structure? */
57
+ export declare function structural(token: string): boolean;
58
+ /** A type token is "<token>" of the URN's own flavor, or
59
+ * "<flavor>.<token>" for a node of another flavor. */
60
+ export declare function splitToken(token: string, flavor: string): [string, string];
61
+ /** Split `body` at `separator` outside brackets. `allowEmpty` is true for
62
+ * the items of a list, where an empty item is an empty String; a ":"
63
+ * segment is never empty. */
64
+ export declare function splitTop(body: string, separator: string, allowEmpty?: boolean): string[];
65
+ export { exactMatch };