@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/README.md +86 -16
- package/dist/index.cjs +201 -76
- package/dist/index.d.cts +144 -50
- package/dist/index.d.ts +144 -50
- package/dist/index.js +198 -77
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -124,7 +124,10 @@ function uuid58Encode(uuid: string): string; // always returns a 22-character st
|
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
- **Parameters:**
|
|
127
|
-
- `uuid`: The UUID string to encode
|
|
127
|
+
- `uuid`: The UUID string to encode: hyphenated
|
|
128
|
+
(`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) or 32 hexadecimal digits, in any
|
|
129
|
+
letter case. Other forms (braces, `urn:uuid:`, whitespace, hyphens in other
|
|
130
|
+
positions) are rejected.
|
|
128
131
|
- **Returns:** A 22-character Base58-encoded string
|
|
129
132
|
- **Throws:** `Uuid58EncodeError` if the input is not a valid UUID
|
|
130
133
|
|
|
@@ -139,7 +142,7 @@ function uuid58EncodeSafe(uuid: string): string | Uuid58EncodeError; // string i
|
|
|
139
142
|
```
|
|
140
143
|
|
|
141
144
|
- **Parameters:**
|
|
142
|
-
- `uuid`: The UUID string to encode (
|
|
145
|
+
- `uuid`: The UUID string to encode (same forms as `uuid58Encode`)
|
|
143
146
|
- **Returns:** A 22-character Base58-encoded string, or an `Uuid58EncodeError`
|
|
144
147
|
if the input is not a valid UUID
|
|
145
148
|
- **Note:** This function does not throw; it returns the error object instead.
|
|
@@ -174,26 +177,70 @@ function uuid58DecodeSafe(uuid58: string): string | Uuid58DecodeError;
|
|
|
174
177
|
`Uuid58DecodeError` if the input is not a valid 22-character Base58 string
|
|
175
178
|
- **Note:** This function does not throw; it returns the error object instead.
|
|
176
179
|
|
|
180
|
+
### `uuid58EncodeBytes(bytes: Uint8Array)` / `uuid58EncodeBytesSafe`
|
|
181
|
+
|
|
182
|
+
Encodes 16 bytes, in big-endian (RFC 9562) order, as a 22-character string.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
function uuid58EncodeBytes(bytes: Uint8Array): string;
|
|
186
|
+
function uuid58EncodeBytesSafe(bytes: Uint8Array): string | Uuid58EncodeError;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
- **Throws / returns:** `Uuid58EncodeError` with kind `"invalid_length"` if
|
|
190
|
+
`bytes` is not exactly 16 bytes long
|
|
191
|
+
|
|
192
|
+
### `uuid58DecodeBytes(uuid58: string)` / `uuid58DecodeBytesSafe`
|
|
193
|
+
|
|
194
|
+
Decodes a 22-character string to a new 16-byte `Uint8Array` in big-endian
|
|
195
|
+
(RFC 9562) order.
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
function uuid58DecodeBytes(uuid58: string): Uint8Array;
|
|
199
|
+
function uuid58DecodeBytesSafe(uuid58: string): Uint8Array | Uuid58DecodeError;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
- **Throws / returns:** `Uuid58DecodeError`, as for `uuid58Decode`
|
|
203
|
+
|
|
177
204
|
### Error Classes
|
|
178
205
|
|
|
206
|
+
Both error classes have a `kind` property with the error kind defined by the
|
|
207
|
+
[specification](https://github.com/nakanoasaservice/uuid58/blob/main/spec/SPEC.md)
|
|
208
|
+
(type `Uuid58ErrorKind`). Use it instead of parsing the message, which may
|
|
209
|
+
change.
|
|
210
|
+
|
|
211
|
+
| `kind` | Meaning |
|
|
212
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
213
|
+
| `"invalid_length"` | Wrong length: not 32 or 36 characters (UUID), not 22 (uuid58), or not 16 bytes |
|
|
214
|
+
| `"invalid_character"` | Right length, but a character not allowed in its position |
|
|
215
|
+
| `"overflow"` | (decoding only) Well formed, but the value is greater than 2^128 − 1 |
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
const result = uuid58DecodeSafe(input);
|
|
219
|
+
if (result instanceof Uuid58DecodeError && result.kind === "overflow") {
|
|
220
|
+
// ...
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
179
224
|
#### `Uuid58EncodeError`
|
|
180
225
|
|
|
181
|
-
Error thrown when an invalid UUID string is provided for
|
|
182
|
-
|
|
183
|
-
with optional hyphens).
|
|
226
|
+
Error thrown when an invalid UUID string or byte array is provided for
|
|
227
|
+
encoding. `kind` is `"invalid_length"` or `"invalid_character"`.
|
|
184
228
|
|
|
185
229
|
```typescript
|
|
186
|
-
class Uuid58EncodeError extends Error
|
|
230
|
+
class Uuid58EncodeError extends Error {
|
|
231
|
+
readonly kind: "invalid_length" | "invalid_character";
|
|
232
|
+
}
|
|
187
233
|
```
|
|
188
234
|
|
|
189
235
|
#### `Uuid58DecodeError`
|
|
190
236
|
|
|
191
|
-
Error thrown when an invalid
|
|
192
|
-
|
|
193
|
-
that are not exactly 22 characters long.
|
|
237
|
+
Error thrown when an invalid string is provided for decoding. `kind` is
|
|
238
|
+
`"invalid_length"`, `"invalid_character"`, or `"overflow"`.
|
|
194
239
|
|
|
195
240
|
```typescript
|
|
196
|
-
class Uuid58DecodeError extends Error
|
|
241
|
+
class Uuid58DecodeError extends Error {
|
|
242
|
+
readonly kind: Uuid58ErrorKind;
|
|
243
|
+
}
|
|
197
244
|
```
|
|
198
245
|
|
|
199
246
|
### `isUuid58(uuid58: string)`
|
|
@@ -272,14 +319,37 @@ characters. The Base58 alphabet used in this library is the same as Bitcoin's,
|
|
|
272
319
|
which excludes similar-looking characters (0, O, I, l) to prevent visual
|
|
273
320
|
ambiguity and confusion.
|
|
274
321
|
|
|
322
|
+
This package implements the
|
|
323
|
+
[uuid58 specification](https://github.com/nakanoasaservice/uuid58/blob/main/spec/SPEC.md).
|
|
324
|
+
Since 2.0 it follows the spec fully. 1.x accepted hyphens anywhere in the
|
|
325
|
+
input of `uuid58Encode` (see Section 9.1 of the spec).
|
|
326
|
+
|
|
327
|
+
## Migrating from 1.x
|
|
328
|
+
|
|
329
|
+
2.0 has two breaking changes:
|
|
330
|
+
|
|
331
|
+
- **Stricter UUID input.** `uuid58Encode` and `uuid58EncodeSafe` accept only
|
|
332
|
+
the hyphenated form (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) and 32
|
|
333
|
+
hexadecimal digits. 1.x removed every hyphen first, so inputs such as
|
|
334
|
+
`f4b247f-d1f87-45d4-aa06-1c6fc0a8dfaf` or `f4b247fd-1f8745d4aa061c6fc0a8dfaf`
|
|
335
|
+
were accepted; they are now rejected. Normalize such input yourself before
|
|
336
|
+
encoding if you need it, for example with `uuid.replace(/-/g, "")`.
|
|
337
|
+
- **Error constructors** take a second argument, the error kind
|
|
338
|
+
(`new Uuid58EncodeError(message, kind)`). This matters only if you
|
|
339
|
+
construct these errors yourself.
|
|
340
|
+
|
|
341
|
+
New in 2.0: the `kind` property on errors, and `uuid58EncodeBytes` /
|
|
342
|
+
`uuid58DecodeBytes` for `Uint8Array`.
|
|
343
|
+
|
|
275
344
|
## Development
|
|
276
345
|
|
|
277
|
-
This
|
|
346
|
+
This package lives in the `js/` directory of the repository and uses
|
|
347
|
+
[Vite+](https://viteplus.dev/) (`vp`) and pnpm. Run these commands in `js/`:
|
|
278
348
|
|
|
279
349
|
```bash
|
|
280
350
|
pnpm install
|
|
281
351
|
pnpm check # format, lint and type check
|
|
282
|
-
pnpm test # run tests
|
|
352
|
+
pnpm test # run tests, including the spec's conformance vectors
|
|
283
353
|
pnpm bench # build and benchmark (see bench/README.md)
|
|
284
354
|
pnpm build # build the npm package into dist/
|
|
285
355
|
```
|
|
@@ -289,14 +359,14 @@ by `vp pack` and is not committed.
|
|
|
289
359
|
|
|
290
360
|
### Releasing
|
|
291
361
|
|
|
292
|
-
1. Bump `version` in `package.json` and merge the change into `main`.
|
|
293
|
-
2. Tag that commit and push the tag:
|
|
362
|
+
1. Bump `version` in `js/package.json` and merge the change into `main`.
|
|
363
|
+
2. Tag that commit with a `js/` prefix and push the tag:
|
|
294
364
|
|
|
295
365
|
```bash
|
|
296
|
-
git tag v1.2.3 && git push origin v1.2.3
|
|
366
|
+
git tag js/v1.2.3 && git push origin js/v1.2.3
|
|
297
367
|
```
|
|
298
368
|
|
|
299
|
-
The `Publish` workflow then checks that the tag matches `package.json` and
|
|
369
|
+
The `Publish` workflow then checks that the tag matches `js/package.json` and
|
|
300
370
|
is on `main`, runs the tests, publishes to JSR and npm, and creates the
|
|
301
371
|
GitHub Release.
|
|
302
372
|
|
package/dist/index.cjs
CHANGED
|
@@ -17,49 +17,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
19
|
const UUID58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
20
|
-
// check.ts
|
|
21
|
-
const MAX_UUID58 = "YcVfxkQb6JRzqk5kF2tNLv";
|
|
22
|
-
/**
|
|
23
|
-
* Regular expression pattern for validating UUID58 strings.
|
|
24
|
-
*
|
|
25
|
-
* This regex ensures that a string:
|
|
26
|
-
* - Contains exactly 22 characters
|
|
27
|
-
* - Uses only characters from the Base58 alphabet
|
|
28
|
-
*
|
|
29
|
-
* **Note:** This regex does NOT validate that the decoded value fits within
|
|
30
|
-
* 128 bits (UUID size). For complete validation including 128-bit range
|
|
31
|
-
* checking, use {@link isUuid58} instead.
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* const isValid = UUID58_REGEX.test("XDY9dmBbcMBXqcRvYw8xJ2"); // true
|
|
36
|
-
* const isValid = UUID58_REGEX.test("invalid"); // false
|
|
37
|
-
* const isValid = UUID58_REGEX.test("123"); // false (too short)
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
const UUID58_REGEX = /^[1-9A-HJ-NP-Za-km-z]{22}$/u;
|
|
41
|
-
/**
|
|
42
|
-
* Checks if a given string is a valid UUID58-encodable string.
|
|
43
|
-
* This function efficiently validates without performing full decoding.
|
|
44
|
-
*
|
|
45
|
-
* A valid UUID58 string must:
|
|
46
|
-
* - Be exactly 22 characters long
|
|
47
|
-
* - Contain only characters from the Base58 alphabet
|
|
48
|
-
* - Decode to a value that fits within 128 bits (UUID size)
|
|
49
|
-
*
|
|
50
|
-
* @param uuid58 - The string to check
|
|
51
|
-
* @returns `true` if the string is a valid UUID58 string, `false` otherwise
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```typescript
|
|
55
|
-
* isUuid58("XDY9dmBbcMBXqcRvYw8xJ2"); // true
|
|
56
|
-
* isUuid58("invalid"); // false
|
|
57
|
-
* isUuid58("O0lI"); // false (contains invalid characters)
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
function isUuid58(uuid58) {
|
|
61
|
-
return UUID58_REGEX.test(uuid58) && uuid58 <= MAX_UUID58;
|
|
62
|
-
}
|
|
63
20
|
// internal.ts
|
|
64
21
|
const MAX_PREVIEW_LENGTH = 40;
|
|
65
22
|
/**
|
|
@@ -106,31 +63,26 @@ function previewInput(input) {
|
|
|
106
63
|
var _Uuid58DecodeError;
|
|
107
64
|
const BASE58_INDEX = (/* @__PURE__ */ new Int8Array(128)).fill(-1);
|
|
108
65
|
for (let i = 0; i < 58; i++) BASE58_INDEX[UUID58_ALPHABET.charCodeAt(i)] = i;
|
|
66
|
+
const MAX_UINT128 = (1n << 128n) - 1n;
|
|
109
67
|
/**
|
|
110
|
-
* Error thrown when an invalid
|
|
111
|
-
*
|
|
68
|
+
* Error thrown when an invalid uuid58 string is provided for decoding.
|
|
69
|
+
* Its {@link Uuid58DecodeError.kind | kind} tells why.
|
|
112
70
|
*/
|
|
113
|
-
var Uuid58DecodeError = class extends Error {
|
|
71
|
+
var Uuid58DecodeError = class extends Error {
|
|
72
|
+
constructor(message, kind) {
|
|
73
|
+
super(message);
|
|
74
|
+
this.kind = kind;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
114
77
|
_Uuid58DecodeError = Uuid58DecodeError;
|
|
115
78
|
_Uuid58DecodeError.prototype.name = "Uuid58DecodeError";
|
|
116
79
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* @param uuid58 - The 22-character Base58-encoded UUID string to decode
|
|
122
|
-
* @returns A standard UUID string (lowercase, with hyphens), or an
|
|
123
|
-
* `Uuid58DecodeError` if the input is not a valid 22-character Base58 string
|
|
124
|
-
* @note This function does not throw; it returns the error object instead.
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```typescript
|
|
128
|
-
* const uuid = uuid58DecodeSafe("XDY9dmBbcMBXqcRvYw8xJ2"); // returns "f4b247fd-1f87-45d4-aa06-1c6fc0a8dfaf"
|
|
129
|
-
* const error = uuid58DecodeSafe("invalid"); // returns Uuid58DecodeError
|
|
130
|
-
* ```
|
|
80
|
+
* @internal
|
|
81
|
+
* Decodes a uuid58 string to an unsigned 128-bit integer, checking length,
|
|
82
|
+
* then characters, then range (specification, Section 4).
|
|
131
83
|
*/
|
|
132
|
-
function
|
|
133
|
-
if (uuid58.length !== 22) return new Uuid58DecodeError(`Expected Base58 string of length 22, but received string of length ${uuid58.length}: ${previewInput(uuid58)}
|
|
84
|
+
function decodeUint128(uuid58) {
|
|
85
|
+
if (uuid58.length !== 22) return new Uuid58DecodeError(`Expected Base58 string of length 22, but received string of length ${uuid58.length}: ${previewInput(uuid58)}`, "invalid_length");
|
|
134
86
|
let num = 0n;
|
|
135
87
|
for (let start = 0; start < 22;) {
|
|
136
88
|
const end = start === 0 ? 4 : start + 9;
|
|
@@ -140,15 +92,36 @@ function uuid58DecodeSafe(uuid58) {
|
|
|
140
92
|
const index = code < 128 ? BASE58_INDEX[code] : -1;
|
|
141
93
|
if (index < 0) {
|
|
142
94
|
const char = String.fromCodePoint(uuid58.codePointAt(i));
|
|
143
|
-
return new Uuid58DecodeError(`Invalid Base58 character ${JSON.stringify(char)} found in input: ${previewInput(uuid58)}
|
|
95
|
+
return new Uuid58DecodeError(`Invalid Base58 character ${JSON.stringify(char)} found in input: ${previewInput(uuid58)}`, "invalid_character");
|
|
144
96
|
}
|
|
145
97
|
chunk = chunk * 58 + index;
|
|
146
98
|
}
|
|
147
99
|
num = num * BASE58_CHUNK + BigInt(chunk);
|
|
148
100
|
start = end;
|
|
149
101
|
}
|
|
102
|
+
if (num > MAX_UINT128) return new Uuid58DecodeError(`Decoded value exceeds 128 bits of UUID: ${previewInput(uuid58)}`, "overflow");
|
|
103
|
+
return num;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Converts a 22-character Base58-encoded string back to a standard UUID format, but instead of
|
|
107
|
+
* throwing an error for invalid input, it returns an `Uuid58DecodeError`
|
|
108
|
+
* instance.
|
|
109
|
+
*
|
|
110
|
+
* @param uuid58 - The 22-character Base58-encoded UUID string to decode
|
|
111
|
+
* @returns A standard UUID string (lowercase, with hyphens), or an
|
|
112
|
+
* `Uuid58DecodeError` if the input is not a valid 22-character Base58 string
|
|
113
|
+
* @note This function does not throw; it returns the error object instead.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const uuid = uuid58DecodeSafe("XDY9dmBbcMBXqcRvYw8xJ2"); // returns "f4b247fd-1f87-45d4-aa06-1c6fc0a8dfaf"
|
|
118
|
+
* const error = uuid58DecodeSafe("invalid"); // returns Uuid58DecodeError (kind: "invalid_length")
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
function uuid58DecodeSafe(uuid58) {
|
|
122
|
+
const num = decodeUint128(uuid58);
|
|
123
|
+
if (typeof num !== "bigint") return num;
|
|
150
124
|
const hex = num.toString(16).padStart(32, "0");
|
|
151
|
-
if (hex.length !== 32) return new Uuid58DecodeError(`Decoded value exceeds 128 bits of UUID: ${previewInput(uuid58)}`);
|
|
152
125
|
return hex.slice(0, 8) + "-" + hex.slice(8, 12) + "-" + hex.slice(12, 16) + "-" + hex.slice(16, 20) + "-" + hex.slice(20);
|
|
153
126
|
}
|
|
154
127
|
/**
|
|
@@ -171,18 +144,44 @@ function uuid58Decode(uuid58) {
|
|
|
171
144
|
// encode.ts
|
|
172
145
|
var _Uuid58EncodeError;
|
|
173
146
|
const HEX32_REGEX = /^[0-9a-f]{32}$/i;
|
|
147
|
+
const HYPHENATED_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
174
148
|
/**
|
|
175
|
-
* Error thrown when an invalid UUID string is provided for
|
|
176
|
-
*
|
|
149
|
+
* Error thrown when an invalid UUID string or byte array is provided for
|
|
150
|
+
* encoding. Its {@link Uuid58EncodeError.kind | kind} tells why.
|
|
177
151
|
*/
|
|
178
|
-
var Uuid58EncodeError = class extends Error {
|
|
152
|
+
var Uuid58EncodeError = class extends Error {
|
|
153
|
+
constructor(message, kind) {
|
|
154
|
+
super(message);
|
|
155
|
+
this.kind = kind;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
179
158
|
_Uuid58EncodeError = Uuid58EncodeError;
|
|
180
159
|
_Uuid58EncodeError.prototype.name = "Uuid58EncodeError";
|
|
181
160
|
/**
|
|
161
|
+
* @internal
|
|
162
|
+
* Parses a UUID string in one of the two forms of the specification
|
|
163
|
+
* (Section 5.1) into an unsigned 128-bit integer.
|
|
164
|
+
*/
|
|
165
|
+
function parseUuidString(uuid) {
|
|
166
|
+
let hex;
|
|
167
|
+
if (uuid.length === 32) {
|
|
168
|
+
if (!HEX32_REGEX.test(uuid)) return new Uuid58EncodeError(`Invalid UUID: ${previewInput(uuid)} must be 32 hexadecimal digits`, "invalid_character");
|
|
169
|
+
hex = uuid;
|
|
170
|
+
} else if (uuid.length === 36) {
|
|
171
|
+
if (!HYPHENATED_REGEX.test(uuid)) return new Uuid58EncodeError(`Invalid UUID: ${previewInput(uuid)} must be in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, "invalid_character");
|
|
172
|
+
hex = uuid.slice(0, 8) + uuid.slice(9, 13) + uuid.slice(14, 18) + uuid.slice(19, 23) + uuid.slice(24);
|
|
173
|
+
} else return new Uuid58EncodeError(`Invalid UUID length: expected 32 or 36 characters, got ${uuid.length} in ${previewInput(uuid)}`, "invalid_length");
|
|
174
|
+
return BigInt("0x" + hex);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
182
177
|
* Converts a standard UUID string to a 22-character Base58-encoded format, but instead of
|
|
183
178
|
* throwing an error for invalid input, it returns an `Uuid58EncodeError` instance.
|
|
184
179
|
*
|
|
185
|
-
*
|
|
180
|
+
* Only two forms are accepted, in any letter case: the hyphenated form
|
|
181
|
+
* (`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`) and 32 hexadecimal digits. Braces,
|
|
182
|
+
* `urn:uuid:`, whitespace, and hyphens in other positions are rejected.
|
|
183
|
+
*
|
|
184
|
+
* @param uuid - The UUID string to encode (hyphenated, or 32 hexadecimal digits)
|
|
186
185
|
* @returns A 22-character Base58-encoded string, or an `Uuid58EncodeError` if the input is
|
|
187
186
|
* not a valid UUID
|
|
188
187
|
* @note This function does not throw; it returns the error object instead.
|
|
@@ -196,21 +195,20 @@ _Uuid58EncodeError.prototype.name = "Uuid58EncodeError";
|
|
|
196
195
|
* // Uppercase UUID is also accepted
|
|
197
196
|
* const base58 = uuid58EncodeSafe("F4B247FD-1F87-45D4-AA06-1C6FC0A8DFAF"); // returns "XDY9dmBbcMBXqcRvYw8xJ2"
|
|
198
197
|
* // Invalid UUID format
|
|
199
|
-
* const error = uuid58EncodeSafe("invalid"); // returns Uuid58EncodeError
|
|
198
|
+
* const error = uuid58EncodeSafe("invalid"); // returns Uuid58EncodeError (kind: "invalid_length")
|
|
200
199
|
* ```
|
|
201
200
|
*/
|
|
202
201
|
function uuid58EncodeSafe(uuid) {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
if (!HEX32_REGEX.test(hex)) return new Uuid58EncodeError(`Invalid UUID format: ${previewInput(uuid)} contains non-hexadecimal characters`);
|
|
206
|
-
return encodeUint128(BigInt("0x" + hex));
|
|
202
|
+
const num = parseUuidString(uuid);
|
|
203
|
+
return typeof num === "bigint" ? encodeUint128(num) : num;
|
|
207
204
|
}
|
|
208
205
|
/**
|
|
209
206
|
* Converts a standard UUID string to a 22-character Base58-encoded format.
|
|
210
207
|
*
|
|
211
|
-
* @param uuid - The UUID string to encode
|
|
212
|
-
* (
|
|
213
|
-
*
|
|
208
|
+
* @param uuid - The UUID string to encode, either hyphenated
|
|
209
|
+
* ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") or as 32 hexadecimal digits
|
|
210
|
+
* ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"). Hexadecimal digits may be in any case.
|
|
211
|
+
* Other forms, including hyphens in other positions, are rejected.
|
|
214
212
|
* @returns A 22-character Base58-encoded string representation of the UUID
|
|
215
213
|
* @throws {Uuid58EncodeError} If the input string is not a valid UUID format
|
|
216
214
|
*
|
|
@@ -229,6 +227,129 @@ function uuid58Encode(uuid) {
|
|
|
229
227
|
if (result instanceof Uuid58EncodeError) throw result;
|
|
230
228
|
return result;
|
|
231
229
|
}
|
|
230
|
+
// bytes.ts
|
|
231
|
+
/**
|
|
232
|
+
* Encodes 16 bytes, in big-endian (RFC 9562) order, as a 22-character uuid58
|
|
233
|
+
* string, but instead of throwing an error for invalid input, it returns an
|
|
234
|
+
* `Uuid58EncodeError` instance.
|
|
235
|
+
*
|
|
236
|
+
* @param bytes - The 16 bytes of the UUID
|
|
237
|
+
* @returns A 22-character uuid58 string, or an `Uuid58EncodeError` (kind
|
|
238
|
+
* `"invalid_length"`) if `bytes` is not exactly 16 bytes long
|
|
239
|
+
* @note This function does not throw; it returns the error object instead.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const bytes = new Uint8Array(16); // nil UUID
|
|
244
|
+
* uuid58EncodeBytesSafe(bytes); // returns "1111111111111111111111"
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
function uuid58EncodeBytesSafe(bytes) {
|
|
248
|
+
if (bytes.length !== 16) return new Uuid58EncodeError(`Invalid byte length: expected 16 bytes, got ${bytes.length}`, "invalid_length");
|
|
249
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, 16);
|
|
250
|
+
return encodeUint128(view.getBigUint64(0) << 64n | view.getBigUint64(8));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Encodes 16 bytes, in big-endian (RFC 9562) order, as a 22-character uuid58
|
|
254
|
+
* string.
|
|
255
|
+
*
|
|
256
|
+
* @param bytes - The 16 bytes of the UUID
|
|
257
|
+
* @returns A 22-character uuid58 string
|
|
258
|
+
* @throws {Uuid58EncodeError} If `bytes` is not exactly 16 bytes long
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```typescript
|
|
262
|
+
* uuid58EncodeBytes(new Uint8Array(16)); // returns "1111111111111111111111"
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
function uuid58EncodeBytes(bytes) {
|
|
266
|
+
const result = uuid58EncodeBytesSafe(bytes);
|
|
267
|
+
if (result instanceof Uuid58EncodeError) throw result;
|
|
268
|
+
return result;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Decodes a uuid58 string to 16 bytes in big-endian (RFC 9562) order, but
|
|
272
|
+
* instead of throwing an error for invalid input, it returns an
|
|
273
|
+
* `Uuid58DecodeError` instance.
|
|
274
|
+
*
|
|
275
|
+
* @param uuid58 - The 22-character uuid58 string to decode
|
|
276
|
+
* @returns A new 16-byte `Uint8Array`, or an `Uuid58DecodeError`
|
|
277
|
+
* @note This function does not throw; it returns the error object instead.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```typescript
|
|
281
|
+
* uuid58DecodeBytesSafe("1111111111111111111111"); // returns Uint8Array(16) of zeros
|
|
282
|
+
* ```
|
|
283
|
+
*/
|
|
284
|
+
function uuid58DecodeBytesSafe(uuid58) {
|
|
285
|
+
const num = decodeUint128(uuid58);
|
|
286
|
+
if (typeof num !== "bigint") return num;
|
|
287
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
288
|
+
const view = new DataView(bytes.buffer);
|
|
289
|
+
view.setBigUint64(0, num >> 64n);
|
|
290
|
+
view.setBigUint64(8, num & 18446744073709551615n);
|
|
291
|
+
return bytes;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Decodes a uuid58 string to 16 bytes in big-endian (RFC 9562) order.
|
|
295
|
+
*
|
|
296
|
+
* @param uuid58 - The 22-character uuid58 string to decode
|
|
297
|
+
* @returns A new 16-byte `Uint8Array`
|
|
298
|
+
* @throws {Uuid58DecodeError} If the input is not a valid uuid58 string
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```typescript
|
|
302
|
+
* const bytes = uuid58DecodeBytes("XDY9dmBbcMBXqcRvYw8xJ2");
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
function uuid58DecodeBytes(uuid58) {
|
|
306
|
+
const result = uuid58DecodeBytesSafe(uuid58);
|
|
307
|
+
if (result instanceof Uuid58DecodeError) throw result;
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
// check.ts
|
|
311
|
+
const MAX_UUID58 = "YcVfxkQb6JRzqk5kF2tNLv";
|
|
312
|
+
/**
|
|
313
|
+
* Regular expression pattern for validating UUID58 strings.
|
|
314
|
+
*
|
|
315
|
+
* This regex ensures that a string:
|
|
316
|
+
* - Contains exactly 22 characters
|
|
317
|
+
* - Uses only characters from the Base58 alphabet
|
|
318
|
+
*
|
|
319
|
+
* **Note:** This regex does NOT validate that the decoded value fits within
|
|
320
|
+
* 128 bits (UUID size). For complete validation including 128-bit range
|
|
321
|
+
* checking, use {@link isUuid58} instead.
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* ```typescript
|
|
325
|
+
* const isValid = UUID58_REGEX.test("XDY9dmBbcMBXqcRvYw8xJ2"); // true
|
|
326
|
+
* const isValid = UUID58_REGEX.test("invalid"); // false
|
|
327
|
+
* const isValid = UUID58_REGEX.test("123"); // false (too short)
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
330
|
+
const UUID58_REGEX = /^[1-9A-HJ-NP-Za-km-z]{22}$/u;
|
|
331
|
+
/**
|
|
332
|
+
* Checks if a given string is a valid UUID58-encodable string.
|
|
333
|
+
* This function efficiently validates without performing full decoding.
|
|
334
|
+
*
|
|
335
|
+
* A valid UUID58 string must:
|
|
336
|
+
* - Be exactly 22 characters long
|
|
337
|
+
* - Contain only characters from the Base58 alphabet
|
|
338
|
+
* - Decode to a value that fits within 128 bits (UUID size)
|
|
339
|
+
*
|
|
340
|
+
* @param uuid58 - The string to check
|
|
341
|
+
* @returns `true` if the string is a valid UUID58 string, `false` otherwise
|
|
342
|
+
*
|
|
343
|
+
* @example
|
|
344
|
+
* ```typescript
|
|
345
|
+
* isUuid58("XDY9dmBbcMBXqcRvYw8xJ2"); // true
|
|
346
|
+
* isUuid58("invalid"); // false
|
|
347
|
+
* isUuid58("O0lI"); // false (contains invalid characters)
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
function isUuid58(uuid58) {
|
|
351
|
+
return UUID58_REGEX.test(uuid58) && uuid58 <= MAX_UUID58;
|
|
352
|
+
}
|
|
232
353
|
// generate.ts
|
|
233
354
|
const random = /* @__PURE__ */ new BigUint64Array(2);
|
|
234
355
|
/**
|
|
@@ -257,6 +378,10 @@ exports.Uuid58EncodeError = Uuid58EncodeError;
|
|
|
257
378
|
exports.isUuid58 = isUuid58;
|
|
258
379
|
exports.uuid58 = uuid58;
|
|
259
380
|
exports.uuid58Decode = uuid58Decode;
|
|
381
|
+
exports.uuid58DecodeBytes = uuid58DecodeBytes;
|
|
382
|
+
exports.uuid58DecodeBytesSafe = uuid58DecodeBytesSafe;
|
|
260
383
|
exports.uuid58DecodeSafe = uuid58DecodeSafe;
|
|
261
384
|
exports.uuid58Encode = uuid58Encode;
|
|
385
|
+
exports.uuid58EncodeBytes = uuid58EncodeBytes;
|
|
386
|
+
exports.uuid58EncodeBytesSafe = uuid58EncodeBytesSafe;
|
|
262
387
|
exports.uuid58EncodeSafe = uuid58EncodeSafe;
|