@sdxc/uuid 2026.10.2 → 2026.10.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @sdxc/uuid
2
2
 
3
- Validate, assert and generate UUIDs behind a branded UUID type.
3
+ Validate and assert UUIDs behind a branded UUID type, and generate v4 or v7 values.
4
4
 
5
5
  A UUID reaches your code as a `string`, and every layer below re-checks it because the type
6
6
  says nothing. This package checks once and hands back a `UUID`: the same string, carrying
@@ -13,24 +13,38 @@ raw path segment it came from.
13
13
  npm add @sdxc/uuid
14
14
  ```
15
15
 
16
- `generateUUID` calls the platform's
17
- [`crypto.randomUUID()`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID).
18
- [`@sdxc/typeid`](https://www.npmjs.com/package/@sdxc/typeid) installs alongside it when you
16
+ The generators use the platform's Web Crypto API, available in browsers, Node, Bun, Deno and
17
+ Cloudflare Workers. [`@sdxc/typeid`](https://www.npmjs.com/package/@sdxc/typeid) installs alongside it when you
19
18
  want prefixed identifiers built on these values.
20
19
 
21
20
  ## Usage
22
21
 
23
22
  ### Generate An Identifier
24
23
 
24
+ Each UUID version is its own entry point, and each exports the same `generateUUID()`. The
25
+ import path picks the version, so switching an identifier to the other one changes only that
26
+ line.
27
+
28
+ ```typescript
29
+ import { generateUUID } from "@sdxc/uuid/v7";
30
+
31
+ let id = generateUUID();
32
+ // "019a6f3e-5c21-7a4b-9d3e-8f1a2b3c4d5e" as UUID
33
+ ```
34
+
35
+ A v7 value starts with the millisecond it was created, so identifiers sort in creation order
36
+ and new database rows append to the end of their primary-key index. Import from
37
+ `@sdxc/uuid/v4` instead when the identifier must not reveal when it was created:
38
+
25
39
  ```typescript
26
- import { generateUUID } from "@sdxc/uuid";
40
+ import { generateUUID } from "@sdxc/uuid/v4";
27
41
 
28
42
  let id = generateUUID();
29
43
  // "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d" as UUID
30
44
  ```
31
45
 
32
- The longhand is `crypto.randomUUID()` followed by the check that brands its result, which is
33
- what lets the value flow into anything typed as `UUID` without a cast.
46
+ Either way the result is already branded, so it flows into anything typed as `UUID` without a
47
+ cast.
34
48
 
35
49
  ### Narrow A String You Received
36
50
 
@@ -112,21 +126,23 @@ Narrows a string to `UUID` for the rest of the enclosing scope, throwing when it
112
126
  `InvalidUUIDTypeError` for a non-string, `InvalidUUIDLengthError` for a string that is not 36
113
127
  characters, and `InvalidUUIDFormatError` for 36 characters in the wrong shape.
114
128
 
115
- ### `generateUUID(): UUID`
129
+ ### `generateUUID(): UUID` from `@sdxc/uuid/v7`
130
+
131
+ Returns a new version 7 UUID as defined by [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562):
132
+ a 48-bit millisecond timestamp, a 12-bit counter and 62 random bits. Values sort in the order
133
+ they were generated, including several created in the same millisecond, which matters on
134
+ runtimes such as Cloudflare Workers where `Date.now()` holds still until the next I/O. The
135
+ guarantee holds within one process or isolate; values from two processes in the same
136
+ millisecond are unique and share no defined order.
116
137
 
117
- Returns a new random UUID from `crypto.randomUUID()`, already narrowed to `UUID`. That is a
118
- version 4 UUID as defined by [RFC 9562](https://www.rfc-editor.org/rfc/rfc9562), random in
119
- every position the format leaves free — including when it was created, so two values carry
120
- no ordering.
138
+ The timestamp is readable by anyone holding the value, including through a
139
+ [`@sdxc/typeid`](https://www.npmjs.com/package/@sdxc/typeid) prefix. Use it for identifiers
140
+ whose creation time is fine to expose, and keep secrets on random values.
121
141
 
122
- ### `generateUUIDv7(): UUID`
142
+ ### `generateUUID(): UUID` from `@sdxc/uuid/v4`
123
143
 
124
- Returns a new version 7 UUID: a 48-bit millisecond timestamp followed by 74 random bits, so
125
- values sort the way they were created. Use this one where
126
- [`@sdxc/typeid`](https://www.npmjs.com/package/@sdxc/typeid) is going to prefix the result
127
- and something reads that prefixed id's order — a paginated listing, a table with no separate
128
- `created_at` to sort by. Reach for `generateUUID` instead when the id must not reveal when it
129
- was minted, such as a value handed to someone outside the system.
144
+ Returns a new version 4 UUID from `crypto.randomUUID()`: 122 random bits, so two values carry
145
+ no ordering and none reveals when it was created.
130
146
 
131
147
  ### Errors
132
148
 
@@ -198,7 +214,7 @@ takes a `UUID` and a prefix and encodes them as one sortable, self-describing st
198
214
 
199
215
  ```typescript
