uniku 0.4.1 → 0.4.3
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 +54 -561
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,618 +2,111 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/uniku)
|
|
4
4
|
[](https://npmjs.com/package/uniku)
|
|
5
|
-
[](https://bundlephobia.com/package/uniku)
|
|
6
5
|
[](https://github.com/jkomyno/uniku/actions/workflows/ci.yaml)
|
|
7
|
-
[](https://jkomyno.github.io/uniku/)
|
|
8
7
|
[](https://opensource.org/licenses/MIT)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
Ten ID strategies behind one consistent, type-safe API. The fastest implementation of each in our [current CI benchmark](https://jkomyno.github.io/uniku/docs/guides/performance/).
|
|
11
10
|
|
|
12
|
-
> **uniku** */uˈniːku
|
|
11
|
+
> **uniku** */uˈniːku/*: Maltese for "unique"
|
|
12
|
+
|
|
13
|
+
[Documentation](https://jkomyno.github.io/uniku/) · [Getting started](https://jkomyno.github.io/uniku/docs/getting-started/) · [Choosing an ID](https://jkomyno.github.io/uniku/docs/guides/choosing-an-id/) · [API reference](https://jkomyno.github.io/uniku/docs/reference/entry-points/) · [CLI](https://jkomyno.github.io/uniku/docs/cli/)
|
|
13
14
|
|
|
14
15
|
```ts
|
|
15
16
|
import { uuidv7 } from 'uniku/uuid/v7'
|
|
16
17
|
|
|
17
18
|
const id = uuidv7()
|
|
18
19
|
// => "018e5e5c-7c8a-7000-8000-000000000000"
|
|
19
|
-
|
|
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
|
-
```
|
|
24
|
-
|
|
25
|
-
## At a Glance
|
|
26
|
-
|
|
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
|
-
| XID | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
|
|
39
|
-
| Tree-shakeable | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
|
|
40
|
-
| ESM-only | ✅ | ✅¹ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
|
|
41
|
-
| Edge/Workers | ✅ | ✅ | ✅ | ✅ | ⚠️ | ✅ | ⚠️ | ⚠️ | ⚠️ |
|
|
42
|
-
| Byte ↔ String | ✅ | ✅ | ✅ | - | ⚠️² | - | ✅ | ✅ | ✅ |
|
|
43
|
-
|
|
44
|
-
> **Notes:**
|
|
45
|
-
> - Byte ↔ String conversion doesn't make sense for nanoid and cuid2, since they are string-native formats with no canonical binary representation.
|
|
46
|
-
> - ¹ `uuid@13` is ESM-only; earlier versions support CommonJS.
|
|
47
|
-
> - ² `ulid` only provides timestamp encoding/decoding, not full binary serialization.
|
|
48
|
-
|
|
49
|
-
### Works Everywhere
|
|
50
|
-
|
|
51
|
-
<p align="center">
|
|
52
|
-
<img src="https://img.shields.io/badge/Node.js-339933?logo=nodedotjs&logoColor=white" alt="Node.js">
|
|
53
|
-
<img src="https://img.shields.io/badge/Deno-000000?logo=deno&logoColor=white" alt="Deno">
|
|
54
|
-
<img src="https://img.shields.io/badge/Bun-000000?logo=bun&logoColor=white" alt="Bun">
|
|
55
|
-
<img src="https://img.shields.io/badge/Cloudflare_Workers-F38020?logo=cloudflare&logoColor=white" alt="Cloudflare Workers">
|
|
56
|
-
<img src="https://img.shields.io/badge/Vercel_Edge-000000?logo=vercel&logoColor=white" alt="Vercel Edge">
|
|
57
|
-
<img src="https://img.shields.io/badge/Browsers-4285F4?logo=googlechrome&logoColor=white" alt="Browsers">
|
|
58
|
-
</p>
|
|
59
|
-
|
|
60
|
-
Uses `globalThis.crypto` (Web Crypto API) — no Node.js-specific APIs.
|
|
61
|
-
|
|
62
|
-
## Performance
|
|
63
|
-
|
|
64
|
-
| Generator | uniku vs npm |
|
|
65
|
-
|-----------|-------------:|
|
|
66
|
-
| ULID | **85× faster** |
|
|
67
|
-
| CUID2 | **8× faster** |
|
|
68
|
-
| KSUID | **1.5× faster** |
|
|
69
|
-
| ObjectID | **1.1× faster** |
|
|
70
|
-
| TSID | **1.7× faster** |
|
|
71
|
-
| XID | **1.3× faster** |
|
|
72
|
-
| UUID v7 | **1.1× faster** |
|
|
73
|
-
| Nanoid | **1.9× faster** |
|
|
74
|
-
| Nanoid (10 chars) | **1.3× faster** |
|
|
75
|
-
| TypeID | **2.6× faster** |
|
|
76
|
-
| UUID v4 | npm is 1.1× faster |
|
|
77
|
-
|
|
78
|
-
## Which ID Should I Use?
|
|
79
|
-
|
|
80
|
-
### Quick Recommendations
|
|
81
|
-
|
|
82
|
-
| Use Case | Recommended | Why |
|
|
83
|
-
|----------|-------------|-----|
|
|
84
|
-
| Database primary keys | **UUID v7** or **ULID** | Time-ordered for index efficiency |
|
|
85
|
-
| API/domain identifiers | **TypeID** | UUID v7 with readable type prefixes like `user_...` |
|
|
86
|
-
| URL shorteners | **Nanoid** | Compact, URL-safe characters |
|
|
87
|
-
| Prevent enumeration | **CUID2** | Non-sequential, secure |
|
|
88
|
-
| Maximum compatibility | **UUID v4** | Universal standard |
|
|
89
|
-
| Distributed systems needing sortable, URL-safe IDs | **ULID** | Millisecond ordering + 80-bit entropy |
|
|
90
|
-
| Very high-volume distributed systems | **KSUID** | Time-ordered with 128-bit entropy |
|
|
91
|
-
| MongoDB `_id` compatibility | **ObjectID** | Drop-in match for MongoDB's native document ID format |
|
|
92
|
-
| Native 64-bit sortable integer ID | **TSID** | Fits a database `BIGINT` primary key, no UUID-sized overhead |
|
|
93
|
-
| Go rs/xid compatibility | **XID** | Compact, time-ordered 12-byte identifier |
|
|
94
|
-
|
|
95
|
-
### Detailed Guide
|
|
96
|
-
|
|
97
|
-
**UUID v4** — Use when you need maximum compatibility with existing systems.
|
|
98
|
-
- 36 characters, 122-bit entropy
|
|
99
|
-
- Universally supported by databases (native `UUID` type)
|
|
100
|
-
- Not time-ordered (random distribution)
|
|
101
|
-
- Best for: Legacy systems, cross-platform compatibility
|
|
102
|
-
|
|
103
|
-
**UUID v7** — Use for database primary keys when you need RFC compliance.
|
|
104
|
-
- 36 characters, time-ordered with millisecond precision
|
|
105
|
-
- Native database support as `UUID` type
|
|
106
|
-
- Strictly monotonic within same millisecond
|
|
107
|
-
- Best for: PostgreSQL/MySQL primary keys, API identifiers
|
|
108
|
-
|
|
109
|
-
**ULID** — Use for database primary keys when you want URL-safe IDs.
|
|
110
|
-
- 26 characters, time-ordered with millisecond precision
|
|
111
|
-
- URL-safe (no hyphens), case-insensitive
|
|
112
|
-
- 80-bit random entropy per millisecond
|
|
113
|
-
- Best for: APIs, URLs, distributed systems needing sortability
|
|
114
|
-
|
|
115
|
-
**TypeID** — Use when API IDs should expose their entity type.
|
|
116
|
-
- Prefix plus 26-character UUID v7 suffix, e.g. `user_01h2xcejqtf2nbrexx3vqjhp41`
|
|
117
|
-
- Empty prefix support for canonical 26-character TypeIDs
|
|
118
|
-
- Time-ordered through UUID v7, with lowercase snake_case type prefixes
|
|
119
|
-
- Best for: Public API identifiers, logs, debugging, domain-specific IDs
|
|
120
|
-
|
|
121
|
-
**KSUID** — Use when you need highest collision resistance.
|
|
122
|
-
- 27 characters, time-ordered with second precision
|
|
123
|
-
- URL-safe, 128-bit random entropy (highest of all formats)
|
|
124
|
-
- Best for: Very high-volume distributed systems, event sourcing
|
|
125
|
-
|
|
126
|
-
**ObjectID** — Use when you need drop-in compatibility with MongoDB.
|
|
127
|
-
- 24-character lowercase hex, time-ordered with second precision
|
|
128
|
-
- 4-byte timestamp + 5-byte per-process random + 3-byte always-incrementing counter
|
|
129
|
-
- Counter never resets on a new timestamp (unlike ULID/UUIDv7/KSUID sequences)
|
|
130
|
-
- Best for: MongoDB `_id` fields, systems already speaking the ObjectID wire format
|
|
131
|
-
|
|
132
|
-
**TSID** — Use when you need a native 64-bit sortable integer ID, e.g. a database `BIGINT` primary key.
|
|
133
|
-
- 64-bit Snowflake-style value, time-ordered with millisecond precision
|
|
134
|
-
- Returns a `bigint` by default (the only uniku generator that isn't string-primary); `toString`/`fromString` convert to/from the 13-character canonical string
|
|
135
|
-
- 42-bit timestamp + 10-bit node + 12-bit per-millisecond counter, with a persistent lazily-random node ID
|
|
136
|
-
- Best for: relational database `BIGINT` primary keys, high-throughput distributed ID generation without central coordination
|
|
137
|
-
|
|
138
|
-
**Nanoid** — Use for short, URL-friendly identifiers.
|
|
139
|
-
- 21 characters (configurable), 126-bit entropy
|
|
140
|
-
- Customizable alphabet and length
|
|
141
|
-
- Not time-ordered
|
|
142
|
-
- Best for: URL shorteners, session tokens, invite codes
|
|
143
|
-
|
|
144
|
-
**CUID2** — Use when ID unpredictability matters.
|
|
145
|
-
- 24 characters (configurable), hash-based
|
|
146
|
-
- Non-sequential, prevents enumeration attacks
|
|
147
|
-
- Not time-ordered
|
|
148
|
-
- Best for: User-facing IDs where security matters
|
|
149
|
-
|
|
150
|
-
### Binary Serialization: When to Use `toBytes()`
|
|
151
|
-
|
|
152
|
-
Formats with binary representations (UUID, ULID, KSUID, ObjectID, TSID) support `toBytes()` and `fromBytes()` for efficient storage:
|
|
153
|
-
|
|
154
|
-
```ts
|
|
155
|
-
// Store as BINARY(16) instead of VARCHAR(36)
|
|
156
|
-
const bytes = uuidv7.toBytes(id) // 16 bytes
|
|
157
|
-
db.insert({ id: bytes })
|
|
158
|
-
|
|
159
|
-
// Retrieve and convert back
|
|
160
|
-
const id = uuidv7.fromBytes(row.id)
|
|
161
20
|
```
|
|
162
21
|
|
|
163
|
-
|
|
164
|
-
- **Up to 50% smaller**: E.g., 16 bytes vs 36 characters for UUID
|
|
165
|
-
- **Faster indexing**: Binary comparison is faster than string comparison
|
|
166
|
-
- **Reduced I/O**: Less data transferred between app and database
|
|
167
|
-
|
|
168
|
-
**When to use binary:**
|
|
169
|
-
- High-volume tables (millions of rows)
|
|
170
|
-
- Performance-critical queries on ID columns
|
|
171
|
-
- Storage cost is a concern
|
|
172
|
-
|
|
173
|
-
**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.
|
|
174
|
-
|
|
175
|
-
**When to keep strings:**
|
|
176
|
-
- Debugging/logging convenience
|
|
177
|
-
- API responses (humans read them)
|
|
178
|
-
- Small tables where savings are negligible
|
|
22
|
+
## Why `uniku`?
|
|
179
23
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
| ObjectID | 24 chars | 12 bytes | 50% |
|
|
187
|
-
| TSID | 13 chars* | 8 bytes | 38% |
|
|
188
|
-
| Nanoid | 21 chars | N/A | - |
|
|
189
|
-
| CUID2 | 24 chars | N/A | - |
|
|
24
|
+
- **Fastest across the benchmark suite.** Every strategy beats its dedicated npm alternative in the current isolated-process CI benchmark, from 1.2× for UUID v4 and ObjectID to 116× for ULID.
|
|
25
|
+
- **Ten strategies, one type-safe API.** UUID v4/v7, ULID, TypeID, CUID v2, Nanoid, KSUID, ObjectID, XID, and TSID each expose a callable generator with matching typed helpers.
|
|
26
|
+
- **Portable by default.** Runs on Node.js, Bun, Deno, browsers, edge runtimes, and Cloudflare Workers using `globalThis.crypto`.
|
|
27
|
+
- **Tree-shakeable entry points.** Import only the generator you use; the package root is intentionally not exported.
|
|
28
|
+
- **Useful at system boundaries.** Validate unknown input, convert canonical binary formats to bytes, or write directly into a caller-owned buffer.
|
|
29
|
+
- **Small dependency surface.** CUID v2 is the only generator with a runtime dependency (`@noble/hashes`).
|
|
190
30
|
|
|
191
|
-
|
|
192
|
-
>
|
|
193
|
-
> \* 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.
|
|
31
|
+
## Install
|
|
194
32
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
# pnpm (recommended)
|
|
199
|
-
pnpm add uniku
|
|
200
|
-
|
|
201
|
-
# bun
|
|
202
|
-
bun add uniku
|
|
203
|
-
|
|
204
|
-
# npm
|
|
33
|
+
```sh
|
|
205
34
|
npm install uniku
|
|
206
|
-
|
|
207
|
-
# yarn
|
|
208
|
-
yarn add uniku
|
|
209
|
-
|
|
210
|
-
# deno
|
|
211
|
-
deno install npm:uniku
|
|
212
35
|
```
|
|
213
36
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
| Import | Minified + gzipped |
|
|
217
|
-
|--------|-------------------:|
|
|
218
|
-
| `uniku/uuid/v4` | ~1.1 KB |
|
|
219
|
-
| `uniku/uuid/v7` | ~1.4 KB |
|
|
220
|
-
| `uniku/ulid` | ~1.8 KB |
|
|
221
|
-
| `uniku/typeid` | ~1.6 KB |
|
|
222
|
-
| `uniku/cuid/v2` | ~992 B* |
|
|
223
|
-
| `uniku/cuid2` | ~1007 B* |
|
|
224
|
-
| `uniku/nanoid` | ~1.2 KB |
|
|
225
|
-
| `uniku/ksuid` | ~1.3 KB |
|
|
226
|
-
| `uniku/objectid` | ~1.3 KB |
|
|
227
|
-
| `uniku/tsid` | ~1.4 KB |
|
|
228
|
-
| `uniku/xid` | ~1.9 KB |
|
|
229
|
-
| `uniku/generators` | ~98 B |
|
|
230
|
-
|
|
231
|
-
* The CUID2 entry point imports SHA3-512 from `@noble/hashes`; this table's entry-point size excludes that external dependency.
|
|
232
|
-
|
|
233
|
-
## Quick Start
|
|
234
|
-
|
|
235
|
-
### UUID v4 (random)
|
|
37
|
+
The [getting-started guide](https://jkomyno.github.io/uniku/docs/getting-started/) also covers pnpm, Bun, and Deno.
|
|
236
38
|
|
|
237
|
-
|
|
238
|
-
import { uuidv4 } from 'uniku/uuid/v4'
|
|
239
|
-
|
|
240
|
-
const id = uuidv4()
|
|
241
|
-
// => "550e8400-e29b-41d4-a716-446655440000"
|
|
242
|
-
|
|
243
|
-
// Convert to/from bytes
|
|
244
|
-
const bytes = uuidv4.toBytes(id)
|
|
245
|
-
const str = uuidv4.fromBytes(bytes)
|
|
246
|
-
```
|
|
39
|
+
## Quick start
|
|
247
40
|
|
|
248
|
-
|
|
41
|
+
Every generator has its own entry point and keeps related operations on the generator function:
|
|
249
42
|
|
|
250
43
|
```ts
|
|
251
44
|
import { uuidv7 } from 'uniku/uuid/v7'
|
|
252
45
|
|
|
253
46
|
const id = uuidv7()
|
|
254
|
-
|
|
47
|
+
const bytes = uuidv7.toBytes(id)
|
|
48
|
+
const restored = uuidv7.fromBytes(bytes)
|
|
255
49
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
50
|
+
if (uuidv7.isValid(restored)) {
|
|
51
|
+
console.log(uuidv7.timestamp(restored))
|
|
52
|
+
}
|
|
259
53
|
```
|
|
260
54
|
|
|
261
|
-
|
|
55
|
+
There are no barrel exports:
|
|
262
56
|
|
|
263
57
|
```ts
|
|
58
|
+
import { uuidv4 } from 'uniku/uuid/v4'
|
|
264
59
|
import { ulid } from 'uniku/ulid'
|
|
265
|
-
|
|
266
|
-
const id = ulid()
|
|
267
|
-
// => "01HW9T2W9W9YJ3JZ1H4P4M2T8Q"
|
|
268
|
-
|
|
269
|
-
// Convert to/from bytes
|
|
270
|
-
const bytes = ulid.toBytes(id)
|
|
271
|
-
const str = ulid.fromBytes(bytes)
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### TypeID (prefixed UUID v7)
|
|
275
|
-
|
|
276
|
-
```ts
|
|
277
60
|
import { typeid } from 'uniku/typeid'
|
|
278
|
-
|
|
279
|
-
const id = typeid('user')
|
|
280
|
-
// => "user_01h2xcejqtf2nbrexx3vqjhp41"
|
|
281
|
-
|
|
282
|
-
const uuid = typeid.toUuid(id)
|
|
283
|
-
const restored = typeid.fromUuid('user', uuid)
|
|
284
|
-
|
|
285
|
-
const canonical = typeid('')
|
|
286
|
-
// => "01h2xcejqtf2nbrexx3vqjhp41"
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
### CUID2 (secure, non-time-ordered)
|
|
290
|
-
|
|
291
|
-
```ts
|
|
292
61
|
import { cuidv2 } from 'uniku/cuid/v2'
|
|
293
|
-
|
|
294
|
-
const id = cuidv2()
|
|
295
|
-
// => "pfh0haxfpzowht3oi213cqos"
|
|
296
|
-
|
|
297
|
-
// Custom length
|
|
298
|
-
const shortId = cuidv2({ length: 10 })
|
|
299
|
-
// => "tz4a98xxat"
|
|
300
|
-
|
|
301
|
-
// Validation (type guard)
|
|
302
|
-
if (cuidv2.isValid(maybeId)) {
|
|
303
|
-
console.log(maybeId) // TypeScript knows this is a string
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
> `uniku/cuid/v2` is the canonical entry point. The older `uniku/cuid2` import
|
|
308
|
-
> still works and re-exports the same generator, but is now deprecated — prefer
|
|
309
|
-
> `import { cuidv2 } from 'uniku/cuid/v2'`.
|
|
310
|
-
|
|
311
|
-
### Nanoid (URL-friendly, custom alphabet)
|
|
312
|
-
|
|
313
|
-
```ts
|
|
314
62
|
import { nanoid } from 'uniku/nanoid'
|
|
315
|
-
|
|
316
|
-
const id = nanoid()
|
|
317
|
-
// => "V1StGXR8_Z5jdHi6B-myT"
|
|
318
|
-
|
|
319
|
-
// Custom size
|
|
320
|
-
const shortId = nanoid(10)
|
|
321
|
-
// => "IRFa-VaY2b"
|
|
322
|
-
|
|
323
|
-
// Custom alphabet and size via options
|
|
324
|
-
const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })
|
|
325
|
-
// => "4f90d13a42bc"
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
### KSUID (time-ordered, high entropy)
|
|
329
|
-
|
|
330
|
-
```ts
|
|
331
63
|
import { ksuid } from 'uniku/ksuid'
|
|
332
|
-
|
|
333
|
-
const id = ksuid()
|
|
334
|
-
// => "2QnJjKLvpSfpZqGiPPxVwWLMy2p"
|
|
335
|
-
|
|
336
|
-
// Convert to/from bytes
|
|
337
|
-
const bytes = ksuid.toBytes(id)
|
|
338
|
-
const str = ksuid.fromBytes(bytes)
|
|
339
|
-
|
|
340
|
-
// Extract timestamp (milliseconds)
|
|
341
|
-
const ts = ksuid.timestamp(id)
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### ObjectID (time-ordered, MongoDB-compatible)
|
|
345
|
-
|
|
346
|
-
```ts
|
|
347
64
|
import { objectid } from 'uniku/objectid'
|
|
348
|
-
|
|
349
|
-
const id = objectid()
|
|
350
|
-
// => "667c3f2a1e2b3c4d5e6f7081"
|
|
351
|
-
|
|
352
|
-
// Convert to/from bytes
|
|
353
|
-
const bytes = objectid.toBytes(id)
|
|
354
|
-
const str = objectid.fromBytes(bytes)
|
|
355
|
-
|
|
356
|
-
// Extract timestamp (milliseconds)
|
|
357
|
-
const ts = objectid.timestamp(id)
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### TSID (time-ordered, 64-bit integer)
|
|
361
|
-
|
|
362
|
-
```ts
|
|
363
|
-
import { tsid } from 'uniku/tsid'
|
|
364
|
-
|
|
365
|
-
// Generate a TSID bigint (the primary type, not a string)
|
|
366
|
-
const id = tsid()
|
|
367
|
-
// => 862301223059968074n
|
|
368
|
-
|
|
369
|
-
// Convert to/from the canonical 13-character string
|
|
370
|
-
const str = tsid.toString(id)
|
|
371
|
-
// => "0QXW2CK4XZM2A"
|
|
372
|
-
tsid.fromString(str) === id // true
|
|
373
|
-
|
|
374
|
-
// Extract timestamp (milliseconds, full precision)
|
|
375
|
-
const ts = tsid.timestamp(id)
|
|
376
|
-
|
|
377
|
-
// Convert to/from 8 bytes
|
|
378
|
-
const bytes = tsid.toBytes(id)
|
|
379
|
-
const restored = tsid.fromBytes(bytes)
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### XID (time-ordered, rs/xid-compatible)
|
|
383
|
-
|
|
384
|
-
```ts
|
|
385
65
|
import { xid } from 'uniku/xid'
|
|
386
|
-
|
|
387
|
-
const id = xid()
|
|
388
|
-
// => "9m4e2mr0ui3e8a215n4g"
|
|
389
|
-
|
|
390
|
-
const bytes = xid.toBytes(id)
|
|
391
|
-
const restored = xid.fromBytes(bytes)
|
|
392
|
-
const timestamp = xid.timestamp(id)
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
XID caches a random identity per runtime and uses a shared, always-incrementing counter. Supply `machineId`, `processId`, `secs`, and `counter` for deterministic output.
|
|
396
|
-
|
|
397
|
-
## Migrating to uniku
|
|
398
|
-
|
|
399
|
-
### From `uuid`
|
|
400
|
-
|
|
401
|
-
```diff
|
|
402
|
-
- import { v4 as uuidv4 } from 'uuid'
|
|
403
|
-
+ import { uuidv4 } from 'uniku/uuid/v4'
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### From `nanoid`
|
|
407
|
-
|
|
408
|
-
```diff
|
|
409
|
-
- import { nanoid } from 'nanoid'
|
|
410
|
-
+ import { nanoid } from 'uniku/nanoid'
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
### From `ulid`
|
|
414
|
-
|
|
415
|
-
```diff
|
|
416
|
-
- import { ulid } from 'ulid'
|
|
417
|
-
+ import { ulid } from 'uniku/ulid'
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
### From `@paralleldrive/cuid2`
|
|
421
|
-
|
|
422
|
-
```diff
|
|
423
|
-
- import { createId } from '@paralleldrive/cuid2'
|
|
424
|
-
+ import { cuid2 } from 'uniku/cuid2'
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
### From `@owpz/ksuid`
|
|
428
|
-
|
|
429
|
-
```diff
|
|
430
|
-
- import { KSUID } from '@owpz/ksuid'
|
|
431
|
-
+ import { ksuid } from 'uniku/ksuid'
|
|
432
|
-
|
|
433
|
-
- const id = KSUID.random().toString()
|
|
434
|
-
+ const id = ksuid()
|
|
435
|
-
|
|
436
|
-
- const parsed = KSUID.parse(str)
|
|
437
|
-
- const timestamp = parsed.timestamp
|
|
438
|
-
+ const timestamp = ksuid.timestamp(str)
|
|
439
|
-
```
|
|
440
|
-
|
|
441
|
-
### From `bson`
|
|
442
|
-
|
|
443
|
-
```diff
|
|
444
|
-
- import { ObjectId } from 'bson'
|
|
445
|
-
+ import { objectid } from 'uniku/objectid'
|
|
446
|
-
|
|
447
|
-
- const id = new ObjectId().toHexString()
|
|
448
|
-
+ const id = objectid()
|
|
449
|
-
|
|
450
|
-
- const timestamp = new ObjectId(str).getTimestamp().getTime()
|
|
451
|
-
+ const timestamp = objectid.timestamp(str)
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
## API Reference
|
|
455
|
-
|
|
456
|
-
### `uuidv4` (from `uniku/uuid/v4`)
|
|
457
|
-
|
|
458
|
-
```ts
|
|
459
|
-
uuidv4(options?: UuidV4Options): string
|
|
460
|
-
uuidv4(options: UuidV4Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
461
|
-
uuidv4.toBytes(id: string): Uint8Array
|
|
462
|
-
uuidv4.fromBytes(bytes: Uint8Array): string
|
|
463
|
-
uuidv4.isValid(id: unknown): id is string
|
|
464
|
-
uuidv4.NIL // "00000000-0000-0000-0000-000000000000"
|
|
465
|
-
uuidv4.MAX // "ffffffff-ffff-ffff-ffff-ffffffffffff"
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
### `uuidv7` (from `uniku/uuid/v7`)
|
|
469
|
-
|
|
470
|
-
```ts
|
|
471
|
-
uuidv7(options?: UuidV7Options): string
|
|
472
|
-
uuidv7(options: UuidV7Options | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
473
|
-
uuidv7.toBytes(id: string): Uint8Array
|
|
474
|
-
uuidv7.fromBytes(bytes: Uint8Array): string
|
|
475
|
-
uuidv7.timestamp(id: string): number
|
|
476
|
-
uuidv7.isValid(id: unknown): id is string
|
|
477
|
-
uuidv7.NIL // "00000000-0000-0000-0000-000000000000"
|
|
478
|
-
uuidv7.MAX // "ffffffff-ffff-ffff-ffff-ffffffffffff"
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### `ulid` (from `uniku/ulid`)
|
|
482
|
-
|
|
483
|
-
```ts
|
|
484
|
-
ulid(options?: UlidOptions): string
|
|
485
|
-
ulid(options: UlidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
486
|
-
ulid.toBytes(id: string): Uint8Array
|
|
487
|
-
ulid.fromBytes(bytes: Uint8Array): string
|
|
488
|
-
ulid.timestamp(id: string): number
|
|
489
|
-
ulid.isValid(id: unknown): id is string
|
|
490
|
-
ulid.NIL // "00000000000000000000000000"
|
|
491
|
-
ulid.MAX // "7ZZZZZZZZZZZZZZZZZZZZZZZZZ"
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
### `typeid` (from `uniku/typeid`)
|
|
495
|
-
|
|
496
|
-
```ts
|
|
497
|
-
typeid(prefix: string, options?: TypeidOptions): string
|
|
498
|
-
typeid.toBytes(id: string): Uint8Array
|
|
499
|
-
typeid.fromBytes(prefix: string, bytes: Uint8Array): string
|
|
500
|
-
typeid.toUuid(id: string): string
|
|
501
|
-
typeid.fromUuid(prefix: string, uuid: string): string
|
|
502
|
-
typeid.timestamp(id: string): number
|
|
503
|
-
typeid.prefix(id: string): string
|
|
504
|
-
typeid.suffix(id: string): string
|
|
505
|
-
typeid.isValid(id: unknown): id is string
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Use an empty prefix (`typeid('')`) to generate Jetify-compatible prefixless TypeIDs.
|
|
509
|
-
|
|
510
|
-
### `cuidv2` (from `uniku/cuid/v2`)
|
|
511
|
-
|
|
512
|
-
```ts
|
|
513
|
-
cuidv2(options?: CuidV2Options): string
|
|
514
|
-
cuidv2.isValid(id: unknown): id is string
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
> Also available (deprecated) as `cuid2` from `uniku/cuid2` — the same generator
|
|
518
|
-
> under the pre-versioned entry point.
|
|
519
|
-
|
|
520
|
-
### `nanoid` (from `uniku/nanoid`)
|
|
521
|
-
|
|
522
|
-
```ts
|
|
523
|
-
nanoid(): string
|
|
524
|
-
nanoid(size: number): string
|
|
525
|
-
nanoid(options: NanoidOptions): string
|
|
526
|
-
nanoid.isValid(id: unknown): id is string
|
|
527
|
-
|
|
528
|
-
// Constant
|
|
529
|
-
URL_ALPHABET // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### `ksuid` (from `uniku/ksuid`)
|
|
533
|
-
|
|
534
|
-
```ts
|
|
535
|
-
ksuid(options?: KsuidOptions): string
|
|
536
|
-
ksuid(options: KsuidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
537
|
-
ksuid.toBytes(id: string): Uint8Array
|
|
538
|
-
ksuid.fromBytes(bytes: Uint8Array): string
|
|
539
|
-
ksuid.timestamp(id: string): number
|
|
540
|
-
ksuid.isValid(id: unknown): id is string
|
|
541
|
-
ksuid.NIL // "000000000000000000000000000"
|
|
542
|
-
ksuid.MAX // "aWgEPTl1tmebfsQzFP4bxwgy80V"
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
### `objectid` (from `uniku/objectid`)
|
|
546
|
-
|
|
547
|
-
```ts
|
|
548
|
-
objectid(options?: ObjectIdOptions): string
|
|
549
|
-
objectid(options: ObjectIdOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
550
|
-
objectid.toBytes(id: string): Uint8Array
|
|
551
|
-
objectid.fromBytes(bytes: Uint8Array): string
|
|
552
|
-
objectid.timestamp(id: string): number
|
|
553
|
-
objectid.isValid(id: unknown): id is string
|
|
554
|
-
objectid.NIL // "000000000000000000000000"
|
|
555
|
-
objectid.MAX // "ffffffffffffffffffffffff"
|
|
66
|
+
import { tsid } from 'uniku/tsid'
|
|
556
67
|
```
|
|
557
68
|
|
|
558
|
-
|
|
69
|
+
## Choose an ID
|
|
559
70
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
tsid.MAX // 18446744073709551615n
|
|
571
|
-
```
|
|
71
|
+
| Situation | Start with | Why |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| Database primary keys | UUID v7 or ULID | Time-ordered values improve index locality |
|
|
74
|
+
| Public API resources | TypeID | UUID v7 with a readable, domain-specific prefix |
|
|
75
|
+
| Short URLs and invite codes | Nanoid | Compact and URL-safe |
|
|
76
|
+
| Values that should resist enumeration | CUID v2 | Non-sequential and secure |
|
|
77
|
+
| MongoDB `_id` compatibility | ObjectID | Matches MongoDB's 12-byte ObjectID format |
|
|
78
|
+
| Go rs/xid compatibility | XID | Matches rs/xid's text and binary representation |
|
|
79
|
+
| Native `BIGINT` storage | TSID | A sortable 64-bit integer rather than a UUID-shaped string |
|
|
80
|
+
| Broad standards compatibility | UUID v4 | The conventional random UUID format |
|
|
572
81
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
### `xid` (from `uniku/xid`)
|
|
576
|
-
|
|
577
|
-
```ts
|
|
578
|
-
xid(options?: XidOptions): string
|
|
579
|
-
xid(options: XidOptions | undefined, buf: Uint8Array, offset?: number): Uint8Array
|
|
580
|
-
xid.toBytes(id: string): Uint8Array
|
|
581
|
-
xid.fromBytes(bytes: Uint8Array): string
|
|
582
|
-
xid.timestamp(id: string): number
|
|
583
|
-
xid.isValid(id: unknown): id is string
|
|
584
|
-
xid.NIL // "00000000000000000000"
|
|
585
|
-
xid.MAX // "vvvvvvvvvvvvvvvvvvvg"
|
|
586
|
-
```
|
|
82
|
+
The [choosing guide](https://jkomyno.github.io/uniku/docs/guides/choosing-an-id/) covers ordering, timestamp leakage, storage boundaries, and format-specific trade-offs.
|
|
587
83
|
|
|
588
84
|
## Documentation
|
|
589
85
|
|
|
590
|
-
|
|
591
|
-
|
|
86
|
+
- [Getting started](https://jkomyno.github.io/uniku/docs/getting-started/): installation, direct imports, validation, and byte helpers
|
|
87
|
+
- [Generator reference](https://jkomyno.github.io/uniku/docs/reference/entry-points/): generated from the public TypeScript signatures and JSDoc
|
|
88
|
+
- [Migration guide](https://jkomyno.github.io/uniku/docs/migration/from-other-libraries/): move from `uuid`, `nanoid`, `ulid`, CUID2, KSUID, BSON, TSID, or XID libraries
|
|
89
|
+
- [Performance and bundle size](https://jkomyno.github.io/uniku/docs/guides/performance/): benchmark methodology, current measurements, and reproduction commands
|
|
90
|
+
- [Stability contract](https://github.com/jkomyno/uniku/blob/main/docs/STABILITY.md): the entry points and compatibility policy planned for `uniku@1.0.0`
|
|
592
91
|
|
|
593
|
-
##
|
|
92
|
+
## CLI companion
|
|
594
93
|
|
|
595
|
-
|
|
94
|
+
`@uniku/cli` generates, validates, and inspects IDs from a terminal or shell pipeline. Install the current standalone binary:
|
|
596
95
|
|
|
597
|
-
|
|
598
|
-
-
|
|
599
|
-
- [TypeID-JS](https://github.com/jetify-com/typeid-js): official JavaScript implementation of TypeID
|
|
600
|
-
- [@paralleldrive/cuid2](https://github.com/paralleldrive/cuid2): secure, collision-resistant IDs
|
|
601
|
-
- [@owpz/ksuid](https://github.com/owpz/ksuid): K-Sortable Unique Identifier
|
|
602
|
-
- [bson](https://github.com/mongodb/js-bson): official MongoDB BSON library, including the ObjectId implementation
|
|
603
|
-
- [nanoid](https://github.com/ai/nanoid): tiny, URL-friendly unique string ID generator
|
|
604
|
-
- [tsid-ts](https://github.com/yubinTW/tsid-ts): TypeScript implementation of TSID, a Snowflake-style 64-bit Time-Sorted Unique Identifier
|
|
96
|
+
```sh
|
|
97
|
+
curl -fsSL https://raw.githubusercontent.com/jkomyno/uniku/main/install.sh | sh
|
|
605
98
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
99
|
+
uniku uuid -v 7
|
|
100
|
+
uniku validate 018e5e5c-7c8a-7000-8000-000000000000
|
|
101
|
+
uniku inspect 018e5e5c-7c8a-7000-8000-000000000000
|
|
102
|
+
```
|
|
609
103
|
|
|
610
|
-
|
|
104
|
+
See the [CLI documentation](https://jkomyno.github.io/uniku/docs/cli/) for package-manager installs and the complete command reference.
|
|
611
105
|
|
|
612
|
-
|
|
106
|
+
## Contributing
|
|
613
107
|
|
|
614
|
-
|
|
615
|
-
- X: [@jkomyno](https://x.com/jkomyno)
|
|
108
|
+
See [CONTRIBUTING.md](https://github.com/jkomyno/uniku/blob/main/CONTRIBUTING.md) for the development workflow, tests, benchmarks, and preview releases.
|
|
616
109
|
|
|
617
110
|
## License
|
|
618
111
|
|
|
619
|
-
MIT
|
|
112
|
+
MIT © [Alberto Schiabel](https://github.com/jkomyno)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "uniku",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.3",
|
|
5
5
|
"description": "Minimal, tree-shakeable unique ID generators for every JavaScript runtime",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alberto Schiabel",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "git+https://github.com/jkomyno/uniku.git",
|
|
15
15
|
"directory": "packages/uniku"
|
|
16
16
|
},
|
|
17
|
-
"homepage": "https://github.
|
|
17
|
+
"homepage": "https://jkomyno.github.io/uniku/",
|
|
18
18
|
"bugs": {
|
|
19
19
|
"url": "https://github.com/jkomyno/uniku/issues"
|
|
20
20
|
},
|