@nakanoaas/uuid58 1.2.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,49 +16,6 @@
16
16
  * ```
17
17
  */
18
18
  const UUID58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
19
- // check.ts
20
- const MAX_UUID58 = "YcVfxkQb6JRzqk5kF2tNLv";
21
- /**
22
- * Regular expression pattern for validating UUID58 strings.
23
- *
24
- * This regex ensures that a string:
25
- * - Contains exactly 22 characters
26
- * - Uses only characters from the Base58 alphabet
27
- *
28
- * **Note:** This regex does NOT validate that the decoded value fits within
29
- * 128 bits (UUID size). For complete validation including 128-bit range
30
- * checking, use {@link isUuid58} instead.
31
- *
32
- * @example
33
- * ```typescript
34
- * const isValid = UUID58_REGEX.test("XDY9dmBbcMBXqcRvYw8xJ2"); // true
35
- * const isValid = UUID58_REGEX.test("invalid"); // false
36
- * const isValid = UUID58_REGEX.test("123"); // false (too short)
37
- * ```
38
- */
39
- const UUID58_REGEX = /^[1-9A-HJ-NP-Za-km-z]{22}$/u;
40
- /**
41
- * Checks if a given string is a valid UUID58-encodable string.
42
- * This function efficiently validates without performing full decoding.
43
- *
44
- * A valid UUID58 string must:
45
- * - Be exactly 22 characters long
46
- * - Contain only characters from the Base58 alphabet
47
- * - Decode to a value that fits within 128 bits (UUID size)
48
- *
49
- * @param uuid58 - The string to check
50
- * @returns `true` if the string is a valid UUID58 string, `false` otherwise
51
- *
52
- * @example
53
- * ```typescript
54
- * isUuid58("XDY9dmBbcMBXqcRvYw8xJ2"); // true
55
- * isUuid58("invalid"); // false
56
- * isUuid58("O0lI"); // false (contains invalid characters)
57
- * ```
58
- */
59
- function isUuid58(uuid58) {
60
- return UUID58_REGEX.test(uuid58) && uuid58 <= MAX_UUID58;
61
- }
62
19
  // internal.ts
63
20
  const MAX_PREVIEW_LENGTH = 40;
64
21
  /**
@@ -105,31 +62,26 @@ function previewInput(input) {
105
62
  var _Uuid58DecodeError;
106
63
  const BASE58_INDEX = (/* @__PURE__ */ new Int8Array(128)).fill(-1);
107
64
  for (let i = 0; i < 58; i++) BASE58_INDEX[UUID58_ALPHABET.charCodeAt(i)] = i;
65
+ const MAX_UINT128 = (1n << 128n) - 1n;
108
66
  /**
109
- * Error thrown when an invalid Base58 string is provided for decoding.
110
- * This includes strings containing characters not in the Base58 alphabet.
67
+ * Error thrown when an invalid uuid58 string is provided for decoding.
68
+ * Its {@link Uuid58DecodeError.kind | kind} tells why.
111
69
  */
112
- var Uuid58DecodeError = class extends Error {};
70
+ var Uuid58DecodeError = class extends Error {
71
+ constructor(message, kind) {
72
+ super(message);
73
+ this.kind = kind;
74
+ }
75
+ };
113
76
  _Uuid58DecodeError = Uuid58DecodeError;
114
77
  _Uuid58DecodeError.prototype.name = "Uuid58DecodeError";
115
78
  /**
116
- * Converts a 22-character Base58-encoded string back to a standard UUID format, but instead of
117
- * throwing an error for invalid input, it returns an `Uuid58DecodeError`
118
- * instance.
119
- *
120
- * @param uuid58 - The 22-character Base58-encoded UUID string to decode
121
- * @returns A standard UUID string (lowercase, with hyphens), or an
122
- * `Uuid58DecodeError` if the input is not a valid 22-character Base58 string
123
- * @note This function does not throw; it returns the error object instead.
124
- *
125
- * @example
126
- * ```typescript
127
- * const uuid = uuid58DecodeSafe("XDY9dmBbcMBXqcRvYw8xJ2"); // returns "f4b247fd-1f87-45d4-aa06-1c6fc0a8dfaf"
128
- * const error = uuid58DecodeSafe("invalid"); // returns Uuid58DecodeError
129
- * ```
79
+ * @internal
80
+ * Decodes a uuid58 string to an unsigned 128-bit integer, checking length,
81
+ * then characters, then range (specification, Section 4).
130
82
  */
131
- function uuid58DecodeSafe(uuid58) {
132
- if (uuid58.length !== 22) return new Uuid58DecodeError(`Expected Base58 string of length 22, but received string of length ${uuid58.length}: ${previewInput(uuid58)}`);
83
+ function decodeUint128(uuid58) {
84
+ if (uuid58.length !== 22) return new Uuid58DecodeError(`Expected Base58 string of length 22, but received string of length ${uuid58.length}: ${previewInput(uuid58)}`, "invalid_length");
133
85
  let num = 0n;
134
86
  for (let start = 0; start < 22;) {
135
87
  const end = start === 0 ? 4 : start + 9;
@@ -139,15 +91,36 @@ function uuid58DecodeSafe(uuid58) {
139
91
  const index = code < 128 ? BASE58_INDEX[code] : -1;
140
92
  if (index < 0) {
141
93
  const char = String.fromCodePoint(uuid58.codePointAt(i));
142
- return new Uuid58DecodeError(`Invalid Base58 character ${JSON.stringify(char)} found in input: ${previewInput(uuid58)}`);
94
+ return new Uuid58DecodeError(`Invalid Base58 character ${JSON.stringify(char)} found in input: ${previewInput(uuid58)}`, "invalid_character");
143
95
  }
144
96
  chunk = chunk * 58 + index;
145
97
  }
146
98
  num = num * BASE58_CHUNK + BigInt(chunk);
147
99
  start = end;
148
100
  }
101
+ if (num > MAX_UINT128) return new Uuid58DecodeError(`Decoded value exceeds 128 bits of UUID: ${previewInput(uuid58)}`, "overflow");
102
+ return num;
103
+ }
104
+ /**
105
+ * Converts a 22-character Base58-encoded string back to a standard UUID format, but instead of
106
+ * throwing an error for invalid input, it returns an `Uuid58DecodeError`
107
+ * instance.
108
+ *
109
+ * @param uuid58 - The 22-character Base58-encoded UUID string to decode
110
+ * @returns A standard UUID string (lowercase, with hyphens), or an
111
+ * `Uuid58DecodeError` if the input is not a valid 22-character Base58 string
112
+ * @note This function does not throw; it returns the error object instead.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * const uuid = uuid58DecodeSafe("XDY9dmBbcMBXqcRvYw8xJ2"); // returns "f4b247fd-1f87-45d4-aa06-1c6fc0a8dfaf"
117
+ * const error = uuid58DecodeSafe("invalid"); // returns Uuid58DecodeError (kind: "invalid_length")
118
+ * ```
119
+ */
120
+ function uuid58DecodeSafe(uuid58) {
121
+ const num = decodeUint128(uuid58);
122
+ if (typeof num !== "bigint") return num;
149
123
  const hex = num.toString(16).padStart(32, "0");
150
- if (hex.length !== 32) return new Uuid58DecodeError(`Decoded value exceeds 128 bits of UUID: ${previewInput(uuid58)}`);
151
124
  return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
152
125
  }
