@lacspace/id 1.0.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/LICENSE +51 -0
- package/README.md +67 -0
- package/dist/index.cjs +89 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +80 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Lacspace Free Licence
|
|
2
|
+
Version 1.0, August 2026
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Lacspace
|
|
5
|
+
|
|
6
|
+
PREAMBLE
|
|
7
|
+
|
|
8
|
+
This software is published by Lacspace under the Lacspace Free Licence โ a free,
|
|
9
|
+
permissive licence that lets you use this software for any purpose, including in
|
|
10
|
+
commercial products and services, at no cost. It grants the same freedoms as
|
|
11
|
+
common permissive open-source licences; the only condition is that this notice
|
|
12
|
+
travels with the software. The canonical, always-current text of this licence is
|
|
13
|
+
maintained at https://lacspace.com/licenses/lacspace-free-1.0
|
|
14
|
+
|
|
15
|
+
GRANT OF RIGHTS
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person or organisation
|
|
18
|
+
obtaining a copy of this software and its associated documentation and data files
|
|
19
|
+
(the "Software"), to deal in the Software without restriction, including without
|
|
20
|
+
limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
21
|
+
sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
|
22
|
+
Software is furnished to do so, subject to the conditions below. These rights are
|
|
23
|
+
granted for any purpose, personal or commercial, and are perpetual, worldwide,
|
|
24
|
+
non-exclusive, and royalty-free.
|
|
25
|
+
|
|
26
|
+
CONDITIONS
|
|
27
|
+
|
|
28
|
+
The above copyright notice, this permission notice, and the name of this licence
|
|
29
|
+
("Lacspace Free Licence") shall be included in all copies or substantial portions
|
|
30
|
+
of the Software.
|
|
31
|
+
|
|
32
|
+
TRADEMARKS
|
|
33
|
+
|
|
34
|
+
This licence does not grant permission to use the trade names, trademarks, service
|
|
35
|
+
marks, logos, or product names of Lacspace, except as required to reproduce the
|
|
36
|
+
notice above or to describe the origin of the Software in a truthful manner.
|
|
37
|
+
|
|
38
|
+
DISCLAIMER OF WARRANTY AND LIMITATION OF LIABILITY
|
|
39
|
+
|
|
40
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
41
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
42
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
43
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN
|
|
44
|
+
AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
|
45
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
The Lacspace Free Licence is a source-available, permissive licence and is not (as
|
|
50
|
+
of this version) an OSI-approved licence. In substance it grants the same freedoms
|
|
51
|
+
as the MIT Licence. Learn more at https://lacspace.com/licenses
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @lacspace/id
|
|
4
|
+
|
|
5
|
+
**Unique IDs done right โ UUID v4, time-sortable UUID v7, Nano-ID-style & short codes.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@lacspace/id)
|
|
8
|
+
[](https://bundlephobia.com/package/@lacspace/id)
|
|
9
|
+
[](https://www.npmjs.com/package/@lacspace/id)
|
|
10
|
+
[](https://github.com/lacspace/npm-packages/blob/main/LICENSE)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
> Every ID kind you actually need, in one tiny package โ including **UUID v7**, the time-sortable UUID that makes a fantastic database primary key. Cryptographically random (Web Crypto), monotonic within a millisecond, zero-dependency.
|
|
15
|
+
|
|
16
|
+
- ๐ `uuidv4()` โ classic random UUID
|
|
17
|
+
- โฑ๏ธ `uuidv7()` โ **time-sortable** UUID (great index-friendly primary key)
|
|
18
|
+
- ๐ค `nanoid()` / `shortId()` โ URL-safe random strings
|
|
19
|
+
- ๐ท๏ธ `id("user")` โ prefixed ids like `user_9f8cโฆ`
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @lacspace/id # or pnpm add / yarn add / bun add
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Use
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { uuidv4, uuidv7, nanoid, shortId, id } from "@lacspace/id";
|
|
31
|
+
|
|
32
|
+
uuidv4(); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
33
|
+
uuidv7(); // "0192e7a1-3c2f-7abc-8def-1234567890ab" โ sorts by time
|
|
34
|
+
nanoid(); // "V1StGXR8_Z5jdHi6B-myT"
|
|
35
|
+
shortId(); // "Ab3xK9_p"
|
|
36
|
+
id("user"); // "user_9f8c1a3e7b2d4f6a"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Why v7? Because random UUIDs (v4) scatter across a database index, hurting insert performance. **v7 is lexicographically sortable by creation time** โ index-friendly *and* globally unique โ and `uuidv7Time(id)` reads the timestamp back out.
|
|
40
|
+
|
|
41
|
+
## API
|
|
42
|
+
|
|
43
|
+
| Export | Description |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `uuidv4()` | random UUID |
|
|
46
|
+
| `uuidv7(now?)` ยท `uuidv7Time(id)` | time-sortable UUID + timestamp extract |
|
|
47
|
+
| `nanoid(size=21)` ยท `shortId(size=8)` | URL-safe random strings |
|
|
48
|
+
| `id(prefix, size=16)` | prefixed id |
|
|
49
|
+
| `isUuid(s)` ยท `uuidVersion(s)` | validation |
|
|
50
|
+
|
|
51
|
+
## Licensing
|
|
52
|
+
|
|
53
|
+
This package is **free** under the **[Lacspace Free Licence](https://lacspace.com/licenses/lacspace-free-1.0)** โ MIT-equivalent freedoms. Use it in personal and commercial projects at no cost; just keep the notice.
|
|
54
|
+
|
|
55
|
+
Not every Lacspace package is free. We also offer **Commercial**, **Client-specific** and **Private** packages under separate terms โ see the **[Lacspace Licence Centre](https://lacspace.com/licenses)**.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
<div align="center">
|
|
60
|
+
|
|
61
|
+
**Part of the Lacspace ecosystem โ zero-dependency, isomorphic TypeScript packages.**
|
|
62
|
+
|
|
63
|
+
[All packages โ](https://lacspace.com/packages) ยท [npm org โ](https://www.npmjs.com/org/lacspace) ยท [Licence Centre โ](https://lacspace.com/licenses) ยท [GitHub โ](https://github.com/lacspace/npm-packages)
|
|
64
|
+
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div align="center"><sub>Built with care by <a href="https://lacspace.com">Lacspace</a> ยท Lacspace Free Licence ยท <a href="https://github.com/lacspace/npm-packages">source</a></sub></div>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
function getRandom(bytes) {
|
|
5
|
+
const c = globalThis.crypto;
|
|
6
|
+
if (!c || typeof c.getRandomValues !== "function") {
|
|
7
|
+
throw new Error("Web Crypto getRandomValues is unavailable in this environment.");
|
|
8
|
+
}
|
|
9
|
+
c.getRandomValues(bytes);
|
|
10
|
+
return bytes;
|
|
11
|
+
}
|
|
12
|
+
var HEX = [];
|
|
13
|
+
for (let i = 0; i < 256; i++) HEX.push((i + 256).toString(16).slice(1));
|
|
14
|
+
function bytesToUuid(b) {
|
|
15
|
+
return HEX[b[0]] + HEX[b[1]] + HEX[b[2]] + HEX[b[3]] + "-" + HEX[b[4]] + HEX[b[5]] + "-" + HEX[b[6]] + HEX[b[7]] + "-" + HEX[b[8]] + HEX[b[9]] + "-" + HEX[b[10]] + HEX[b[11]] + HEX[b[12]] + HEX[b[13]] + HEX[b[14]] + HEX[b[15]];
|
|
16
|
+
}
|
|
17
|
+
function uuidv4() {
|
|
18
|
+
const b = getRandom(new Uint8Array(16));
|
|
19
|
+
b[6] = b[6] & 15 | 64;
|
|
20
|
+
b[8] = b[8] & 63 | 128;
|
|
21
|
+
return bytesToUuid(b);
|
|
22
|
+
}
|
|
23
|
+
var lastV7Time = 0;
|
|
24
|
+
var v7Counter = 0;
|
|
25
|
+
function uuidv7(now) {
|
|
26
|
+
const time = now ?? Date.now();
|
|
27
|
+
if (time === lastV7Time) v7Counter++;
|
|
28
|
+
else {
|
|
29
|
+
lastV7Time = time;
|
|
30
|
+
v7Counter = 0;
|
|
31
|
+
}
|
|
32
|
+
const b = getRandom(new Uint8Array(16));
|
|
33
|
+
b[0] = time / 2 ** 40 & 255;
|
|
34
|
+
b[1] = time / 2 ** 32 & 255;
|
|
35
|
+
b[2] = time / 2 ** 24 & 255;
|
|
36
|
+
b[3] = time / 2 ** 16 & 255;
|
|
37
|
+
b[4] = time / 2 ** 8 & 255;
|
|
38
|
+
b[5] = time & 255;
|
|
39
|
+
b[6] = 112 | v7Counter >> 8 & 15;
|
|
40
|
+
b[7] = v7Counter & 255;
|
|
41
|
+
b[8] = b[8] & 63 | 128;
|
|
42
|
+
return bytesToUuid(b);
|
|
43
|
+
}
|
|
44
|
+
function uuidv7Time(uuid) {
|
|
45
|
+
const hex = uuid.replace(/-/g, "").slice(0, 12);
|
|
46
|
+
return parseInt(hex, 16);
|
|
47
|
+
}
|
|
48
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
49
|
+
function isUuid(s) {
|
|
50
|
+
return UUID_RE.test(s);
|
|
51
|
+
}
|
|
52
|
+
function uuidVersion(s) {
|
|
53
|
+
return isUuid(s) ? parseInt(s[14], 16) : null;
|
|
54
|
+
}
|
|
55
|
+
var NANO_ALPHABET = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
56
|
+
var URL_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
|
|
57
|
+
function randomFromAlphabet(size, alphabet) {
|
|
58
|
+
const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
|
|
59
|
+
const step = Math.ceil(1.6 * mask * size / alphabet.length);
|
|
60
|
+
let id2 = "";
|
|
61
|
+
while (id2.length < size) {
|
|
62
|
+
const bytes = getRandom(new Uint8Array(step));
|
|
63
|
+
for (let i = 0; i < step && id2.length < size; i++) {
|
|
64
|
+
const idx = bytes[i] & mask;
|
|
65
|
+
if (idx < alphabet.length) id2 += alphabet[idx];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return id2;
|
|
69
|
+
}
|
|
70
|
+
function nanoid(size = 21) {
|
|
71
|
+
return randomFromAlphabet(size, NANO_ALPHABET);
|
|
72
|
+
}
|
|
73
|
+
function shortId(size = 8) {
|
|
74
|
+
return randomFromAlphabet(size, URL_ALPHABET);
|
|
75
|
+
}
|
|
76
|
+
function id(prefix, size = 16) {
|
|
77
|
+
return `${prefix}_${nanoid(size)}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exports.id = id;
|
|
81
|
+
exports.isUuid = isUuid;
|
|
82
|
+
exports.nanoid = nanoid;
|
|
83
|
+
exports.shortId = shortId;
|
|
84
|
+
exports.uuidVersion = uuidVersion;
|
|
85
|
+
exports.uuidv4 = uuidv4;
|
|
86
|
+
exports.uuidv7 = uuidv7;
|
|
87
|
+
exports.uuidv7Time = uuidv7Time;
|
|
88
|
+
//# sourceMappingURL=index.cjs.map
|
|
89
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["id"],"mappings":";;;AAQA,SAAS,UAAU,KAAA,EAA+B;AAChD,EAAA,MAAM,IAAK,UAAA,CAAmC,MAAA;AAC9C,EAAA,IAAI,CAAC,CAAA,IAAK,OAAO,CAAA,CAAE,oBAAoB,UAAA,EAAY;AACjD,IAAA,MAAM,IAAI,MAAM,gEAAgE,CAAA;AAAA,EAClF;AACA,EAAA,CAAA,CAAE,gBAAgB,KAAK,CAAA;AACvB,EAAA,OAAO,KAAA;AACT;AAEA,IAAM,MAAgB,EAAC;AACvB,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,EAAK,KAAK,GAAA,CAAI,IAAA,CAAA,CAAM,CAAA,GAAI,GAAA,EAAO,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA;AAExE,SAAS,YAAY,CAAA,EAAuB;AAC1C,EAAA,OACE,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,GAAA,CAAI,EAAE,CAAC,CAAE,CAAA,GAAK,GAAA,GACxD,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,GAAA,CAAI,EAAE,CAAC,CAAE,CAAA,GAAK,GAAA,GAC5B,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,GAAA,CAAI,EAAE,CAAC,CAAE,CAAA,GAAK,GAAA,GAC5B,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,IAAI,CAAA,CAAE,CAAC,CAAE,CAAA,GAAK,MAC5B,GAAA,CAAI,CAAA,CAAE,EAAE,CAAE,CAAA,GAAK,IAAI,CAAA,CAAE,EAAE,CAAE,CAAA,GAAK,IAAI,CAAA,CAAE,EAAE,CAAE,CAAA,GAAK,GAAA,CAAI,EAAE,EAAE,CAAE,CAAA,GAAK,GAAA,CAAI,EAAE,EAAE,CAAE,IAAK,GAAA,CAAI,CAAA,CAAE,EAAE,CAAE,CAAA;AAEzF;AAGO,SAAS,MAAA,GAAiB;AAC/B,EAAA,MAAM,CAAA,GAAI,SAAA,CAAU,IAAI,UAAA,CAAW,EAAE,CAAC,CAAA;AACtC,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,IAAK,EAAA,GAAQ,EAAA;AACxB,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,IAAK,EAAA,GAAQ,GAAA;AACxB,EAAA,OAAO,YAAY,CAAC,CAAA;AACtB;AAEA,IAAI,UAAA,GAAa,CAAA;AACjB,IAAI,SAAA,GAAY,CAAA;AAOT,SAAS,OAAO,GAAA,EAAsB;AAC3C,EAAA,MAAM,IAAA,GAAO,GAAA,IAAO,IAAA,CAAK,GAAA,EAAI;AAC7B,EAAA,IAAI,SAAS,UAAA,EAAY,SAAA,EAAA;AAAA,OACpB;AAAE,IAAA,UAAA,GAAa,IAAA;AAAM,IAAA,SAAA,GAAY,CAAA;AAAA,EAAG;AAEzC,EAAA,MAAM,CAAA,GAAI,SAAA,CAAU,IAAI,UAAA,CAAW,EAAE,CAAC,CAAA;AAEtC,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,IAAA,GAAO,CAAA,IAAK,EAAA,GAAM,GAAA;AAC1B,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,IAAA,GAAO,CAAA,IAAK,EAAA,GAAM,GAAA;AAC1B,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,IAAA,GAAO,CAAA,IAAK,EAAA,GAAM,GAAA;AAC1B,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,IAAA,GAAO,CAAA,IAAK,EAAA,GAAM,GAAA;AAC1B,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,IAAA,GAAO,CAAA,IAAK,CAAA,GAAK,GAAA;AACzB,EAAA,CAAA,CAAE,CAAC,IAAI,IAAA,GAAO,GAAA;AAEd,EAAA,CAAA,CAAE,CAAC,CAAA,GAAI,GAAA,GAAS,SAAA,IAAa,CAAA,GAAK,EAAA;AAClC,EAAA,CAAA,CAAE,CAAC,IAAI,SAAA,GAAY,GAAA;AACnB,EAAA,CAAA,CAAE,CAAC,CAAA,GAAK,CAAA,CAAE,CAAC,IAAK,EAAA,GAAQ,GAAA;AACxB,EAAA,OAAO,YAAY,CAAC,CAAA;AACtB;AAGO,SAAS,WAAW,IAAA,EAAsB;AAC/C,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,EAAE,CAAA;AAC9C,EAAA,OAAO,QAAA,CAAS,KAAK,EAAE,CAAA;AACzB;AAEA,IAAM,OAAA,GAAU,4EAAA;AAGT,SAAS,OAAO,CAAA,EAAoB;AACzC,EAAA,OAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AACvB;AAGO,SAAS,YAAY,CAAA,EAA0B;AACpD,EAAA,OAAO,MAAA,CAAO,CAAC,CAAA,GAAI,QAAA,CAAS,EAAE,EAAE,CAAA,EAAI,EAAE,CAAA,GAAI,IAAA;AAC5C;AAEA,IAAM,aAAA,GAAgB,kEAAA;AACtB,IAAM,YAAA,GAAe,kEAAA;AAErB,SAAS,kBAAA,CAAmB,MAAc,QAAA,EAA0B;AAClE,EAAA,MAAM,IAAA,GAAA,CAAQ,KAAM,EAAA,GAAK,IAAA,CAAK,MAAO,QAAA,CAAS,MAAA,GAAS,CAAA,GAAK,CAAC,CAAA,IAAM,CAAA;AACnE,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA,CAAM,MAAM,IAAA,GAAO,IAAA,GAAQ,SAAS,MAAM,CAAA;AAC5D,EAAA,IAAIA,GAAAA,GAAK,EAAA;AACT,EAAA,OAAOA,GAAAA,CAAG,SAAS,IAAA,EAAM;AACvB,IAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,IAAI,UAAA,CAAW,IAAI,CAAC,CAAA;AAC5C,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,QAAQA,GAAAA,CAAG,MAAA,GAAS,MAAM,CAAA,EAAA,EAAK;AACjD,MAAA,MAAM,GAAA,GAAM,KAAA,CAAM,CAAC,CAAA,GAAK,IAAA;AACxB,MAAA,IAAI,MAAM,QAAA,CAAS,MAAA,EAAQA,GAAAA,IAAM,SAAS,GAAG,CAAA;AAAA,IAC/C;AAAA,EACF;AACA,EAAA,OAAOA,GAAAA;AACT;AAGO,SAAS,MAAA,CAAO,OAAO,EAAA,EAAY;AACxC,EAAA,OAAO,kBAAA,CAAmB,MAAM,aAAa,CAAA;AAC/C;AAGO,SAAS,OAAA,CAAQ,OAAO,CAAA,EAAW;AACxC,EAAA,OAAO,kBAAA,CAAmB,MAAM,YAAY,CAAA;AAC9C;AAGO,SAAS,EAAA,CAAG,MAAA,EAAgB,IAAA,GAAO,EAAA,EAAY;AACpD,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,MAAA,CAAO,IAAI,CAAC,CAAA,CAAA;AAClC","file":"index.cjs","sourcesContent":["/**\n * @lacspace/id\n *\n * Unique IDs done right โ UUID v4, **UUID v7** (time-sortable), Nano-ID-style\n * and short URL-safe codes. Cryptographically random (Web Crypto), monotonic\n * v7 within the same millisecond, zero-dependency and isomorphic.\n */\n\nfunction getRandom(bytes: Uint8Array): Uint8Array {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || typeof c.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto getRandomValues is unavailable in this environment.\");\n }\n c.getRandomValues(bytes);\n return bytes;\n}\n\nconst HEX: string[] = [];\nfor (let i = 0; i < 256; i++) HEX.push((i + 0x100).toString(16).slice(1));\n\nfunction bytesToUuid(b: Uint8Array): string {\n return (\n HEX[b[0]!]! + HEX[b[1]!]! + HEX[b[2]!]! + HEX[b[3]!]! + \"-\" +\n HEX[b[4]!]! + HEX[b[5]!]! + \"-\" +\n HEX[b[6]!]! + HEX[b[7]!]! + \"-\" +\n HEX[b[8]!]! + HEX[b[9]!]! + \"-\" +\n HEX[b[10]!]! + HEX[b[11]!]! + HEX[b[12]!]! + HEX[b[13]!]! + HEX[b[14]!]! + HEX[b[15]!]!\n );\n}\n\n/** RFC 4122 UUID v4 (random). */\nexport function uuidv4(): string {\n const b = getRandom(new Uint8Array(16));\n b[6] = (b[6]! & 0x0f) | 0x40; // version 4\n b[8] = (b[8]! & 0x3f) | 0x80; // variant\n return bytesToUuid(b);\n}\n\nlet lastV7Time = 0;\nlet v7Counter = 0;\n\n/**\n * UUID v7 โ a time-sortable UUID (48-bit Unix-ms timestamp + randomness).\n * Great as a primary key: lexicographically sortable by creation time, index-\n * friendly, still globally unique. Monotonic within the same millisecond.\n */\nexport function uuidv7(now?: number): string {\n const time = now ?? Date.now();\n if (time === lastV7Time) v7Counter++;\n else { lastV7Time = time; v7Counter = 0; }\n\n const b = getRandom(new Uint8Array(16));\n // 48-bit timestamp (big-endian) in bytes 0..5\n b[0] = (time / 2 ** 40) & 0xff;\n b[1] = (time / 2 ** 32) & 0xff;\n b[2] = (time / 2 ** 24) & 0xff;\n b[3] = (time / 2 ** 16) & 0xff;\n b[4] = (time / 2 ** 8) & 0xff;\n b[5] = time & 0xff;\n // rand_a (12 bits) holds a monotonic counter so same-ms ids stay sortable\n b[6] = 0x70 | ((v7Counter >> 8) & 0x0f); // version 7 + counter high nibble\n b[7] = v7Counter & 0xff; // counter low byte\n b[8] = (b[8]! & 0x3f) | 0x80; // variant\n return bytesToUuid(b);\n}\n\n/** Extract the timestamp (ms) embedded in a UUID v7. */\nexport function uuidv7Time(uuid: string): number {\n const hex = uuid.replace(/-/g, \"\").slice(0, 12);\n return parseInt(hex, 16);\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/** Is this a well-formed UUID? */\nexport function isUuid(s: string): boolean {\n return UUID_RE.test(s);\n}\n\n/** UUID version digit (1โ8), or null if not a UUID. */\nexport function uuidVersion(s: string): number | null {\n return isUuid(s) ? parseInt(s[14]!, 16) : null;\n}\n\nconst NANO_ALPHABET = \"useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict\";\nconst URL_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\";\n\nfunction randomFromAlphabet(size: number, alphabet: string): string {\n const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1;\n const step = Math.ceil((1.6 * mask * size) / alphabet.length);\n let id = \"\";\n while (id.length < size) {\n const bytes = getRandom(new Uint8Array(step));\n for (let i = 0; i < step && id.length < size; i++) {\n const idx = bytes[i]! & mask;\n if (idx < alphabet.length) id += alphabet[idx];\n }\n }\n return id;\n}\n\n/** Nano-ID-style URL-safe id (default 21 chars). */\nexport function nanoid(size = 21): string {\n return randomFromAlphabet(size, NANO_ALPHABET);\n}\n\n/** Short, URL-safe, human-shareable code (default 8 chars). */\nexport function shortId(size = 8): string {\n return randomFromAlphabet(size, URL_ALPHABET);\n}\n\n/** A prefixed id, e.g. `id(\"user\")` โ \"user_9f8cโฆ\". Uses nanoid. */\nexport function id(prefix: string, size = 16): string {\n return `${prefix}_${nanoid(size)}`;\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lacspace/id
|
|
3
|
+
*
|
|
4
|
+
* Unique IDs done right โ UUID v4, **UUID v7** (time-sortable), Nano-ID-style
|
|
5
|
+
* and short URL-safe codes. Cryptographically random (Web Crypto), monotonic
|
|
6
|
+
* v7 within the same millisecond, zero-dependency and isomorphic.
|
|
7
|
+
*/
|
|
8
|
+
/** RFC 4122 UUID v4 (random). */
|
|
9
|
+
declare function uuidv4(): string;
|
|
10
|
+
/**
|
|
11
|
+
* UUID v7 โ a time-sortable UUID (48-bit Unix-ms timestamp + randomness).
|
|
12
|
+
* Great as a primary key: lexicographically sortable by creation time, index-
|
|
13
|
+
* friendly, still globally unique. Monotonic within the same millisecond.
|
|
14
|
+
*/
|
|
15
|
+
declare function uuidv7(now?: number): string;
|
|
16
|
+
/** Extract the timestamp (ms) embedded in a UUID v7. */
|
|
17
|
+
declare function uuidv7Time(uuid: string): number;
|
|
18
|
+
/** Is this a well-formed UUID? */
|
|
19
|
+
declare function isUuid(s: string): boolean;
|
|
20
|
+
/** UUID version digit (1โ8), or null if not a UUID. */
|
|
21
|
+
declare function uuidVersion(s: string): number | null;
|
|
22
|
+
/** Nano-ID-style URL-safe id (default 21 chars). */
|
|
23
|
+
declare function nanoid(size?: number): string;
|
|
24
|
+
/** Short, URL-safe, human-shareable code (default 8 chars). */
|
|
25
|
+
declare function shortId(size?: number): string;
|
|
26
|
+
/** A prefixed id, e.g. `id("user")` โ "user_9f8cโฆ". Uses nanoid. */
|
|
27
|
+
declare function id(prefix: string, size?: number): string;
|
|
28
|
+
|
|
29
|
+
export { id, isUuid, nanoid, shortId, uuidVersion, uuidv4, uuidv7, uuidv7Time };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lacspace/id
|
|
3
|
+
*
|
|
4
|
+
* Unique IDs done right โ UUID v4, **UUID v7** (time-sortable), Nano-ID-style
|
|
5
|
+
* and short URL-safe codes. Cryptographically random (Web Crypto), monotonic
|
|
6
|
+
* v7 within the same millisecond, zero-dependency and isomorphic.
|
|
7
|
+
*/
|
|
8
|
+
/** RFC 4122 UUID v4 (random). */
|
|
9
|
+
declare function uuidv4(): string;
|
|
10
|
+
/**
|
|
11
|
+
* UUID v7 โ a time-sortable UUID (48-bit Unix-ms timestamp + randomness).
|
|
12
|
+
* Great as a primary key: lexicographically sortable by creation time, index-
|
|
13
|
+
* friendly, still globally unique. Monotonic within the same millisecond.
|
|
14
|
+
*/
|
|
15
|
+
declare function uuidv7(now?: number): string;
|
|
16
|
+
/** Extract the timestamp (ms) embedded in a UUID v7. */
|
|
17
|
+
declare function uuidv7Time(uuid: string): number;
|
|
18
|
+
/** Is this a well-formed UUID? */
|
|
19
|
+
declare function isUuid(s: string): boolean;
|
|
20
|
+
/** UUID version digit (1โ8), or null if not a UUID. */
|
|
21
|
+
declare function uuidVersion(s: string): number | null;
|
|
22
|
+
/** Nano-ID-style URL-safe id (default 21 chars). */
|
|
23
|
+
declare function nanoid(size?: number): string;
|
|
24
|
+
/** Short, URL-safe, human-shareable code (default 8 chars). */
|
|
25
|
+
declare function shortId(size?: number): string;
|
|
26
|
+
/** A prefixed id, e.g. `id("user")` โ "user_9f8cโฆ". Uses nanoid. */
|
|
27
|
+
declare function id(prefix: string, size?: number): string;
|
|
28
|
+
|
|
29
|
+
export { id, isUuid, nanoid, shortId, uuidVersion, uuidv4, uuidv7, uuidv7Time };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
function getRandom(bytes) {
|
|
3
|
+
const c = globalThis.crypto;
|
|
4
|
+
if (!c || typeof c.getRandomValues !== "function") {
|
|
5
|
+
throw new Error("Web Crypto getRandomValues is unavailable in this environment.");
|
|
6
|
+
}
|
|
7
|
+
c.getRandomValues(bytes);
|
|
8
|
+
return bytes;
|
|
9
|
+
}
|
|
10
|
+
var HEX = [];
|
|
11
|
+
for (let i = 0; i < 256; i++) HEX.push((i + 256).toString(16).slice(1));
|
|
12
|
+
function bytesToUuid(b) {
|
|
13
|
+
return HEX[b[0]] + HEX[b[1]] + HEX[b[2]] + HEX[b[3]] + "-" + HEX[b[4]] + HEX[b[5]] + "-" + HEX[b[6]] + HEX[b[7]] + "-" + HEX[b[8]] + HEX[b[9]] + "-" + HEX[b[10]] + HEX[b[11]] + HEX[b[12]] + HEX[b[13]] + HEX[b[14]] + HEX[b[15]];
|
|
14
|
+
}
|
|
15
|
+
function uuidv4() {
|
|
16
|
+
const b = getRandom(new Uint8Array(16));
|
|
17
|
+
b[6] = b[6] & 15 | 64;
|
|
18
|
+
b[8] = b[8] & 63 | 128;
|
|
19
|
+
return bytesToUuid(b);
|
|
20
|
+
}
|
|
21
|
+
var lastV7Time = 0;
|
|
22
|
+
var v7Counter = 0;
|
|
23
|
+
function uuidv7(now) {
|
|
24
|
+
const time = now ?? Date.now();
|
|
25
|
+
if (time === lastV7Time) v7Counter++;
|
|
26
|
+
else {
|
|
27
|
+
lastV7Time = time;
|
|
28
|
+
v7Counter = 0;
|
|
29
|
+
}
|
|
30
|
+
const b = getRandom(new Uint8Array(16));
|
|
31
|
+
b[0] = time / 2 ** 40 & 255;
|
|
32
|
+
b[1] = time / 2 ** 32 & 255;
|
|
33
|
+
b[2] = time / 2 ** 24 & 255;
|
|
34
|
+
b[3] = time / 2 ** 16 & 255;
|
|
35
|
+
b[4] = time / 2 ** 8 & 255;
|
|
36
|
+
b[5] = time & 255;
|
|
37
|
+
b[6] = 112 | v7Counter >> 8 & 15;
|
|
38
|
+
b[7] = v7Counter & 255;
|
|
39
|
+
b[8] = b[8] & 63 | 128;
|
|
40
|
+
return bytesToUuid(b);
|
|
41
|
+
}
|
|
42
|
+
function uuidv7Time(uuid) {
|
|
43
|
+
const hex = uuid.replace(/-/g, "").slice(0, 12);
|
|
44
|
+
return parseInt(hex, 16);
|
|
45
|
+
}
|
|
46
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
47
|
+
function isUuid(s) {
|
|
48
|
+
return UUID_RE.test(s);
|
|
49
|
+
}
|
|
50
|
+
function uuidVersion(s) {
|
|
51
|
+
return isUuid(s) ? parseInt(s[14], 16) : null;
|
|
52
|
+
}
|
|
53
|
+
var NANO_ALPHABET = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
|
|
54
|
+
var URL_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
|
|
55
|
+
function randomFromAlphabet(size, alphabet) {
|
|
56
|
+
const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
|
|
57
|
+
const step = Math.ceil(1.6 * mask * size / alphabet.length);
|
|
58
|
+
let id2 = "";
|
|
59
|
+
while (id2.length < size) {
|
|
60
|
+
const bytes = getRandom(new Uint8Array(step));
|
|
61
|
+
for (let i = 0; i < step && id2.length < size; i++) {
|
|
62
|
+
const idx = bytes[i] & mask;
|
|
63
|
+
if (idx < alphabet.length) id2 += alphabet[idx];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return id2;
|
|
67
|
+
}
|
|
68
|
+
function nanoid(size = 21) {
|
|
69
|
+
return randomFromAlphabet(size, NANO_ALPHABET);
|
|
70
|
+
}
|
|
71
|
+
function shortId(size = 8) {
|
|
72
|
+
return randomFromAlphabet(size, URL_ALPHABET);
|
|
73
|
+
}
|
|
74
|
+
function id(prefix, size = 16) {
|
|
75
|
+
return `${prefix}_${nanoid(size)}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { id, isUuid, nanoid, shortId, uuidVersion, uuidv4, uuidv7, uuidv7Time };
|
|
79
|
+
//# sourceMappingURL=index.js.map
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["id"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @lacspace/id\n *\n * Unique IDs done right โ UUID v4, **UUID v7** (time-sortable), Nano-ID-style\n * and short URL-safe codes. Cryptographically random (Web Crypto), monotonic\n * v7 within the same millisecond, zero-dependency and isomorphic.\n */\n\nfunction getRandom(bytes: Uint8Array): Uint8Array {\n const c = (globalThis as { crypto?: Crypto }).crypto;\n if (!c || typeof c.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto getRandomValues is unavailable in this environment.\");\n }\n c.getRandomValues(bytes);\n return bytes;\n}\n\nconst HEX: string[] = [];\nfor (let i = 0; i < 256; i++) HEX.push((i + 0x100).toString(16).slice(1));\n\nfunction bytesToUuid(b: Uint8Array): string {\n return (\n HEX[b[0]!]! + HEX[b[1]!]! + HEX[b[2]!]! + HEX[b[3]!]! + \"-\" +\n HEX[b[4]!]! + HEX[b[5]!]! + \"-\" +\n HEX[b[6]!]! + HEX[b[7]!]! + \"-\" +\n HEX[b[8]!]! + HEX[b[9]!]! + \"-\" +\n HEX[b[10]!]! + HEX[b[11]!]! + HEX[b[12]!]! + HEX[b[13]!]! + HEX[b[14]!]! + HEX[b[15]!]!\n );\n}\n\n/** RFC 4122 UUID v4 (random). */\nexport function uuidv4(): string {\n const b = getRandom(new Uint8Array(16));\n b[6] = (b[6]! & 0x0f) | 0x40; // version 4\n b[8] = (b[8]! & 0x3f) | 0x80; // variant\n return bytesToUuid(b);\n}\n\nlet lastV7Time = 0;\nlet v7Counter = 0;\n\n/**\n * UUID v7 โ a time-sortable UUID (48-bit Unix-ms timestamp + randomness).\n * Great as a primary key: lexicographically sortable by creation time, index-\n * friendly, still globally unique. Monotonic within the same millisecond.\n */\nexport function uuidv7(now?: number): string {\n const time = now ?? Date.now();\n if (time === lastV7Time) v7Counter++;\n else { lastV7Time = time; v7Counter = 0; }\n\n const b = getRandom(new Uint8Array(16));\n // 48-bit timestamp (big-endian) in bytes 0..5\n b[0] = (time / 2 ** 40) & 0xff;\n b[1] = (time / 2 ** 32) & 0xff;\n b[2] = (time / 2 ** 24) & 0xff;\n b[3] = (time / 2 ** 16) & 0xff;\n b[4] = (time / 2 ** 8) & 0xff;\n b[5] = time & 0xff;\n // rand_a (12 bits) holds a monotonic counter so same-ms ids stay sortable\n b[6] = 0x70 | ((v7Counter >> 8) & 0x0f); // version 7 + counter high nibble\n b[7] = v7Counter & 0xff; // counter low byte\n b[8] = (b[8]! & 0x3f) | 0x80; // variant\n return bytesToUuid(b);\n}\n\n/** Extract the timestamp (ms) embedded in a UUID v7. */\nexport function uuidv7Time(uuid: string): number {\n const hex = uuid.replace(/-/g, \"\").slice(0, 12);\n return parseInt(hex, 16);\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/** Is this a well-formed UUID? */\nexport function isUuid(s: string): boolean {\n return UUID_RE.test(s);\n}\n\n/** UUID version digit (1โ8), or null if not a UUID. */\nexport function uuidVersion(s: string): number | null {\n return isUuid(s) ? parseInt(s[14]!, 16) : null;\n}\n\nconst NANO_ALPHABET = \"useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict\";\nconst URL_ALPHABET = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\";\n\nfunction randomFromAlphabet(size: number, alphabet: string): string {\n const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1;\n const step = Math.ceil((1.6 * mask * size) / alphabet.length);\n let id = \"\";\n while (id.length < size) {\n const bytes = getRandom(new Uint8Array(step));\n for (let i = 0; i < step && id.length < size; i++) {\n const idx = bytes[i]! & mask;\n if (idx < alphabet.length) id += alphabet[idx];\n }\n }\n return id;\n}\n\n/** Nano-ID-style URL-safe id (default 21 chars). */\nexport function nanoid(size = 21): string {\n return randomFromAlphabet(size, NANO_ALPHABET);\n}\n\n/** Short, URL-safe, human-shareable code (default 8 chars). */\nexport function shortId(size = 8): string {\n return randomFromAlphabet(size, URL_ALPHABET);\n}\n\n/** A prefixed id, e.g. `id(\"user\")` โ \"user_9f8cโฆ\". Uses nanoid. */\nexport function id(prefix: string, size = 16): string {\n return `${prefix}_${nanoid(size)}`;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lacspace/id",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Unique IDs done right โ UUID v4, time-sortable UUID v7, Nano-ID-style and short URL-safe codes. Cryptographically random (Web Crypto), zero-dependency, isomorphic.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": false,
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"uuid",
|
|
31
|
+
"uuidv4",
|
|
32
|
+
"uuidv7",
|
|
33
|
+
"uuid-v7",
|
|
34
|
+
"nanoid",
|
|
35
|
+
"short-id",
|
|
36
|
+
"unique-id",
|
|
37
|
+
"id-generator",
|
|
38
|
+
"time-sortable",
|
|
39
|
+
"web-crypto",
|
|
40
|
+
"zero-dependency",
|
|
41
|
+
"isomorphic",
|
|
42
|
+
"typescript"
|
|
43
|
+
],
|
|
44
|
+
"author": "Lacspace <contact@lacspace.com>",
|
|
45
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
46
|
+
"homepage": "https://lacspace.com/packages",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "git+https://github.com/lacspace/npm-packages.git",
|
|
50
|
+
"directory": "id"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/lacspace/npm-packages/issues"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|