uniku 0.0.1 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +106 -3
- package/build/cuid2/cuid2.d.mts +51 -0
- package/build/cuid2/cuid2.d.mts.map +1 -0
- package/build/cuid2/cuid2.mjs +134 -0
- package/build/cuid2/cuid2.mjs.map +1 -0
- package/build/nanoid/nanoid.d.mts +39 -0
- package/build/nanoid/nanoid.d.mts.map +1 -0
- package/build/nanoid/nanoid.mjs +73 -0
- package/build/nanoid/nanoid.mjs.map +1 -0
- package/build/random-Dul5R6oJ.mjs +17 -0
- package/build/random-Dul5R6oJ.mjs.map +1 -0
- package/build/ulid/ulid.d.mts +30 -0
- package/build/ulid/ulid.d.mts.map +1 -0
- package/build/ulid/ulid.mjs +192 -0
- package/build/ulid/ulid.mjs.map +1 -0
- package/build/{v4.d.mts → uuid/v4.d.mts} +1 -0
- package/build/uuid/v4.d.mts.map +1 -0
- package/build/{v4.mjs → uuid/v4.mjs} +9 -3
- package/build/uuid/v4.mjs.map +1 -0
- package/build/{v7.d.mts → uuid/v7.d.mts} +3 -1
- package/build/uuid/v7.d.mts.map +1 -0
- package/build/{v7.mjs → uuid/v7.mjs} +17 -4
- package/build/uuid/v7.mjs.map +1 -0
- package/build/{uuid-DaimLSJD.mjs → uuid-cJ5hOOIR.mjs} +2 -17
- package/build/uuid-cJ5hOOIR.mjs.map +1 -0
- package/package.json +39 -8
- package/build/uuid-DaimLSJD.mjs.map +0 -1
- package/build/v4.d.mts.map +0 -1
- package/build/v4.mjs.map +0 -1
- package/build/v7.d.mts.map +0 -1
- package/build/v7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://bundlephobia.com/package/uniku)
|
|
7
7
|
|
|
8
|
-
Minimal, tree-shakeable UUID utilities for every JavaScript runtime.
|
|
8
|
+
Minimal, tree-shakeable UUID and ULID utilities for every JavaScript runtime.
|
|
9
9
|
|
|
10
10
|
> *uniku* means "unique" in Maltese.
|
|
11
11
|
|
|
@@ -20,8 +20,11 @@ Minimal, tree-shakeable UUID utilities for every JavaScript runtime.
|
|
|
20
20
|
|
|
21
21
|
## Supported ID generation algorithms
|
|
22
22
|
|
|
23
|
-
- UUID v4 ([RFC 4122](
|
|
24
|
-
- UUID v7 ([RFC 9562](
|
|
23
|
+
- UUID v4 ([RFC 4122](../../rfcs/rfc4122.txt))
|
|
24
|
+
- UUID v7 ([RFC 9562](../../rfcs/rfc9562.txt), time-ordered, monotonic sequencing)
|
|
25
|
+
- ULID ([spec](../../rfcs/ulid.txt), time-ordered, monotonic sequencing)
|
|
26
|
+
- CUID2 ([spec](../../rfcs/cuid2.txt), secure, non-time-ordered)
|
|
27
|
+
- Nanoid ([spec](../../rfcs/nanoid.txt), URL-friendly, custom alphabet support)
|
|
25
28
|
|
|
26
29
|
## Installation
|
|
27
30
|
|
|
@@ -71,6 +74,59 @@ const ids = [uuidv7(), uuidv7(), uuidv7()]
|
|
|
71
74
|
ids.sort() // Already in creation order
|
|
72
75
|
```
|
|
73
76
|
|
|
77
|
+
### ULID (time-ordered)
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { ulid } from 'uniku/ulid'
|
|
81
|
+
|
|
82
|
+
const id = ulid()
|
|
83
|
+
// => "01HW9T2W9W9YJ3JZ1H4P4M2T8Q"
|
|
84
|
+
|
|
85
|
+
// Convert to/from bytes
|
|
86
|
+
const bytes = ulid.toBytes(id)
|
|
87
|
+
const str = ulid.fromBytes(bytes)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### CUID2 (secure, non-time-ordered)
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { cuid2 } from 'uniku/cuid2'
|
|
94
|
+
|
|
95
|
+
const id = cuid2()
|
|
96
|
+
// => "pfh0haxfpzowht3oi213cqos"
|
|
97
|
+
|
|
98
|
+
// Custom length
|
|
99
|
+
const shortId = cuid2({ length: 10 })
|
|
100
|
+
// => "tz4a98xxat"
|
|
101
|
+
|
|
102
|
+
// Validation (type guard)
|
|
103
|
+
const maybeId: unknown = getUserInput()
|
|
104
|
+
if (cuid2.isValid(maybeId)) {
|
|
105
|
+
console.log(maybeId) // TypeScript knows this is a string
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Nanoid (URL-friendly, custom alphabet)
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
import { nanoid } from 'uniku/nanoid'
|
|
113
|
+
|
|
114
|
+
const id = nanoid()
|
|
115
|
+
// => "V1StGXR8_Z5jdHi6B-myT"
|
|
116
|
+
|
|
117
|
+
// Custom size
|
|
118
|
+
const shortId = nanoid(10)
|
|
119
|
+
// => "IRFa-VaY2b"
|
|
120
|
+
|
|
121
|
+
// Custom alphabet and size via options
|
|
122
|
+
const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })
|
|
123
|
+
// => "4f90d13a42bc"
|
|
124
|
+
|
|
125
|
+
// Validation (default alphabet only)
|
|
126
|
+
nanoid.isValid(id)
|
|
127
|
+
// => true
|
|
128
|
+
```
|
|
129
|
+
|
|
74
130
|
## API Reference
|
|
75
131
|
|
|
76
132
|
### `uuidv4` (from `uniku/uuid/v4`)
|
|
@@ -94,6 +150,8 @@ uuidv7(options: Version7Options | undefined, buf: Uint8Array, offset?: number):
|
|
|
94
150
|
|
|
95
151
|
uuidv7.toBytes(id: string): Uint8Array
|
|
96
152
|
uuidv7.fromBytes(bytes: Uint8Array): string
|
|
153
|
+
uuidv7.timestamp(id: string): number
|
|
154
|
+
uuidv7.isValid(id: string): boolean
|
|
97
155
|
```
|
|
98
156
|
|
|
99
157
|
**Options:**
|
|
@@ -101,6 +159,51 @@ uuidv7.fromBytes(bytes: Uint8Array): string
|
|
|
101
159
|
- `seq?: number` - Sequence number for monotonicity
|
|
102
160
|
- `random?: Uint8Array` - 16 bytes of random data
|
|
103
161
|
|
|
162
|
+
### `ulid` (from `uniku/ulid`)
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
ulid(options?: UlidOptions): string
|
|
166
|
+
ulid(options: UlidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
167
|
+
|
|
168
|
+
ulid.toBytes(id: string): Uint8Array
|
|
169
|
+
ulid.fromBytes(bytes: Uint8Array): string
|
|
170
|
+
ulid.timestamp(id: string): number
|
|
171
|
+
ulid.isValid(id: string): boolean
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Options:**
|
|
175
|
+
- `msecs?: number` - Timestamp in milliseconds (defaults to `Date.now()`)
|
|
176
|
+
- `random?: Uint8Array` - 16 bytes of random data (only first 10 bytes used)
|
|
177
|
+
|
|
178
|
+
### `cuid2` (from `uniku/cuid2`)
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
cuid2(options?: Cuid2Options): string
|
|
182
|
+
cuid2.isValid(id: unknown): id is string
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Options:**
|
|
186
|
+
- `length?: number` - ID length (2-32, default 24)
|
|
187
|
+
- `random?: Uint8Array` - Custom random bytes for testing
|
|
188
|
+
|
|
189
|
+
Note: Unlike UUID and ULID, CUID2 does not provide `toBytes`/`fromBytes` because it is a string-native format with no canonical binary representation.
|
|
190
|
+
|
|
191
|
+
### `nanoid` (from `uniku/nanoid`)
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
nanoid(): string
|
|
195
|
+
nanoid(size: number): string
|
|
196
|
+
nanoid(options: NanoidOptions): string
|
|
197
|
+
nanoid.isValid(id: unknown): id is string
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Options:**
|
|
201
|
+
- `size?: number` - ID length (default 21, max 2048)
|
|
202
|
+
- `alphabet?: string` - Custom alphabet (2-256 printable ASCII characters)
|
|
203
|
+
- `random?: Uint8Array` - Custom random bytes for testing
|
|
204
|
+
|
|
205
|
+
Note: `isValid()` only validates IDs against the default URL-safe alphabet (`A-Za-z0-9_-`). IDs generated with custom alphabets cannot be validated with this method.
|
|
206
|
+
|
|
104
207
|
## Documentation
|
|
105
208
|
|
|
106
209
|
For advanced usage, examples, and contributing guidelines, see the [full documentation on GitHub](https://github.com/jkomyno/uniku).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/cuid2/cuid2.d.ts
|
|
2
|
+
type Cuid2Options = {
|
|
3
|
+
/**
|
|
4
|
+
* Length of the generated ID (2-32 characters).
|
|
5
|
+
* Default: 24
|
|
6
|
+
*/
|
|
7
|
+
length?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Custom random bytes for deterministic testing.
|
|
10
|
+
* Must be at least 1 byte. For adequate entropy, use at least 16 bytes.
|
|
11
|
+
* Note: The fingerprint always uses cryptographically secure random bytes,
|
|
12
|
+
* regardless of this option.
|
|
13
|
+
*/
|
|
14
|
+
random?: Uint8Array;
|
|
15
|
+
};
|
|
16
|
+
type Cuid2 = {
|
|
17
|
+
(options?: Cuid2Options): string;
|
|
18
|
+
isValid(id: unknown): id is string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Generate a CUID2 string.
|
|
22
|
+
*
|
|
23
|
+
* CUID2 is a secure, collision-resistant identifier that hashes multiple
|
|
24
|
+
* entropy sources using SHA3-512. Unlike time-ordered IDs (ULID, UUID v7),
|
|
25
|
+
* CUID2 prevents enumeration attacks by making IDs non-predictable.
|
|
26
|
+
*
|
|
27
|
+
* Note: CUID2 does not provide toBytes/fromBytes because it is a string-native
|
|
28
|
+
* format with no canonical binary representation (unlike UUID's 16-byte format).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { cuid2 } from 'uniku/cuid2'
|
|
33
|
+
*
|
|
34
|
+
* const id = cuid2()
|
|
35
|
+
* // => "pfh0haxfpzowht3oi213cqos"
|
|
36
|
+
*
|
|
37
|
+
* // Custom length
|
|
38
|
+
* const shortId = cuid2({ length: 10 })
|
|
39
|
+
* // => "tz4a98xxat"
|
|
40
|
+
*
|
|
41
|
+
* // Validation (type guard)
|
|
42
|
+
* const maybeId: unknown = getUserInput()
|
|
43
|
+
* if (cuid2.isValid(maybeId)) {
|
|
44
|
+
* console.log(maybeId.length) // TypeScript knows maybeId is string
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare const cuid2: Cuid2;
|
|
49
|
+
//#endregion
|
|
50
|
+
export { Cuid2, Cuid2Options, cuid2 };
|
|
51
|
+
//# sourceMappingURL=cuid2.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cuid2.d.mts","names":["cuid2: Cuid2"],"sources":["../../src/cuid2/cuid2.ts"],"sourcesContent":[],"mappings":";KAEY,YAAA;EAAZ;AAeA;AA2MA;;;;;;;;;WA9MW;;KAGC,KAAA;aACC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0MAA,OAAO"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { sha3_512 } from "@noble/hashes/sha3.js";
|
|
2
|
+
|
|
3
|
+
//#region src/cuid2/cuid2.ts
|
|
4
|
+
const DEFAULT_LENGTH = 24;
|
|
5
|
+
const MAX_LENGTH = 32;
|
|
6
|
+
const MIN_LENGTH = 2;
|
|
7
|
+
const INITIAL_COUNT_MAX = 476782367;
|
|
8
|
+
const CUID2_REGEX = /^[a-z][0-9a-z]+$/;
|
|
9
|
+
const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
10
|
+
const LETTER_ALPHABET = "abcdefghijklmnopqrstuvwxyz";
|
|
11
|
+
const textEncoder = new TextEncoder();
|
|
12
|
+
const RANDOM_BUFFER_SIZE = 256;
|
|
13
|
+
const randomBuffer = new Uint8Array(RANDOM_BUFFER_SIZE);
|
|
14
|
+
let randomBufferOffset = RANDOM_BUFFER_SIZE;
|
|
15
|
+
/**
|
|
16
|
+
* Module-level state for counter and fingerprint.
|
|
17
|
+
* Counter is initialized lazily on first call to prevent unnecessary crypto operations.
|
|
18
|
+
* Fingerprint is also generated lazily on first call.
|
|
19
|
+
*/
|
|
20
|
+
const state = {
|
|
21
|
+
counter: void 0,
|
|
22
|
+
fingerprint: void 0
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Initialize counter using crypto for consistency with other entropy sources.
|
|
26
|
+
*/
|
|
27
|
+
function initializeCounter() {
|
|
28
|
+
const buffer = new Uint8Array(4);
|
|
29
|
+
globalThis.crypto.getRandomValues(buffer);
|
|
30
|
+
return ((buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]) >>> 0) % (INITIAL_COUNT_MAX + 1);
|
|
31
|
+
}
|
|
32
|
+
function bufToBigInt(buf) {
|
|
33
|
+
let value = 0n;
|
|
34
|
+
for (const byte of buf) value = value * 256n + BigInt(byte);
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
function bigIntToBase36(value) {
|
|
38
|
+
if (value === 0n) return "0";
|
|
39
|
+
const chars = [];
|
|
40
|
+
while (value > 0n) {
|
|
41
|
+
chars.push(ALPHABET[Number(value % 36n)]);
|
|
42
|
+
value = value / 36n;
|
|
43
|
+
}
|
|
44
|
+
return chars.reverse().join("");
|
|
45
|
+
}
|
|
46
|
+
function randomLetter(random) {
|
|
47
|
+
return LETTER_ALPHABET[Math.floor(random() * 26)];
|
|
48
|
+
}
|
|
49
|
+
function createEntropy(length, random) {
|
|
50
|
+
const chars = new Array(length);
|
|
51
|
+
for (let i = 0; i < length; i++) chars[i] = ALPHABET[Math.floor(random() * 36)];
|
|
52
|
+
return chars.join("");
|
|
53
|
+
}
|
|
54
|
+
function hash(input) {
|
|
55
|
+
return sha3_512(textEncoder.encode(input));
|
|
56
|
+
}
|
|
57
|
+
const BIG_LENGTH = 32;
|
|
58
|
+
function createFingerprint() {
|
|
59
|
+
const random = getCryptoRandom;
|
|
60
|
+
return bigIntToBase36(bufToBigInt(hash(Object.keys(globalThis).toString() + createEntropy(BIG_LENGTH, random)))).slice(1, BIG_LENGTH + 1);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get a random number in [0, 1) using batched CSPRNG.
|
|
64
|
+
* Uses a pre-allocated buffer to reduce crypto.getRandomValues calls.
|
|
65
|
+
*/
|
|
66
|
+
function getCryptoRandom() {
|
|
67
|
+
if (randomBufferOffset >= RANDOM_BUFFER_SIZE) {
|
|
68
|
+
globalThis.crypto.getRandomValues(randomBuffer);
|
|
69
|
+
randomBufferOffset = 0;
|
|
70
|
+
}
|
|
71
|
+
const value = (randomBuffer[randomBufferOffset] * 16777216 + randomBuffer[randomBufferOffset + 1] * 65536 + randomBuffer[randomBufferOffset + 2] * 256 + randomBuffer[randomBufferOffset + 3]) / 4294967296;
|
|
72
|
+
randomBufferOffset += 4;
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function getRandomFn(random) {
|
|
76
|
+
if (random) {
|
|
77
|
+
if (random.length === 0) throw new Error("Random byte array cannot be empty");
|
|
78
|
+
let index = 0;
|
|
79
|
+
return () => {
|
|
80
|
+
const value = random[index % random.length] / 256;
|
|
81
|
+
index += 1;
|
|
82
|
+
return value;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return getCryptoRandom;
|
|
86
|
+
}
|
|
87
|
+
function cuid2Fn(options) {
|
|
88
|
+
const length = options?.length ?? DEFAULT_LENGTH;
|
|
89
|
+
if (length < MIN_LENGTH || length > MAX_LENGTH) throw new RangeError(`CUID2 length must be between ${MIN_LENGTH} and ${MAX_LENGTH}. Received: ${length}`);
|
|
90
|
+
const random = getRandomFn(options?.random);
|
|
91
|
+
if (state.counter === void 0) state.counter = initializeCounter();
|
|
92
|
+
if (state.fingerprint === void 0) state.fingerprint = createFingerprint();
|
|
93
|
+
const firstLetter = randomLetter(random);
|
|
94
|
+
const time = Date.now().toString(36);
|
|
95
|
+
state.counter += 1;
|
|
96
|
+
const count = state.counter.toString(36);
|
|
97
|
+
return firstLetter + bigIntToBase36(bufToBigInt(hash(time + createEntropy(length, random) + count + state.fingerprint))).slice(1, length);
|
|
98
|
+
}
|
|
99
|
+
function isValid(id) {
|
|
100
|
+
return typeof id === "string" && id.length >= MIN_LENGTH && id.length <= MAX_LENGTH && CUID2_REGEX.test(id);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Generate a CUID2 string.
|
|
104
|
+
*
|
|
105
|
+
* CUID2 is a secure, collision-resistant identifier that hashes multiple
|
|
106
|
+
* entropy sources using SHA3-512. Unlike time-ordered IDs (ULID, UUID v7),
|
|
107
|
+
* CUID2 prevents enumeration attacks by making IDs non-predictable.
|
|
108
|
+
*
|
|
109
|
+
* Note: CUID2 does not provide toBytes/fromBytes because it is a string-native
|
|
110
|
+
* format with no canonical binary representation (unlike UUID's 16-byte format).
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```ts
|
|
114
|
+
* import { cuid2 } from 'uniku/cuid2'
|
|
115
|
+
*
|
|
116
|
+
* const id = cuid2()
|
|
117
|
+
* // => "pfh0haxfpzowht3oi213cqos"
|
|
118
|
+
*
|
|
119
|
+
* // Custom length
|
|
120
|
+
* const shortId = cuid2({ length: 10 })
|
|
121
|
+
* // => "tz4a98xxat"
|
|
122
|
+
*
|
|
123
|
+
* // Validation (type guard)
|
|
124
|
+
* const maybeId: unknown = getUserInput()
|
|
125
|
+
* if (cuid2.isValid(maybeId)) {
|
|
126
|
+
* console.log(maybeId.length) // TypeScript knows maybeId is string
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
const cuid2 = Object.assign(cuid2Fn, { isValid });
|
|
131
|
+
|
|
132
|
+
//#endregion
|
|
133
|
+
export { cuid2 };
|
|
134
|
+
//# sourceMappingURL=cuid2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cuid2.mjs","names":["state: { counter: number | undefined; fingerprint: string | undefined }","chars: string[]","cuid2: Cuid2"],"sources":["../../src/cuid2/cuid2.ts"],"sourcesContent":["import { sha3_512 } from '@noble/hashes/sha3.js'\n\nexport type Cuid2Options = {\n /**\n * Length of the generated ID (2-32 characters).\n * Default: 24\n */\n length?: number\n /**\n * Custom random bytes for deterministic testing.\n * Must be at least 1 byte. For adequate entropy, use at least 16 bytes.\n * Note: The fingerprint always uses cryptographically secure random bytes,\n * regardless of this option.\n */\n random?: Uint8Array\n}\n\nexport type Cuid2 = {\n (options?: Cuid2Options): string\n isValid(id: unknown): id is string\n}\n\nconst DEFAULT_LENGTH = 24\nconst MAX_LENGTH = 32\nconst MIN_LENGTH = 2\n\n// Maximum initial counter value from cuid2 spec - provides ~29 bits of\n// initial entropy to prevent cross-process collisions at startup\nconst INITIAL_COUNT_MAX = 476782367\n\n// Validation regex: first char must be a-z, rest can be a-z or 0-9\nconst CUID2_REGEX = /^[a-z][0-9a-z]+$/\n\n// Base36 alphabet\nconst ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz'\nconst LETTER_ALPHABET = 'abcdefghijklmnopqrstuvwxyz'\n\n// Reusable TextEncoder instance (stateless, safe to share)\nconst textEncoder = new TextEncoder()\n\n// Pre-allocated buffer for batched random byte generation (64 random numbers per batch)\nconst RANDOM_BUFFER_SIZE = 256\nconst randomBuffer = new Uint8Array(RANDOM_BUFFER_SIZE)\nlet randomBufferOffset = RANDOM_BUFFER_SIZE // Force initial fill\n\n/**\n * Module-level state for counter and fingerprint.\n * Counter is initialized lazily on first call to prevent unnecessary crypto operations.\n * Fingerprint is also generated lazily on first call.\n */\nconst state: { counter: number | undefined; fingerprint: string | undefined } = {\n counter: undefined,\n fingerprint: undefined,\n}\n\n/**\n * Initialize counter using crypto for consistency with other entropy sources.\n */\nfunction initializeCounter(): number {\n const buffer = new Uint8Array(4)\n globalThis.crypto.getRandomValues(buffer)\n return (((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]) >>> 0) % (INITIAL_COUNT_MAX + 1)\n}\n\n// --- Base36 utilities ---\n\nfunction bufToBigInt(buf: Uint8Array): bigint {\n let value = 0n\n for (const byte of buf) {\n value = value * 256n + BigInt(byte)\n }\n return value\n}\n\nfunction bigIntToBase36(value: bigint): string {\n if (value === 0n) return '0'\n const chars: string[] = []\n while (value > 0n) {\n chars.push(ALPHABET[Number(value % 36n)])\n value = value / 36n\n }\n return chars.reverse().join('')\n}\n\nfunction randomLetter(random: () => number): string {\n return LETTER_ALPHABET[Math.floor(random() * 26)]\n}\n\nfunction createEntropy(length: number, random: () => number): string {\n const chars = new Array<string>(length)\n for (let i = 0; i < length; i++) {\n chars[i] = ALPHABET[Math.floor(random() * 36)]\n }\n return chars.join('')\n}\n\n// --- SHA3 hash wrapper ---\n\nfunction hash(input: string): Uint8Array {\n return sha3_512(textEncoder.encode(input))\n}\n\n// --- Fingerprint generation ---\n\nconst BIG_LENGTH = 32\n\nfunction createFingerprint(): string {\n // Always use CSPRNG for fingerprint to ensure security regardless of custom random option\n const random = getCryptoRandom\n const globals = Object.keys(globalThis).toString()\n const sourceString = globals + createEntropy(BIG_LENGTH, random)\n const hashed = hash(sourceString)\n return bigIntToBase36(bufToBigInt(hashed)).slice(1, BIG_LENGTH + 1)\n}\n\n// --- Random function factory ---\n\n/**\n * Get a random number in [0, 1) using batched CSPRNG.\n * Uses a pre-allocated buffer to reduce crypto.getRandomValues calls.\n */\nfunction getCryptoRandom(): number {\n if (randomBufferOffset >= RANDOM_BUFFER_SIZE) {\n globalThis.crypto.getRandomValues(randomBuffer)\n randomBufferOffset = 0\n }\n const value =\n (randomBuffer[randomBufferOffset] * 0x1000000 +\n randomBuffer[randomBufferOffset + 1] * 0x10000 +\n randomBuffer[randomBufferOffset + 2] * 0x100 +\n randomBuffer[randomBufferOffset + 3]) /\n 0x100000000\n randomBufferOffset += 4\n return value\n}\n\nfunction getRandomFn(random?: Uint8Array): () => number {\n if (random) {\n if (random.length === 0) {\n throw new Error('Random byte array cannot be empty')\n }\n let index = 0\n return () => {\n const value = random[index % random.length] / 256\n index += 1\n return value\n }\n }\n return getCryptoRandom\n}\n\n// --- Main generator ---\n\nfunction cuid2Fn(options?: Cuid2Options): string {\n const length = options?.length ?? DEFAULT_LENGTH\n\n if (length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(`CUID2 length must be between ${MIN_LENGTH} and ${MAX_LENGTH}. Received: ${length}`)\n }\n\n const random = getRandomFn(options?.random)\n\n // Initialize counter lazily on first call\n if (state.counter === undefined) {\n state.counter = initializeCounter()\n }\n\n // Initialize fingerprint lazily on first call (always uses CSPRNG)\n if (state.fingerprint === undefined) {\n state.fingerprint = createFingerprint()\n }\n\n const firstLetter = randomLetter(random)\n const time = Date.now().toString(36)\n state.counter += 1\n const count = state.counter.toString(36)\n const salt = createEntropy(length, random)\n\n const hashInput = time + salt + count + state.fingerprint\n const hashed = hash(hashInput)\n const base36Hash = bigIntToBase36(bufToBigInt(hashed))\n\n // Drop first char of hash to avoid histogram bias, prepend random letter\n return firstLetter + base36Hash.slice(1, length)\n}\n\n// --- Validation (type guard) ---\n\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && id.length >= MIN_LENGTH && id.length <= MAX_LENGTH && CUID2_REGEX.test(id)\n}\n\n/**\n * Generate a CUID2 string.\n *\n * CUID2 is a secure, collision-resistant identifier that hashes multiple\n * entropy sources using SHA3-512. Unlike time-ordered IDs (ULID, UUID v7),\n * CUID2 prevents enumeration attacks by making IDs non-predictable.\n *\n * Note: CUID2 does not provide toBytes/fromBytes because it is a string-native\n * format with no canonical binary representation (unlike UUID's 16-byte format).\n *\n * @example\n * ```ts\n * import { cuid2 } from 'uniku/cuid2'\n *\n * const id = cuid2()\n * // => \"pfh0haxfpzowht3oi213cqos\"\n *\n * // Custom length\n * const shortId = cuid2({ length: 10 })\n * // => \"tz4a98xxat\"\n *\n * // Validation (type guard)\n * const maybeId: unknown = getUserInput()\n * if (cuid2.isValid(maybeId)) {\n * console.log(maybeId.length) // TypeScript knows maybeId is string\n * }\n * ```\n */\nexport const cuid2: Cuid2 = Object.assign(cuid2Fn, {\n isValid,\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/nanoid/nanoid.d.ts
|
|
2
|
+
/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */
|
|
3
|
+
declare const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
4
|
+
type NanoidOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Random bytes for deterministic output (testing).
|
|
7
|
+
* Must provide enough bytes for rejection sampling (~size * 2).
|
|
8
|
+
*/
|
|
9
|
+
random?: Uint8Array;
|
|
10
|
+
/**
|
|
11
|
+
* Custom alphabet to use. Default: URL-safe A-Za-z0-9_-
|
|
12
|
+
* Must be 2-256 printable ASCII characters (32-126) with no duplicates.
|
|
13
|
+
*/
|
|
14
|
+
alphabet?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Length of generated ID. Default: 21. Maximum: 2048.
|
|
17
|
+
*/
|
|
18
|
+
size?: number;
|
|
19
|
+
};
|
|
20
|
+
type Nanoid = {
|
|
21
|
+
/** Generate nanoid with default settings */
|
|
22
|
+
(): string;
|
|
23
|
+
/** Generate nanoid with custom size */
|
|
24
|
+
(size: number): string;
|
|
25
|
+
/** Generate nanoid with options */
|
|
26
|
+
(options: NanoidOptions): string;
|
|
27
|
+
/**
|
|
28
|
+
* Validate a nanoid string against the default URL-safe alphabet.
|
|
29
|
+
* Note: Does not validate IDs generated with custom alphabets.
|
|
30
|
+
*/
|
|
31
|
+
isValid(id: unknown): id is string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Generate a URL-friendly unique string ID.
|
|
35
|
+
*/
|
|
36
|
+
declare const nanoid: Nanoid;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { Nanoid, NanoidOptions, URL_ALPHABET, nanoid };
|
|
39
|
+
//# sourceMappingURL=nanoid.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanoid.d.mts","names":["nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":[],"mappings":";;AACa,cAAA,YAAA,GAAA,kEAAA;AAMD,KAAA,aAAA,GAAA;EAiBZ;AAsHA;;;WAlIW;;;;;;;;;;;KAYC,MAAA;;;;;;YAMA;;;;;;;;;;cAgHCA,QAAQ"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//#region src/nanoid/nanoid.ts
|
|
2
|
+
/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */
|
|
3
|
+
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
4
|
+
const DEFAULT_SIZE = 21;
|
|
5
|
+
const MAX_SIZE = 2048;
|
|
6
|
+
const NANOID_REGEX = /^[A-Za-z0-9_-]+$/;
|
|
7
|
+
/**
|
|
8
|
+
* Validate alphabet: 2-256 printable ASCII chars, no duplicates
|
|
9
|
+
*/
|
|
10
|
+
function validateAlphabet(alphabet) {
|
|
11
|
+
if (alphabet.length < 2) throw new Error("Alphabet must contain at least 2 characters");
|
|
12
|
+
if (alphabet.length > 256) throw new Error("Alphabet must not exceed 256 characters");
|
|
13
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const char of alphabet) {
|
|
15
|
+
const code = char.charCodeAt(0);
|
|
16
|
+
if (code < 32 || code > 126) throw new Error("Alphabet must contain only printable ASCII characters (32-126)");
|
|
17
|
+
if (seen.has(char)) throw new Error(`Duplicate character in alphabet: "${char}"`);
|
|
18
|
+
seen.add(char);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate size parameter
|
|
23
|
+
*/
|
|
24
|
+
function validateSize(size) {
|
|
25
|
+
if (!Number.isInteger(size) || size < 0) throw new RangeError("Size must be a non-negative integer");
|
|
26
|
+
if (size > MAX_SIZE) throw new RangeError(`Size must not exceed ${MAX_SIZE}`);
|
|
27
|
+
}
|
|
28
|
+
function nanoidFn(sizeOrOptions) {
|
|
29
|
+
let size = DEFAULT_SIZE;
|
|
30
|
+
let alphabet = URL_ALPHABET;
|
|
31
|
+
let randomBytes;
|
|
32
|
+
if (typeof sizeOrOptions === "number") size = sizeOrOptions;
|
|
33
|
+
else if (sizeOrOptions) {
|
|
34
|
+
size = sizeOrOptions.size ?? DEFAULT_SIZE;
|
|
35
|
+
alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET;
|
|
36
|
+
randomBytes = sizeOrOptions.random;
|
|
37
|
+
if (sizeOrOptions.alphabet) validateAlphabet(alphabet);
|
|
38
|
+
}
|
|
39
|
+
validateSize(size);
|
|
40
|
+
if (size === 0) return "";
|
|
41
|
+
const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
|
|
42
|
+
const step = Math.ceil(1.6 * mask * size / alphabet.length);
|
|
43
|
+
let id = "";
|
|
44
|
+
let randomOffset = 0;
|
|
45
|
+
while (id.length < size) {
|
|
46
|
+
let bytes;
|
|
47
|
+
if (randomBytes) {
|
|
48
|
+
if (randomBytes.length - randomOffset < step) throw new Error(`Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`);
|
|
49
|
+
bytes = randomBytes.subarray(randomOffset, randomOffset + step);
|
|
50
|
+
randomOffset += step;
|
|
51
|
+
} else bytes = globalThis.crypto.getRandomValues(new Uint8Array(step));
|
|
52
|
+
for (let i = 0; i < bytes.length && id.length < size; i++) {
|
|
53
|
+
const index = bytes[i] & mask;
|
|
54
|
+
if (index < alphabet.length) id += alphabet[index];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return id;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validate a nanoid string against the default URL-safe alphabet.
|
|
61
|
+
* Note: Does not validate IDs generated with custom alphabets.
|
|
62
|
+
*/
|
|
63
|
+
function isValid(id) {
|
|
64
|
+
return typeof id === "string" && id.length > 0 && NANOID_REGEX.test(id);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generate a URL-friendly unique string ID.
|
|
68
|
+
*/
|
|
69
|
+
const nanoid = Object.assign(nanoidFn, { isValid });
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
export { URL_ALPHABET, nanoid };
|
|
73
|
+
//# sourceMappingURL=nanoid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanoid.mjs","names":["randomBytes: Uint8Array | undefined","bytes: Uint8Array","nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":["/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */\nexport const URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'\n\nconst DEFAULT_SIZE = 21\nconst MAX_SIZE = 2048\nconst NANOID_REGEX = /^[A-Za-z0-9_-]+$/\n\nexport type NanoidOptions = {\n /**\n * Random bytes for deterministic output (testing).\n * Must provide enough bytes for rejection sampling (~size * 2).\n */\n random?: Uint8Array\n /**\n * Custom alphabet to use. Default: URL-safe A-Za-z0-9_-\n * Must be 2-256 printable ASCII characters (32-126) with no duplicates.\n */\n alphabet?: string\n /**\n * Length of generated ID. Default: 21. Maximum: 2048.\n */\n size?: number\n}\n\nexport type Nanoid = {\n /** Generate nanoid with default settings */\n (): string\n /** Generate nanoid with custom size */\n (size: number): string\n /** Generate nanoid with options */\n (options: NanoidOptions): string\n /**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\n isValid(id: unknown): id is string\n}\n\n/**\n * Validate alphabet: 2-256 printable ASCII chars, no duplicates\n */\nfunction validateAlphabet(alphabet: string): void {\n if (alphabet.length < 2) {\n throw new Error('Alphabet must contain at least 2 characters')\n }\n if (alphabet.length > 256) {\n throw new Error('Alphabet must not exceed 256 characters')\n }\n const seen = new Set<string>()\n for (const char of alphabet) {\n const code = char.charCodeAt(0)\n if (code < 32 || code > 126) {\n throw new Error('Alphabet must contain only printable ASCII characters (32-126)')\n }\n if (seen.has(char)) {\n throw new Error(`Duplicate character in alphabet: \"${char}\"`)\n }\n seen.add(char)\n }\n}\n\n/**\n * Validate size parameter\n */\nfunction validateSize(size: number): void {\n if (!Number.isInteger(size) || size < 0) {\n throw new RangeError('Size must be a non-negative integer')\n }\n if (size > MAX_SIZE) {\n throw new RangeError(`Size must not exceed ${MAX_SIZE}`)\n }\n}\n\n// Overloads\nfunction nanoidFn(): string\nfunction nanoidFn(size: number): string\nfunction nanoidFn(options: NanoidOptions): string\nfunction nanoidFn(sizeOrOptions?: number | NanoidOptions): string {\n let size = DEFAULT_SIZE\n let alphabet = URL_ALPHABET\n let randomBytes: Uint8Array | undefined\n\n if (typeof sizeOrOptions === 'number') {\n size = sizeOrOptions\n } else if (sizeOrOptions) {\n size = sizeOrOptions.size ?? DEFAULT_SIZE\n alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET\n randomBytes = sizeOrOptions.random\n if (sizeOrOptions.alphabet) {\n validateAlphabet(alphabet)\n }\n }\n\n validateSize(size)\n\n if (size === 0) return ''\n\n // Calculate mask: smallest power-of-2 minus 1 that covers alphabet size\n const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1\n // Calculate step: random bytes per batch (1.6x accounts for rejection)\n const step = Math.ceil((1.6 * mask * size) / alphabet.length)\n\n let id = ''\n let randomOffset = 0\n\n while (id.length < size) {\n let bytes: Uint8Array\n if (randomBytes) {\n if (randomBytes.length - randomOffset < step) {\n throw new Error(\n `Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`,\n )\n }\n bytes = randomBytes.subarray(randomOffset, randomOffset + step)\n randomOffset += step\n } else {\n bytes = globalThis.crypto.getRandomValues(new Uint8Array(step))\n }\n\n for (let i = 0; i < bytes.length && id.length < size; i++) {\n const index = bytes[i] & mask\n if (index < alphabet.length) {\n id += alphabet[index]\n }\n // Otherwise reject and continue (no modulo bias)\n }\n }\n\n return id\n}\n\n/**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && id.length > 0 && NANOID_REGEX.test(id)\n}\n\n/**\n * Generate a URL-friendly unique string ID.\n */\nexport const nanoid: Nanoid = Object.assign(nanoidFn, {\n isValid,\n})\n"],"mappings":";;AACA,MAAa,eAAe;AAE5B,MAAM,eAAe;AACrB,MAAM,WAAW;AACjB,MAAM,eAAe;;;;AAoCrB,SAAS,iBAAiB,UAAwB;AAChD,KAAI,SAAS,SAAS,EACpB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,KAAI,SAAS,SAAS,IACpB,OAAM,IAAI,MAAM,0CAA0C;CAE5D,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,OAAO,KAAK,WAAW,EAAE;AAC/B,MAAI,OAAO,MAAM,OAAO,IACtB,OAAM,IAAI,MAAM,iEAAiE;AAEnF,MAAI,KAAK,IAAI,KAAK,CAChB,OAAM,IAAI,MAAM,qCAAqC,KAAK,GAAG;AAE/D,OAAK,IAAI,KAAK;;;;;;AAOlB,SAAS,aAAa,MAAoB;AACxC,KAAI,CAAC,OAAO,UAAU,KAAK,IAAI,OAAO,EACpC,OAAM,IAAI,WAAW,sCAAsC;AAE7D,KAAI,OAAO,SACT,OAAM,IAAI,WAAW,wBAAwB,WAAW;;AAQ5D,SAAS,SAAS,eAAgD;CAChE,IAAI,OAAO;CACX,IAAI,WAAW;CACf,IAAIA;AAEJ,KAAI,OAAO,kBAAkB,SAC3B,QAAO;UACE,eAAe;AACxB,SAAO,cAAc,QAAQ;AAC7B,aAAW,cAAc,YAAY;AACrC,gBAAc,cAAc;AAC5B,MAAI,cAAc,SAChB,kBAAiB,SAAS;;AAI9B,cAAa,KAAK;AAElB,KAAI,SAAS,EAAG,QAAO;CAGvB,MAAM,QAAQ,KAAM,KAAK,KAAK,MAAO,SAAS,SAAS,IAAK,EAAE,IAAK;CAEnE,MAAM,OAAO,KAAK,KAAM,MAAM,OAAO,OAAQ,SAAS,OAAO;CAE7D,IAAI,KAAK;CACT,IAAI,eAAe;AAEnB,QAAO,GAAG,SAAS,MAAM;EACvB,IAAIC;AACJ,MAAI,aAAa;AACf,OAAI,YAAY,SAAS,eAAe,KACtC,OAAM,IAAI,MACR,4CAA4C,KAAK,cAAc,YAAY,SAAS,eACrF;AAEH,WAAQ,YAAY,SAAS,cAAc,eAAe,KAAK;AAC/D,mBAAgB;QAEhB,SAAQ,WAAW,OAAO,gBAAgB,IAAI,WAAW,KAAK,CAAC;AAGjE,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,UAAU,GAAG,SAAS,MAAM,KAAK;GACzD,MAAM,QAAQ,MAAM,KAAK;AACzB,OAAI,QAAQ,SAAS,OACnB,OAAM,SAAS;;;AAMrB,QAAO;;;;;;AAOT,SAAS,QAAQ,IAA2B;AAC1C,QAAO,OAAO,OAAO,YAAY,GAAG,SAAS,KAAK,aAAa,KAAK,GAAG;;;;;AAMzE,MAAaC,SAAiB,OAAO,OAAO,UAAU,EACpD,SACD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/uuid/common/random.ts
|
|
2
|
+
function randomBytes(out) {
|
|
3
|
+
globalThis.crypto.getRandomValues(out);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Generate 16 bytes of cryptographically strong random data.
|
|
7
|
+
* Used internally by UUID generators.
|
|
8
|
+
*/
|
|
9
|
+
function rng() {
|
|
10
|
+
const bytes = new Uint8Array(16);
|
|
11
|
+
randomBytes(bytes);
|
|
12
|
+
return bytes;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { rng as t };
|
|
17
|
+
//# sourceMappingURL=random-Dul5R6oJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"random-Dul5R6oJ.mjs","names":[],"sources":["../src/uuid/common/random.ts"],"sourcesContent":["export function randomBytes(out: Uint8Array): void {\n const crypto = globalThis.crypto\n crypto.getRandomValues(out)\n}\n\n/**\n * Generate 16 bytes of cryptographically strong random data.\n * Used internally by UUID generators.\n */\nexport function rng(): Uint8Array {\n const bytes = new Uint8Array(16)\n randomBytes(bytes)\n return bytes\n}\n"],"mappings":";AAAA,SAAgB,YAAY,KAAuB;AAEjD,CADe,WAAW,OACnB,gBAAgB,IAAI;;;;;;AAO7B,SAAgB,MAAkB;CAChC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAY,MAAM;AAClB,QAAO"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/ulid/ulid.d.ts
|
|
2
|
+
type UlidOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* 16 bytes of random data to use for ULID generation.
|
|
5
|
+
* Only the first 10 bytes are used.
|
|
6
|
+
*/
|
|
7
|
+
random?: Uint8Array;
|
|
8
|
+
/**
|
|
9
|
+
* Timestamp in milliseconds since Unix epoch.
|
|
10
|
+
* Defaults to Date.now().
|
|
11
|
+
*/
|
|
12
|
+
msecs?: number;
|
|
13
|
+
};
|
|
14
|
+
type Ulid = {
|
|
15
|
+
(): string;
|
|
16
|
+
<TBuf extends Uint8Array = Uint8Array>(options: UlidOptions | undefined, buf: TBuf, offset?: number): TBuf;
|
|
17
|
+
(options?: UlidOptions, buf?: undefined, offset?: number): string;
|
|
18
|
+
toBytes(id: string): Uint8Array;
|
|
19
|
+
fromBytes(bytes: Uint8Array): string;
|
|
20
|
+
timestamp(id: string): number;
|
|
21
|
+
isValid(id: string): boolean;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Generate a ULID string or write the bytes into a buffer.
|
|
25
|
+
* It also includes helpers to convert to and from byte arrays.
|
|
26
|
+
*/
|
|
27
|
+
declare const ulid: Ulid;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { Ulid, UlidOptions, ulid };
|
|
30
|
+
//# sourceMappingURL=ulid.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulid.d.mts","names":["ulid: Ulid"],"sources":["../../src/ulid/ulid.ts"],"sourcesContent":[],"mappings":";KAGY,WAAA;EAAZ;AAaA;;;EAEkD,MAAA,CAAA,EAVvC,UAUuC;EAA8B;;;;EAG7D,KAAA,CAAA,EAAA,MAAA;CAAA;AA4HNA,KAjID,IAAA,GAiIO;;gBA/HH,aAAa,qBAAqB,8BAA8B,wBAAwB;aAC3F;uBACU;mBACJ;;;;;;;;cA4HNA,MAAM"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { t as rng } from "../random-Dul5R6oJ.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/ulid/common/crockford.ts
|
|
4
|
+
const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
5
|
+
const DECODING = new Uint8Array(128);
|
|
6
|
+
DECODING.fill(255);
|
|
7
|
+
for (let i = 0; i < 32; i += 1) {
|
|
8
|
+
const upper = ENCODING.charCodeAt(i);
|
|
9
|
+
const lower = ENCODING[i].toLowerCase().charCodeAt(0);
|
|
10
|
+
DECODING[upper] = i;
|
|
11
|
+
DECODING[lower] = i;
|
|
12
|
+
}
|
|
13
|
+
const TIME_LEN = 10;
|
|
14
|
+
/**
|
|
15
|
+
* Encode a 48-bit timestamp to a 10-character Crockford Base32 string.
|
|
16
|
+
* Uses unrolled division for performance.
|
|
17
|
+
*/
|
|
18
|
+
function encodeTime(time) {
|
|
19
|
+
return ENCODING[Math.floor(time / 35184372088832) & 31] + ENCODING[Math.floor(time / 1099511627776) & 31] + ENCODING[Math.floor(time / 34359738368) & 31] + ENCODING[Math.floor(time / 1073741824) & 31] + ENCODING[Math.floor(time / 33554432) & 31] + ENCODING[Math.floor(time / 1048576) & 31] + ENCODING[Math.floor(time / 32768) & 31] + ENCODING[Math.floor(time / 1024) & 31] + ENCODING[Math.floor(time / 32) & 31] + ENCODING[time & 31];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Encode 10 bytes (80 bits) of random data to a 16-character Crockford Base32 string.
|
|
23
|
+
*/
|
|
24
|
+
function encodeRandom(bytes) {
|
|
25
|
+
return ENCODING[bytes[0] >> 3 & 31] + ENCODING[(bytes[0] << 2 | bytes[1] >> 6) & 31] + ENCODING[bytes[1] >> 1 & 31] + ENCODING[(bytes[1] << 4 | bytes[2] >> 4) & 31] + ENCODING[(bytes[2] << 1 | bytes[3] >> 7) & 31] + ENCODING[bytes[3] >> 2 & 31] + ENCODING[(bytes[3] << 3 | bytes[4] >> 5) & 31] + ENCODING[bytes[4] & 31] + ENCODING[bytes[5] >> 3 & 31] + ENCODING[(bytes[5] << 2 | bytes[6] >> 6) & 31] + ENCODING[bytes[6] >> 1 & 31] + ENCODING[(bytes[6] << 4 | bytes[7] >> 4) & 31] + ENCODING[(bytes[7] << 1 | bytes[8] >> 7) & 31] + ENCODING[bytes[8] >> 2 & 31] + ENCODING[(bytes[8] << 3 | bytes[9] >> 5) & 31] + ENCODING[bytes[9] & 31];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Decode the first 10 characters of a ULID string to a timestamp (Unix epoch milliseconds).
|
|
29
|
+
*/
|
|
30
|
+
function decodeTime(str) {
|
|
31
|
+
let time = 0;
|
|
32
|
+
for (let i = 0; i < TIME_LEN; i += 1) {
|
|
33
|
+
const value = DECODING[str.charCodeAt(i)];
|
|
34
|
+
if (value === 255) throw new Error(`Invalid ULID character: ${str[i]}`);
|
|
35
|
+
time = time * 32 + value;
|
|
36
|
+
}
|
|
37
|
+
return time;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Decode a 26-character ULID string to 16 bytes.
|
|
41
|
+
* Inlines all lookups to avoid intermediate array allocation.
|
|
42
|
+
*/
|
|
43
|
+
function decodeToBytes(str) {
|
|
44
|
+
if (str.length !== 26) throw new Error("ULID string must be 26 characters");
|
|
45
|
+
const bytes = new Uint8Array(16);
|
|
46
|
+
const v0 = DECODING[str.charCodeAt(0)];
|
|
47
|
+
const v1 = DECODING[str.charCodeAt(1)];
|
|
48
|
+
const v2 = DECODING[str.charCodeAt(2)];
|
|
49
|
+
const v3 = DECODING[str.charCodeAt(3)];
|
|
50
|
+
const v4 = DECODING[str.charCodeAt(4)];
|
|
51
|
+
const v5 = DECODING[str.charCodeAt(5)];
|
|
52
|
+
const v6 = DECODING[str.charCodeAt(6)];
|
|
53
|
+
const v7 = DECODING[str.charCodeAt(7)];
|
|
54
|
+
const v8 = DECODING[str.charCodeAt(8)];
|
|
55
|
+
const v9 = DECODING[str.charCodeAt(9)];
|
|
56
|
+
const v10 = DECODING[str.charCodeAt(10)];
|
|
57
|
+
const v11 = DECODING[str.charCodeAt(11)];
|
|
58
|
+
const v12 = DECODING[str.charCodeAt(12)];
|
|
59
|
+
const v13 = DECODING[str.charCodeAt(13)];
|
|
60
|
+
const v14 = DECODING[str.charCodeAt(14)];
|
|
61
|
+
const v15 = DECODING[str.charCodeAt(15)];
|
|
62
|
+
const v16 = DECODING[str.charCodeAt(16)];
|
|
63
|
+
const v17 = DECODING[str.charCodeAt(17)];
|
|
64
|
+
const v18 = DECODING[str.charCodeAt(18)];
|
|
65
|
+
const v19 = DECODING[str.charCodeAt(19)];
|
|
66
|
+
const v20 = DECODING[str.charCodeAt(20)];
|
|
67
|
+
const v21 = DECODING[str.charCodeAt(21)];
|
|
68
|
+
const v22 = DECODING[str.charCodeAt(22)];
|
|
69
|
+
const v23 = DECODING[str.charCodeAt(23)];
|
|
70
|
+
const v24 = DECODING[str.charCodeAt(24)];
|
|
71
|
+
const v25 = DECODING[str.charCodeAt(25)];
|
|
72
|
+
if ((v0 | v1 | v2 | v3 | v4 | v5 | v6 | v7 | v8 | v9 | v10 | v11 | v12 | v13 | v14 | v15 | v16 | v17 | v18 | v19 | v20 | v21 | v22 | v23 | v24 | v25) & 128) {
|
|
73
|
+
for (let i = 0; i < 26; i += 1) if (DECODING[str.charCodeAt(i)] === 255) throw new Error(`Invalid ULID character: ${str[i]}`);
|
|
74
|
+
}
|
|
75
|
+
bytes[0] = v0 << 5 | v1;
|
|
76
|
+
bytes[1] = v2 << 3 | v3 >> 2;
|
|
77
|
+
bytes[2] = v3 << 6 | v4 << 1 | v5 >> 4;
|
|
78
|
+
bytes[3] = v5 << 4 | v6 >> 1;
|
|
79
|
+
bytes[4] = v6 << 7 | v7 << 2 | v8 >> 3;
|
|
80
|
+
bytes[5] = v8 << 5 | v9;
|
|
81
|
+
bytes[6] = v10 << 3 | v11 >> 2;
|
|
82
|
+
bytes[7] = v11 << 6 | v12 << 1 | v13 >> 4;
|
|
83
|
+
bytes[8] = v13 << 4 | v14 >> 1;
|
|
84
|
+
bytes[9] = v14 << 7 | v15 << 2 | v16 >> 3;
|
|
85
|
+
bytes[10] = v16 << 5 | v17;
|
|
86
|
+
bytes[11] = v18 << 3 | v19 >> 2;
|
|
87
|
+
bytes[12] = v19 << 6 | v20 << 1 | v21 >> 4;
|
|
88
|
+
bytes[13] = v21 << 4 | v22 >> 1;
|
|
89
|
+
bytes[14] = v22 << 7 | v23 << 2 | v24 >> 3;
|
|
90
|
+
bytes[15] = v24 << 5 | v25;
|
|
91
|
+
return bytes;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Encode 16 bytes to a 26-character ULID string.
|
|
95
|
+
*/
|
|
96
|
+
function bytesToUlid(bytes) {
|
|
97
|
+
if (bytes.length < 16) throw new Error("Byte array must be at least 16 bytes");
|
|
98
|
+
let time = 0;
|
|
99
|
+
for (let i = 0; i < 6; i += 1) time = time * 256 + bytes[i];
|
|
100
|
+
return encodeTime(time) + encodeRandom(bytes.subarray(6, 16));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/ulid/ulid.ts
|
|
105
|
+
const ULID_REGEX = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i;
|
|
106
|
+
/**
|
|
107
|
+
* Module-level state for maintaining monotonic ordering within the same millisecond.
|
|
108
|
+
*
|
|
109
|
+
* IMPORTANT: This state persists across all ulid() calls in the module's lifetime.
|
|
110
|
+
* - In serverless/edge functions with warm starts, state persists between invocations.
|
|
111
|
+
* - For isolated state, pass explicit `msecs` and `random` via options.
|
|
112
|
+
* - Tests should mock Date.now() or provide explicit options for deterministic behavior.
|
|
113
|
+
*/
|
|
114
|
+
const state = {
|
|
115
|
+
msecs: -Infinity,
|
|
116
|
+
lastRandom: new Uint8Array(10)
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Increment a byte array by 1, propagating carry from LSB to MSB.
|
|
120
|
+
* Returns a new array (does not modify input).
|
|
121
|
+
*/
|
|
122
|
+
function incrementBytes(bytes) {
|
|
123
|
+
const result = new Uint8Array(bytes);
|
|
124
|
+
for (let i = result.length - 1; i >= 0; i -= 1) {
|
|
125
|
+
if (result[i] < 255) {
|
|
126
|
+
result[i] += 1;
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
result[i] = 0;
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
function ulidBytes(time, random, buf, offset = 0) {
|
|
134
|
+
if (!buf) {
|
|
135
|
+
buf = new Uint8Array(16);
|
|
136
|
+
offset = 0;
|
|
137
|
+
} else if (offset < 0 || offset + 16 > buf.length) throw new RangeError(`ULID byte range ${offset}:${offset + 15} is out of buffer bounds`);
|
|
138
|
+
buf[offset] = time / 1099511627776 & 255;
|
|
139
|
+
buf[offset + 1] = time / 4294967296 & 255;
|
|
140
|
+
buf[offset + 2] = time / 16777216 & 255;
|
|
141
|
+
buf[offset + 3] = time / 65536 & 255;
|
|
142
|
+
buf[offset + 4] = time / 256 & 255;
|
|
143
|
+
buf[offset + 5] = time & 255;
|
|
144
|
+
for (let i = 0; i < 10; i += 1) buf[offset + 6 + i] = random[i];
|
|
145
|
+
return buf;
|
|
146
|
+
}
|
|
147
|
+
function ulidFn(options, buf, offset = 0) {
|
|
148
|
+
let time;
|
|
149
|
+
let random;
|
|
150
|
+
/**
|
|
151
|
+
* Note: by default, Cloudflare Workers "freezes" time during request handling to prevent
|
|
152
|
+
* side-channel attacks. This means that Date.now() will return the same value for the entire
|
|
153
|
+
* duration of a request.
|
|
154
|
+
* Implications:
|
|
155
|
+
* - all ULIDs generated within a single request will have the same timestamp.
|
|
156
|
+
* - the monotonic ordering will rely entirely on incrementing the random portion.
|
|
157
|
+
*/
|
|
158
|
+
const defaultTime = Date.now();
|
|
159
|
+
if (options) {
|
|
160
|
+
time = options.msecs ?? defaultTime;
|
|
161
|
+
random = options.random ?? rng();
|
|
162
|
+
} else {
|
|
163
|
+
time = defaultTime;
|
|
164
|
+
if (time > state.msecs) {
|
|
165
|
+
random = rng();
|
|
166
|
+
state.msecs = time;
|
|
167
|
+
state.lastRandom.set(random.subarray(0, 10));
|
|
168
|
+
} else {
|
|
169
|
+
random = incrementBytes(state.lastRandom);
|
|
170
|
+
state.lastRandom = random;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (buf) {
|
|
174
|
+
ulidBytes(time, random, buf, offset);
|
|
175
|
+
return buf;
|
|
176
|
+
}
|
|
177
|
+
return encodeTime(time) + encodeRandom(random);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Generate a ULID string or write the bytes into a buffer.
|
|
181
|
+
* It also includes helpers to convert to and from byte arrays.
|
|
182
|
+
*/
|
|
183
|
+
const ulid = Object.assign(ulidFn, {
|
|
184
|
+
toBytes: (id) => decodeToBytes(id),
|
|
185
|
+
fromBytes: (bytes) => bytesToUlid(bytes),
|
|
186
|
+
timestamp: (id) => decodeTime(id.slice(0, 10)),
|
|
187
|
+
isValid: (id) => ULID_REGEX.test(id)
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
export { ulid };
|
|
192
|
+
//# sourceMappingURL=ulid.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ulid.mjs","names":["state: UlidState","time: number","random: Uint8Array","ulid: Ulid"],"sources":["../../src/ulid/common/crockford.ts","../../src/ulid/ulid.ts"],"sourcesContent":["// Crockford's Base32 alphabet (excludes I, L, O, U to avoid confusion)\nconst ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'\n\n// Pre-computed decoding table indexed by ASCII code (0-127)\n// Uses Uint8Array for cache-efficient lookups via charCodeAt\nconst DECODING = new Uint8Array(128)\nDECODING.fill(255) // 255 = invalid marker\nfor (let i = 0; i < ENCODING.length; i += 1) {\n const upper = ENCODING.charCodeAt(i)\n const lower = ENCODING[i].toLowerCase().charCodeAt(0)\n DECODING[upper] = i\n DECODING[lower] = i\n}\n\nconst TIME_LEN = 10\n\n/**\n * Encode a 48-bit timestamp to a 10-character Crockford Base32 string.\n * Uses unrolled division for performance.\n */\nexport function encodeTime(time: number): string {\n // Unrolled encoding - each step divides by 32 and extracts 5 bits\n // Powers of 32: 32^9=0x200000000000, 32^8=0x10000000000, etc.\n return (\n ENCODING[Math.floor(time / 0x200000000000) & 0x1f] +\n ENCODING[Math.floor(time / 0x10000000000) & 0x1f] +\n ENCODING[Math.floor(time / 0x800000000) & 0x1f] +\n ENCODING[Math.floor(time / 0x40000000) & 0x1f] +\n ENCODING[Math.floor(time / 0x2000000) & 0x1f] +\n ENCODING[Math.floor(time / 0x100000) & 0x1f] +\n ENCODING[Math.floor(time / 0x8000) & 0x1f] +\n ENCODING[Math.floor(time / 0x400) & 0x1f] +\n ENCODING[Math.floor(time / 0x20) & 0x1f] +\n ENCODING[time & 0x1f]\n )\n}\n\n/**\n * Encode 10 bytes (80 bits) of random data to a 16-character Crockford Base32 string.\n */\nexport function encodeRandom(bytes: Uint8Array): string {\n // Each character encodes 5 bits. 80 bits = 16 characters.\n // Single concatenation expression for optimal performance.\n return (\n ENCODING[(bytes[0] >> 3) & 0x1f] +\n ENCODING[((bytes[0] << 2) | (bytes[1] >> 6)) & 0x1f] +\n ENCODING[(bytes[1] >> 1) & 0x1f] +\n ENCODING[((bytes[1] << 4) | (bytes[2] >> 4)) & 0x1f] +\n ENCODING[((bytes[2] << 1) | (bytes[3] >> 7)) & 0x1f] +\n ENCODING[(bytes[3] >> 2) & 0x1f] +\n ENCODING[((bytes[3] << 3) | (bytes[4] >> 5)) & 0x1f] +\n ENCODING[bytes[4] & 0x1f] +\n ENCODING[(bytes[5] >> 3) & 0x1f] +\n ENCODING[((bytes[5] << 2) | (bytes[6] >> 6)) & 0x1f] +\n ENCODING[(bytes[6] >> 1) & 0x1f] +\n ENCODING[((bytes[6] << 4) | (bytes[7] >> 4)) & 0x1f] +\n ENCODING[((bytes[7] << 1) | (bytes[8] >> 7)) & 0x1f] +\n ENCODING[(bytes[8] >> 2) & 0x1f] +\n ENCODING[((bytes[8] << 3) | (bytes[9] >> 5)) & 0x1f] +\n ENCODING[bytes[9] & 0x1f]\n )\n}\n\n/**\n * Decode the first 10 characters of a ULID string to a timestamp (Unix epoch milliseconds).\n */\nexport function decodeTime(str: string): number {\n let time = 0\n for (let i = 0; i < TIME_LEN; i += 1) {\n const value = DECODING[str.charCodeAt(i)]\n if (value === 255) {\n throw new Error(`Invalid ULID character: ${str[i]}`)\n }\n time = time * 32 + value\n }\n return time\n}\n\n/**\n * Decode a 26-character ULID string to 16 bytes.\n * Inlines all lookups to avoid intermediate array allocation.\n */\nexport function decodeToBytes(str: string): Uint8Array {\n if (str.length !== 26) {\n throw new Error('ULID string must be 26 characters')\n }\n\n const bytes = new Uint8Array(16)\n\n // Inline all 26 character lookups\n const v0 = DECODING[str.charCodeAt(0)]\n const v1 = DECODING[str.charCodeAt(1)]\n const v2 = DECODING[str.charCodeAt(2)]\n const v3 = DECODING[str.charCodeAt(3)]\n const v4 = DECODING[str.charCodeAt(4)]\n const v5 = DECODING[str.charCodeAt(5)]\n const v6 = DECODING[str.charCodeAt(6)]\n const v7 = DECODING[str.charCodeAt(7)]\n const v8 = DECODING[str.charCodeAt(8)]\n const v9 = DECODING[str.charCodeAt(9)]\n const v10 = DECODING[str.charCodeAt(10)]\n const v11 = DECODING[str.charCodeAt(11)]\n const v12 = DECODING[str.charCodeAt(12)]\n const v13 = DECODING[str.charCodeAt(13)]\n const v14 = DECODING[str.charCodeAt(14)]\n const v15 = DECODING[str.charCodeAt(15)]\n const v16 = DECODING[str.charCodeAt(16)]\n const v17 = DECODING[str.charCodeAt(17)]\n const v18 = DECODING[str.charCodeAt(18)]\n const v19 = DECODING[str.charCodeAt(19)]\n const v20 = DECODING[str.charCodeAt(20)]\n const v21 = DECODING[str.charCodeAt(21)]\n const v22 = DECODING[str.charCodeAt(22)]\n const v23 = DECODING[str.charCodeAt(23)]\n const v24 = DECODING[str.charCodeAt(24)]\n const v25 = DECODING[str.charCodeAt(25)]\n\n // Validate all characters (255 = invalid marker)\n if (\n (v0 |\n v1 |\n v2 |\n v3 |\n v4 |\n v5 |\n v6 |\n v7 |\n v8 |\n v9 |\n v10 |\n v11 |\n v12 |\n v13 |\n v14 |\n v15 |\n v16 |\n v17 |\n v18 |\n v19 |\n v20 |\n v21 |\n v22 |\n v23 |\n v24 |\n v25) &\n 0x80\n ) {\n // Find the invalid character for error message\n for (let i = 0; i < 26; i += 1) {\n if (DECODING[str.charCodeAt(i)] === 255) {\n throw new Error(`Invalid ULID character: ${str[i]}`)\n }\n }\n }\n\n // Timestamp: first 10 characters -> bytes 0-5\n bytes[0] = (v0 << 5) | v1\n bytes[1] = (v2 << 3) | (v3 >> 2)\n bytes[2] = (v3 << 6) | (v4 << 1) | (v5 >> 4)\n bytes[3] = (v5 << 4) | (v6 >> 1)\n bytes[4] = (v6 << 7) | (v7 << 2) | (v8 >> 3)\n bytes[5] = (v8 << 5) | v9\n\n // Random: last 16 characters -> bytes 6-15\n bytes[6] = (v10 << 3) | (v11 >> 2)\n bytes[7] = (v11 << 6) | (v12 << 1) | (v13 >> 4)\n bytes[8] = (v13 << 4) | (v14 >> 1)\n bytes[9] = (v14 << 7) | (v15 << 2) | (v16 >> 3)\n bytes[10] = (v16 << 5) | v17\n bytes[11] = (v18 << 3) | (v19 >> 2)\n bytes[12] = (v19 << 6) | (v20 << 1) | (v21 >> 4)\n bytes[13] = (v21 << 4) | (v22 >> 1)\n bytes[14] = (v22 << 7) | (v23 << 2) | (v24 >> 3)\n bytes[15] = (v24 << 5) | v25\n\n return bytes\n}\n\n/**\n * Encode 16 bytes to a 26-character ULID string.\n */\nexport function bytesToUlid(bytes: Uint8Array): string {\n if (bytes.length < 16) {\n throw new Error('Byte array must be at least 16 bytes')\n }\n\n // Timestamp: bytes 0-5 -> 10 characters\n let time = 0\n for (let i = 0; i < 6; i += 1) {\n time = time * 256 + bytes[i]\n }\n const timeStr = encodeTime(time)\n\n // Random: bytes 6-15 -> 16 characters\n const randomStr = encodeRandom(bytes.subarray(6, 16))\n\n return timeStr + randomStr\n}\n","import { rng } from '../uuid/common/random'\nimport { bytesToUlid, decodeTime, decodeToBytes, encodeRandom, encodeTime } from './common/crockford'\n\nexport type UlidOptions = {\n /**\n * 16 bytes of random data to use for ULID generation.\n * Only the first 10 bytes are used.\n */\n random?: Uint8Array\n /**\n * Timestamp in milliseconds since Unix epoch.\n * Defaults to Date.now().\n */\n msecs?: number\n}\n\nexport type Ulid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: UlidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: UlidOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: string): boolean\n}\n\n// Validation regex: first char [0-7] to prevent overflow, rest from Crockford alphabet\nconst ULID_REGEX = /^[0-7][0-9A-HJKMNP-TV-Z]{25}$/i\n\ntype UlidState = {\n msecs: number\n lastRandom: Uint8Array\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same millisecond.\n *\n * IMPORTANT: This state persists across all ulid() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs` and `random` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: UlidState = {\n msecs: -Infinity,\n lastRandom: new Uint8Array(10),\n}\n\n/**\n * Increment a byte array by 1, propagating carry from LSB to MSB.\n * Returns a new array (does not modify input).\n */\nfunction incrementBytes(bytes: Uint8Array): Uint8Array {\n const result = new Uint8Array(bytes)\n for (let i = result.length - 1; i >= 0; i -= 1) {\n if (result[i] < 255) {\n result[i] += 1\n return result\n }\n result[i] = 0\n }\n return result\n}\n\nfunction ulidBytes(time: number, random: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(16)\n offset = 0\n } else if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`ULID byte range ${offset}:${offset + 15} is out of buffer bounds`)\n }\n\n // Timestamp (48-bit big-endian milliseconds since Unix epoch) -> bytes 0-5\n buf[offset] = (time / 0x10000000000) & 0xff\n buf[offset + 1] = (time / 0x100000000) & 0xff\n buf[offset + 2] = (time / 0x1000000) & 0xff\n buf[offset + 3] = (time / 0x10000) & 0xff\n buf[offset + 4] = (time / 0x100) & 0xff\n buf[offset + 5] = time & 0xff\n\n // Random (80 bits) -> bytes 6-15\n for (let i = 0; i < 10; i += 1) {\n buf[offset + 6 + i] = random[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a ULID string.\n */\nfunction ulidFn(options?: UlidOptions, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with ULID bytes.\n */\nfunction ulidFn<TBuf extends Uint8Array = Uint8Array>(\n options: UlidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction ulidFn<TBuf extends Uint8Array = Uint8Array>(options?: UlidOptions, buf?: TBuf, offset = 0): string | TBuf {\n let time: number\n let random: Uint8Array\n\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all ULIDs generated within a single request will have the same timestamp.\n * - the monotonic ordering will rely entirely on incrementing the random portion.\n */\n const defaultTime = Date.now()\n\n if (options) {\n // Explicit options provided - use them directly without monotonic state\n time = options.msecs ?? defaultTime\n random = options.random ?? rng()\n } else {\n time = defaultTime\n\n if (time > state.msecs) {\n // New millisecond: generate fresh random\n random = rng()\n state.msecs = time\n state.lastRandom.set(random.subarray(0, 10))\n } else {\n // Same millisecond: increment random portion for monotonic ordering\n random = incrementBytes(state.lastRandom)\n state.lastRandom = random\n }\n }\n\n if (buf) {\n ulidBytes(time, random, buf, offset)\n return buf\n }\n\n // String mode: encode directly without buffer allocation\n return encodeTime(time) + encodeRandom(random)\n}\n\n/**\n * Generate a ULID string or write the bytes into a buffer.\n * It also includes helpers to convert to and from byte arrays.\n */\nexport const ulid: Ulid = Object.assign(ulidFn, {\n toBytes: (id: string) => decodeToBytes(id),\n fromBytes: (bytes: Uint8Array) => bytesToUlid(bytes),\n timestamp: (id: string) => decodeTime(id.slice(0, 10)),\n isValid: (id: string) => ULID_REGEX.test(id),\n})\n"],"mappings":";;;AACA,MAAM,WAAW;AAIjB,MAAM,WAAW,IAAI,WAAW,IAAI;AACpC,SAAS,KAAK,IAAI;AAClB,KAAK,IAAI,IAAI,GAAG,IAAI,IAAiB,KAAK,GAAG;CAC3C,MAAM,QAAQ,SAAS,WAAW,EAAE;CACpC,MAAM,QAAQ,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE;AACrD,UAAS,SAAS;AAClB,UAAS,SAAS;;AAGpB,MAAM,WAAW;;;;;AAMjB,SAAgB,WAAW,MAAsB;AAG/C,QACE,SAAS,KAAK,MAAM,OAAO,eAAe,GAAG,MAC7C,SAAS,KAAK,MAAM,OAAO,cAAc,GAAG,MAC5C,SAAS,KAAK,MAAM,OAAO,YAAY,GAAG,MAC1C,SAAS,KAAK,MAAM,OAAO,WAAW,GAAG,MACzC,SAAS,KAAK,MAAM,OAAO,SAAU,GAAG,MACxC,SAAS,KAAK,MAAM,OAAO,QAAS,GAAG,MACvC,SAAS,KAAK,MAAM,OAAO,MAAO,GAAG,MACrC,SAAS,KAAK,MAAM,OAAO,KAAM,GAAG,MACpC,SAAS,KAAK,MAAM,OAAO,GAAK,GAAG,MACnC,SAAS,OAAO;;;;;AAOpB,SAAgB,aAAa,OAA2B;AAGtD,QACE,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAS,MAAM,KAAK,MACpB,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAU,MAAM,MAAM,IAAK,MAC3B,UAAW,MAAM,MAAM,IAAM,MAAM,MAAM,KAAM,MAC/C,SAAS,MAAM,KAAK;;;;;AAOxB,SAAgB,WAAW,KAAqB;CAC9C,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,UAAU,KAAK,GAAG;EACpC,MAAM,QAAQ,SAAS,IAAI,WAAW,EAAE;AACxC,MAAI,UAAU,IACZ,OAAM,IAAI,MAAM,2BAA2B,IAAI,KAAK;AAEtD,SAAO,OAAO,KAAK;;AAErB,QAAO;;;;;;AAOT,SAAgB,cAAc,KAAyB;AACrD,KAAI,IAAI,WAAW,GACjB,OAAM,IAAI,MAAM,oCAAoC;CAGtD,MAAM,QAAQ,IAAI,WAAW,GAAG;CAGhC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,KAAK,SAAS,IAAI,WAAW,EAAE;CACrC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;CACvC,MAAM,MAAM,SAAS,IAAI,WAAW,GAAG;AAGvC,MACG,KACC,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,KACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,MACA,OACF,KAGA;OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,EAC3B,KAAI,SAAS,IAAI,WAAW,EAAE,MAAM,IAClC,OAAM,IAAI,MAAM,2BAA2B,IAAI,KAAK;;AAM1D,OAAM,KAAM,MAAM,IAAK;AACvB,OAAM,KAAM,MAAM,IAAM,MAAM;AAC9B,OAAM,KAAM,MAAM,IAAM,MAAM,IAAM,MAAM;AAC1C,OAAM,KAAM,MAAM,IAAM,MAAM;AAC9B,OAAM,KAAM,MAAM,IAAM,MAAM,IAAM,MAAM;AAC1C,OAAM,KAAM,MAAM,IAAK;AAGvB,OAAM,KAAM,OAAO,IAAM,OAAO;AAChC,OAAM,KAAM,OAAO,IAAM,OAAO,IAAM,OAAO;AAC7C,OAAM,KAAM,OAAO,IAAM,OAAO;AAChC,OAAM,KAAM,OAAO,IAAM,OAAO,IAAM,OAAO;AAC7C,OAAM,MAAO,OAAO,IAAK;AACzB,OAAM,MAAO,OAAO,IAAM,OAAO;AACjC,OAAM,MAAO,OAAO,IAAM,OAAO,IAAM,OAAO;AAC9C,OAAM,MAAO,OAAO,IAAM,OAAO;AACjC,OAAM,MAAO,OAAO,IAAM,OAAO,IAAM,OAAO;AAC9C,OAAM,MAAO,OAAO,IAAK;AAEzB,QAAO;;;;;AAMT,SAAgB,YAAY,OAA2B;AACrD,KAAI,MAAM,SAAS,GACjB,OAAM,IAAI,MAAM,uCAAuC;CAIzD,IAAI,OAAO;AACX,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,EAC1B,QAAO,OAAO,MAAM,MAAM;AAO5B,QALgB,WAAW,KAAK,GAGd,aAAa,MAAM,SAAS,GAAG,GAAG,CAAC;;;;;ACvKvD,MAAM,aAAa;;;;;;;;;AAenB,MAAMA,QAAmB;CACvB,OAAO;CACP,YAAY,IAAI,WAAW,GAAG;CAC/B;;;;;AAMD,SAAS,eAAe,OAA+B;CACrD,MAAM,SAAS,IAAI,WAAW,MAAM;AACpC,MAAK,IAAI,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC9C,MAAI,OAAO,KAAK,KAAK;AACnB,UAAO,MAAM;AACb,UAAO;;AAET,SAAO,KAAK;;AAEd,QAAO;;AAGT,SAAS,UAAU,MAAc,QAAoB,KAAkB,SAAS,GAAe;AAC7F,KAAI,CAAC,KAAK;AACR,QAAM,IAAI,WAAW,GAAG;AACxB,WAAS;YACA,SAAS,KAAK,SAAS,KAAK,IAAI,OACzC,OAAM,IAAI,WAAW,mBAAmB,OAAO,GAAG,SAAS,GAAG,0BAA0B;AAI1F,KAAI,UAAW,OAAO,gBAAiB;AACvC,KAAI,SAAS,KAAM,OAAO,aAAe;AACzC,KAAI,SAAS,KAAM,OAAO,WAAa;AACvC,KAAI,SAAS,KAAM,OAAO,QAAW;AACrC,KAAI,SAAS,KAAM,OAAO,MAAS;AACnC,KAAI,SAAS,KAAK,OAAO;AAGzB,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,EAC3B,KAAI,SAAS,IAAI,KAAK,OAAO;AAG/B,QAAO;;AAeT,SAAS,OAA6C,SAAuB,KAAY,SAAS,GAAkB;CAClH,IAAIC;CACJ,IAAIC;;;;;;;;;CAUJ,MAAM,cAAc,KAAK,KAAK;AAE9B,KAAI,SAAS;AAEX,SAAO,QAAQ,SAAS;AACxB,WAAS,QAAQ,UAAU,KAAK;QAC3B;AACL,SAAO;AAEP,MAAI,OAAO,MAAM,OAAO;AAEtB,YAAS,KAAK;AACd,SAAM,QAAQ;AACd,SAAM,WAAW,IAAI,OAAO,SAAS,GAAG,GAAG,CAAC;SACvC;AAEL,YAAS,eAAe,MAAM,WAAW;AACzC,SAAM,aAAa;;;AAIvB,KAAI,KAAK;AACP,YAAU,MAAM,QAAQ,KAAK,OAAO;AACpC,SAAO;;AAIT,QAAO,WAAW,KAAK,GAAG,aAAa,OAAO;;;;;;AAOhD,MAAaC,OAAa,OAAO,OAAO,QAAQ;CAC9C,UAAU,OAAe,cAAc,GAAG;CAC1C,YAAY,UAAsB,YAAY,MAAM;CACpD,YAAY,OAAe,WAAW,GAAG,MAAM,GAAG,GAAG,CAAC;CACtD,UAAU,OAAe,WAAW,KAAK,GAAG;CAC7C,CAAC"}
|
|
@@ -12,6 +12,7 @@ type UuidV4 = {
|
|
|
12
12
|
(options?: Version4Options, buf?: undefined, offset?: number): string;
|
|
13
13
|
toBytes(id: string): Uint8Array;
|
|
14
14
|
fromBytes(bytes: Uint8Array): string;
|
|
15
|
+
isValid(id: string): boolean;
|
|
15
16
|
};
|
|
16
17
|
/**
|
|
17
18
|
* Generate a UUID v4 string or write the bytes into a buffer.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.d.mts","names":["uuidv4: UuidV4"],"sources":["../../src/uuid/v4.ts"],"sourcesContent":[],"mappings":";KAGY,eAAA;EAAZ;AAQA;;;EAEkD,MAAA,CAAA,EALvC,UAKuC;CAAkC;AAAwB,KAFhG,MAAA,GAEgG;EAC/F,EAAA,EAAA,MAAA;EACU,CAAA,aAFP,UAEO,GAFM,UAEN,CAAA,CAAA,OAAA,EAF2B,eAE3B,GAAA,SAAA,EAAA,GAAA,EAF6D,IAE7D,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAFqF,IAErF;EACJ,CAAA,OAAA,CAAA,EAFN,eAEM,EAAA,GAAA,CAAA,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;EAAA,OAAA,CAAA,EAAA,EAAA,MAAA,CAAA,EADI,UACJ;EAkEnB,SAAaA,CAAAA,KAAQ,EAlEF,UAkEE,CAAA,EAAA,MAAA;;;;;;;cAARA,QAAQ"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as rng } from "../random-Dul5R6oJ.mjs";
|
|
2
|
+
import { n as parseUuid, t as formatUuid } from "../uuid-cJ5hOOIR.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/uuid/v4.ts
|
|
5
|
+
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
6
|
function v4Bytes(rnds, buf, offset = 0) {
|
|
5
7
|
if (rnds.length < 16) throw new Error("Random bytes length must be >= 16");
|
|
6
8
|
rnds[6] = rnds[6] & 15 | 64;
|
|
@@ -15,13 +17,17 @@ function v4(options, buf, offset) {
|
|
|
15
17
|
const bytes = v4Bytes(options?.random ?? rng(), buf, offset);
|
|
16
18
|
return buf ?? formatUuid(bytes);
|
|
17
19
|
}
|
|
20
|
+
function isValid(id) {
|
|
21
|
+
return UUID_V4_REGEX.test(id);
|
|
22
|
+
}
|
|
18
23
|
/**
|
|
19
24
|
* Generate a UUID v4 string or write the bytes into a buffer.
|
|
20
25
|
* It also includes helpers to convert to and from byte arrays.
|
|
21
26
|
*/
|
|
22
27
|
const uuidv4 = Object.assign(v4, {
|
|
23
|
-
toBytes:
|
|
24
|
-
fromBytes:
|
|
28
|
+
toBytes: parseUuid,
|
|
29
|
+
fromBytes: formatUuid,
|
|
30
|
+
isValid
|
|
25
31
|
});
|
|
26
32
|
|
|
27
33
|
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v4.mjs","names":["uuidv4: UuidV4"],"sources":["../../src/uuid/v4.ts"],"sourcesContent":["import { rng } from './common/random'\nimport { formatUuid, parseUuid } from './common/uuid'\n\nexport type Version4Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Bytes at index 6 and 8 will be modified in-place to set version/variant bits.\n */\n random?: Uint8Array\n}\n\nexport type UuidV4 = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: Version4Options | undefined, buf: TBuf, offset?: number): TBuf\n (options?: Version4Options, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n isValid(id: string): boolean\n}\n\nconst UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\nfunction v4Bytes(rnds: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16')\n }\n\n // Set RFC 4122 version (4) and variant (10xx) bits.\n // Note: This modifies the input array in-place.\n rnds[6] = (rnds[6] & 0x0f) | 0x40\n rnds[8] = (rnds[8] & 0x3f) | 0x80\n\n if (!buf) {\n // No output buffer provided - return the modified random bytes directly\n return rnds\n }\n\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`)\n }\n\n // Copy 16 UUID bytes into the provided buffer slice.\n for (let i = 0; i < 16; i += 1) {\n buf[offset + i] = rnds[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v4(options?: Version4Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v4<TBuf extends Uint8Array = Uint8Array>(\n options: Version4Options | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction v4<TBuf extends Uint8Array = Uint8Array>(\n options?: Version4Options,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n if (globalThis.crypto?.randomUUID && !buf && !options) {\n return globalThis.crypto.randomUUID()\n }\n\n const bytes = v4Bytes(options?.random ?? rng(), buf, offset)\n return buf ?? formatUuid(bytes)\n}\n\nfunction isValid(id: string): boolean {\n return UUID_V4_REGEX.test(id)\n}\n\n/**\n * Generate a UUID v4 string or write the bytes into a buffer.\n * It also includes helpers to convert to and from byte arrays.\n */\nexport const uuidv4: UuidV4 = Object.assign(v4, {\n toBytes: parseUuid,\n fromBytes: formatUuid,\n isValid,\n})\n"],"mappings":";;;;AAoBA,MAAM,gBAAgB;AAEtB,SAAS,QAAQ,MAAkB,KAAkB,SAAS,GAAe;AAC3E,KAAI,KAAK,SAAS,GAChB,OAAM,IAAI,MAAM,oCAAoC;AAKtD,MAAK,KAAM,KAAK,KAAK,KAAQ;AAC7B,MAAK,KAAM,KAAK,KAAK,KAAQ;AAE7B,KAAI,CAAC,IAEH,QAAO;AAGT,KAAI,SAAS,KAAK,SAAS,KAAK,IAAI,OAClC,OAAM,IAAI,WAAW,mBAAmB,OAAO,GAAG,SAAS,GAAG,0BAA0B;AAI1F,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,EAC3B,KAAI,SAAS,KAAK,KAAK;AAGzB,QAAO;;AAeT,SAAS,GACP,SACA,KACA,QACe;AACf,KAAI,WAAW,QAAQ,cAAc,CAAC,OAAO,CAAC,QAC5C,QAAO,WAAW,OAAO,YAAY;CAGvC,MAAM,QAAQ,QAAQ,SAAS,UAAU,KAAK,EAAE,KAAK,OAAO;AAC5D,QAAO,OAAO,WAAW,MAAM;;AAGjC,SAAS,QAAQ,IAAqB;AACpC,QAAO,cAAc,KAAK,GAAG;;;;;;AAO/B,MAAaA,SAAiB,OAAO,OAAO,IAAI;CAC9C,SAAS;CACT,WAAW;CACX;CACD,CAAC"}
|
|
@@ -14,10 +14,12 @@ type UuidV7 = {
|
|
|
14
14
|
(options?: Version7Options, buf?: undefined, offset?: number): string;
|
|
15
15
|
toBytes(id: string): Uint8Array;
|
|
16
16
|
fromBytes(bytes: Uint8Array): string;
|
|
17
|
+
timestamp(id: string): number;
|
|
18
|
+
isValid(id: string): boolean;
|
|
17
19
|
};
|
|
18
20
|
/**
|
|
19
21
|
* Generate a UUID v7 string or write the bytes into a buffer.
|
|
20
|
-
* It also includes helpers to convert to and from byte arrays.
|
|
22
|
+
* It also includes helpers to convert to and from byte arrays.j
|
|
21
23
|
*/
|
|
22
24
|
declare const uuidv7: UuidV7;
|
|
23
25
|
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v7.d.mts","names":["uuidv7: UuidV7"],"sources":["../../src/uuid/v7.ts"],"sourcesContent":[],"mappings":";KAGY,eAAA;EAAZ;AAUA;;;EAEkD,MAAA,CAAA,EAPvC,UAOuC;EAAkC,KAAA,CAAA,EAAA,MAAA;EAAwB,GAAA,CAAA,EAAA,MAAA;CAC/F;AACU,KAJX,MAAA,GAIW;EACJ,EAAA,EAAA,MAAA;EAAA,CAAA,aAHH,UAGG,GAHU,UAGV,CAAA,CAAA,OAAA,EAH+B,eAG/B,GAAA,SAAA,EAAA,GAAA,EAHiE,IAGjE,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAHyF,IAGzF;EA4InB,CAAA,OAAqB,CAARA,EA9IA,eA8IQ,EAAA,GAAA,CAAA,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;uBA7IE;mBACJ;;;;;;;;cA4INA,QAAQ"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as rng } from "../random-Dul5R6oJ.mjs";
|
|
2
|
+
import { n as parseUuid, t as formatUuid } from "../uuid-cJ5hOOIR.mjs";
|
|
2
3
|
|
|
3
4
|
//#region src/uuid/v7.ts
|
|
5
|
+
const UUID_V7_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
4
6
|
/**
|
|
5
7
|
* Module-level state for maintaining monotonic ordering within the same millisecond.
|
|
6
8
|
*
|
|
@@ -67,13 +69,24 @@ function v7(options, buf, offset) {
|
|
|
67
69
|
}
|
|
68
70
|
return buf ?? formatUuid(bytes);
|
|
69
71
|
}
|
|
72
|
+
function timestamp(id) {
|
|
73
|
+
const bytes = parseUuid(id);
|
|
74
|
+
let msecs = 0;
|
|
75
|
+
for (let i = 0; i < 6; i += 1) msecs = msecs * 256 + bytes[i];
|
|
76
|
+
return msecs;
|
|
77
|
+
}
|
|
78
|
+
function isValid(id) {
|
|
79
|
+
return UUID_V7_REGEX.test(id);
|
|
80
|
+
}
|
|
70
81
|
/**
|
|
71
82
|
* Generate a UUID v7 string or write the bytes into a buffer.
|
|
72
|
-
* It also includes helpers to convert to and from byte arrays.
|
|
83
|
+
* It also includes helpers to convert to and from byte arrays.j
|
|
73
84
|
*/
|
|
74
85
|
const uuidv7 = Object.assign(v7, {
|
|
75
|
-
toBytes:
|
|
76
|
-
fromBytes:
|
|
86
|
+
toBytes: parseUuid,
|
|
87
|
+
fromBytes: formatUuid,
|
|
88
|
+
timestamp,
|
|
89
|
+
isValid
|
|
77
90
|
});
|
|
78
91
|
|
|
79
92
|
//#endregion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v7.mjs","names":["state: V7State","bytes: Uint8Array","uuidv7: UuidV7"],"sources":["../../src/uuid/v7.ts"],"sourcesContent":["import { rng } from './common/random'\nimport { formatUuid, parseUuid } from './common/uuid'\n\nexport type Version7Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Several bytes will be overwritten with timestamp, version, and variant data.\n */\n random?: Uint8Array\n msecs?: number\n seq?: number\n}\n\nexport type UuidV7 = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: Version7Options | undefined, buf: TBuf, offset?: number): TBuf\n (options?: Version7Options, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: string): boolean\n}\n\nconst UUID_V7_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\ntype V7State = {\n msecs?: number\n seq?: number\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same millisecond.\n *\n * IMPORTANT: This state persists across all uuidv7() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs` and `seq` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: V7State = {}\n\nfunction updateV7State(current: V7State, now: number, rnds: Uint8Array): V7State {\n // Initialize state on first call (-Infinity ensures any real timestamp is greater)\n current.msecs ??= -Infinity\n current.seq ??= 0\n\n if (now > current.msecs) {\n // New millisecond tick: reseed sequence from random bytes.\n current.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n current.msecs = now\n } else {\n // Same millisecond: increment sequence, roll over into the next tick if needed.\n current.seq = (current.seq + 1) | 0\n if (current.seq === 0) {\n current.msecs += 1\n }\n }\n\n return current\n}\n\nfunction v7Bytes(rnds: Uint8Array, msecs?: number, seq?: number, buf?: Uint8Array, offset = 0): Uint8Array {\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16')\n }\n\n if (!buf) {\n buf = new Uint8Array(16)\n offset = 0\n } else if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`)\n }\n\n msecs ??= Date.now()\n // Derive a 30-bit sequence if not provided by the caller.\n seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n\n // Timestamp (48-bit big-endian milliseconds since Unix epoch).\n buf[offset++] = (msecs / 0x10000000000) & 0xff\n buf[offset++] = (msecs / 0x100000000) & 0xff\n buf[offset++] = (msecs / 0x1000000) & 0xff\n buf[offset++] = (msecs / 0x10000) & 0xff\n buf[offset++] = (msecs / 0x100) & 0xff\n buf[offset++] = msecs & 0xff\n\n // Set version (7) and variant (10xx), then pack sequence and random tail bytes.\n buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f)\n buf[offset++] = (seq >>> 20) & 0xff\n buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f)\n buf[offset++] = (seq >>> 6) & 0xff\n // Lower seq bits plus 2 random bits to complete the 128-bit payload.\n buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03)\n buf[offset++] = rnds[11]\n buf[offset++] = rnds[12]\n buf[offset++] = rnds[13]\n buf[offset++] = rnds[14]\n buf[offset++] = rnds[15]\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v7(options?: Version7Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v7<TBuf extends Uint8Array = Uint8Array>(\n options: Version7Options | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction v7<TBuf extends Uint8Array = Uint8Array>(\n options?: Version7Options,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n let bytes: Uint8Array\n\n if (options) {\n bytes = v7Bytes(options.random ?? rng(), options.msecs, options.seq, buf, offset)\n } else {\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all UUIDv7 generated within a single request will have the same timestamp.\n * - the monotonic ordering will rely entirely on the `state.seq` counter\n */\n const now = Date.now()\n const rnds = rng()\n\n updateV7State(state, now, rnds)\n\n bytes = v7Bytes(rnds, state.msecs, state.seq, buf, offset)\n }\n\n return buf ?? formatUuid(bytes)\n}\n\nfunction timestamp(id: string): number {\n const bytes = parseUuid(id)\n let msecs = 0\n for (let i = 0; i < 6; i += 1) {\n msecs = msecs * 256 + bytes[i]\n }\n return msecs\n}\n\nfunction isValid(id: string): boolean {\n return UUID_V7_REGEX.test(id)\n}\n\n/**\n * Generate a UUID v7 string or write the bytes into a buffer.\n * It also includes helpers to convert to and from byte arrays.j\n */\nexport const uuidv7: UuidV7 = Object.assign(v7, {\n toBytes: parseUuid,\n fromBytes: formatUuid,\n timestamp,\n isValid,\n})\n"],"mappings":";;;;AAuBA,MAAM,gBAAgB;;;;;;;;;AAetB,MAAMA,QAAiB,EAAE;AAEzB,SAAS,cAAc,SAAkB,KAAa,MAA2B;AAE/E,SAAQ,UAAU;AAClB,SAAQ,QAAQ;AAEhB,KAAI,MAAM,QAAQ,OAAO;AAEvB,UAAQ,MAAO,KAAK,MAAM,KAAO,KAAK,MAAM,KAAO,KAAK,MAAM,IAAK,KAAK;AACxE,UAAQ,QAAQ;QACX;AAEL,UAAQ,MAAO,QAAQ,MAAM,IAAK;AAClC,MAAI,QAAQ,QAAQ,EAClB,SAAQ,SAAS;;AAIrB,QAAO;;AAGT,SAAS,QAAQ,MAAkB,OAAgB,KAAc,KAAkB,SAAS,GAAe;AACzG,KAAI,KAAK,SAAS,GAChB,OAAM,IAAI,MAAM,oCAAoC;AAGtD,KAAI,CAAC,KAAK;AACR,QAAM,IAAI,WAAW,GAAG;AACxB,WAAS;YACA,SAAS,KAAK,SAAS,KAAK,IAAI,OACzC,OAAM,IAAI,WAAW,mBAAmB,OAAO,GAAG,SAAS,GAAG,0BAA0B;AAG1F,WAAU,KAAK,KAAK;AAEpB,SAAU,KAAK,KAAK,OAAS,KAAO,KAAK,MAAM,KAAO,KAAK,MAAM,IAAK,KAAK;AAG3E,KAAI,YAAa,QAAQ,gBAAiB;AAC1C,KAAI,YAAa,QAAQ,aAAe;AACxC,KAAI,YAAa,QAAQ,WAAa;AACtC,KAAI,YAAa,QAAQ,QAAW;AACpC,KAAI,YAAa,QAAQ,MAAS;AAClC,KAAI,YAAY,QAAQ;AAGxB,KAAI,YAAY,MAAS,QAAQ,KAAM;AACvC,KAAI,YAAa,QAAQ,KAAM;AAC/B,KAAI,YAAY,MAAS,QAAQ,KAAM;AACvC,KAAI,YAAa,QAAQ,IAAK;AAE9B,KAAI,YAAc,OAAO,IAAK,MAAS,KAAK,MAAM;AAClD,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AAErB,QAAO;;AAeT,SAAS,GACP,SACA,KACA,QACe;CACf,IAAIC;AAEJ,KAAI,QACF,SAAQ,QAAQ,QAAQ,UAAU,KAAK,EAAE,QAAQ,OAAO,QAAQ,KAAK,KAAK,OAAO;MAC5E;;;;;;;;;EASL,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,OAAO,KAAK;AAElB,gBAAc,OAAO,KAAK,KAAK;AAE/B,UAAQ,QAAQ,MAAM,MAAM,OAAO,MAAM,KAAK,KAAK,OAAO;;AAG5D,QAAO,OAAO,WAAW,MAAM;;AAGjC,SAAS,UAAU,IAAoB;CACrC,MAAM,QAAQ,UAAU,GAAG;CAC3B,IAAI,QAAQ;AACZ,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,EAC1B,SAAQ,QAAQ,MAAM,MAAM;AAE9B,QAAO;;AAGT,SAAS,QAAQ,IAAqB;AACpC,QAAO,cAAc,KAAK,GAAG;;;;;;AAO/B,MAAaC,SAAiB,OAAO,OAAO,IAAI;CAC9C,SAAS;CACT,WAAW;CACX;CACA;CACD,CAAC"}
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
//#region src/uuid/common/random.ts
|
|
2
|
-
function randomBytes(out) {
|
|
3
|
-
globalThis.crypto.getRandomValues(out);
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Generate 16 bytes of cryptographically strong random data.
|
|
7
|
-
* Used internally by UUID generators.
|
|
8
|
-
*/
|
|
9
|
-
function rng() {
|
|
10
|
-
const bytes = new Uint8Array(16);
|
|
11
|
-
randomBytes(bytes);
|
|
12
|
-
return bytes;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
//#endregion
|
|
16
1
|
//#region src/uuid/common/hex.ts
|
|
17
2
|
const HEX_TABLE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
18
3
|
function bytesToHex(bytes) {
|
|
@@ -60,5 +45,5 @@ function parseUuid(value) {
|
|
|
60
45
|
}
|
|
61
46
|
|
|
62
47
|
//#endregion
|
|
63
|
-
export { parseUuid as n,
|
|
64
|
-
//# sourceMappingURL=uuid-
|
|
48
|
+
export { parseUuid as n, formatUuid as t };
|
|
49
|
+
//# sourceMappingURL=uuid-cJ5hOOIR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid-cJ5hOOIR.mjs","names":[],"sources":["../src/uuid/common/hex.ts","../src/uuid/common/uuid.ts"],"sourcesContent":["// Pre-computed lookup table for byte-to-hex conversion (0x00 -> \"00\", 0xff -> \"ff\")\nconst HEX_TABLE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))\n\nexport function bytesToHex(bytes: Uint8Array): string {\n // Build string directly, without intermediate array allocation\n let hex = ''\n for (let i = 0; i < bytes.length; i += 1) {\n hex += HEX_TABLE[bytes[i]]\n }\n return hex\n}\n\n// ASCII code ranges for hex character validation\nconst ASCII_0 = 48 // '0'\nconst ASCII_9 = 57 // '9'\nconst ASCII_A = 65 // 'A'\nconst ASCII_F = 70 // 'F'\nconst ASCII_a = 97 // 'a'\nconst ASCII_f = 102 // 'f'\n\nfunction hexValue(code: number): number {\n if (code >= ASCII_0 && code <= ASCII_9) {\n return code - ASCII_0\n }\n if (code >= ASCII_A && code <= ASCII_F) {\n return code - ASCII_A + 10\n }\n if (code >= ASCII_a && code <= ASCII_f) {\n return code - ASCII_a + 10\n }\n return -1 // Invalid hex character\n}\n\nexport function hexToBytes(hex: string, out: Uint8Array): void {\n if (hex.length !== out.length * 2) {\n throw new Error(`hex string must be ${out.length * 2} chars, got ${hex.length}`)\n }\n\n for (let i = 0; i < out.length; i += 1) {\n const hi = hexValue(hex.charCodeAt(i * 2))\n const lo = hexValue(hex.charCodeAt(i * 2 + 1))\n if (hi === -1 || lo === -1) {\n throw new Error('hex string contains invalid characters')\n }\n out[i] = (hi << 4) | lo\n }\n}\n","import { bytesToHex, hexToBytes } from './hex'\n\nconst UUID_BYTE_LENGTH = 16\nconst UUID_STRING_LENGTH = 36\n\nexport function formatUuid(bytes: Uint8Array): string {\n // precondition: bytes.length === UUID_BYTE_LENGTH\n const hex = bytesToHex(bytes)\n\n // Build UUID string with dashes at positions 8, 13, 18, 23\n // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\nexport function parseUuid(value: string): Uint8Array {\n if (value.length !== UUID_STRING_LENGTH) {\n throw new Error('uuid string must be 36 characters')\n }\n\n // Validate separator positions directly (more efficient than full loop)\n if (value[8] !== '-' || value[13] !== '-' || value[18] !== '-' || value[23] !== '-') {\n throw new Error('uuid string has invalid separators')\n }\n\n const hex = value.slice(0, 8) + value.slice(9, 13) + value.slice(14, 18) + value.slice(19, 23) + value.slice(24)\n\n const bytes = new Uint8Array(UUID_BYTE_LENGTH)\n hexToBytes(hex, bytes)\n return bytes\n}\n"],"mappings":";AACA,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,GAAG,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAExF,SAAgB,WAAW,OAA2B;CAEpD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,QAAO,UAAU,MAAM;AAEzB,QAAO;;AAIT,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,SAAS,SAAS,MAAsB;AACtC,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO;AAEhB,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,QAAO;;AAGT,SAAgB,WAAW,KAAa,KAAuB;AAC7D,KAAI,IAAI,WAAW,IAAI,SAAS,EAC9B,OAAM,IAAI,MAAM,sBAAsB,IAAI,SAAS,EAAE,cAAc,IAAI,SAAS;AAGlF,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;EACtC,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,EAAE,CAAC;EAC1C,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;AAC9C,MAAI,OAAO,MAAM,OAAO,GACtB,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,KAAM,MAAM,IAAK;;;;;;AC1CzB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAE3B,SAAgB,WAAW,OAA2B;CAEpD,MAAM,MAAM,WAAW,MAAM;AAI7B,QAAO,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,GAAG;;AAG1G,SAAgB,UAAU,OAA2B;AACnD,KAAI,MAAM,WAAW,mBACnB,OAAM,IAAI,MAAM,oCAAoC;AAItD,KAAI,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAC9E,OAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,GAAG;CAEhH,MAAM,QAAQ,IAAI,WAAW,iBAAiB;AAC9C,YAAW,KAAK,MAAM;AACtB,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "uniku",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "Minimal, tree-shakeable UUID utilities for every JavaScript runtime",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alberto Schiabel",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"uuid",
|
|
23
23
|
"uuidv4",
|
|
24
24
|
"uuidv7",
|
|
25
|
+
"ulid",
|
|
26
|
+
"cuid2",
|
|
27
|
+
"nanoid",
|
|
25
28
|
"unique",
|
|
26
29
|
"id",
|
|
27
30
|
"identifier",
|
|
@@ -51,29 +54,57 @@
|
|
|
51
54
|
],
|
|
52
55
|
"exports": {
|
|
53
56
|
"./uuid/v4": {
|
|
54
|
-
"types": "./build/v4.d.mts",
|
|
57
|
+
"types": "./build/uuid/v4.d.mts",
|
|
55
58
|
"import": {
|
|
56
59
|
"@jkomyno/source": "./src/uuid/v4.ts",
|
|
57
|
-
"default": "./build/v4.mjs"
|
|
60
|
+
"default": "./build/uuid/v4.mjs"
|
|
58
61
|
},
|
|
59
|
-
"default": "./build/v4.mjs"
|
|
62
|
+
"default": "./build/uuid/v4.mjs"
|
|
60
63
|
},
|
|
61
64
|
"./uuid/v7": {
|
|
62
|
-
"types": "./build/v7.d.mts",
|
|
65
|
+
"types": "./build/uuid/v7.d.mts",
|
|
63
66
|
"import": {
|
|
64
67
|
"@jkomyno/source": "./src/uuid/v7.ts",
|
|
65
|
-
"default": "./build/v7.mjs"
|
|
68
|
+
"default": "./build/uuid/v7.mjs"
|
|
66
69
|
},
|
|
67
|
-
"default": "./build/v7.mjs"
|
|
70
|
+
"default": "./build/uuid/v7.mjs"
|
|
71
|
+
},
|
|
72
|
+
"./ulid": {
|
|
73
|
+
"types": "./build/ulid/ulid.d.mts",
|
|
74
|
+
"import": {
|
|
75
|
+
"@jkomyno/source": "./src/ulid/ulid.ts",
|
|
76
|
+
"default": "./build/ulid/ulid.mjs"
|
|
77
|
+
},
|
|
78
|
+
"default": "./build/ulid/ulid.mjs"
|
|
79
|
+
},
|
|
80
|
+
"./cuid2": {
|
|
81
|
+
"types": "./build/cuid2/cuid2.d.mts",
|
|
82
|
+
"import": {
|
|
83
|
+
"@jkomyno/source": "./src/cuid2/cuid2.ts",
|
|
84
|
+
"default": "./build/cuid2/cuid2.mjs"
|
|
85
|
+
},
|
|
86
|
+
"default": "./build/cuid2/cuid2.mjs"
|
|
87
|
+
},
|
|
88
|
+
"./nanoid": {
|
|
89
|
+
"types": "./build/nanoid/nanoid.d.mts",
|
|
90
|
+
"import": {
|
|
91
|
+
"@jkomyno/source": "./src/nanoid/nanoid.ts",
|
|
92
|
+
"default": "./build/nanoid/nanoid.mjs"
|
|
93
|
+
},
|
|
94
|
+
"default": "./build/nanoid/nanoid.mjs"
|
|
68
95
|
},
|
|
69
96
|
"./package.json": "./package.json"
|
|
70
97
|
},
|
|
98
|
+
"dependencies": {
|
|
99
|
+
"@noble/hashes": "^2.0.1"
|
|
100
|
+
},
|
|
71
101
|
"scripts": {
|
|
72
102
|
"clean": "rimraf build .turbo",
|
|
73
103
|
"build": "tsdown --config-loader unrun",
|
|
74
104
|
"build:watch": "tsdown --config-loader unrun --watch",
|
|
75
105
|
"test": "vitest run",
|
|
76
106
|
"test:unit": "vitest --project unit run",
|
|
77
|
-
"bench": "vitest bench"
|
|
107
|
+
"bench": "vitest bench",
|
|
108
|
+
"typecheck": "tsc --noEmit"
|
|
78
109
|
}
|
|
79
110
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uuid-DaimLSJD.mjs","names":[],"sources":["../src/uuid/common/random.ts","../src/uuid/common/hex.ts","../src/uuid/common/uuid.ts"],"sourcesContent":["export function randomBytes(out: Uint8Array): void {\n const crypto = globalThis.crypto\n crypto.getRandomValues(out)\n}\n\n/**\n * Generate 16 bytes of cryptographically strong random data.\n * Used internally by UUID generators.\n */\nexport function rng(): Uint8Array {\n const bytes = new Uint8Array(16)\n randomBytes(bytes)\n return bytes\n}\n","// Pre-computed lookup table for byte-to-hex conversion (0x00 -> \"00\", 0xff -> \"ff\")\nconst HEX_TABLE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))\n\nexport function bytesToHex(bytes: Uint8Array): string {\n // Build string directly, without intermediate array allocation\n let hex = ''\n for (let i = 0; i < bytes.length; i += 1) {\n hex += HEX_TABLE[bytes[i]]\n }\n return hex\n}\n\n// ASCII code ranges for hex character validation\nconst ASCII_0 = 48 // '0'\nconst ASCII_9 = 57 // '9'\nconst ASCII_A = 65 // 'A'\nconst ASCII_F = 70 // 'F'\nconst ASCII_a = 97 // 'a'\nconst ASCII_f = 102 // 'f'\n\nfunction hexValue(code: number): number {\n if (code >= ASCII_0 && code <= ASCII_9) {\n return code - ASCII_0\n }\n if (code >= ASCII_A && code <= ASCII_F) {\n return code - ASCII_A + 10\n }\n if (code >= ASCII_a && code <= ASCII_f) {\n return code - ASCII_a + 10\n }\n return -1 // Invalid hex character\n}\n\nexport function hexToBytes(hex: string, out: Uint8Array): void {\n if (hex.length !== out.length * 2) {\n throw new Error(`hex string must be ${out.length * 2} chars, got ${hex.length}`)\n }\n\n for (let i = 0; i < out.length; i += 1) {\n const hi = hexValue(hex.charCodeAt(i * 2))\n const lo = hexValue(hex.charCodeAt(i * 2 + 1))\n if (hi === -1 || lo === -1) {\n throw new Error('hex string contains invalid characters')\n }\n out[i] = (hi << 4) | lo\n }\n}\n","import { bytesToHex, hexToBytes } from './hex'\n\nconst UUID_BYTE_LENGTH = 16\nconst UUID_STRING_LENGTH = 36\n\nexport function formatUuid(bytes: Uint8Array): string {\n // precondition: bytes.length === UUID_BYTE_LENGTH\n const hex = bytesToHex(bytes)\n\n // Build UUID string with dashes at positions 8, 13, 18, 23\n // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\nexport function parseUuid(value: string): Uint8Array {\n if (value.length !== UUID_STRING_LENGTH) {\n throw new Error('uuid string must be 36 characters')\n }\n\n // Validate separator positions directly (more efficient than full loop)\n if (value[8] !== '-' || value[13] !== '-' || value[18] !== '-' || value[23] !== '-') {\n throw new Error('uuid string has invalid separators')\n }\n\n const hex = value.slice(0, 8) + value.slice(9, 13) + value.slice(14, 18) + value.slice(19, 23) + value.slice(24)\n\n const bytes = new Uint8Array(UUID_BYTE_LENGTH)\n hexToBytes(hex, bytes)\n return bytes\n}\n"],"mappings":";AAAA,SAAgB,YAAY,KAAuB;AAEjD,CADe,WAAW,OACnB,gBAAgB,IAAI;;;;;;AAO7B,SAAgB,MAAkB;CAChC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAY,MAAM;AAClB,QAAO;;;;;ACXT,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,GAAG,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAExF,SAAgB,WAAW,OAA2B;CAEpD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,QAAO,UAAU,MAAM;AAEzB,QAAO;;AAIT,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,SAAS,SAAS,MAAsB;AACtC,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO;AAEhB,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,QAAO;;AAGT,SAAgB,WAAW,KAAa,KAAuB;AAC7D,KAAI,IAAI,WAAW,IAAI,SAAS,EAC9B,OAAM,IAAI,MAAM,sBAAsB,IAAI,SAAS,EAAE,cAAc,IAAI,SAAS;AAGlF,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;EACtC,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,EAAE,CAAC;EAC1C,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;AAC9C,MAAI,OAAO,MAAM,OAAO,GACtB,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,KAAM,MAAM,IAAK;;;;;;AC1CzB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAE3B,SAAgB,WAAW,OAA2B;CAEpD,MAAM,MAAM,WAAW,MAAM;AAI7B,QAAO,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,GAAG;;AAG1G,SAAgB,UAAU,OAA2B;AACnD,KAAI,MAAM,WAAW,mBACnB,OAAM,IAAI,MAAM,oCAAoC;AAItD,KAAI,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAC9E,OAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,GAAG;CAEhH,MAAM,QAAQ,IAAI,WAAW,iBAAiB;AAC9C,YAAW,KAAK,MAAM;AACtB,QAAO"}
|
package/build/v4.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v4.d.mts","names":["uuidv4: UuidV4"],"sources":["../src/uuid/v4.ts"],"sourcesContent":[],"mappings":";KAGY,eAAA;EAAZ;AAQA;;;EAEkD,MAAA,CAAA,EALvC,UAKuC;CAAkC;AAAwB,KAFhG,MAAA,GAEgG;EAC/F,EAAA,EAAA,MAAA;EACU,CAAA,aAFP,UAEO,GAFM,UAEN,CAAA,CAAA,OAAA,EAF2B,eAE3B,GAAA,SAAA,EAAA,GAAA,EAF6D,IAE7D,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAFqF,IAErF;EACJ,CAAA,OAAA,CAAA,EAFN,eAEM,EAAA,GAAA,CAAA,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;EAAA,OAAA,CAAA,EAAA,EAAA,MAAA,CAAA,EADI,UACJ;EA2DnB,SAAaA,CAAAA,KAAQ,EA3DF,UA2DE,CAAA,EAAA,MAAA;;;;;;cAARA,QAAQ"}
|
package/build/v4.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v4.mjs","names":["uuidv4: UuidV4"],"sources":["../src/uuid/v4.ts"],"sourcesContent":["import { rng } from './common/random'\nimport { formatUuid, parseUuid } from './common/uuid'\n\nexport type Version4Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Bytes at index 6 and 8 will be modified in-place to set version/variant bits.\n */\n random?: Uint8Array\n}\n\nexport type UuidV4 = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: Version4Options | undefined, buf: TBuf, offset?: number): TBuf\n (options?: Version4Options, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n}\n\nfunction v4Bytes(rnds: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16')\n }\n\n // Set RFC 4122 version (4) and variant (10xx) bits.\n // Note: This modifies the input array in-place.\n rnds[6] = (rnds[6] & 0x0f) | 0x40\n rnds[8] = (rnds[8] & 0x3f) | 0x80\n\n if (!buf) {\n // No output buffer provided - return the modified random bytes directly\n return rnds\n }\n\n if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`)\n }\n\n // Copy 16 UUID bytes into the provided buffer slice.\n for (let i = 0; i < 16; i += 1) {\n buf[offset + i] = rnds[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v4(options?: Version4Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v4<TBuf extends Uint8Array = Uint8Array>(\n options: Version4Options | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction v4<TBuf extends Uint8Array = Uint8Array>(\n options?: Version4Options,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n if (globalThis.crypto?.randomUUID && !buf && !options) {\n return globalThis.crypto.randomUUID()\n }\n\n const bytes = v4Bytes(options?.random ?? rng(), buf, offset)\n return buf ?? formatUuid(bytes)\n}\n\n/**\n * Generate a UUID v4 string or write the bytes into a buffer.\n * It also includes helpers to convert to and from byte arrays.\n */\nexport const uuidv4: UuidV4 = Object.assign(v4, {\n toBytes: (id: string) => parseUuid(id),\n fromBytes: (bytes: Uint8Array) => formatUuid(bytes),\n})\n"],"mappings":";;;AAmBA,SAAS,QAAQ,MAAkB,KAAkB,SAAS,GAAe;AAC3E,KAAI,KAAK,SAAS,GAChB,OAAM,IAAI,MAAM,oCAAoC;AAKtD,MAAK,KAAM,KAAK,KAAK,KAAQ;AAC7B,MAAK,KAAM,KAAK,KAAK,KAAQ;AAE7B,KAAI,CAAC,IAEH,QAAO;AAGT,KAAI,SAAS,KAAK,SAAS,KAAK,IAAI,OAClC,OAAM,IAAI,WAAW,mBAAmB,OAAO,GAAG,SAAS,GAAG,0BAA0B;AAI1F,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK,EAC3B,KAAI,SAAS,KAAK,KAAK;AAGzB,QAAO;;AAeT,SAAS,GACP,SACA,KACA,QACe;AACf,KAAI,WAAW,QAAQ,cAAc,CAAC,OAAO,CAAC,QAC5C,QAAO,WAAW,OAAO,YAAY;CAGvC,MAAM,QAAQ,QAAQ,SAAS,UAAU,KAAK,EAAE,KAAK,OAAO;AAC5D,QAAO,OAAO,WAAW,MAAM;;;;;;AAOjC,MAAaA,SAAiB,OAAO,OAAO,IAAI;CAC9C,UAAU,OAAe,UAAU,GAAG;CACtC,YAAY,UAAsB,WAAW,MAAM;CACpD,CAAC"}
|
package/build/v7.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v7.d.mts","names":["uuidv7: UuidV7"],"sources":["../src/uuid/v7.ts"],"sourcesContent":[],"mappings":";KAGY,eAAA;EAAZ;AAUA;;;EAEkD,MAAA,CAAA,EAPvC,UAOuC;EAAkC,KAAA,CAAA,EAAA,MAAA;EAAwB,GAAA,CAAA,EAAA,MAAA;CAC/F;AACU,KAJX,MAAA,GAIW;EACJ,EAAA,EAAA,MAAA;EAAA,CAAA,aAHH,UAGG,GAHU,UAGV,CAAA,CAAA,OAAA,EAH+B,eAG/B,GAAA,SAAA,EAAA,GAAA,EAHiE,IAGjE,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAHyF,IAGzF;EA2HnB,CAAA,OAAqB,CAARA,EA7HA,eA6HQ,EAAA,GAAA,CAAA,EAAA,SAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA,EAAA,MAAA;uBA5HE;mBACJ;;;;;;cA2HNA,QAAQ"}
|
package/build/v7.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"v7.mjs","names":["state: V7State","bytes: Uint8Array","uuidv7: UuidV7"],"sources":["../src/uuid/v7.ts"],"sourcesContent":["import { rng } from './common/random'\nimport { formatUuid, parseUuid } from './common/uuid'\n\nexport type Version7Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Several bytes will be overwritten with timestamp, version, and variant data.\n */\n random?: Uint8Array\n msecs?: number\n seq?: number\n}\n\nexport type UuidV7 = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: Version7Options | undefined, buf: TBuf, offset?: number): TBuf\n (options?: Version7Options, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n}\n\ntype V7State = {\n msecs?: number\n seq?: number\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same millisecond.\n *\n * IMPORTANT: This state persists across all uuidv7() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs` and `seq` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: V7State = {}\n\nfunction updateV7State(current: V7State, now: number, rnds: Uint8Array): V7State {\n // Initialize state on first call (-Infinity ensures any real timestamp is greater)\n current.msecs ??= -Infinity\n current.seq ??= 0\n\n if (now > current.msecs) {\n // New millisecond tick: reseed sequence from random bytes.\n current.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n current.msecs = now\n } else {\n // Same millisecond: increment sequence, roll over into the next tick if needed.\n current.seq = (current.seq + 1) | 0\n if (current.seq === 0) {\n current.msecs += 1\n }\n }\n\n return current\n}\n\nfunction v7Bytes(rnds: Uint8Array, msecs?: number, seq?: number, buf?: Uint8Array, offset = 0): Uint8Array {\n if (rnds.length < 16) {\n throw new Error('Random bytes length must be >= 16')\n }\n\n if (!buf) {\n buf = new Uint8Array(16)\n offset = 0\n } else if (offset < 0 || offset + 16 > buf.length) {\n throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`)\n }\n\n msecs ??= Date.now()\n // Derive a 30-bit sequence if not provided by the caller.\n seq ??= ((rnds[6] * 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n\n // Timestamp (48-bit big-endian milliseconds since Unix epoch).\n buf[offset++] = (msecs / 0x10000000000) & 0xff\n buf[offset++] = (msecs / 0x100000000) & 0xff\n buf[offset++] = (msecs / 0x1000000) & 0xff\n buf[offset++] = (msecs / 0x10000) & 0xff\n buf[offset++] = (msecs / 0x100) & 0xff\n buf[offset++] = msecs & 0xff\n\n // Set version (7) and variant (10xx), then pack sequence and random tail bytes.\n buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f)\n buf[offset++] = (seq >>> 20) & 0xff\n buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f)\n buf[offset++] = (seq >>> 6) & 0xff\n // Lower seq bits plus 2 random bits to complete the 128-bit payload.\n buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03)\n buf[offset++] = rnds[11]\n buf[offset++] = rnds[12]\n buf[offset++] = rnds[13]\n buf[offset++] = rnds[14]\n buf[offset++] = rnds[15]\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v7(options?: Version7Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v7<TBuf extends Uint8Array = Uint8Array>(\n options: Version7Options | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction v7<TBuf extends Uint8Array = Uint8Array>(\n options?: Version7Options,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n let bytes: Uint8Array\n\n if (options) {\n bytes = v7Bytes(options.random ?? rng(), options.msecs, options.seq, buf, offset)\n } else {\n /**\n * Note: by default, Cloudflare Workers \"freezes\" time during request handling to prevent\n * side-channel attacks. This means that Date.now() will return the same value for the entire\n * duration of a request.\n * Implications:\n * - all UUIDv7 generated within a single request will have the same timestamp.\n * - the monotonic ordering will rely entirely on the `state.seq` counter\n */\n const now = Date.now()\n const rnds = rng()\n\n updateV7State(state, now, rnds)\n\n bytes = v7Bytes(rnds, state.msecs, state.seq, buf, offset)\n }\n\n return buf ?? formatUuid(bytes)\n}\n\n/**\n * Generate a UUID v7 string or write the bytes into a buffer.\n * It also includes helpers to convert to and from byte arrays.\n */\nexport const uuidv7: UuidV7 = Object.assign(v7, {\n toBytes: (id: string) => parseUuid(id),\n fromBytes: (bytes: Uint8Array) => formatUuid(bytes),\n})\n"],"mappings":";;;;;;;;;;;AAkCA,MAAMA,QAAiB,EAAE;AAEzB,SAAS,cAAc,SAAkB,KAAa,MAA2B;AAE/E,SAAQ,UAAU;AAClB,SAAQ,QAAQ;AAEhB,KAAI,MAAM,QAAQ,OAAO;AAEvB,UAAQ,MAAO,KAAK,MAAM,KAAO,KAAK,MAAM,KAAO,KAAK,MAAM,IAAK,KAAK;AACxE,UAAQ,QAAQ;QACX;AAEL,UAAQ,MAAO,QAAQ,MAAM,IAAK;AAClC,MAAI,QAAQ,QAAQ,EAClB,SAAQ,SAAS;;AAIrB,QAAO;;AAGT,SAAS,QAAQ,MAAkB,OAAgB,KAAc,KAAkB,SAAS,GAAe;AACzG,KAAI,KAAK,SAAS,GAChB,OAAM,IAAI,MAAM,oCAAoC;AAGtD,KAAI,CAAC,KAAK;AACR,QAAM,IAAI,WAAW,GAAG;AACxB,WAAS;YACA,SAAS,KAAK,SAAS,KAAK,IAAI,OACzC,OAAM,IAAI,WAAW,mBAAmB,OAAO,GAAG,SAAS,GAAG,0BAA0B;AAG1F,WAAU,KAAK,KAAK;AAEpB,SAAU,KAAK,KAAK,OAAS,KAAO,KAAK,MAAM,KAAO,KAAK,MAAM,IAAK,KAAK;AAG3E,KAAI,YAAa,QAAQ,gBAAiB;AAC1C,KAAI,YAAa,QAAQ,aAAe;AACxC,KAAI,YAAa,QAAQ,WAAa;AACtC,KAAI,YAAa,QAAQ,QAAW;AACpC,KAAI,YAAa,QAAQ,MAAS;AAClC,KAAI,YAAY,QAAQ;AAGxB,KAAI,YAAY,MAAS,QAAQ,KAAM;AACvC,KAAI,YAAa,QAAQ,KAAM;AAC/B,KAAI,YAAY,MAAS,QAAQ,KAAM;AACvC,KAAI,YAAa,QAAQ,IAAK;AAE9B,KAAI,YAAc,OAAO,IAAK,MAAS,KAAK,MAAM;AAClD,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AACrB,KAAI,YAAY,KAAK;AAErB,QAAO;;AAeT,SAAS,GACP,SACA,KACA,QACe;CACf,IAAIC;AAEJ,KAAI,QACF,SAAQ,QAAQ,QAAQ,UAAU,KAAK,EAAE,QAAQ,OAAO,QAAQ,KAAK,KAAK,OAAO;MAC5E;;;;;;;;;EASL,MAAM,MAAM,KAAK,KAAK;EACtB,MAAM,OAAO,KAAK;AAElB,gBAAc,OAAO,KAAK,KAAK;AAE/B,UAAQ,QAAQ,MAAM,MAAM,OAAO,MAAM,KAAK,KAAK,OAAO;;AAG5D,QAAO,OAAO,WAAW,MAAM;;;;;;AAOjC,MAAaC,SAAiB,OAAO,OAAO,IAAI;CAC9C,UAAU,OAAe,UAAU,GAAG;CACtC,YAAY,UAAsB,WAAW,MAAM;CACpD,CAAC"}
|