153
126
  /**
@@ -170,18 +143,44 @@ function uuid58Decode(uuid58) {
170
143
  // encode.ts
171
144
  var _Uuid58EncodeError;
172
145
  const HEX32_REGEX = /^[0-9a-f]{32}$/i;
146
+ const HYPHENATED_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
173
147
  /**
174
- * Error thrown when an invalid UUID string is provided for encoding.
175
- * This includes strings that don't match the standard UUID format (32 hexadecimal characters with optional hyphens).
148
+ * Error thrown when an invalid UUID string or byte array is provided for
149
+ * encoding. Its {@link Uuid58EncodeError.kind | kind} tells why.
176
150
  */
177
- var Uuid58EncodeError = class extends Error {};
151
+ var Uuid58EncodeError = class extends Error {
152
+ constructor(message, kind) {
153
+ super(message);
154
+ this.kind = kind;
155
+ }
156
+ };
178
157
  _Uuid58EncodeError = Uuid58EncodeError;
179
158
  _Uuid58EncodeError.prototype.name = "Uuid58EncodeError";
180
159
  /**
160
+ * @internal
161
+ * Parses a UUID string in one of the two forms of the specification
162
+ * (Section 5.1) into an unsigned 128-bit integer.
163
+ */
164
+ function parseUuidString(uuid) {
165
+ let hex;
166
+ if (uuid.length === 32) {
167
+ if (!HEX32_REGEX.test(uuid)) return new Uuid58EncodeError(`Invalid UUID: ${previewInput(uuid)} must be 32 hexadecimal digits`, "invalid_character");
168
+ hex = uuid;
169
+ } else if (uuid.length === 36) {
170
+ if (!HYPHENATED_REGEX.test(uuid)) return new Uuid58EncodeError(`Invalid UUID: ${previewInput(uuid)} must be in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, "invalid_character");
171
+ hex = uuid.slice(0, 8) + uuid.slice(9, 13) + uuid.slice(14, 18) + uuid.slice(19, 23) + uuid.slice(24);
172
+ } else return new Uuid58EncodeError(`Invalid UUID length: expected 32 or 36 characters, got ${uuid.length} in ${previewInput(uuid)}`, "invalid_length");
173
+ return BigInt("0x" + hex);
174
+ }
175
+ /**
181
176
  * Converts a standard UUID string to a 22-character Base58-encoded format, but instead of
182
177
  * throwing an error for invalid input, it returns an `Uuid58EncodeError` instance.
183
178
  *
184
- * @param uuid - The UUID string to encode (with or without hyphens)
179
+ * Only two forms are accepted, in any letter case: the hyphenated form
180
+ * (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) and 32 hexadecimal digits. Braces,
181
+ * `urn:uuid:`, whitespace, and hyphens in other positions are rejected.
182
+ *
183
+ * @param uuid - The UUID string to encode (hyphenated, or 32 hexadecimal digits)
185
184
  * @returns A 22-character Base58-encoded string, or an `Uuid58EncodeError` if the input is
186
185
  * not a valid UUID
187
186
  * @note This function does not throw; it returns the error object instead.
@@ -195,21 +194,20 @@ _Uuid58EncodeError.prototype.name = "Uuid58EncodeError";
195
194
  * // Uppercase UUID is also accepted
196
195
  * const base58 = uuid58EncodeSafe("F4B247FD-1F87-45D4-AA06-1C6FC0A8DFAF"); // returns "XDY9dmBbcMBXqcRvYw8xJ2"
197
196
  * // Invalid UUID format
198
- * const error = uuid58EncodeSafe("invalid"); // returns Uuid58EncodeError
197
+ * const error = uuid58EncodeSafe("invalid"); // returns Uuid58EncodeError (kind: "invalid_length")
199
198
  * ```
200
199
  */
201
200
  function uuid58EncodeSafe(uuid) {
202
- const hex = uuid.replace(/-/g, "");
203
- if (hex.length !== 32) return new Uuid58EncodeError(`Invalid UUID length: expected 32 characters (excluding hyphens), got ${hex.length} characters in ${previewInput(uuid)}`);
204
- if (!HEX32_REGEX.test(hex)) return new Uuid58EncodeError(`Invalid UUID format: ${previewInput(uuid)} contains non-hexadecimal characters`);
205
- return encodeUint128(BigInt("0x" + hex));
201
+ const num = parseUuidString(uuid);
202
+ return typeof num === "bigint" ? encodeUint128(num) : num;
206
203
  }
207
204
  /**
208
205
  * Converts a standard UUID string to a 22-character Base58-encoded format.
209
206
  *
210
- * @param uuid - The UUID string to encode. Can be provided with or without hyphens
211
- * (format: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" or "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").
212
- * The input is case-insensitive, so both uppercase and lowercase hexadecimal characters are accepted.
207
+ * @param uuid - The UUID string to encode, either hyphenated
208
+ * ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") or as 32 hexadecimal digits
209
+ * ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"). Hexadecimal digits may be in any case.
210
+ * Other forms, including hyphens in other positions, are rejected.
213
211
  * @returns A 22-character Base58-encoded string representation of the UUID
214
212
  * @throws {Uuid58EncodeError} If the input string is not a valid UUID format
215
213
  *
@@ -228,6 +226,129 @@ function uuid58Encode(uuid) {
228
226
  if (result instanceof Uuid58EncodeError) throw result;
229
227
  return result;
230
228
  }
229
+ // bytes.ts
230
+ /**
231
+ * Encodes 16 bytes, in big-endian (RFC 9562) order, as a 22-character uuid58
232
+ * string, but instead of throwing an error for invalid input, it returns an
233
+ * `Uuid58EncodeError` instance.
234
+ *
235
+ * @param bytes - The 16 bytes of the UUID
236
+ * @returns A 22-character uuid58 string, or an `Uuid58EncodeError` (kind
237
+ * `"invalid_length"`) if `bytes` is not exactly 16 bytes long
238
+ * @note This function does not throw; it returns the error object instead.
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const bytes = new Uint8Array(16); // nil UUID
243
+ * uuid58EncodeBytesSafe(bytes); // returns "1111111111111111111111"
244
+ * ```
245
+ */
246
+ function uuid58EncodeBytesSafe(bytes) {
247
+ if (bytes.length !== 16) return new Uuid58EncodeError(`Invalid byte length: expected 16 bytes, got ${bytes.length}`, "invalid_length");
248
+ const view = new DataView(bytes.buffer, bytes.byteOffset, 16);
249
+ return encodeUint128(view.getBigUint64(0) << 64n | view.getBigUint64(8));
250
+ }
251
+ /**
252
+ * Encodes 16 bytes, in big-endian (RFC 9562) order, as a 22-character uuid58
253
+ * string.
254
+ *
255
+ * @param bytes - The 16 bytes of the UUID
256
+ * @returns A 22-character uuid58 string
257
+ * @throws {Uuid58EncodeError} If `bytes` is not exactly 16 bytes long
258
+ *
259
+ * @example
260
+ * ```typescript
261
+ * uuid58EncodeBytes(new Uint8Array(16)); // returns "1111111111111111111111"
262
+ * ```
263
+ */
264
+ function uuid58EncodeBytes(bytes) {
265
+ const result = uuid58EncodeBytesSafe(bytes);
266
+ if (result instanceof Uuid58EncodeError) throw result;
267
+ return result;
268
+ }
269
+ /**
270
+ * Decodes a uuid58 string to 16 bytes in big-endian (RFC 9562) order, but
271
+ * instead of throwing an error for invalid input, it returns an
272
+ * `Uuid58DecodeError` instance.
273
+ *
274
+ * @param uuid58 - The 22-character uuid58 string to decode
275
+ * @returns A new 16-byte `Uint8Array`, or an `Uuid58DecodeError`
276
+ * @note This function does not throw; it returns the error object instead.
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * uuid58DecodeBytesSafe("1111111111111111111111"); // returns Uint8Array(16) of zeros
281
+ * ```
282
+ */
283
+ function uuid58DecodeBytesSafe(uuid58) {
284
+ const num = decodeUint128(uuid58);
285
+ if (typeof num !== "bigint") return num;
286
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
287
+ const view = new DataView(bytes.buffer);
288
+ view.setBigUint64(0, num >> 64n);
289
+ view.setBigUint64(8, num & 18446744073709551615n);
290
+ return bytes;
291
+ }
292
+ /**
293
+ * Decodes a uuid58 string to 16 bytes in big-endian (RFC 9562) order.
294
+ *
295
+ * @param uuid58 - The 22-character uuid58 string to decode
296
+ * @returns A new 16-byte `Uint8Array`
297
+ * @throws {Uuid58DecodeError} If the input is not a valid uuid58 string
298
+ *
299
+ * @example
300
+ * ```typescript
301
+ * const bytes = uuid58DecodeBytes("XDY9dmBbcMBXqcRvYw8xJ2");
302
+ * ```
303
+ */
304
+ function uuid58DecodeBytes(uuid58) {
305
+ const result = uuid58DecodeBytesSafe(uuid58);
306
+ if (result instanceof Uuid58DecodeError) throw result;
307
+ return result;
308
+ }
309
+ // check.ts
310
+ const MAX_UUID58 = "YcVfxkQb6JRzqk5kF2tNLv";
311
+ /**
312
+ * Regular expression pattern for validating UUID58 strings.
313
+ *
314
+ * This regex ensures that a string:
315
+ * - Contains exactly 22 characters
316
+ * - Uses only characters from the Base58 alphabet
317
+ *
318
+ * **Note:** This regex does NOT validate that the decoded value fits within
319
+ * 128 bits (UUID size). For complete validation including 128-bit range
320
+ * checking, use {@link isUuid58} instead.
321
+ *
322
+ * @example
323
+ * ```typescript
324
+ * const isValid = UUID58_REGEX.test("XDY9dmBbcMBXqcRvYw8xJ2"); // true
325
+ * const isValid = UUID58_REGEX.test("invalid"); // false
326
+ * const isValid = UUID58_REGEX.test("123"); // false (too short)
327
+ * ```
328
+ */
329
+ const UUID58_REGEX = /^[1-9A-HJ-NP-Za-km-z]{22}$/u;
330
+ /**
331
+ * Checks if a given string is a valid UUID58-encodable string.
332
+ * This function efficiently validates without performing full decoding.
333
+ *
334
+ * A valid UUID58 string must:
335
+ * - Be exactly 22 characters long
336
+ * - Contain only characters from the Base58 alphabet
337
+ * - Decode to a value that fits within 128 bits (UUID size)
338
+ *
339
+ * @param uuid58 - The string to check
340
+ * @returns `true` if the string is a valid UUID58 string, `false` otherwise
341
+ *
342
+ * @example
343
+ * ```typescript
344
+ * isUuid58("XDY9dmBbcMBXqcRvYw8xJ2"); // true
345
+ * isUuid58("invalid"); // false
346
+ * isUuid58("O0lI"); // false (contains invalid characters)
347
+ * ```
348
+ */
349
+ function isUuid58(uuid58) {
350
+ return UUID58_REGEX.test(uuid58) && uuid58 <= MAX_UUID58;
351
+ }
231
352
  // generate.ts
232
353
  const random = /* @__PURE__ */ new BigUint64Array(2);
233
354
  /**
@@ -249,4 +370,4 @@ function uuid58() {
249
370
  const low = random[1] & 4611686018427387903n | 9223372036854775808n;
250
371
  return encodeUint128(high << 64n | low);
251
372
  }
252
- export { UUID58_ALPHABET, UUID58_REGEX, Uuid58DecodeError, Uuid58EncodeError, isUuid58, uuid58, uuid58Decode, uuid58DecodeSafe, uuid58Encode, uuid58EncodeSafe };
373
+ export { UUID58_ALPHABET, UUID58_REGEX, Uuid58DecodeError, Uuid58EncodeError, isUuid58, uuid58, uuid58Decode, uuid58DecodeBytes, uuid58DecodeBytesSafe, uuid58DecodeSafe, uuid58Encode, uuid58EncodeBytes, uuid58EncodeBytesSafe, uuid58EncodeSafe };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nakanoaas/uuid58",
3
- "version": "1.2.3",
3
+ "version": "2.0.0",
4
4
  "description": "A tiny, zero-dependency library for generating and converting UUIDs to Base58-encoded strings.",
5
5
  "bugs": {
6
6
  "url": "https://github.com/nakanoasaservice/uuid58/issues"
@@ -8,7 +8,8 @@
8
8
  "license": "MIT",
9
9
  "repository": {
10
10
  "type": "git",
11
- "url": "git+https://github.com/nakanoasaservice/uuid58.git"
11
+ "url": "git+https://github.com/nakanoasaservice/uuid58.git",
12
+ "directory": "js"
12
13
  },
13
14
  "files": [
14
15
  "dist"