uniku 0.4.1 → 0.4.2
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 +52 -560
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,618 +2,110 @@
|
|
|
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
|
-
One library. Every ID format. Every
|
|
9
|
+
One library. Every ID format. Every modern JavaScript runtime.
|
|
11
10
|
|
|
12
11
|
> **uniku** */uˈniːku/* — Maltese for "unique"
|
|
13
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/)
|
|
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
|
-
| KSUID | 27 chars | 20 bytes | 26% |
|
|
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
|
+
- **One focused package** — UUID v4/v7, ULID, TypeID, CUID v2, Nanoid, KSUID, ObjectID, XID, and TSID.
|
|
25
|
+
- **Portable by default** — Runs on Node.js, Bun, Deno, browsers, edge runtimes, and Cloudflare Workers using `globalThis.crypto`.
|
|
26
|
+
- **Tree-shakeable entry points** — Import only the generator you use; the package root is intentionally not exported.
|
|
27
|
+
- **Useful at system boundaries** — Validate unknown input, convert canonical binary formats to bytes, or write directly into a caller-owned buffer.
|
|
28
|
+
- **Small dependency surface** — CUID v2 is the only generator with a runtime dependency (`@noble/hashes`).
|
|
190
29
|
|
|
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.
|
|
30
|
+
## Install
|
|
194
31
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
# pnpm (recommended)
|
|
199
|
-
pnpm add uniku
|
|
200
|
-
|
|
201
|
-
# bun
|
|
202
|
-
bun add uniku
|
|
203
|
-
|
|
204
|
-
# npm
|
|
32
|
+
```sh
|
|
205
33
|
npm install uniku
|
|
206
|
-
|
|
207
|
-
# yarn
|
|
208
|
-
yarn add uniku
|
|
209
|
-
|
|
210
|
-
# deno
|
|
211
|
-
deno install npm:uniku
|
|
212
34
|
```
|
|
213
35
|
|
|
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)
|
|
36
|
+
The [getting-started guide](https://jkomyno.github.io/uniku/docs/getting-started/) also covers pnpm, Bun, and Deno.
|
|
236
37
|
|
|
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
|
-
```
|
|
38
|
+
## Quick start
|
|
247
39
|
|
|
248
|
-
|
|
40
|
+
Every generator has its own entry point and keeps related operations on the generator function:
|
|
249
41
|
|
|
250
42
|
```ts
|
|
251
43
|
import { uuidv7 } from 'uniku/uuid/v7'
|
|
252
44
|
|
|
253
45
|
const id = uuidv7()
|
|
254
|
-
|
|
46
|
+
const bytes = uuidv7.toBytes(id)
|
|
47
|
+
const restored = uuidv7.fromBytes(bytes)
|
|
255
48
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
49
|
+
if (uuidv7.isValid(restored)) {
|
|
50
|
+
console.log(uuidv7.timestamp(restored))
|
|
51
|
+
}
|
|
259
52
|
```
|
|
260
53
|
|
|
261
|
-
|
|
54
|
+
There are no barrel exports:
|
|
262
55
|
|
|
263
56
|
```ts
|
|
57
|
+
import { uuidv4 } from 'uniku/uuid/v4'
|
|
264
58
|
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
59
|
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
60
|
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
61
|
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
62
|
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
63
|
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
64
|
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"
|
|
65
|
+
import { tsid } from 'uniku/tsid'
|
|
556
66
|
```
|
|
557
67
|
|
|
558
|
-
|
|
68
|
+
## Choose an ID
|
|
559
69
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
tsid.MAX // 18446744073709551615n
|
|
571
|
-
```
|
|
70
|
+
| Situation | Start with | Why |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| Database primary keys | UUID v7 or ULID | Time-ordered values improve index locality |
|
|
73
|
+
| Public API resources | TypeID | UUID v7 with a readable, domain-specific prefix |
|
|
74
|
+
| Short URLs and invite codes | Nanoid | Compact and URL-safe |
|
|
75
|
+
| Values that should resist enumeration | CUID v2 | Non-sequential and secure |
|
|
76
|
+
| MongoDB `_id` compatibility | ObjectID | Matches MongoDB's 12-byte ObjectID format |
|
|
77
|
+
| Go rs/xid compatibility | XID | Matches rs/xid's text and binary representation |
|
|
78
|
+
| Native `BIGINT` storage | TSID | A sortable 64-bit integer rather than a UUID-shaped string |
|
|
79
|
+
| Broad standards compatibility | UUID v4 | The conventional random UUID format |
|
|
572
80
|
|
|
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
|
-
```
|
|
81
|
+
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
82
|
|
|
588
83
|
## Documentation
|
|
589
84
|
|
|
590
|
-
|
|
591
|
-
|
|
85
|
+
- [Getting started](https://jkomyno.github.io/uniku/docs/getting-started/) — installation, direct imports, validation, and byte helpers
|
|
86
|
+
- [Generator reference](https://jkomyno.github.io/uniku/docs/reference/entry-points/) — generated from the public TypeScript signatures and JSDoc
|
|
87
|
+
- [Migration guide](https://jkomyno.github.io/uniku/docs/migration/from-other-libraries/) — move from `uuid`, `nanoid`, `ulid`, CUID2, KSUID, BSON, TSID, or XID libraries
|
|
88
|
+
- [Performance and bundle size](https://jkomyno.github.io/uniku/docs/guides/performance/) — benchmark methodology, current measurements, and reproduction commands
|
|
89
|
+
- [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
90
|
|
|
593
|
-
##
|
|
91
|
+
## CLI companion
|
|
594
92
|
|
|
595
|
-
|
|
93
|
+
`@uniku/cli` generates, validates, and inspects IDs from a terminal or shell pipeline. Install the current standalone binary:
|
|
596
94
|
|
|
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
|
|
95
|
+
```sh
|
|
96
|
+
curl -fsSL https://raw.githubusercontent.com/jkomyno/uniku/main/install.sh | sh
|
|
605
97
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
98
|
+
uniku uuid -v 7
|
|
99
|
+
uniku validate 018e5e5c-7c8a-7000-8000-000000000000
|
|
100
|
+
uniku inspect 018e5e5c-7c8a-7000-8000-000000000000
|
|
101
|
+
```
|
|
609
102
|
|
|
610
|
-
|
|
103
|
+
See the [CLI documentation](https://jkomyno.github.io/uniku/docs/cli/) for package-manager installs and the complete command reference.
|
|
611
104
|
|
|
612
|
-
|
|
105
|
+
## Contributing
|
|
613
106
|
|
|
614
|
-
|
|
615
|
-
- X: [@jkomyno](https://x.com/jkomyno)
|
|
107
|
+
See [CONTRIBUTING.md](https://github.com/jkomyno/uniku/blob/main/CONTRIBUTING.md) for the development workflow, tests, benchmarks, and preview releases.
|
|
616
108
|
|
|
617
109
|
## License
|
|
618
110
|
|
|
619
|
-
MIT
|
|
111
|
+
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.2",
|
|
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
|
},
|