uniku 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -22
- package/build/{bytes-DzcOKIx1.mjs → bytes-xqWxFYsM.mjs} +1 -1
- package/build/{bytes-DzcOKIx1.mjs.map → bytes-xqWxFYsM.mjs.map} +1 -1
- package/build/cuid/v2.d.mts +2 -0
- package/build/cuid/v2.mjs +1 -0
- package/build/cuid2/cuid2.d.mts +5 -1
- package/build/cuid2/cuid2.d.mts.map +1 -1
- package/build/cuid2/cuid2.mjs +1 -1
- package/build/cuid2/cuid2.mjs.map +1 -1
- package/build/errors-Dgoyi-ci.d.mts +38 -0
- package/build/errors-Dgoyi-ci.d.mts.map +1 -0
- package/build/errors.d.mts +2 -38
- package/build/errors.mjs +1 -1
- package/build/errors.mjs.map +1 -1
- package/build/generators.d.mts +17 -0
- package/build/generators.d.mts.map +1 -0
- package/build/generators.mjs +2 -0
- package/build/generators.mjs.map +1 -0
- package/build/ksuid/ksuid.d.mts +3 -5
- package/build/ksuid/ksuid.d.mts.map +1 -1
- package/build/ksuid/ksuid.mjs +1 -1
- package/build/ksuid/ksuid.mjs.map +1 -1
- package/build/nanoid/nanoid.d.mts +3 -6
- package/build/nanoid/nanoid.d.mts.map +1 -1
- package/build/nanoid/nanoid.mjs +1 -1
- package/build/nanoid/nanoid.mjs.map +1 -1
- package/build/objectid/objectid.d.mts +60 -0
- package/build/objectid/objectid.d.mts.map +1 -0
- package/build/objectid/objectid.mjs +2 -0
- package/build/objectid/objectid.mjs.map +1 -0
- package/build/random-Chp-Nkzi.mjs +2 -0
- package/build/{random-CGMm8owG.mjs.map → random-Chp-Nkzi.mjs.map} +1 -1
- package/build/tsid/tsid.d.mts +82 -0
- package/build/tsid/tsid.d.mts.map +1 -0
- package/build/tsid/tsid.mjs +2 -0
- package/build/tsid/tsid.mjs.map +1 -0
- package/build/typeid/typeid.d.mts +1 -1
- package/build/typeid/typeid.d.mts.map +1 -1
- package/build/typeid/typeid.mjs.map +1 -1
- package/build/ulid/ulid.d.mts +3 -5
- package/build/ulid/ulid.d.mts.map +1 -1
- package/build/ulid/ulid.mjs +1 -1
- package/build/ulid/ulid.mjs.map +1 -1
- package/build/uuid/v4.d.mts +3 -5
- package/build/uuid/v4.d.mts.map +1 -1
- package/build/uuid/v4.mjs +1 -1
- package/build/uuid/v4.mjs.map +1 -1
- package/build/uuid/v7.d.mts +3 -5
- package/build/uuid/v7.d.mts.map +1 -1
- package/build/uuid/v7.mjs +1 -1
- package/build/uuid/v7.mjs.map +1 -1
- package/build/{uuid-Ub6ladEr.mjs → uuid-BBjwHv7J.mjs} +1 -1
- package/build/uuid-BBjwHv7J.mjs.map +1 -0
- package/package.json +35 -5
- package/src/cuid/v2.ts +19 -0
- package/src/cuid2/cuid2.ts +4 -0
- package/src/generators.ts +17 -0
- package/src/objectid/hex.ts +89 -0
- package/src/objectid/objectid.ts +266 -0
- package/src/tsid/crockford64.ts +75 -0
- package/src/tsid/tsid.ts +310 -0
- package/build/errors.d.mts.map +0 -1
- package/build/random-CGMm8owG.mjs +0 -2
- package/build/tsconfig.tsbuildinfo +0 -1
- package/build/uuid-Ub6ladEr.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -24,19 +24,21 @@ console.log(first < second && second < third) // true
|
|
|
24
24
|
|
|
25
25
|
## At a Glance
|
|
26
26
|
|
|
27
|
-
| | uniku | [uuid](https://github.com/uuidjs/uuid) | [nanoid](https://github.com/ai/nanoid) | [ulid](https://github.com/ulid/javascript) | [cuid2](https://github.com/paralleldrive/cuid2) | [ksuid](https://github.com/owpz/ksuid) |
|
|
28
|
-
|
|
29
|
-
| UUID v4 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
30
|
-
| UUID v7 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
31
|
-
| TypeID | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
| KSUID | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
27
|
+
| | uniku | [uuid](https://github.com/uuidjs/uuid) | [typeid-js](https://github.com/jetify-com/typeid-js) | [nanoid](https://github.com/ai/nanoid) | [ulid](https://github.com/ulid/javascript) | [cuid2](https://github.com/paralleldrive/cuid2) | [ksuid](https://github.com/owpz/ksuid) | [bson](https://github.com/mongodb/js-bson) | [tsid-ts](https://github.com/yubinTW/tsid-ts) |
|
|
28
|
+
|--------------------|:-----:|:----:|:---------:|:------:|:----:|:-----:|:-----:|:-----:|:-----:|
|
|
29
|
+
| UUID v4 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
30
|
+
| UUID v7 | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
31
|
+
| TypeID | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
32
|
+
| Nanoid | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
33
|
+
| ULID | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
34
|
+
| CUID2 | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
35
|
+
| KSUID | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
|
|
36
|
+
| ObjectID | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
|
|
37
|
+
| TSID | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
|
|
38
|
+
| Tree-shakeable | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
39
|
+
| ESM-only | ✅ | ✅¹ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
40
|
+
| Edge/Workers | ✅ | ✅ | ✅ | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
41
|
+
| Byte ↔ String | ✅ | ✅ | ✅ | - | ⚠️² | - | ✅ | ✅ | ✅ |
|
|
40
42
|
|
|
41
43
|
> **Notes:**
|
|
42
44
|
> - Byte ↔ String conversion doesn't make sense for nanoid and cuid2, since they are string-native formats with no canonical binary representation.
|
|
@@ -63,6 +65,8 @@ Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
|
|
|
63
65
|
| ULID | **85× faster** |
|
|
64
66
|
| CUID2 | **8× faster** |
|
|
65
67
|
| KSUID | **1.5× faster** |
|
|
68
|
+
| ObjectID | **1.1× faster** |
|
|
69
|
+
| TSID | **1.7× faster** |
|
|
66
70
|
| UUID v7 | **1.1× faster** |
|
|
67
71
|
| Nanoid | **~comparable speed** |
|
|
68
72
|
| Nanoid (10 chars) | npm is 1.1× faster |
|
|
@@ -82,6 +86,8 @@ Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
|
|
|
82
86
|
| Maximum compatibility | **UUID v4** | Universal standard |
|
|
83
87
|
| Distributed systems needing sortable, URL-safe IDs | **ULID** | Millisecond ordering + 80-bit entropy |
|
|
84
88
|
| Very high-volume distributed systems | **KSUID** | Time-ordered with 128-bit entropy |
|
|
89
|
+
| MongoDB `_id` compatibility | **ObjectID** | Drop-in match for MongoDB's native document ID format |
|
|
90
|
+
| Native 64-bit sortable integer ID | **TSID** | Fits a database `BIGINT` primary key, no UUID-sized overhead |
|
|
85
91
|
|
|
86
92
|
### Detailed Guide
|
|
87
93
|
|
|
@@ -114,6 +120,18 @@ Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
|
|
|
114
120
|
- URL-safe, 128-bit random entropy (highest of all formats)
|
|
115
121
|
- Best for: Very high-volume distributed systems, event sourcing
|
|
116
122
|
|
|
123
|
+
**ObjectID** — Use when you need drop-in compatibility with MongoDB.
|
|
124
|
+
- 24-character lowercase hex, time-ordered with second precision
|
|
125
|
+
- 4-byte timestamp + 5-byte per-process random + 3-byte always-incrementing counter
|
|
126
|
+
- Counter never resets on a new timestamp (unlike ULID/UUIDv7/KSUID sequences)
|
|
127
|
+
- Best for: MongoDB `_id` fields, systems already speaking the ObjectID wire format
|
|
128
|
+
|
|
129
|
+
**TSID** — Use when you need a native 64-bit sortable integer ID, e.g. a database `BIGINT` primary key.
|
|
130
|
+
- 64-bit Snowflake-style value, time-ordered with millisecond precision
|
|
131
|
+
- Returns a `bigint` by default (the only uniku generator that isn't string-primary); `toString`/`fromString` convert to/from the 13-character canonical string
|
|
132
|
+
- 42-bit timestamp + 10-bit node + 12-bit per-millisecond counter, with a persistent lazily-random node ID
|
|
133
|
+
- Best for: relational database `BIGINT` primary keys, high-throughput distributed ID generation without central coordination
|
|
134
|
+
|
|
117
135
|
**Nanoid** — Use for short, URL-friendly identifiers.
|
|
118
136
|
- 21 characters (configurable), 126-bit entropy
|
|
119
137
|
- Customizable alphabet and length
|
|
@@ -128,7 +146,7 @@ Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
|
|
|
128
146
|
|
|
129
147
|
### Binary Serialization: When to Use `toBytes()`
|
|
130
148
|
|
|
131
|
-
Formats with binary representations (UUID, ULID, KSUID) support `toBytes()` and `fromBytes()` for efficient storage:
|
|
149
|
+
Formats with binary representations (UUID, ULID, KSUID, ObjectID, TSID) support `toBytes()` and `fromBytes()` for efficient storage:
|
|
132
150
|
|
|
133
151
|
```ts
|
|
134
152
|
// Store as BINARY(16) instead of VARCHAR(36)
|
|
@@ -149,6 +167,8 @@ const id = uuidv7.fromBytes(row.id)
|
|
|
149
167
|
- Performance-critical queries on ID columns
|
|
150
168
|
- Storage cost is a concern
|
|
151
169
|
|
|
170
|
+
**Exception — `tsid`:** `toBytes()`/`fromBytes()` operate on `bigint`, not `string`, since `bigint` is TSID's primary type (see [`tsid`](#tsid-from-unikutsid) in the API Reference below). Every other generator's `toBytes()`/`fromBytes()` operate on its primary `string` type.
|
|
171
|
+
|
|
152
172
|
**When to keep strings:**
|
|
153
173
|
- Debugging/logging convenience
|
|
154
174
|
- API responses (humans read them)
|
|
@@ -160,10 +180,14 @@ const id = uuidv7.fromBytes(row.id)
|
|
|
160
180
|
| ULID | 26 chars | 16 bytes | 38% |
|
|
161
181
|
| TypeID | prefix + 27 chars | 16 bytes | prefix-dependent |
|
|
162
182
|
| KSUID | 27 chars | 20 bytes | 26% |
|
|
183
|
+
| ObjectID | 24 chars | 12 bytes | 50% |
|
|
184
|
+
| TSID | 13 chars* | 8 bytes | 38% |
|
|
163
185
|
| Nanoid | 21 chars | N/A | - |
|
|
164
186
|
| CUID2 | 24 chars | N/A | - |
|
|
165
187
|
|
|
166
188
|
> **Note**: Nanoid and CUID2 don't have binary representations because they're string-native formats with no canonical byte encoding.
|
|
189
|
+
>
|
|
190
|
+
> \* TSID's primary type is `bigint`, not a string; the 13-character figure is its canonical `toString()` form, shown here only for a size comparison against the other formats' native strings.
|
|
167
191
|
|
|
168
192
|
## Installation
|
|
169
193
|
|
|
@@ -192,9 +216,13 @@ deno install npm:uniku
|
|
|
192
216
|
| `uniku/uuid/v7` | ~1.4 KB |
|
|
193
217
|
| `uniku/ulid` | ~1.8 KB |
|
|
194
218
|
| `uniku/typeid` | ~1.6 KB |
|
|
219
|
+
| `uniku/cuid/v2` | ~992 B* |
|
|
195
220
|
| `uniku/cuid2` | ~1007 B* |
|
|
196
221
|
| `uniku/nanoid` | ~1.1 KB |
|
|
197
222
|
| `uniku/ksuid` | ~1.3 KB |
|
|
223
|
+
| `uniku/objectid` | ~1.3 KB |
|
|
224
|
+
| `uniku/tsid` | ~1.4 KB |
|
|
225
|
+
| `uniku/generators` | ~98 B |
|
|
198
226
|
|
|
199
227
|
* The CUID2 entry point imports SHA3-512 from `@noble/hashes`; this table's entry-point size excludes that external dependency.
|
|
200
228
|
|
|
@@ -257,21 +285,25 @@ const canonical = typeid('')
|
|
|
257
285
|
### CUID2 (secure, non-time-ordered)
|
|
258
286
|
|
|
259
287
|
```ts
|
|
260
|
-
import {
|
|
288
|
+
import { cuidv2 } from 'uniku/cuid/v2'
|
|
261
289
|
|
|
262
|
-
const id =
|
|
290
|
+
const id = cuidv2()
|
|
263
291
|
// => "pfh0haxfpzowht3oi213cqos"
|
|
264
292
|
|
|
265
293
|
// Custom length
|
|
266
|
-
const shortId =
|
|
294
|
+
const shortId = cuidv2({ length: 10 })
|
|
267
295
|
// => "tz4a98xxat"
|
|
268
296
|
|
|
269
297
|
// Validation (type guard)
|
|
270
|
-
if (
|
|
298
|
+
if (cuidv2.isValid(maybeId)) {
|
|
271
299
|
console.log(maybeId) // TypeScript knows this is a string
|
|
272
300
|
}
|
|
273
301
|
```
|
|
274
302
|
|
|
303
|
+
> `uniku/cuid/v2` is the canonical entry point. The older `uniku/cuid2` import
|
|
304
|
+
> still works and re-exports the same generator, but is now deprecated — prefer
|
|
305
|
+
> `import { cuidv2 } from 'uniku/cuid/v2'`.
|
|
306
|
+
|
|
275
307
|
### Nanoid (URL-friendly, custom alphabet)
|
|
276
308
|
|
|
277
309
|
```ts
|
|
@@ -305,6 +337,44 @@ const str = ksuid.fromBytes(bytes)
|
|
|
305
337
|
const ts = ksuid.timestamp(id)
|
|
306
338
|
```
|
|
307
339
|
|
|
340
|
+
### ObjectID (time-ordered, MongoDB-compatible)
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
import { objectid } from 'uniku/objectid'
|
|
344
|
+
|
|
345
|
+
const id = objectid()
|
|
346
|
+
// => "667c3f2a1e2b3c4d5e6f7081"
|
|
347
|
+
|
|
348
|
+
// Convert to/from bytes
|
|
349
|
+
const bytes = objectid.toBytes(id)
|
|
350
|
+
const str = objectid.fromBytes(bytes)
|
|
351
|
+
|
|
352
|
+
// Extract timestamp (milliseconds)
|
|
353
|
+
const ts = objectid.timestamp(id)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### TSID (time-ordered, 64-bit integer)
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { tsid } from 'uniku/tsid'
|
|
360
|
+
|
|
361
|
+
// Generate a TSID bigint (the primary type, not a string)
|
|
362
|
+
const id = tsid()
|
|
363
|
+
// => 862301223059968074n
|
|
364
|
+
|
|
365
|
+
// Convert to/from the canonical 13-character string
|
|
366
|
+
const str = tsid.toString(id)
|
|
367
|
+
// => "0QXW2CK4XZM2A"
|
|
368
|
+
tsid.fromString(str) === id // true
|
|
369
|
+
|
|
370
|
+
// Extract timestamp (milliseconds, full precision)
|
|
371
|
+
const ts = tsid.timestamp(id)
|
|
372
|
+
|
|
373
|
+
// Convert to/from 8 bytes
|
|
374
|
+
const bytes = tsid.toBytes(id)
|
|
375
|
+
const restored = tsid.fromBytes(bytes)
|
|
376
|
+
```
|
|
377
|
+
|
|
308
378
|
## Migrating to uniku
|
|
309
379
|
|
|
310
380
|
### From `uuid`
|
|
@@ -349,6 +419,19 @@ const ts = ksuid.timestamp(id)
|
|
|
349
419
|
+ const timestamp = ksuid.timestamp(str)
|
|
350
420
|
```
|
|
351
421
|
|
|
422
|
+
### From `bson`
|
|
423
|
+
|
|
424
|
+
```diff
|
|
425
|
+
- import { ObjectId } from 'bson'
|
|
426
|
+
+ import { objectid } from 'uniku/objectid'
|
|
427
|
+
|
|
428
|
+
- const id = new ObjectId().toHexString()
|
|
429
|
+
+ const id = objectid()
|
|
430
|
+
|
|
431
|
+
- const timestamp = new ObjectId(str).getTimestamp().getTime()
|
|
432
|
+
+ const timestamp = objectid.timestamp(str)
|
|
433
|
+
```
|
|
434
|
+
|
|
352
435
|
## API Reference
|
|
353
436
|
|
|
354
437
|
### `uuidv4` (from `uniku/uuid/v4`)
|
|
@@ -405,13 +488,16 @@ typeid.isValid(id: unknown): id is string
|
|
|
405
488
|
|
|
406
489
|
Use an empty prefix (`typeid('')`) to generate Jetify-compatible prefixless TypeIDs.
|
|
407
490
|
|
|
408
|
-
### `
|
|
491
|
+
### `cuidv2` (from `uniku/cuid/v2`)
|
|
409
492
|
|
|
410
493
|
```ts
|
|
411
|
-
|
|
412
|
-
|
|
494
|
+
cuidv2(options?: CuidV2Options): string
|
|
495
|
+
cuidv2.isValid(id: unknown): id is string
|
|
413
496
|
```
|
|
414
497
|
|
|
498
|
+
> Also available (deprecated) as `cuid2` from `uniku/cuid2` — the same generator
|
|
499
|
+
> under the pre-versioned entry point.
|
|
500
|
+
|
|
415
501
|
### `nanoid` (from `uniku/nanoid`)
|
|
416
502
|
|
|
417
503
|
```ts
|
|
@@ -437,9 +523,40 @@ ksuid.NIL // "000000000000000000000000000"
|
|
|
437
523
|
ksuid.MAX // "aWgEPTl1tmebfsQzFP4bxwgy80V"
|
|
438
524
|
```
|
|
439
525
|
|
|
526
|
+
### `objectid` (from `uniku/objectid`)
|
|
527
|
+
|
|
528
|
+
```ts
|
|
529
|
+
objectid(options?: ObjectIdOptions): string
|
|
530
|
+
objectid(options: ObjectIdOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
531
|
+
objectid.toBytes(id: string): Uint8Array
|
|
532
|
+
objectid.fromBytes(bytes: Uint8Array): string
|
|
533
|
+
objectid.timestamp(id: string): number
|
|
534
|
+
objectid.isValid(id: unknown): id is string
|
|
535
|
+
objectid.NIL // "000000000000000000000000"
|
|
536
|
+
objectid.MAX // "ffffffffffffffffffffffff"
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### `tsid` (from `uniku/tsid`)
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
tsid(options?: TsidOptions): bigint
|
|
543
|
+
tsid(options: TsidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
544
|
+
tsid.toBytes(id: bigint): Uint8Array
|
|
545
|
+
tsid.fromBytes(bytes: Uint8Array): bigint
|
|
546
|
+
tsid.toString(id: bigint): string
|
|
547
|
+
tsid.fromString(str: string): bigint
|
|
548
|
+
tsid.timestamp(id: bigint, epoch?: number): number
|
|
549
|
+
tsid.isValid(id: unknown): id is bigint
|
|
550
|
+
tsid.NIL // 0n
|
|
551
|
+
tsid.MAX // 18446744073709551615n
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
> Unlike every other generator listed here, `tsid()` returns `bigint` by default, and `toBytes`/`fromBytes`/`timestamp`/`isValid` all operate on that `bigint`. `toString`/`fromString` are the boundary conversions to/from the 13-character canonical Crockford Base32 string.
|
|
555
|
+
|
|
440
556
|
## Documentation
|
|
441
557
|
|
|
442
558
|
For advanced usage, examples, and contributing guidelines, see the [full documentation on GitHub](https://github.com/jkomyno/uniku).
|
|
559
|
+
The [`uniku@1.x` stability contract](../../docs/STABILITY.md) defines the supported entry points, runtimes, and release gates.
|
|
443
560
|
|
|
444
561
|
## Related Projects
|
|
445
562
|
|
|
@@ -447,10 +564,12 @@ Third-party libraries that inspired this project:
|
|
|
447
564
|
|
|
448
565
|
- [uuid](https://github.com/uuidjs/uuid): the most popular UUID library for JavaScript
|
|
449
566
|
- [ulid](https://github.com/ulid/javascript): the reference ULID implementation for JavaScript
|
|
450
|
-
- [TypeID](https://github.com/jetify-com/typeid):
|
|
567
|
+
- [TypeID-JS](https://github.com/jetify-com/typeid-js): official JavaScript implementation of TypeID
|
|
451
568
|
- [@paralleldrive/cuid2](https://github.com/paralleldrive/cuid2): secure, collision-resistant IDs
|
|
452
569
|
- [@owpz/ksuid](https://github.com/owpz/ksuid): K-Sortable Unique Identifier
|
|
570
|
+
- [bson](https://github.com/mongodb/js-bson): official MongoDB BSON library, including the ObjectId implementation
|
|
453
571
|
- [nanoid](https://github.com/ai/nanoid): tiny, URL-friendly unique string ID generator
|
|
572
|
+
- [tsid-ts](https://github.com/yubinTW/tsid-ts): TypeScript implementation of TSID, a Snowflake-style 64-bit Time-Sorted Unique Identifier
|
|
454
573
|
|
|
455
574
|
Other:
|
|
456
575
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
function e(e){for(let t=e.length-1;t>=0;--t){if(e[t]<255)return e[t]+=1,!0;e[t]=0}return!1}function t(e,t,n){e[t]=n/1099511627776&255,e[t+1]=n/4294967296&255,e[t+2]=n/16777216&255,e[t+3]=n/65536&255,e[t+4]=n/256&255,e[t+5]=n&255}function n(e,t,n){e[t]=n>>>24&255,e[t+1]=n>>>16&255,e[t+2]=n>>>8&255,e[t+3]=n&255}export{n,t as r,e as t};
|
|
2
|
-
//# sourceMappingURL=bytes-
|
|
2
|
+
//# sourceMappingURL=bytes-xqWxFYsM.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bytes-
|
|
1
|
+
{"version":3,"file":"bytes-xqWxFYsM.mjs","names":[],"sources":["../src/common/bytes.ts"],"sourcesContent":["/**\n * Common byte manipulation utilities shared across ID generators.\n */\n\n/**\n * Increment a byte array by 1 in-place, propagating carry from LSB to MSB.\n * Mutates the input array directly - no allocation.\n *\n * Used by ULID and KSUID for monotonic ordering within the same time unit.\n *\n * @returns true if increment succeeded, false if all bytes overflowed to 0\n */\nexport function incrementBytesInPlace(bytes: Uint8Array): boolean {\n for (let i = bytes.length - 1; i >= 0; i -= 1) {\n if (bytes[i] < 255) {\n bytes[i] += 1\n return true\n }\n bytes[i] = 0\n }\n\n // All bytes overflowed to 0 - astronomically unlikely (1 in 2^80 for ULID, 1 in 2^128 for KSUID)\n return false\n}\n\n/**\n * Write a 48-bit timestamp as big-endian bytes.\n * Used by UUID v7 and ULID for millisecond-precision timestamps.\n *\n * @param buf - Target buffer\n * @param offset - Starting byte offset\n * @param msecs - Milliseconds since Unix epoch (must fit in 48 bits)\n */\nexport function writeTimestamp48(buf: Uint8Array, offset: number, msecs: number): void {\n buf[offset] = (msecs / 0x10000000000) & 0xff\n buf[offset + 1] = (msecs / 0x100000000) & 0xff\n buf[offset + 2] = (msecs / 0x1000000) & 0xff\n buf[offset + 3] = (msecs / 0x10000) & 0xff\n buf[offset + 4] = (msecs / 0x100) & 0xff\n buf[offset + 5] = msecs & 0xff\n}\n\n/**\n * Write a 32-bit timestamp as big-endian bytes.\n * Used by KSUID for second-precision timestamps.\n *\n * `writeTimestamp32(buf, offset, timestamp)` is equivalent to `new DataView(buf.buffer, offset, 4).setUint32(offset, timestamp, false)`,\n * but it's faster because it doesn't require creating a DataView object.\n *\n * @param buf - Target buffer\n * @param offset - Starting byte offset\n * @param secs - Seconds (must fit in 32 bits)\n */\nexport function writeTimestamp32(buf: Uint8Array, offset: number, secs: number): void {\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"],"mappings":"AAYA,SAAgB,EAAsB,EAA4B,CAChE,IAAK,IAAI,EAAI,EAAM,OAAS,EAAG,GAAK,EAAG,IAAQ,CAC7C,GAAI,EAAM,GAAK,IAEb,MADA,GAAM,IAAM,EACL,GAET,EAAM,GAAK,CACb,CAGA,MAAO,EACT,CAUA,SAAgB,EAAiB,EAAiB,EAAgB,EAAqB,CACrF,EAAI,GAAW,EAAQ,cAAiB,IACxC,EAAI,EAAS,GAAM,EAAQ,WAAe,IAC1C,EAAI,EAAS,GAAM,EAAQ,SAAa,IACxC,EAAI,EAAS,GAAM,EAAQ,MAAW,IACtC,EAAI,EAAS,GAAM,EAAQ,IAAS,IACpC,EAAI,EAAS,GAAK,EAAQ,GAC5B,CAaA,SAAgB,EAAiB,EAAiB,EAAgB,EAAoB,CACpF,EAAI,GAAW,IAAS,GAAM,IAC9B,EAAI,EAAS,GAAM,IAAS,GAAM,IAClC,EAAI,EAAS,GAAM,IAAS,EAAK,IACjC,EAAI,EAAS,GAAK,EAAO,GAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cuid2 as e}from"../cuid2/cuid2.mjs";export{e as cuidv2};
|
package/build/cuid2/cuid2.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError } from "../errors-Dgoyi-ci.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/cuid2/cuid2.d.ts
|
|
4
4
|
type Cuid2Options = {
|
|
@@ -46,6 +46,10 @@ type Cuid2 = {
|
|
|
46
46
|
* console.log(maybeId.length) // TypeScript knows maybeId is string
|
|
47
47
|
* }
|
|
48
48
|
* ```
|
|
49
|
+
*
|
|
50
|
+
* @deprecated Use `cuidv2` from `uniku/cuid/v2` instead. This entry point keeps
|
|
51
|
+
* working unchanged, but `uniku/cuid/v2` is the canonical versioned subpath
|
|
52
|
+
* (mirroring `uniku/uuid/v4` / `uniku/uuid/v7`).
|
|
49
53
|
*/
|
|
50
54
|
declare const cuid2: Cuid2;
|
|
51
55
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cuid2.d.mts","names":[
|
|
1
|
+
{"version":3,"file":"cuid2.d.mts","names":[],"sources":["../../src/cuid2/cuid2.ts"],"mappings":";;;KAIY,YAAA;;;AAAZ;;EAKE,MAAA;EAOS;;;;;AAAA;EAAT,MAAA,GAAS,UAAA;AAAA;AAAA,KAGC,KAAA;EAAA,CACT,OAAA,GAAU,YAAA;EACX,OAAA,CAAQ,EAAA,YAAc,EAAA;AAAA;;;;;AAAA;AA6LxB;;;;AAAoB;;;;;;;;;;;;;;;;;;;;;;;cAAP,KAAA,EAAO,KAAA"}
|
package/build/cuid2/cuid2.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{n as e}from"../random-Chp-Nkzi.mjs";import{InvalidInputError as t,UniqueIdError as n}from"../errors.mjs";import{sha3_512 as r}from"@noble/hashes/sha3.js";const i=/^[a-z][0-9a-z]+$/,a=`0123456789abcdefghijklmnopqrstuvwxyz`,o=new TextEncoder,s={counter:void 0,fingerprint:void 0};function c(){return e()%476782368}function l(e){let t=0n;for(let n of e)t=t*256n+BigInt(n);return t}function u(e){if(e===0n)return`0`;let t=[];for(;e>0n;)t.push(a[Number(e%36n)]),e/=36n;return t.reverse().join(``)}function d(e){return`abcdefghijklmnopqrstuvwxyz`[Math.floor(e()*26)]}function f(e,t){let n=Array(e);for(let r=0;r<e;r++)n[r]=a[Math.floor(t()*36)];return n.join(``)}function p(e){return r(o.encode(e))}function m(){let e=h;return u(l(p(Object.keys(globalThis).toString()+f(32,e)))).slice(1,33)}function h(){return e()/4294967296}function g(e){if(e){if(e.length===0)throw new t(`CUID2_RANDOM_BYTES_EMPTY`,`Random byte array cannot be empty`);let n=0;return()=>{let t=e[n%e.length]/256;return n+=1,t}}return h}function _(e){let n=e?.length??24;if(n<2||n>32)throw new t(`CUID2_LENGTH_OUT_OF_RANGE`,`CUID2 length must be between 2 and 32. Received: ${n}`);let r=g(e?.random);s.counter===void 0&&(s.counter=c()),s.fingerprint===void 0&&(s.fingerprint=m());let i=d(r),a=Date.now().toString(36);s.counter+=1;let o=s.counter.toString(36);return i+u(l(p(a+f(n,r)+o+s.fingerprint))).slice(1,n)}function v(e){return typeof e==`string`&&e.length>=2&&e.length<=32&&i.test(e)}const y=Object.assign(_,{isValid:v});export{t as InvalidInputError,n as UniqueIdError,y as cuid2};
|
|
2
2
|
//# sourceMappingURL=cuid2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cuid2.mjs","names":[
|
|
1
|
+
{"version":3,"file":"cuid2.mjs","names":[],"sources":["../../src/cuid2/cuid2.ts"],"sourcesContent":["import { sha3_512 } from '@noble/hashes/sha3.js'\nimport { randomUint32 } from '../common/random'\nimport { InvalidInputError } from '../errors'\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/**\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 return randomUint32() % (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 */\nfunction getCryptoRandom(): number {\n return randomUint32() / 0x100000000\n}\n\nfunction getRandomFn(random?: Uint8Array): () => number {\n if (random) {\n if (random.length === 0) {\n throw new InvalidInputError('CUID2_RANDOM_BYTES_EMPTY', '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 InvalidInputError(\n 'CUID2_LENGTH_OUT_OF_RANGE',\n `CUID2 length must be between ${MIN_LENGTH} and ${MAX_LENGTH}. Received: ${length}`,\n )\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 *\n * @deprecated Use `cuidv2` from `uniku/cuid/v2` instead. This entry point keeps\n * working unchanged, but `uniku/cuid/v2` is the canonical versioned subpath\n * (mirroring `uniku/uuid/v4` / `uniku/uuid/v7`).\n */\nexport const cuid2: Cuid2 = Object.assign(cuid2Fn, {\n isValid,\n})\n\nexport { InvalidInputError, UniqueIdError } from '../errors'\n"],"mappings":"iKAwBA,MASM,EAAc,mBAGd,EAAW,uCAIX,EAAc,IAAI,YAOlB,EAA0E,CAC9E,QAAS,IAAA,GACT,YAAa,IAAA,EACf,EAKA,SAAS,GAA4B,CACnC,OAAO,EAAa,EAAK,SAC3B,CAIA,SAAS,EAAY,EAAyB,CAC5C,IAAI,EAAQ,GACZ,IAAK,IAAM,KAAQ,EACjB,EAAQ,EAAQ,KAAO,OAAO,CAAI,EAEpC,OAAO,CACT,CAEA,SAAS,EAAe,EAAuB,CAC7C,GAAI,IAAU,GAAI,MAAO,IACzB,IAAM,EAAkB,CAAC,EACzB,KAAO,EAAQ,IACb,EAAM,KAAK,EAAS,OAAO,EAAQ,GAAG,EAAE,EACxC,GAAgB,IAElB,OAAO,EAAM,QAAQ,CAAC,CAAC,KAAK,EAAE,CAChC,CAEA,SAAS,EAAa,EAA8B,CAClD,MAAO,6BAAgB,KAAK,MAAM,EAAO,EAAI,EAAE,EACjD,CAEA,SAAS,EAAc,EAAgB,EAA8B,CACnE,IAAM,EAAY,MAAc,CAAM,EACtC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAQ,IAC1B,EAAM,GAAK,EAAS,KAAK,MAAM,EAAO,EAAI,EAAE,GAE9C,OAAO,EAAM,KAAK,EAAE,CACtB,CAIA,SAAS,EAAK,EAA2B,CACvC,OAAO,EAAS,EAAY,OAAO,CAAK,CAAC,CAC3C,CAMA,SAAS,GAA4B,CAEnC,IAAM,EAAS,EAIf,OAAO,EAAe,EADP,EAFC,OAAO,KAAK,UAAU,CAAC,CAAC,SACb,EAAI,EAAc,GAAY,CAAM,CAExB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAG,EAAc,CACpE,CAOA,SAAS,GAA0B,CACjC,OAAO,EAAa,EAAI,UAC1B,CAEA,SAAS,EAAY,EAAmC,CACtD,GAAI,EAAQ,CACV,GAAI,EAAO,SAAW,EACpB,MAAM,IAAI,EAAkB,2BAA4B,mCAAmC,EAE7F,IAAI,EAAQ,EACZ,UAAa,CACX,IAAM,EAAQ,EAAO,EAAQ,EAAO,QAAU,IAE9C,MADA,IAAS,EACF,CACT,CACF,CACA,OAAO,CACT,CAIA,SAAS,EAAQ,EAAgC,CAC/C,IAAM,EAAS,GAAS,QAAU,GAElC,GAAI,EAAS,GAAc,EAAS,GAClC,MAAM,IAAI,EACR,4BACA,oDAA2E,GAC7E,EAGF,IAAM,EAAS,EAAY,GAAS,MAAM,EAGtC,EAAM,UAAY,IAAA,KACpB,EAAM,QAAU,EAAkB,GAIhC,EAAM,cAAgB,IAAA,KACxB,EAAM,YAAc,EAAkB,GAGxC,IAAM,EAAc,EAAa,CAAM,EACjC,EAAO,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EACnC,EAAM,SAAW,EACjB,IAAM,EAAQ,EAAM,QAAQ,SAAS,EAAE,EAQvC,OAAO,EAHY,EAAe,EADnB,EADG,EAFL,EAAc,EAAQ,CAEP,EAAI,EAAQ,EAAM,WAEK,CAAC,CAGtB,CAAC,CAAC,MAAM,EAAG,CAAM,CACjD,CAIA,SAAS,EAAQ,EAA2B,CAC1C,OAAO,OAAO,GAAO,UAAY,EAAG,QAAU,GAAc,EAAG,QAAU,IAAc,EAAY,KAAK,CAAE,CAC5G,CAkCA,MAAa,EAAe,OAAO,OAAO,EAAS,CACjD,SACF,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base error for all uniku errors.
|
|
4
|
+
* Provides `_tag` for discriminated matching (compatible with Effect's `catchTag`)
|
|
5
|
+
* and `code` for machine-readable error identification.
|
|
6
|
+
*/
|
|
7
|
+
declare abstract class UniqueIdError extends Error {
|
|
8
|
+
abstract readonly _tag: string;
|
|
9
|
+
abstract readonly code: string;
|
|
10
|
+
constructor(message: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Thrown when generator arguments are invalid (bad size, alphabet, length, timestamp, version).
|
|
14
|
+
*/
|
|
15
|
+
declare class InvalidInputError extends UniqueIdError {
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly _tag = "InvalidInputError";
|
|
18
|
+
constructor(code: string, message: string);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Thrown when parsing/decoding an ID string that has invalid format or characters.
|
|
22
|
+
*/
|
|
23
|
+
declare class ParseError extends UniqueIdError {
|
|
24
|
+
readonly code: string;
|
|
25
|
+
readonly _tag = "ParseError";
|
|
26
|
+
constructor(code: string, message: string);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown when a byte array or buffer is too short or an offset is out of bounds.
|
|
30
|
+
*/
|
|
31
|
+
declare class BufferError extends UniqueIdError {
|
|
32
|
+
readonly code: string;
|
|
33
|
+
readonly _tag = "BufferError";
|
|
34
|
+
constructor(code: string, message: string);
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { UniqueIdError as i, InvalidInputError as n, ParseError as r, BufferError as t };
|
|
38
|
+
//# sourceMappingURL=errors-Dgoyi-ci.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors-Dgoyi-ci.d.mts","names":[],"sources":["../src/errors.ts"],"mappings":";;AAKA;;;;uBAAsB,aAAA,SAAsB,KAAA;EAAA,kBACxB,IAAA;EAAA,kBACA,IAAA;EAElB,WAAA,CAAY,OAAA;AAAA;;AAAA;AASd;cAAa,iBAAA,SAA0B,aAAA;EAAA,SAInC,IAAA;EAAA,SAHO,IAAA;EAET,WAAA,CACE,IAAA,UACA,OAAA;AAAA;;;;cASS,UAAA,SAAmB,aAAA;EAAA,SAI5B,IAAA;EAAA,SAHO,IAAA;EAET,WAAA,CACE,IAAA,UACA,OAAA;AAAA;;;;cASS,WAAA,SAAoB,aAAA;EAAA,SAI7B,IAAA;EAAA,SAHO,IAAA;EAET,WAAA,CACE,IAAA,UACA,OAAA;AAAA"}
|
package/build/errors.d.mts
CHANGED
|
@@ -1,38 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* Base error for all uniku errors.
|
|
4
|
-
* Provides `_tag` for discriminated matching (compatible with Effect's `catchTag`)
|
|
5
|
-
* and `code` for machine-readable error identification.
|
|
6
|
-
*/
|
|
7
|
-
declare abstract class UniqueIdError extends Error {
|
|
8
|
-
abstract readonly _tag: string;
|
|
9
|
-
abstract readonly code: string;
|
|
10
|
-
constructor(message: string);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Thrown when generator arguments are invalid (bad size, alphabet, length, timestamp, version).
|
|
14
|
-
*/
|
|
15
|
-
declare class InvalidInputError extends UniqueIdError {
|
|
16
|
-
readonly code: string;
|
|
17
|
-
readonly _tag: "InvalidInputError";
|
|
18
|
-
constructor(code: string, message: string);
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Thrown when parsing/decoding an ID string that has invalid format or characters.
|
|
22
|
-
*/
|
|
23
|
-
declare class ParseError extends UniqueIdError {
|
|
24
|
-
readonly code: string;
|
|
25
|
-
readonly _tag: "ParseError";
|
|
26
|
-
constructor(code: string, message: string);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Thrown when a byte array or buffer is too short or an offset is out of bounds.
|
|
30
|
-
*/
|
|
31
|
-
declare class BufferError extends UniqueIdError {
|
|
32
|
-
readonly code: string;
|
|
33
|
-
readonly _tag: "BufferError";
|
|
34
|
-
constructor(code: string, message: string);
|
|
35
|
-
}
|
|
36
|
-
//#endregion
|
|
37
|
-
export { BufferError, InvalidInputError, ParseError, UniqueIdError };
|
|
38
|
-
//# sourceMappingURL=errors.d.mts.map
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "./errors-Dgoyi-ci.mjs";
|
|
2
|
+
export { BufferError, InvalidInputError, ParseError, UniqueIdError };
|
package/build/errors.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=class extends Error{constructor(e){super(e),this.name=this.constructor.name}},t=class extends e{_tag=`InvalidInputError`;constructor(e,t){super(t),this.code=e}},n=class extends e{_tag=`ParseError`;constructor(e,t){super(t),this.code=e}},r=class extends e{_tag=`BufferError`;constructor(e,t){super(t),this.code=e}};export{r as BufferError,t as InvalidInputError,n as ParseError,e as UniqueIdError};
|
|
1
|
+
var e=class extends Error{constructor(e){super(e),this.name=this.constructor.name}},t=class extends e{code;_tag=`InvalidInputError`;constructor(e,t){super(t),this.code=e}},n=class extends e{code;_tag=`ParseError`;constructor(e,t){super(t),this.code=e}},r=class extends e{code;_tag=`BufferError`;constructor(e,t){super(t),this.code=e}};export{r as BufferError,t as InvalidInputError,n as ParseError,e as UniqueIdError};
|
|
2
2
|
//# sourceMappingURL=errors.mjs.map
|
package/build/errors.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[
|
|
1
|
+
{"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base error for all uniku errors.\n * Provides `_tag` for discriminated matching (compatible with Effect's `catchTag`)\n * and `code` for machine-readable error identification.\n */\nexport abstract class UniqueIdError extends Error {\n abstract readonly _tag: string\n abstract readonly code: string\n\n constructor(message: string) {\n super(message)\n this.name = this.constructor.name\n }\n}\n\n/**\n * Thrown when generator arguments are invalid (bad size, alphabet, length, timestamp, version).\n */\nexport class InvalidInputError extends UniqueIdError {\n readonly _tag = 'InvalidInputError' as const\n\n constructor(\n readonly code: string,\n message: string,\n ) {\n super(message)\n }\n}\n\n/**\n * Thrown when parsing/decoding an ID string that has invalid format or characters.\n */\nexport class ParseError extends UniqueIdError {\n readonly _tag = 'ParseError' as const\n\n constructor(\n readonly code: string,\n message: string,\n ) {\n super(message)\n }\n}\n\n/**\n * Thrown when a byte array or buffer is too short or an offset is out of bounds.\n */\nexport class BufferError extends UniqueIdError {\n readonly _tag = 'BufferError' as const\n\n constructor(\n readonly code: string,\n message: string,\n ) {\n super(message)\n }\n}\n"],"mappings":"AAKA,IAAsB,EAAtB,cAA4C,KAAM,CAIhD,YAAY,EAAiB,CAC3B,MAAM,CAAO,EACb,KAAK,KAAO,KAAK,YAAY,IAC/B,CACF,EAKa,EAAb,cAAuC,CAAc,CAIxC,KAHX,KAAgB,oBAEhB,YACE,EACA,EACA,CACA,MAAM,CAAO,EAHJ,KAAA,KAAA,CAIX,CACF,EAKa,EAAb,cAAgC,CAAc,CAIjC,KAHX,KAAgB,aAEhB,YACE,EACA,EACA,CACA,MAAM,CAAO,EAHJ,KAAA,KAAA,CAIX,CACF,EAKa,EAAb,cAAiC,CAAc,CAIlC,KAHX,KAAgB,cAEhB,YACE,EACA,EACA,CACA,MAAM,CAAO,EAHJ,KAAA,KAAA,CAIX,CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/generators.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The canonical, ordered list of ID generators supported by uniku.
|
|
4
|
+
*
|
|
5
|
+
* This is the single source of truth for the set of supported ID generators.
|
|
6
|
+
* Tools that need to enumerate or validate ID generators (for example,
|
|
7
|
+
* `@uniku/cli`'s `--type` flag) should derive from this array rather than
|
|
8
|
+
* hand-copying the union.
|
|
9
|
+
*/
|
|
10
|
+
declare const ID_GENERATORS: readonly ["uuid", "ulid", "typeid", "nanoid", "cuid", "ksuid", "objectid", "tsid"];
|
|
11
|
+
/**
|
|
12
|
+
* The union of ID generators supported by uniku, derived from {@link ID_GENERATORS}.
|
|
13
|
+
*/
|
|
14
|
+
type IdGenerator = (typeof ID_GENERATORS)[number];
|
|
15
|
+
//#endregion
|
|
16
|
+
export { ID_GENERATORS, IdGenerator };
|
|
17
|
+
//# sourceMappingURL=generators.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generators.d.mts","names":[],"sources":["../src/generators.ts"],"mappings":";;AAWA;;;;AAA+F;AAK/F;;cALa,aAAA;;AAKqB;;KAAtB,WAAA,WAAsB,aAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generators.mjs","names":[],"sources":["../src/generators.ts"],"sourcesContent":["/**\n * The canonical, ordered list of ID generators supported by uniku.\n *\n * This is the single source of truth for the set of supported ID generators.\n * Tools that need to enumerate or validate ID generators (for example,\n * `@uniku/cli`'s `--type` flag) should derive from this array rather than\n * hand-copying the union.\n */\n// Plain `as const` (not `as const satisfies readonly string[]`): the `satisfies`\n// wrapper defeats isolatedDeclarations' trivial declaration emit for this export.\n// All members are string literals, so the shape guard adds nothing here anyway.\nexport const ID_GENERATORS = ['uuid', 'ulid', 'typeid', 'nanoid', 'cuid', 'ksuid', 'objectid', 'tsid'] as const\n\n/**\n * The union of ID generators supported by uniku, derived from {@link ID_GENERATORS}.\n */\nexport type IdGenerator = (typeof ID_GENERATORS)[number]\n"],"mappings":"AAWA,MAAa,EAAgB,CAAC,OAAQ,OAAQ,SAAU,SAAU,OAAQ,QAAS,WAAY,MAAM"}
|
package/build/ksuid/ksuid.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as UniqueIdError, n as InvalidInputError, r as ParseError, t as BufferError } from "../errors-Dgoyi-ci.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/ksuid/ksuid.d.ts
|
|
4
4
|
type KsuidOptions = {
|
|
@@ -20,10 +20,8 @@ type Ksuid = {
|
|
|
20
20
|
toBytes(id: string): Uint8Array;
|
|
21
21
|
fromBytes(bytes: Uint8Array): string;
|
|
22
22
|
timestamp(id: string): number;
|
|
23
|
-
isValid(id: unknown): id is string;
|
|
24
|
-
/** The
|
|
25
|
-
NIL: string;
|
|
26
|
-
/** The max KSUID (maximum valid value) */
|
|
23
|
+
isValid(id: unknown): id is string; /** The nil KSUID (all zeros) */
|
|
24
|
+
NIL: string; /** The max KSUID (maximum valid value) */
|
|
27
25
|
MAX: string;
|
|
28
26
|
};
|
|
29
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ksuid.d.mts","names":[
|
|
1
|
+
{"version":3,"file":"ksuid.d.mts","names":[],"sources":["../../src/ksuid/ksuid.ts"],"mappings":";;;KAgCY,YAAA;;;AAAZ;EAIE,MAAA,GAAS,UAAA;;;;;;EAMT,IAAA;AAAA;AAAA,KAGU,KAAA;EAAA;EAAA,cAEI,UAAA,GAAa,UAAA,EAAY,OAAA,EAAS,YAAA,cAA0B,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;EAAA,CACtG,OAAA,GAAU,YAAA,EAAc,GAAA,cAAiB,MAAA;EAC1C,OAAA,CAAQ,EAAA,WAAa,UAAA;EACrB,SAAA,CAAU,KAAA,EAAO,UAAA;EACjB,SAAA,CAAU,EAAA;EACV,OAAA,CAAQ,EAAA,YAAc,EAAA;EAEtB,GAAA;EAEA,GAAA;AAAA;;;;;cAiIW,KAAA,EAAO,KAAA"}
|
package/build/ksuid/ksuid.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{r as e}from"../random-Chp-Nkzi.mjs";import{BufferError as t,InvalidInputError as n,ParseError as r,UniqueIdError as i}from"../errors.mjs";import{n as a}from"../bytes-xqWxFYsM.mjs";const o=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`,s=62n,c=(1n<<160n)-1n,l=new Uint8Array(65536);l.fill(255);for(let e=0;e<62;e+=1)l[o.charCodeAt(e)]=e;function u(e){if(e.length<20)throw new t(`KSUID_BYTES_TOO_SHORT`,`KSUID bytes must be at least 20 bytes, got ${e.length}`);let n=0n;for(let t=0;t<20;t+=1)n=n<<8n|BigInt(e[t]);let r=``;for(;n>0n;){let e=n%s;n/=s,r=o[Number(e)]+r}return r.padStart(27,`0`)}function d(e){if(e.length!==27)throw new r(`KSUID_INVALID_LENGTH`,`KSUID string must be 27 characters, got ${e.length}`);let t=0n;for(let n=0;n<27;n+=1){let i=l[e.charCodeAt(n)];if(i===255)throw new r(`KSUID_INVALID_CHAR`,`Invalid KSUID character: ${e[n]}`);t=t*s+BigInt(i)}if(t>c)throw new r(`KSUID_OVERFLOW`,`KSUID string exceeds 160-bit range`);let n=new Uint8Array(20);for(let e=19;e>=0;--e)n[e]=Number(t&255n),t>>=8n;return n}const f=14e8,p=u(new Uint8Array(20).fill(255)),m=/^[0-9A-Za-z]{27}$/;function h(e,n,r,i=0){if(!r)r=new Uint8Array(20),i=0;else if(i<0||i+20>r.length)throw new t(`KSUID_BUFFER_OUT_OF_BOUNDS`,`KSUID byte range ${i}:${i+20-1} is out of buffer bounds`);a(r,i,e);for(let e=0;e<16;e+=1)r[i+4+e]=n[e];return r}function g(t,r,i=0){let a=t?.random;if(a&&a.length<16)throw new n(`KSUID_RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 16 for KSUID`);let o,s=t?.secs;if(s!==void 0){if(s<f)throw new n(`KSUID_TIMESTAMP_TOO_LOW`,`Timestamp must be >= KSUID epoch`);if(s>5694967295)throw new n(`KSUID_TIMESTAMP_TOO_HIGH`,`Timestamp must be <= maximum KSUID timestamp`);o=s-f}else o=Math.floor(Date.now()/1e3)-f;let c=a??e();return r?(h(o,c,r,i),r):u(h(o,c))}function _(e){return d(e)}function v(e){return u(e)}function y(e){let t=d(e);return(((t[0]<<24|t[1]<<16|t[2]<<8|t[3])>>>0)+f)*1e3}function b(e){return typeof e==`string`&&e.length===27&&m.test(e)&&e<=p}const x=Object.assign(g,{toBytes:_,fromBytes:v,timestamp:y,isValid:b,NIL:`000000000000000000000000000`,MAX:p});export{t as BufferError,n as InvalidInputError,r as ParseError,i as UniqueIdError,x as ksuid};
|
|
2
2
|
//# sourceMappingURL=ksuid.mjs.map
|