200
216
  import { TypeID } from "@sdxc/typeid";
201
- import { generateUUID } from "@sdxc/uuid";
217
+ import { generateUUID } from "@sdxc/uuid/v7";
202
218
 
203
219
  let accountId = TypeID.fromUUID("account", generateUUID());
204
220
 
@@ -207,7 +223,8 @@ accountId.toString();
207
223
  ```
208
224
 
209
225
  It asks for a `UUID` rather than a `string`, so the value you generate here is accepted and a
210
- random 36 characters is not.
226
+ random 36 characters is not. TypeID keeps the UUID's sort order, so a v7 identifier stays
227
+ time-ordered once it is prefixed.
211
228
 
212
229
  ## Versioning
213
230
 
package/dist/index.d.ts CHANGED
@@ -9,16 +9,4 @@ export type UUID = BaseUUID & {
9
9
  };
10
10
  export declare function isUUID(value: string): value is UUID;
11
11
  export declare function assertUUID(value: string): asserts value is UUID;
12
- export declare function generateUUID(): UUID;
13
- /**
14
- * Generates a UUID version 7: a 48-bit big-endian Unix millisecond timestamp
15
- * followed by 74 random bits, so values sort the way they were created without
16
- * a separate ordering column.
17
- *
18
- * @returns A UUIDv7, narrowed to {@link UUID}.
19
- * @example
20
- * let id = generateUUIDv7();
21
- * // "018f4d2e-...": sorts after a UUIDv7 minted a millisecond earlier
22
- */
23
- export declare function generateUUIDv7(): UUID;
24
12
  export { InvalidUUIDFormatError, InvalidUUIDLengthError, InvalidUUIDTypeError };
package/dist/index.js CHANGED
@@ -21,38 +21,4 @@ export function assertUUID(value) {
21
21
  throw new InvalidUUIDFormatError(value);
22
22
  }
23
23
  }
24
- export function generateUUID() {
25
- let id = crypto.randomUUID();
26
- assertUUID(id);
27
- return id;
28
- }
29
- /**
30
- * Generates a UUID version 7: a 48-bit big-endian Unix millisecond timestamp
31
- * followed by 74 random bits, so values sort the way they were created without
32
- * a separate ordering column.
33
- *
34
- * @returns A UUIDv7, narrowed to {@link UUID}.
35
- * @example
36
- * let id = generateUUIDv7();
37
- * // "018f4d2e-...": sorts after a UUIDv7 minted a millisecond earlier
38
- */
39
- export function generateUUIDv7() {
40
- let timestamp = BigInt(Date.now());
41
- let random = crypto.getRandomValues(new Uint8Array(10));
42
- let bytes = new Uint8Array(16);
43
- bytes[0] = Number((timestamp >> 40n) & 0xffn);
44
- bytes[1] = Number((timestamp >> 32n) & 0xffn);
45
- bytes[2] = Number((timestamp >> 24n) & 0xffn);
46
- bytes[3] = Number((timestamp >> 16n) & 0xffn);
47
- bytes[4] = Number((timestamp >> 8n) & 0xffn);
48
- bytes[5] = Number(timestamp & 0xffn);
49
- bytes[6] = 0x70 | (random[0] & 0x0f);
50
- bytes[7] = random[1];
51
- bytes[8] = 0x80 | (random[2] & 0x3f);
52
- bytes.set(random.subarray(3), 9);
53
- let hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
54
- let id = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
55
- assertUUID(id);
56
- return id;
57
- }
58
24
  export { InvalidUUIDFormatError, InvalidUUIDLengthError, InvalidUUIDTypeError };
package/dist/v4.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Random UUID generation: version 4 values from the platform's `crypto.randomUUID()`,
3
+ * narrowed to the branded `UUID` type. Every free bit is random, so a value reveals nothing,
4
+ * including when it was created.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { UUID } from "./index.js";
10
+ /**
11
+ * Generates a version 4 UUID: 122 random bits, so values carry no ordering and expose no
12
+ * creation time.
13
+ *
14
+ * @returns A UUIDv4, narrowed to {@link UUID}.
15
+ * @example
16
+ * let id = generateUUID();
17
+ * // "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
18
+ */
19
+ export declare function generateUUID(): UUID;
package/dist/v4.js ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Random UUID generation: version 4 values from the platform's `crypto.randomUUID()`,
3
+ * narrowed to the branded `UUID` type. Every free bit is random, so a value reveals nothing,
4
+ * including when it was created.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { assertUUID } from "./index.js";
10
+ /**
11
+ * Generates a version 4 UUID: 122 random bits, so values carry no ordering and expose no
12
+ * creation time.
13
+ *
14
+ * @returns A UUIDv4, narrowed to {@link UUID}.
15
+ * @example
16
+ * let id = generateUUID();
17
+ * // "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"
18
+ */
19
+ export function generateUUID() {
20
+ let id = crypto.randomUUID();
21
+ assertUUID(id);
22
+ return id;
23
+ }
package/dist/v7.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Time-ordered UUID generation: version 7 values whose string order follows the order they
3
+ * were generated in, including several in the same millisecond, so primary keys append to
4
+ * their index and an id alone can page a listing.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import type { UUID } from "./index.js";
10
+ /**
11
+ * Generates a version 7 UUID (RFC 9562): a 48-bit Unix millisecond timestamp, a 12-bit
12
+ * counter that increments within a millisecond, and 62 random bits. Each value sorts after
13
+ * the previous one from the same isolate, even while `Date.now()` stays put.
14
+ *
15
+ * @returns A UUIDv7, narrowed to {@link UUID}.
16
+ * @example
17
+ * let first = generateUUID();
18
+ * let second = generateUUID();
19
+ * first < second; // true
20
+ */
21
+ export declare function generateUUID(): UUID;
package/dist/v7.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Time-ordered UUID generation: version 7 values whose string order follows the order they
3
+ * were generated in, including several in the same millisecond, so primary keys append to
4
+ * their index and an id alone can page a listing.
5
+ *
6
+ * @author [Sergio Xalambrí](https://sergiodxa.com)
7
+ * @copyright Sergio Xalambrí 2026
8
+ */
9
+ import { assertUUID } from "./index.js";
10
+ /** Largest value the 12-bit `rand_a` counter holds before the timestamp has to advance. */
11
+ const COUNTER_MAX = 0xfff;
12
+ /**
13
+ * The last timestamp written and the counter beside it, shared by every call in the isolate.
14
+ * Keeping the timestamp from going backwards is what makes each value sort after the last.
15
+ */
16
+ const CLOCK = { timestamp: -1, counter: 0 };
17
+ /**
18
+ * Generates a version 7 UUID (RFC 9562): a 48-bit Unix millisecond timestamp, a 12-bit
19
+ * counter that increments within a millisecond, and 62 random bits. Each value sorts after
20
+ * the previous one from the same isolate, even while `Date.now()` stays put.
21
+ *
22
+ * @returns A UUIDv7, narrowed to {@link UUID}.
23
+ * @example
24
+ * let first = generateUUID();
25
+ * let second = generateUUID();
26
+ * first < second; // true
27
+ */
28
+ export function generateUUID() {
29
+ advance(Date.now());
30
+ let random = crypto.getRandomValues(new Uint8Array(8));
31
+ let bytes = new Uint8Array(16);
32
+ let timestamp = CLOCK.timestamp;
33
+ for (let index = 5; index >= 0; index--) {
34
+ bytes[index] = timestamp % 256;
35
+ timestamp = Math.floor(timestamp / 256);
36
+ }
37
+ bytes[6] = 0x70 | (CLOCK.counter >> 8);
38
+ bytes[7] = CLOCK.counter & 0xff;
39
+ bytes[8] = 0x80 | (random[0] & 0x3f);
40
+ bytes.set(random.subarray(1), 9);
41
+ let id = format(bytes);
42
+ assertUUID(id);
43
+ return id;
44
+ }
45
+ /**
46
+ * Moves the clock to `now`, or past the last timestamp when `now` is not later than it.
47
+ * A new millisecond seeds the counter randomly below its midpoint, leaving at least 2048
48
+ * increments; exhausting them moves the timestamp one millisecond ahead, as RFC 9562 allows.
49
+ *
50
+ * @param now Current Unix time in milliseconds.
51
+ */
52
+ function advance(now) {
53
+ if (now > CLOCK.timestamp) {
54
+ CLOCK.timestamp = now;
55
+ CLOCK.counter = seed();
56
+ return;
57
+ }
58
+ CLOCK.counter++;
59
+ if (CLOCK.counter <= COUNTER_MAX)
60
+ return;
61
+ CLOCK.timestamp++;
62
+ CLOCK.counter = seed();
63
+ }
64
+ /**
65
+ * Picks a random starting counter with its top bit clear, so the first value of a
66
+ * millisecond is unpredictable and still leaves half the counter's range to increment into.
67
+ *
68
+ * @returns An integer in `[0, 0x7ff]`.
69
+ */
70
+ function seed() {
71
+ let [value] = crypto.getRandomValues(new Uint16Array(1));
72
+ return value & 0x7ff;
73
+ }
74
+ /**
75
+ * Writes 16 bytes as the canonical lowercase `8-4-4-4-12` UUID string.
76
+ *
77
+ * @param bytes The UUID's bytes in network order.
78
+ * @returns The hyphenated hex form.
79
+ */
80
+ function format(bytes) {
81
+ let hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
82
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
83
+ }
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@sdxc/uuid",
3
- "version": "2026.10.2",
4
- "description": "Validate, assert and generate UUIDs behind a branded UUID type",
3
+ "version": "2026.10.11",
4
+ "description": "Validate and assert UUIDs behind a branded UUID type, and generate v4 or v7 values",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "exports": {
8
- ".": "./dist/index.js"
8
+ ".": "./dist/index.js",
9
+ "./v4": "./dist/v4.js",
10
+ "./v7": "./dist/v7.js"
9
11
  },
10
- "gitHead": "7582e46f150b052ba145945661ac9937f5f26f27",
12
+ "gitHead": "6ac77f0839a66981ca666a16f5cf37a5473df2b1",
11
13
  "publishConfig": {
12
14
  "access": "public"
13
15
  },
@@ -15,5 +17,14 @@
15
17
  "type": "git",
16
18
  "url": "git+https://github.com/sergiodxa/monorepo.git",
17
19
  "directory": "packages/uuid"
20
+ },
21
+ "homepage": "https://sdxc.sergiodxa.com/api/uuid",
22
+ "author": {
23
+ "name": "Sergio Xalambrí",
24
+ "url": "https://sergiodxa.com"
25
+ },
26
+ "funding": {
27
+ "type": "github",
28
+ "url": "https://github.com/sponsors/sergiodxa"
18
29
  }
19
30
  }