uniku 0.0.5 → 0.0.7

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 CHANGED
@@ -1,51 +1,110 @@
1
1
  # uniku
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/uniku.svg)](https://www.npmjs.com/package/uniku)
4
+ [![npm downloads](https://img.shields.io/npm/dm/uniku.svg)](https://npmjs.com/package/uniku)
5
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/uniku)](https://bundlephobia.com/package/uniku)
4
6
  [![CI](https://github.com/jkomyno/uniku/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/jkomyno/uniku/actions/workflows/ci.yaml)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
5
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/uniku)](https://bundlephobia.com/package/uniku)
7
9
 
8
- Minimal, tree-shakeable UUID and ULID utilities for every JavaScript runtime.
10
+ One library. Every ID format. Every runtime. Zero dependencies.
9
11
 
10
- > *uniku* means "unique" in Maltese.
12
+ > **uniku** */uˈniːku/* — Maltese for "unique"
11
13
 
12
- ## Why uniku?
14
+ ```ts
15
+ import { uuidv7 } from 'uniku/uuid/v7'
13
16
 
14
- - **ESM-only** - No CommonJS or legacy support.
15
- - **Universal runtime support** - Works everywhere: Node.js, Cloudflare Workers, Vercel Edge, Deno, browsers. Uses the WebCrypto API (`globalThis.crypto`), not `node:crypto`.
16
- - **Truly tree-shakeable** - Separate entry points mean you only bundle what you import. No barrel exports that defeat dead-code elimination.
17
- - **Byte-level access** - Convert between UUID strings and `Uint8Array` with built-in `toBytes()` and `fromBytes()` helpers.
18
- - **Modern standards** - Includes UUID v7 (RFC 9562) for time-ordered, sortable identifiers with monotonic sequencing.
19
- - **Lightweight** - Designed for use in performance-sensitive contexts like ORMs.
17
+ const id = uuidv7()
18
+ // => "018e5e5c-7c8a-7000-8000-000000000000"
20
19
 
21
- ## Supported ID generation algorithms
20
+ // Time-ordered: IDs sort by creation time
21
+ const [first, second, third] = [uuidv7(), uuidv7(), uuidv7()]
22
+ console.log(first < second && second < third) // true
23
+ ```
22
24
 
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
+ ## At a Glance
26
+
27
+ | | uniku | uuid | nanoid | ulid | cuid2 | ksuid |
28
+ |--------------------|:-----:|:----:|:------:|:----:|:-----:|:-----:|
29
+ | UUID v4 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
30
+ | UUID v7 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
31
+ | ULID | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
32
+ | CUID2 | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
33
+ | Nanoid | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
34
+ | KSUID | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
35
+ | Tree-shakeable | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
36
+ | ESM-only | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
37
+ | Edge/Workers | ✅ | ⚠️ | ✅ | ⚠️ | ✅ | ✅ |
38
+ | Byte ↔ String | ✅ | ✅ | - | ✅ | - | ✅ |
39
+
40
+ > **Note**: Byte ↔ String conversion doesn't make sense for nanoid and cuid2, since they are string-native formats with no canonical binary representation.
41
+
42
+ ### Works Everywhere
43
+
44
+ <p align="center">
45
+ <img src="https://img.shields.io/badge/Node.js-339933?logo=nodedotjs&logoColor=white" alt="Node.js">
46
+ <img src="https://img.shields.io/badge/Deno-000000?logo=deno&logoColor=white" alt="Deno">
47
+ <img src="https://img.shields.io/badge/Bun-000000?logo=bun&logoColor=white" alt="Bun">
48
+ <img src="https://img.shields.io/badge/Cloudflare_Workers-F38020?logo=cloudflare&logoColor=white" alt="Cloudflare Workers">
49
+ <img src="https://img.shields.io/badge/Vercel_Edge-000000?logo=vercel&logoColor=white" alt="Vercel Edge">
50
+ <img src="https://img.shields.io/badge/Browsers-4285F4?logo=googlechrome&logoColor=white" alt="Browsers">
51
+ </p>
52
+
53
+ Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
54
+
55
+ ## Performance
56
+
57
+ | Generator | uniku vs npm |
58
+ |-----------|-------------:|
59
+ | ULID | **77× faster** |
60
+ | CUID2 | **8× faster** |
61
+ | UUID v4 | **1.5× faster** |
62
+ | Nanoid | **1.1× faster** |
63
+ | UUID v7 | npm 1.6× faster* |
64
+ | KSUID | npm 1.8× faster* |
65
+
66
+ <sub>*UUID v7 tradeoff: uniku prioritizes strict monotonic sequencing for database use cases.</sub>
67
+
68
+ ## Which ID Should I Use?
69
+
70
+ | Use Case | Recommended | Why |
71
+ |----------|-------------|-----|
72
+ | Database primary keys | **UUID v7** or **ULID** | Time-ordered for index efficiency |
73
+ | URL shorteners | **Nanoid** | Compact, URL-safe characters |
74
+ | Prevent enumeration | **CUID2** | Non-sequential, secure |
75
+ | Maximum compatibility | **UUID v4** | Universal standard |
76
+ | Distributed systems | **ULID** | Sortable + high entropy |
28
77
 
29
78
  ## Installation
30
79
 
31
- With pnpm:
32
-
33
80
  ```bash
81
+ # pnpm (recommended)
34
82
  pnpm add uniku
35
- ```
36
83
 
37
- With Bun:
84
+ # bun
85
+ bun add uniku
38
86
 
39
- ```bash
40
- bun install uniku
41
- ```
87
+ # npm
88
+ npm install uniku
42
89
 
43
- With npm:
90
+ # yarn
91
+ yarn add uniku
44
92
 
45
- ```bash
46
- npm install uniku
93
+ # deno
94
+ deno install npm:uniku
47
95
  ```
48
96
 
97
+ ### Bundle Impact
98
+
99
+ | Import | Minified + gzipped |
100
+ |--------|-------------------:|
101
+ | `uniku/uuid/v4` | ~930 B |
102
+ | `uniku/uuid/v7` | ~1.1 KB |
103
+ | `uniku/ulid` | ~1.5 KB |
104
+ | `uniku/cuid2` | ~1.1 KB* |
105
+ | `uniku/nanoid` | ~967 B |
106
+ | `uniku/ksuid` | ~1.1 KB |
107
+
49
108
  ## Quick Start
50
109
 
51
110
  ### UUID v4 (random)
@@ -100,7 +159,6 @@ const shortId = cuid2({ length: 10 })
100
159
  // => "tz4a98xxat"
101
160
 
102
161
  // Validation (type guard)
103
- const maybeId: unknown = getUserInput()
104
162
  if (cuid2.isValid(maybeId)) {
105
163
  console.log(maybeId) // TypeScript knows this is a string
106
164
  }
@@ -121,10 +179,66 @@ const shortId = nanoid(10)
121
179
  // Custom alphabet and size via options
122
180
  const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })
123
181
  // => "4f90d13a42bc"
182
+ ```
183
+
184
+ ### KSUID (time-ordered, high entropy)
185
+
186
+ ```ts
187
+ import { ksuid } from 'uniku/ksuid'
188
+
189
+ const id = ksuid()
190
+ // => "2QnJjKLvpSfpZqGiPPxVwWLMy2p"
191
+
192
+ // Convert to/from bytes
193
+ const bytes = ksuid.toBytes(id)
194
+ const str = ksuid.fromBytes(bytes)
195
+
196
+ // Extract timestamp (milliseconds)
197
+ const ts = ksuid.timestamp(id)
198
+ ```
199
+
200
+ ## Migrating to uniku
201
+
202
+ ### From `uuid`
203
+
204
+ ```diff
205
+ - import { v4 as uuidv4 } from 'uuid'
206
+ + import { uuidv4 } from 'uniku/uuid/v4'
207
+ ```
208
+
209
+ ### From `nanoid`
124
210
 
125
- // Validation (default alphabet only)
126
- nanoid.isValid(id)
127
- // => true
211
+ ```diff
212
+ - import { nanoid } from 'nanoid'
213
+ + import { nanoid } from 'uniku/nanoid'
214
+ ```
215
+
216
+ ### From `ulid`
217
+
218
+ ```diff
219
+ - import { ulid } from 'ulid'
220
+ + import { ulid } from 'uniku/ulid'
221
+ ```
222
+
223
+ ### From `@paralleldrive/cuid2`
224
+
225
+ ```diff
226
+ - import { createId } from '@paralleldrive/cuid2'
227
+ + import { cuid2 } from 'uniku/cuid2'
228
+ ```
229
+
230
+ ### From `@owpz/ksuid`
231
+
232
+ ```diff
233
+ - import { KSUID } from '@owpz/ksuid'
234
+ + import { ksuid } from 'uniku/ksuid'
235
+
236
+ - const id = KSUID.random().toString()
237
+ + const id = ksuid()
238
+
239
+ - const parsed = KSUID.parse(str)
240
+ - const timestamp = parsed.timestamp
241
+ + const timestamp = ksuid.timestamp(str)
128
242
  ```
129
243
 
130
244
  ## API Reference
@@ -134,47 +248,32 @@ nanoid.isValid(id)
134
248
  ```ts
135
249
  uuidv4(options?: Version4Options): string
136
250
  uuidv4(options: Version4Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
137
-
138
251
  uuidv4.toBytes(id: string): Uint8Array
139
252
  uuidv4.fromBytes(bytes: Uint8Array): string
140
253
  ```
141
254
 
142
- **Options:**
143
- - `random?: Uint8Array` - 16 bytes of random data
144
-
145
255
  ### `uuidv7` (from `uniku/uuid/v7`)
146
256
 
147
257
  ```ts
148
258
  uuidv7(options?: Version7Options): string
149
259
  uuidv7(options: Version7Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
150
-
151
260
  uuidv7.toBytes(id: string): Uint8Array
152
261
  uuidv7.fromBytes(bytes: Uint8Array): string
153
262
  uuidv7.timestamp(id: string): number
154
263
  uuidv7.isValid(id: string): boolean
155
264
  ```
156
265
 
157
- **Options:**
158
- - `msecs?: number` - Timestamp in milliseconds (defaults to `Date.now()`)
159
- - `seq?: number` - Sequence number for monotonicity
160
- - `random?: Uint8Array` - 16 bytes of random data
161
-
162
266
  ### `ulid` (from `uniku/ulid`)
163
267
 
164
268
  ```ts
165
269
  ulid(options?: UlidOptions): string
166
270
  ulid(options: UlidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
167
-
168
271
  ulid.toBytes(id: string): Uint8Array
169
272
  ulid.fromBytes(bytes: Uint8Array): string
170
273
  ulid.timestamp(id: string): number
171
274
  ulid.isValid(id: string): boolean
172
275
  ```
173
276
 
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
277
  ### `cuid2` (from `uniku/cuid2`)
179
278
 
180
279
  ```ts
@@ -182,12 +281,6 @@ cuid2(options?: Cuid2Options): string
182
281
  cuid2.isValid(id: unknown): id is string
183
282
  ```
184
283
 
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
284
  ### `nanoid` (from `uniku/nanoid`)
192
285
 
193
286
  ```ts
@@ -197,17 +290,42 @@ nanoid(options: NanoidOptions): string
197
290
  nanoid.isValid(id: unknown): id is string
198
291
  ```
199
292
 
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
293
+ ### `ksuid` (from `uniku/ksuid`)
204
294
 
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.
295
+ ```ts
296
+ ksuid(options?: KsuidOptions): string
297
+ ksuid(options: KsuidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
298
+ ksuid.toBytes(id: string): Uint8Array
299
+ ksuid.fromBytes(bytes: Uint8Array): string
300
+ ksuid.timestamp(id: string): number
301
+ ksuid.isValid(id: string): boolean
302
+ ```
206
303
 
207
304
  ## Documentation
208
305
 
209
306
  For advanced usage, examples, and contributing guidelines, see the [full documentation on GitHub](https://github.com/jkomyno/uniku).
210
307
 
308
+ ## Related Projects
309
+
310
+ Third-party libraries that inspired this project:
311
+
312
+ - [uuid](https://github.com/uuidjs/uuid): the most popular UUID library for JavaScript
313
+ - [ulid](https://github.com/ulid/javascript): the reference ULID implementation for JavaScript
314
+ - [@paralleldrive/cuid2](https://github.com/paralleldrive/cuid2): secure, collision-resistant IDs
315
+ - [@owpz/ksuid](https://github.com/owpz/ksuid): K-Sortable Unique Identifier
316
+ - [nanoid](https://github.com/ai/nanoid): tiny, URL-friendly unique string ID generator
317
+
318
+ Other:
319
+
320
+ - [pnpm-monorepo-template](https://github.com/jkomyno/pnpm-monorepo-template): the template I used to create this library
321
+
322
+ ## Author
323
+
324
+ Hi, I'm Alberto Schiabel, aka @jkomyno. You can follow me on:
325
+
326
+ - Github: [@jkomyno](https://github.com/jkomyno)
327
+ - X: [@jkomyno](https://x.com/jkomyno)
328
+
211
329
  ## License
212
330
 
213
- MIT - see [LICENSE](https://github.com/jkomyno/uniku/blob/main/LICENSE)
331
+ MIT — see [LICENSE](https://github.com/jkomyno/uniku/blob/main/LICENSE)
@@ -1,133 +1,2 @@
1
- import { n as getPooledBytes, r as getRandomBytes, t as createPool } from "../random-pool-CRCqpq0I.mjs";
2
- import { sha3_512 } from "@noble/hashes/sha3.js";
3
-
4
- //#region src/cuid2/cuid2.ts
5
- const DEFAULT_LENGTH = 24;
6
- const MAX_LENGTH = 32;
7
- const MIN_LENGTH = 2;
8
- const INITIAL_COUNT_MAX = 476782367;
9
- const CUID2_REGEX = /^[a-z][0-9a-z]+$/;
10
- const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
11
- const LETTER_ALPHABET = "abcdefghijklmnopqrstuvwxyz";
12
- const textEncoder = new TextEncoder();
13
- let pool;
14
- function ensurePool() {
15
- if (!pool) pool = createPool(4);
16
- return pool;
17
- }
18
- /**
19
- * Module-level state for counter and fingerprint.
20
- * Counter is initialized lazily on first call to prevent unnecessary crypto operations.
21
- * Fingerprint is also generated lazily on first call.
22
- */
23
- const state = {
24
- counter: void 0,
25
- fingerprint: void 0
26
- };
27
- /**
28
- * Initialize counter using crypto for consistency with other entropy sources.
29
- */
30
- function initializeCounter() {
31
- const buffer = getRandomBytes(ensurePool(), 4);
32
- return ((buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]) >>> 0) % (INITIAL_COUNT_MAX + 1);
33
- }
34
- function bufToBigInt(buf) {
35
- let value = 0n;
36
- for (const byte of buf) value = value * 256n + BigInt(byte);
37
- return value;
38
- }
39
- function bigIntToBase36(value) {
40
- if (value === 0n) return "0";
41
- const chars = [];
42
- while (value > 0n) {
43
- chars.push(ALPHABET[Number(value % 36n)]);
44
- value = value / 36n;
45
- }
46
- return chars.reverse().join("");
47
- }
48
- function randomLetter(random) {
49
- return LETTER_ALPHABET[Math.floor(random() * 26)];
50
- }
51
- function createEntropy(length, random) {
52
- const chars = new Array(length);
53
- for (let i = 0; i < length; i++) chars[i] = ALPHABET[Math.floor(random() * 36)];
54
- return chars.join("");
55
- }
56
- function hash(input) {
57
- return sha3_512(textEncoder.encode(input));
58
- }
59
- const BIG_LENGTH = 32;
60
- function createFingerprint() {
61
- const random = getCryptoRandom;
62
- return bigIntToBase36(bufToBigInt(hash(Object.keys(globalThis).toString() + createEntropy(BIG_LENGTH, random)))).slice(1, BIG_LENGTH + 1);
63
- }
64
- /**
65
- * Get a random number in [0, 1) using CUID2's own random pool.
66
- * Thread-safe when SharedArrayBuffer is available.
67
- */
68
- function getCryptoRandom() {
69
- const p = ensurePool();
70
- const offset = getPooledBytes(p, 4);
71
- const bytes = p.bytes;
72
- return (bytes[offset] * 16777216 + bytes[offset + 1] * 65536 + bytes[offset + 2] * 256 + bytes[offset + 3]) / 4294967296;
73
- }
74
- function getRandomFn(random) {
75
- if (random) {
76
- if (random.length === 0) throw new Error("Random byte array cannot be empty");
77
- let index = 0;
78
- return () => {
79
- const value = random[index % random.length] / 256;
80
- index += 1;
81
- return value;
82
- };
83
- }
84
- return getCryptoRandom;
85
- }
86
- function cuid2Fn(options) {
87
- const length = options?.length ?? DEFAULT_LENGTH;
88
- if (length < MIN_LENGTH || length > MAX_LENGTH) throw new RangeError(`CUID2 length must be between ${MIN_LENGTH} and ${MAX_LENGTH}. Received: ${length}`);
89
- const random = getRandomFn(options?.random);
90
- if (state.counter === void 0) state.counter = initializeCounter();
91
- if (state.fingerprint === void 0) state.fingerprint = createFingerprint();
92
- const firstLetter = randomLetter(random);
93
- const time = Date.now().toString(36);
94
- state.counter += 1;
95
- const count = state.counter.toString(36);
96
- return firstLetter + bigIntToBase36(bufToBigInt(hash(time + createEntropy(length, random) + count + state.fingerprint))).slice(1, length);
97
- }
98
- function isValid(id) {
99
- return typeof id === "string" && id.length >= MIN_LENGTH && id.length <= MAX_LENGTH && CUID2_REGEX.test(id);
100
- }
101
- /**
102
- * Generate a CUID2 string.
103
- *
104
- * CUID2 is a secure, collision-resistant identifier that hashes multiple
105
- * entropy sources using SHA3-512. Unlike time-ordered IDs (ULID, UUID v7),
106
- * CUID2 prevents enumeration attacks by making IDs non-predictable.
107
- *
108
- * Note: CUID2 does not provide toBytes/fromBytes because it is a string-native
109
- * format with no canonical binary representation (unlike UUID's 16-byte format).
110
- *
111
- * @example
112
- * ```ts
113
- * import { cuid2 } from 'uniku/cuid2'
114
- *
115
- * const id = cuid2()
116
- * // => "pfh0haxfpzowht3oi213cqos"
117
- *
118
- * // Custom length
119
- * const shortId = cuid2({ length: 10 })
120
- * // => "tz4a98xxat"
121
- *
122
- * // Validation (type guard)
123
- * const maybeId: unknown = getUserInput()
124
- * if (cuid2.isValid(maybeId)) {
125
- * console.log(maybeId.length) // TypeScript knows maybeId is string
126
- * }
127
- * ```
128
- */
129
- const cuid2 = Object.assign(cuid2Fn, { isValid });
130
-
131
- //#endregion
132
- export { cuid2 };
1
+ import{sha3_512 as e}from"@noble/hashes/sha3.js";function t(e){let t=4+e*128,n,r=!1;if(typeof SharedArrayBuffer<`u`)try{n=new SharedArrayBuffer(t),r=!0}catch{n=new ArrayBuffer(t)}else n=new ArrayBuffer(t);let i=new Int32Array(n,0,1),a=new Uint8Array(n,4);return crypto.getRandomValues(a),{buffer:n,bytes:a,offset:i,useAtomics:r}}function n(e,t){if(e.useAtomics){let r=Atomics.add(e.offset,0,t);return r+t<=e.bytes.length?r:Atomics.compareExchange(e.offset,0,r+t,t)===r+t?(crypto.getRandomValues(e.bytes),0):n(e,t)}else{let n=e.offset[0];return n+t>e.bytes.length&&(crypto.getRandomValues(e.bytes),n=0),e.offset[0]=n+t,n}}function r(e,t){let r=n(e,t);return e.bytes.subarray(r,r+t)}const i=/^[a-z][0-9a-z]+$/,a=`0123456789abcdefghijklmnopqrstuvwxyz`,o=new TextEncoder;let s;function c(){return s||=t(4),s}const l={counter:void 0,fingerprint:void 0};function u(){let e=r(c(),4);return((e[0]<<24|e[1]<<16|e[2]<<8|e[3])>>>0)%476782368}function d(e){let t=0n;for(let n of e)t=t*256n+BigInt(n);return t}function f(e){if(e===0n)return`0`;let t=[];for(;e>0n;)t.push(a[Number(e%36n)]),e/=36n;return t.reverse().join(``)}function p(e){return`abcdefghijklmnopqrstuvwxyz`[Math.floor(e()*26)]}function m(e,t){let n=Array(e);for(let r=0;r<e;r++)n[r]=a[Math.floor(t()*36)];return n.join(``)}function h(t){return e(o.encode(t))}function g(){let e=_;return f(d(h(Object.keys(globalThis).toString()+m(32,e)))).slice(1,33)}function _(){let e=c(),t=n(e,4),r=e.bytes;return(r[t]*16777216+r[t+1]*65536+r[t+2]*256+r[t+3])/4294967296}function v(e){if(e){if(e.length===0)throw Error(`Random byte array cannot be empty`);let t=0;return()=>{let n=e[t%e.length]/256;return t+=1,n}}return _}function y(e){let t=e?.length??24;if(t<2||t>32)throw RangeError(`CUID2 length must be between 2 and 32. Received: ${t}`);let n=v(e?.random);l.counter===void 0&&(l.counter=u()),l.fingerprint===void 0&&(l.fingerprint=g());let r=p(n),i=Date.now().toString(36);l.counter+=1;let a=l.counter.toString(36);return r+f(d(h(i+m(t,n)+a+l.fingerprint))).slice(1,t)}function b(e){return typeof e==`string`&&e.length>=2&&e.length<=32&&i.test(e)}const x=Object.assign(y,{isValid:b});export{x as cuid2};
133
2
  //# sourceMappingURL=cuid2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"cuid2.mjs","names":["pool: RandomPool | undefined","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'\nimport { createPool, getPooledBytes, getRandomBytes, type RandomPool } from '../common/random-pool'\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// CUID2's own pool - lazily initialized on first use\n// Uses 4 bytes per random() call\nlet pool: RandomPool | undefined\n\nfunction ensurePool(): RandomPool {\n if (!pool) pool = createPool(4)\n return pool\n}\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 = getRandomBytes(ensurePool(), 4)\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 CUID2's own random pool.\n * Thread-safe when SharedArrayBuffer is available.\n */\nfunction getCryptoRandom(): number {\n const p = ensurePool()\n const offset = getPooledBytes(p, 4)\n const bytes = p.bytes\n const value =\n (bytes[offset] * 0x1000000 + bytes[offset + 1] * 0x10000 + bytes[offset + 2] * 0x100 + bytes[offset + 3]) /\n 0x100000000\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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"}
1
+ {"version":3,"file":"cuid2.mjs","names":["buffer: SharedArrayBuffer | ArrayBuffer","pool","pool: RandomPool | undefined","state: { counter: number | undefined; fingerprint: string | undefined }","chars: string[]","cuid2: Cuid2"],"sources":["../../src/common/random-pool.ts","../../src/cuid2/cuid2.ts"],"sourcesContent":["/**\n * Thread-safe random byte pool utilities using Atomics (when SharedArrayBuffer is available).\n * Falls back to regular pooling in environments without SharedArrayBuffer.\n *\n * This module provides STATELESS utilities - each consumer creates and manages its own pool.\n * Pass the pool state to each function (C-style) for explicit, predictable behavior.\n *\n * @example\n * ```ts\n * // Each module creates its own pool\n * let pool: RandomPool | undefined\n *\n * function getBytes(count: number): number {\n * if (!pool) pool = createPool(count)\n * return getPooledBytes(pool, count)\n * }\n * ```\n */\n\nconst POOL_SIZE_MULTIPLIER = 128\nconst POOL_HEADER_SIZE = 4 // 4 bytes for Int32 offset counter\n\n/**\n * Pool state - passed to all pool functions.\n * Each module creates and owns its own pool instance.\n */\nexport type RandomPool = {\n buffer: SharedArrayBuffer | ArrayBuffer\n bytes: Uint8Array\n offset: Int32Array\n useAtomics: boolean\n}\n\n/**\n * Create a new random pool. Uses SharedArrayBuffer + Atomics when available\n * for thread-safety in Web Worker scenarios.\n *\n * @param minSize - Minimum bytes needed per request (pool = minSize * 128)\n */\nexport function createPool(minSize: number): RandomPool {\n const poolSize = minSize * POOL_SIZE_MULTIPLIER\n const totalSize = POOL_HEADER_SIZE + poolSize\n\n let buffer: SharedArrayBuffer | ArrayBuffer\n let useAtomics = false\n\n // Try SharedArrayBuffer first (thread-safe with Atomics)\n if (typeof SharedArrayBuffer !== 'undefined') {\n try {\n buffer = new SharedArrayBuffer(totalSize)\n useAtomics = true\n } catch {\n // SharedArrayBuffer may be disabled (security restrictions)\n buffer = new ArrayBuffer(totalSize)\n }\n } else {\n buffer = new ArrayBuffer(totalSize)\n }\n\n // First 4 bytes = atomic offset counter, rest = random bytes\n const offset = new Int32Array(buffer, 0, 1)\n const bytes = new Uint8Array(buffer, POOL_HEADER_SIZE)\n crypto.getRandomValues(bytes)\n\n return { buffer, bytes, offset, useAtomics }\n}\n\n/**\n * Get pooled random bytes. Thread-safe when SharedArrayBuffer is available.\n * Returns the starting index in the pool for the requested bytes.\n *\n * @param pool - The pool state (from createPool)\n * @param count - Number of bytes needed\n * @returns Starting offset in pool.bytes\n *\n * @example\n * ```ts\n * const offset = getPooledBytes(pool, 21)\n * for (let i = offset; i < offset + 21; i++) {\n * // Use pool.bytes[i]\n * }\n * ```\n */\nexport function getPooledBytes(pool: RandomPool, count: number): number {\n if (pool.useAtomics) {\n // Thread-safe path using Atomics\n const startOffset = Atomics.add(pool.offset, 0, count)\n\n if (startOffset + count <= pool.bytes.length) {\n return startOffset\n }\n\n // Pool exhausted - need to refill\n // Use compareExchange to ensure only one thread refills\n const resetResult = Atomics.compareExchange(pool.offset, 0, startOffset + count, count)\n if (resetResult === startOffset + count) {\n // We won the race - refill the pool\n crypto.getRandomValues(pool.bytes)\n return 0\n }\n\n // Another thread is refilling or already refilled - retry\n return getPooledBytes(pool, count)\n } else {\n // Single-threaded path\n let currentOffset = pool.offset[0]\n\n if (currentOffset + count > pool.bytes.length) {\n crypto.getRandomValues(pool.bytes)\n currentOffset = 0\n }\n\n pool.offset[0] = currentOffset + count\n return currentOffset\n }\n}\n\n/**\n * Get a Uint8Array slice of random bytes from the pool.\n *\n * Note: Returns a subarray VIEW into the pool. If you need the bytes to persist\n * beyond the current synchronous operation, use getRandomBytesCopy() instead.\n *\n * @param pool - The pool state\n * @param count - Number of bytes needed\n */\nexport function getRandomBytes(pool: RandomPool, count: number): Uint8Array {\n const offset = getPooledBytes(pool, count)\n return pool.bytes.subarray(offset, offset + count)\n}\n\n/**\n * Get a COPY of random bytes from the pool.\n * Use this when bytes need to persist (e.g., returned to caller who may use them later).\n *\n * @param pool - The pool state\n * @param count - Number of bytes needed\n */\nexport function getRandomBytesCopy(pool: RandomPool, count: number): Uint8Array {\n const offset = getPooledBytes(pool, count)\n return pool.bytes.slice(offset, offset + count)\n}\n\n/**\n * Fill the provided buffer with random bytes from the pool.\n *\n * @param pool - The pool state\n * @param out - Buffer to fill\n */\nexport function fillRandomBytes(pool: RandomPool, out: Uint8Array): void {\n const offset = getPooledBytes(pool, out.length)\n out.set(pool.bytes.subarray(offset, offset + out.length))\n}\n","import { sha3_512 } from '@noble/hashes/sha3.js'\nimport { createPool, getPooledBytes, getRandomBytes, type RandomPool } from '../common/random-pool'\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// CUID2's own pool - lazily initialized on first use\n// Uses 4 bytes per random() call\nlet pool: RandomPool | undefined\n\nfunction ensurePool(): RandomPool {\n if (!pool) pool = createPool(4)\n return pool\n}\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 = getRandomBytes(ensurePool(), 4)\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 CUID2's own random pool.\n * Thread-safe when SharedArrayBuffer is available.\n */\nfunction getCryptoRandom(): number {\n const p = ensurePool()\n const offset = getPooledBytes(p, 4)\n const bytes = p.bytes\n const value =\n (bytes[offset] * 0x1000000 + bytes[offset + 1] * 0x10000 + bytes[offset + 2] * 0x100 + bytes[offset + 3]) /\n 0x100000000\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,30 @@
1
+ //#region src/ksuid/ksuid.d.ts
2
+ type KsuidOptions = {
3
+ /**
4
+ * 16 bytes of random data to use for KSUID payload.
5
+ */
6
+ random?: Uint8Array;
7
+ /**
8
+ * Timestamp in seconds since Unix epoch.
9
+ * Defaults to Math.floor(Date.now() / 1000).
10
+ * KSUID natively uses second precision.
11
+ */
12
+ secs?: number;
13
+ };
14
+ type Ksuid = {
15
+ (): string;
16
+ <TBuf extends Uint8Array = Uint8Array>(options: KsuidOptions | undefined, buf: TBuf, offset?: number): TBuf;
17
+ (options?: KsuidOptions, 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 KSUID string or write the bytes into a buffer.
25
+ * Also includes helpers to convert to and from byte arrays.
26
+ */
27
+ declare const ksuid: Ksuid;
28
+ //#endregion
29
+ export { Ksuid, KsuidOptions, ksuid };
30
+ //# sourceMappingURL=ksuid.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ksuid.d.mts","names":["ksuid: Ksuid"],"sources":["../../src/ksuid/ksuid.ts"],"sourcesContent":[],"mappings":";KAwBY,YAAA;EAAZ;AAaA;;EAE6B,MAAA,CAAA,EAXlB,UAWkB;EAAqB;;;;;EAG/B,IAAA,CAAA,EAAA,MAAA;CAAA;AAqKNA,KA1KD,KAAA,GA0KQ;;gBAxKJ,aAAa,qBAAqB,+BAA+B,wBAAwB;aAC5F;uBACU;mBACJ;;;;;;;;cAqKNA,OAAO"}
@@ -0,0 +1,2 @@
1
+ import{t as e}from"../random-CPlgK_62.mjs";const t=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`,n=62n,r=new Uint8Array(128);r.fill(255);for(let e=0;e<62;e+=1)r[t.charCodeAt(e)]=e;function i(e){if(e.length<20)throw Error(`KSUID bytes must be at least 20 bytes, got ${e.length}`);let r=0n;for(let t=0;t<20;t+=1)r=r<<8n|BigInt(e[t]);let i=Array(27);for(let e=26;e>=0;--e)i[e]=t[Number(r%n)],r/=n;return i.join(``)}function a(e){if(e.length!==27)throw Error(`KSUID string must be 27 characters, got ${e.length}`);let t=0n;for(let i=0;i<27;i+=1){let a=e.charCodeAt(i);if(a>=128)throw Error(`Invalid KSUID character: ${e[i]}`);let o=r[a];if(o===255)throw Error(`Invalid KSUID character: ${e[i]}`);t=t*n+BigInt(o)}let i=new Uint8Array(20);for(let e=19;e>=0;--e)i[e]=Number(t&255n),t>>=8n;return i}const o=14e8,s=/^[0-9A-Za-z]{27}$/,c={secs:-1/0,lastPayload:new Uint8Array(16)};function l(e){let t=new Uint8Array(e);for(let e=t.length-1;e>=0;--e){if(t[e]<255)return t[e]+=1,t;t[e]=0}return t}function u(e,t,n,r=0){if(!n)n=new Uint8Array(20),r=0;else if(r<0||r+20>n.length)throw RangeError(`KSUID byte range ${r}:${r+20-1} is out of buffer bounds`);n[r]=e>>>24&255,n[r+1]=e>>>16&255,n[r+2]=e>>>8&255,n[r+3]=e&255;for(let e=0;e<16;e+=1)n[r+4+e]=t[e];return n}function d(t,n,r=0){let a,s,d=Date.now(),f=Math.floor(d/1e3)-o;return t?(a=t.secs===void 0?f:t.secs-o,s=t.random?t.random:e()):(a=f,a>c.secs?(s=e(),c.secs=a,c.lastPayload.set(s.subarray(0,16))):(s=l(c.lastPayload),c.lastPayload=s)),n?(u(a,s,n,r),n):i(u(a,s))}function f(e){return a(e)}function p(e){return i(e)}function m(e){let t=a(e);return(((t[0]<<24|t[1]<<16|t[2]<<8|t[3])>>>0)+o)*1e3}function h(e){return typeof e==`string`&&e.length===27&&s.test(e)}const g=Object.assign(d,{toBytes:f,fromBytes:p,timestamp:m,isValid:h});export{g as ksuid};
2
+ //# sourceMappingURL=ksuid.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ksuid.mjs","names":["KSUID_BYTES","KSUID_STRING_LEN","state: KsuidState","secs: number","payload: Uint8Array","ksuid: Ksuid"],"sources":["../../src/ksuid/common/base62.ts","../../src/ksuid/ksuid.ts"],"sourcesContent":["/**\n * Base62 encoding/decoding for KSUID.\n * Alphabet: 0-9A-Za-z (standard Base62 ordering)\n *\n * KSUID binary format: 20 bytes (160 bits)\n * - 4 bytes: timestamp (seconds since KSUID epoch)\n * - 16 bytes: payload (cryptographically random)\n *\n * String format: 27 characters of Base62\n */\n\n// Base62 alphabet: digits (0-9), uppercase (A-Z), lowercase (a-z)\nconst ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst BASE = 62n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\n\n// Pre-computed decode table indexed by ASCII code (0-127)\n// Uses Uint8Array for cache-efficient lookups via charCodeAt\n// Note: Base62 is case-sensitive - 'A' (value 10) and 'a' (value 36) are different\nconst DECODING = new Uint8Array(128)\nDECODING.fill(255) // 255 = invalid marker\n\nfor (let i = 0; i < ALPHABET.length; i += 1) {\n DECODING[ALPHABET.charCodeAt(i)] = i\n}\n\n/**\n * Encode a 20-byte KSUID to a 27-character Base62 string.\n *\n * Algorithm: Convert the 160-bit number to base 62 using BigInt.\n * V8 has highly optimized BigInt operations for this size.\n */\nexport function encodeBase62(bytes: Uint8Array): string {\n if (bytes.length < KSUID_BYTES) {\n throw new Error(`KSUID bytes must be at least ${KSUID_BYTES} bytes, got ${bytes.length}`)\n }\n\n // Convert bytes to BigInt (big-endian)\n let num = 0n\n for (let i = 0; i < KSUID_BYTES; i += 1) {\n num = (num << 8n) | BigInt(bytes[i])\n }\n\n // Convert to Base62 string (build from right to left)\n const result = new Array<string>(KSUID_STRING_LEN)\n\n for (let i = KSUID_STRING_LEN - 1; i >= 0; i -= 1) {\n result[i] = ALPHABET[Number(num % BASE)]\n num = num / BASE\n }\n\n return result.join('')\n}\n\n/**\n * Decode a 27-character Base62 string to a 20-byte KSUID.\n *\n * Algorithm: Convert Base62 string to BigInt, then to bytes.\n * V8 has highly optimized BigInt operations.\n */\nexport function decodeBase62(str: string): Uint8Array {\n if (str.length !== KSUID_STRING_LEN) {\n throw new Error(`KSUID string must be ${KSUID_STRING_LEN} characters, got ${str.length}`)\n }\n\n // Convert Base62 string to BigInt\n let num = 0n\n for (let i = 0; i < KSUID_STRING_LEN; i += 1) {\n const code = str.charCodeAt(i)\n if (code >= 128) {\n throw new Error(`Invalid KSUID character: ${str[i]}`)\n }\n const value = DECODING[code]\n if (value === 255) {\n throw new Error(`Invalid KSUID character: ${str[i]}`)\n }\n num = num * BASE + BigInt(value)\n }\n\n // Convert BigInt to bytes (big-endian)\n const bytes = new Uint8Array(KSUID_BYTES)\n for (let i = KSUID_BYTES - 1; i >= 0; i -= 1) {\n bytes[i] = Number(num & 0xffn)\n num = num >> 8n\n }\n\n return bytes\n}\n","import { rng } from '../uuid/common/random'\nimport { decodeBase62, encodeBase62 } from './common/base62'\n\n/**\n * KSUID (K-Sortable Unique Identifier)\n *\n * A 160-bit identifier consisting of:\n * - 4 bytes: timestamp (seconds since KSUID epoch: May 13, 2014)\n * - 16 bytes: cryptographically random payload\n *\n * Encoded as a 27-character Base62 string.\n */\n\n// KSUID epoch: May 13, 2014 00:00:00 UTC (Unix timestamp in seconds)\nconst KSUID_EPOCH = 1400000000\n\nconst KSUID_BYTES = 20\nconst KSUID_STRING_LEN = 27\nconst TIMESTAMP_BYTES = 4\nconst PAYLOAD_BYTES = 16\n\n// Validation regex: 27 alphanumeric characters (case-insensitive)\nconst KSUID_REGEX = /^[0-9A-Za-z]{27}$/\n\nexport type KsuidOptions = {\n /**\n * 16 bytes of random data to use for KSUID payload.\n */\n random?: Uint8Array\n /**\n * Timestamp in seconds since Unix epoch.\n * Defaults to Math.floor(Date.now() / 1000).\n * KSUID natively uses second precision.\n */\n secs?: number\n}\n\nexport type Ksuid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: KsuidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: KsuidOptions, 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\ntype KsuidState = {\n secs: number\n lastPayload: Uint8Array\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same second.\n *\n * IMPORTANT: This state persists across all ksuid() 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: KsuidState = {\n secs: -Infinity,\n lastPayload: new Uint8Array(PAYLOAD_BYTES),\n}\n\n/**\n * Increment a 16-byte array by 1, propagating carry from LSB to MSB.\n * Returns a new array (does not modify input).\n */\nfunction incrementPayload(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\n/**\n * Write KSUID bytes to a buffer.\n */\nfunction ksuidBytes(secs: number, payload: Uint8Array, buf?: Uint8Array, offset = 0): Uint8Array {\n if (!buf) {\n buf = new Uint8Array(KSUID_BYTES)\n offset = 0\n } else if (offset < 0 || offset + KSUID_BYTES > buf.length) {\n throw new RangeError(`KSUID byte range ${offset}:${offset + KSUID_BYTES - 1} is out of buffer bounds`)\n }\n\n // Timestamp (32-bit big-endian seconds since KSUID epoch) -> bytes 0-3\n buf[offset] = (secs >>> 24) & 0xff\n buf[offset + 1] = (secs >>> 16) & 0xff\n buf[offset + 2] = (secs >>> 8) & 0xff\n buf[offset + 3] = secs & 0xff\n\n // Payload (128 bits) -> bytes 4-19\n for (let i = 0; i < PAYLOAD_BYTES; i += 1) {\n buf[offset + TIMESTAMP_BYTES + i] = payload[i]\n }\n\n return buf\n}\n\n/*\n * Overload: no buffer => return a KSUID string.\n */\nfunction ksuidFn(options?: KsuidOptions, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with KSUID bytes.\n */\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(\n options: KsuidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction ksuidFn<TBuf extends Uint8Array = Uint8Array>(options?: KsuidOptions, buf?: TBuf, offset = 0): string | TBuf {\n let secs: number\n let payload: 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 KSUIDs generated within a single request will have the same timestamp.\n * - the monotonic ordering will rely entirely on incrementing the payload portion.\n */\n const now = Date.now()\n const defaultSecs = Math.floor(now / 1000) - KSUID_EPOCH\n\n if (options) {\n // Explicit options provided - use them directly without monotonic state\n secs = options.secs !== undefined ? options.secs - KSUID_EPOCH : defaultSecs\n\n if (options.random) {\n payload = options.random\n } else {\n payload = rng()\n }\n } else {\n secs = defaultSecs\n\n if (secs > state.secs) {\n // New second: generate fresh random payload\n payload = rng()\n state.secs = secs\n state.lastPayload.set(payload.subarray(0, PAYLOAD_BYTES))\n } else {\n // Same second: increment payload for monotonic ordering\n payload = incrementPayload(state.lastPayload)\n state.lastPayload = payload\n }\n }\n\n if (buf) {\n ksuidBytes(secs, payload, buf, offset)\n return buf\n }\n\n // String mode: create bytes then encode to Base62\n const bytes = ksuidBytes(secs, payload)\n return encodeBase62(bytes)\n}\n\n/**\n * Convert a KSUID string to 20 bytes.\n * Case-insensitive: accepts uppercase, lowercase, or mixed case.\n */\nfunction toBytes(id: string): Uint8Array {\n return decodeBase62(id)\n}\n\n/**\n * Convert 20 bytes to a KSUID string.\n */\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeBase62(bytes)\n}\n\n/**\n * Extract the timestamp from a KSUID string.\n * Returns Unix timestamp in milliseconds for API consistency with ulid/uuidv7.\n * Note: KSUID only has second precision, so the returned value will always end in 000.\n */\nfunction timestamp(id: string): number {\n const bytes = decodeBase62(id)\n // First 4 bytes are big-endian timestamp (seconds since KSUID epoch)\n const secs = ((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0\n // Add KSUID epoch and convert to milliseconds\n return (secs + KSUID_EPOCH) * 1000\n}\n\n/**\n * Validate a KSUID string.\n * Case-insensitive: accepts uppercase, lowercase, or mixed case.\n */\nfunction isValid(id: string): boolean {\n return typeof id === 'string' && id.length === KSUID_STRING_LEN && KSUID_REGEX.test(id)\n}\n\n/**\n * Generate a KSUID string or write the bytes into a buffer.\n * Also includes helpers to convert to and from byte arrays.\n */\nexport const ksuid: Ksuid = Object.assign(ksuidFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n})\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"nanoid.d.mts","names":["nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":[],"mappings":";;AAGa,cAAA,YAAA,GAAA,kEAAA;AASD,KAAA,aAAA,GAAA;EAkBZ;AAoJA;;;;WAhKW;;;;;;;;;;;KAYC,MAAA;;;;;;YAMA;;;;;;;;;;cA8ICA,QAAQ"}
1
+ {"version":3,"file":"nanoid.d.mts","names":["nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":[],"mappings":";;AACa,cAAA,YAAA,GAAA,kEAAA;AAwBD,KAAA,aAAA,GAAA;EAkBZ;AAmJA;;;;WA/JW;;;;;;;;;;;KAYC,MAAA;;;;;;YAMA;;;;;;;;;;cA6ICA,QAAQ"}