@typed/id 0.12.0 → 0.14.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.
@@ -12,14 +12,8 @@ declare const DateTimes_base: import("effect/Context").TagClass<DateTimes, "Date
12
12
  }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, DateTimes | R> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, DateTimes> : Effect.Effect<X, never, DateTimes>;
13
13
  };
14
14
  export declare class DateTimes extends DateTimes_base {
15
- static readonly make: (now: Effect.Effect<number>) => Effect.Effect<{
16
- now: Effect.Effect<number, never, never>;
17
- date: Effect.Effect<Date, never, never>;
18
- }, never, never>;
19
- static readonly Default: Effect.Effect<{
20
- now: Effect.Effect<number, never, never>;
21
- date: Effect.Effect<Date, never, never>;
22
- }, never, never>;
23
- static readonly Fixed: (base: Date) => Layer.Layer<DateTimes, never, never>;
15
+ static readonly make: (now: Effect.Effect<number>) => Layer.Layer<DateTimes>;
16
+ static readonly Default: Layer.Layer<DateTimes>;
17
+ static readonly Fixed: (base: Date) => Layer.Layer<DateTimes>;
24
18
  }
25
19
  export {};
package/dist/DateTimes.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect, Layer } from 'effect';
2
2
  export class DateTimes extends Effect.Tag('DateTimes')() {
3
- static make = (now) => Effect.succeed({
3
+ static make = (now) => Layer.succeed(this, {
4
4
  now,
5
5
  date: now.pipe(Effect.map((millis) => new Date(millis))),
6
6
  });
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimes.js","sourceRoot":"","sources":["../src/DateTimes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAqB,MAAM,QAAQ,CAAA;AAEzD,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAMnD;IACD,MAAM,CAAU,IAAI,GAAG,CAAC,GAA0B,EAAE,EAAE,CACpD,MAAM,CAAC,OAAO,CAAC;QACb,GAAG;QACH,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;KACzD,CAAC,CAAA;IAEJ,MAAM,CAAU,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAEzF,MAAM,CAAU,KAAK,GAAG,CAAC,IAAU,EAAE,EAAE,CACrC,KAAK,CAAC,MAAM,CACV,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;QACjC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAA;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,iBAAiB,CAAC,IAAI,CACtC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACpB,4FAA4F;QAC5F,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CACtE,CACF,CAAA;QAED,OAAO;YACL,GAAG;YACH,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;SACzD,CAAA;IACH,CAAC,CAAC,CACH,CAAA"}
1
+ {"version":3,"file":"DateTimes.js","sourceRoot":"","sources":["../src/DateTimes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAqB,MAAM,QAAQ,CAAA;AAEzD,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAMnD;IACD,MAAM,CAAU,IAAI,GAAG,CAAC,GAA0B,EAA0B,EAAE,CAC5E,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE;QAClB,GAAG;QACH,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;KACzD,CAAC,CAAA;IAEJ,MAAM,CAAU,OAAO,GAA2B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAA;IAEjH,MAAM,CAAU,KAAK,GAAG,CAAC,IAAU,EAA0B,EAAE,CAC7D,KAAK,CAAC,MAAM,CACV,SAAS,EACT,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;QACjC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAA;QAClD,MAAM,GAAG,GAAG,KAAK,CAAC,iBAAiB,CAAC,IAAI,CACtC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;QACpB,4FAA4F;QAC5F,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CACtE,CACF,CAAA;QAED,OAAO;YACL,GAAG;YACH,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;SACzD,CAAA;IACH,CAAC,CAAC,CACH,CAAA"}
@@ -0,0 +1,7 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Schema } from 'effect';
4
+ export declare const Uuid4: Schema.brand<typeof Schema.UUID, "@typed/id/UUID4">;
5
+ export type Uuid4 = Schema.Schema.Type<typeof Uuid4>;
6
+ export declare const isUuid4: (value: string) => value is Uuid4;
7
+ export declare const makeUuid4: Effect.Effect<Uuid4, never, GetRandomValues>;
package/dist/Uuid4.js ADDED
@@ -0,0 +1,14 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Schema } from 'effect';
4
+ import { uuidStringify } from './UuidStringify.js';
5
+ export const Uuid4 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID4'));
6
+ export const isUuid4 = Schema.is(Uuid4);
7
+ export const makeUuid4 = Effect.map(GetRandomValues.apply(16), uuid4FromSeed);
8
+ function uuid4FromSeed(seed) {
9
+ // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
10
+ seed[6] = (seed[6] & 0x0f) | 0x40;
11
+ seed[8] = (seed[8] & 0x3f) | 0x80;
12
+ return uuidStringify(seed);
13
+ }
14
+ //# sourceMappingURL=Uuid4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uuid4.js","sourceRoot":"","sources":["../src/Uuid4.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAGtE,MAAM,CAAC,MAAM,OAAO,GAAsC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;AAE1E,MAAM,CAAC,MAAM,SAAS,GAAiD,MAAM,CAAC,GAAG,CAC/E,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,EACzB,aAAa,CACd,CAAA;AAED,SAAS,aAAa,CAAC,IAAgB;IACrC,gEAAgE;IAChE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IACjC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IAEjC,OAAO,aAAa,CAAC,IAAI,CAAU,CAAA;AACrC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { Layer, Schema } from 'effect';
3
+ export declare const Uuid5: Schema.brand<typeof Schema.UUID, "@typed/id/UUID5">;
4
+ export type Uuid5 = Schema.Schema.Type<typeof Uuid5>;
5
+ export declare const isUuid5: (value: string) => value is Uuid5;
6
+ declare const Sha1_base: import("effect/Context").TagClass<Sha1, "Sha1", {
7
+ readonly hash: (data: Uint8Array) => Effect.Effect<Uint8Array, never>;
8
+ }> & Effect.Tag.Proxy<Sha1, {
9
+ readonly hash: (data: Uint8Array) => Effect.Effect<Uint8Array, never>;
10
+ }> & {
11
+ use: <X>(body: (_: {
12
+ readonly hash: (data: Uint8Array) => Effect.Effect<Uint8Array, never>;
13
+ }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | Sha1> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, Sha1> : Effect.Effect<X, never, Sha1>;
14
+ };
15
+ export declare class Sha1 extends Sha1_base {
16
+ static readonly Default: Layer.Layer<Sha1, never, never>;
17
+ }
18
+ export type Uuid5Namespace = Uint8Array;
19
+ export declare const Uuid5Namespace: {
20
+ readonly DNS: Uint8Array<ArrayBuffer>;
21
+ readonly URL: Uint8Array<ArrayBuffer>;
22
+ readonly OID: Uint8Array<ArrayBuffer>;
23
+ readonly X500: Uint8Array<ArrayBuffer>;
24
+ };
25
+ export declare function makeUuid5(namespace: Uuid5Namespace, name: string): Effect.Effect<Uuid5, never, Sha1>;
26
+ export {};
package/dist/Uuid5.js ADDED
@@ -0,0 +1,48 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { Layer, Schema } from 'effect';
3
+ import { uuidStringify } from './UuidStringify.js';
4
+ export const Uuid5 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID5'));
5
+ export const isUuid5 = Schema.is(Uuid5);
6
+ export class Sha1 extends Effect.Tag('Sha1')() {
7
+ static Default = Layer.succeed(this, {
8
+ hash: (data) => Effect.promise(() => crypto.subtle.digest('SHA-1', data).then((hash) => new Uint8Array(hash))),
9
+ });
10
+ }
11
+ const textEncoder = new TextEncoder();
12
+ // Pre-defined namespaces from RFC 4122
13
+ export const Uuid5Namespace = {
14
+ DNS: new Uint8Array([
15
+ 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
16
+ ]),
17
+ URL: new Uint8Array([
18
+ 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
19
+ ]),
20
+ OID: new Uint8Array([
21
+ 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
22
+ ]),
23
+ X500: new Uint8Array([
24
+ 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
25
+ ]),
26
+ };
27
+ export function makeUuid5(namespace, name) {
28
+ return Effect.gen(function* () {
29
+ const sha1 = yield* Sha1;
30
+ // Convert name to UTF-8 bytes
31
+ const nameBytes = textEncoder.encode(name);
32
+ // Concatenate namespace and name
33
+ const buffer = new Uint8Array(namespace.length + nameBytes.length);
34
+ buffer.set(namespace);
35
+ buffer.set(nameBytes, namespace.length);
36
+ // Hash the concatenated bytes
37
+ const hash = yield* sha1.hash(buffer);
38
+ // Format as UUID v5
39
+ const result = new Uint8Array(16);
40
+ // Copy first 16 bytes of the hash
41
+ result.set(hash.subarray(0, 16));
42
+ // Set version (5) and variant bits
43
+ result[6] = (result[6] & 0x0f) | 0x50; // version 5
44
+ result[8] = (result[8] & 0x3f) | 0x80; // variant 1
45
+ return uuidStringify(result);
46
+ });
47
+ }
48
+ //# sourceMappingURL=Uuid5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uuid5.js","sourceRoot":"","sources":["../src/Uuid5.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Layer, Schema } from 'effect';
4
+ import { DateTimes } from './DateTimes.js';
5
+ export declare const Uuid6: Schema.brand<typeof Schema.UUID, "@typed/id/UUID6">;
6
+ export type Uuid6 = Schema.Schema.Type<typeof Uuid6>;
7
+ export declare const isUuid6: (value: string) => value is Uuid6;
8
+ export type Uuid6Seed = {
9
+ readonly msecs: number;
10
+ readonly nsecs: number;
11
+ readonly clockSeq: number;
12
+ readonly nodeId: Uint8Array;
13
+ };
14
+ declare const Uuid6State_base: import("effect/Context").TagClass<Uuid6State, "Uuid6State", {
15
+ readonly next: Effect.Effect<Uuid6Seed>;
16
+ }> & Effect.Tag.Proxy<Uuid6State, {
17
+ readonly next: Effect.Effect<Uuid6Seed>;
18
+ }> & {
19
+ use: <X>(body: (_: {
20
+ readonly next: Effect.Effect<Uuid6Seed>;
21
+ }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | Uuid6State> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, Uuid6State> : Effect.Effect<X, never, Uuid6State>;
22
+ };
23
+ export declare class Uuid6State extends Uuid6State_base {
24
+ static Default: Layer.Layer<Uuid6State, never, GetRandomValues | DateTimes>;
25
+ }
26
+ export declare const makeUuid6: Effect.Effect<Uuid6, never, Uuid6State>;
27
+ export {};
package/dist/Uuid6.js ADDED
@@ -0,0 +1,104 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Layer, Schema } from 'effect';
4
+ import { DateTimes } from './DateTimes.js';
5
+ import { uuidStringify } from './UuidStringify.js';
6
+ export const Uuid6 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID6'));
7
+ export const isUuid6 = Schema.is(Uuid6);
8
+ export class Uuid6State extends Effect.Tag('Uuid6State')() {
9
+ static Default = Layer.effect(this, Effect.gen(function* () {
10
+ const { now } = yield* DateTimes;
11
+ const getRandomValues = yield* GetRandomValues;
12
+ const state = {
13
+ msecs: Number.NEGATIVE_INFINITY,
14
+ nsecs: 0,
15
+ clockSeq: -1,
16
+ nodeId: undefined,
17
+ };
18
+ function updateState(currentTime, randomBytes) {
19
+ if (currentTime === state.msecs) {
20
+ // Same msec-interval = simulate higher clock resolution by bumping `nsecs`
21
+ state.nsecs++;
22
+ // Check for `nsecs` overflow (nsecs is capped at 10K intervals / msec)
23
+ if (state.nsecs >= 10000) {
24
+ state.nodeId = undefined;
25
+ state.nsecs = 0;
26
+ }
27
+ }
28
+ else if (currentTime > state.msecs) {
29
+ // Reset nsec counter when clock advances to a new msec interval
30
+ state.nsecs = 0;
31
+ }
32
+ else if (currentTime < state.msecs) {
33
+ // Handle clock regression
34
+ state.nodeId = undefined;
35
+ }
36
+ // Init node and clock sequence if needed
37
+ if (!state.nodeId) {
38
+ state.nodeId = randomBytes.slice(10, 16);
39
+ // Set multicast bit
40
+ state.nodeId[0] |= 0x01;
41
+ // Clock sequence must be randomized
42
+ state.clockSeq = ((randomBytes[8] << 8) | randomBytes[9]) & 0x3fff;
43
+ }
44
+ state.msecs = currentTime;
45
+ return {
46
+ msecs: state.msecs,
47
+ nsecs: state.nsecs,
48
+ clockSeq: state.clockSeq,
49
+ nodeId: state.nodeId,
50
+ };
51
+ }
52
+ return {
53
+ next: Effect.gen(function* () {
54
+ const timestamp = yield* now;
55
+ const randomBytes = yield* getRandomValues(16);
56
+ return updateState(timestamp, randomBytes);
57
+ }),
58
+ };
59
+ }));
60
+ }
61
+ export const makeUuid6 = Effect.map(Uuid6State.next, uuid6FromSeed);
62
+ function uuid6FromSeed({ msecs, nsecs, clockSeq, nodeId }) {
63
+ // First generate the fields as they would be in a v1 UUID
64
+ const result = new Uint8Array(16);
65
+ // Offset to Gregorian epoch
66
+ msecs += 12219292800000;
67
+ // `time_low`
68
+ const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
69
+ const time_low_bytes = new Uint8Array([
70
+ (tl >>> 24) & 0xff,
71
+ (tl >>> 16) & 0xff,
72
+ (tl >>> 8) & 0xff,
73
+ tl & 0xff,
74
+ ]);
75
+ // `time_mid` and `time_high`
76
+ const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff;
77
+ const time_mid_high_bytes = new Uint8Array([
78
+ (tmh >>> 8) & 0xff,
79
+ tmh & 0xff,
80
+ ((tmh >>> 24) & 0xf) | 0x10, // include version 1
81
+ (tmh >>> 16) & 0xff,
82
+ ]);
83
+ result[0] = ((time_mid_high_bytes[2] & 0x0f) << 4) | ((time_mid_high_bytes[3] >> 4) & 0x0f);
84
+ result[1] = ((time_mid_high_bytes[3] & 0x0f) << 4) | ((time_mid_high_bytes[0] & 0xf0) >> 4);
85
+ result[2] = ((time_mid_high_bytes[0] & 0x0f) << 4) | ((time_mid_high_bytes[1] & 0xf0) >> 4);
86
+ result[3] = ((time_mid_high_bytes[1] & 0x0f) << 4) | ((time_low_bytes[0] & 0xf0) >> 4);
87
+ result[4] = ((time_low_bytes[0] & 0x0f) << 4) | ((time_low_bytes[1] & 0xf0) >> 4);
88
+ result[5] = ((time_low_bytes[1] & 0x0f) << 4) | ((time_low_bytes[2] & 0xf0) >> 4);
89
+ result[6] = 0x60 | (time_low_bytes[2] & 0x0f);
90
+ result[7] = time_low_bytes[3];
91
+ // clock_seq_hi_and_reserved
92
+ result[8] = (clockSeq >>> 8) | 0x80; // variant bits
93
+ // clock_seq_low
94
+ result[9] = clockSeq & 0xff;
95
+ // node
96
+ result[10] = nodeId[0];
97
+ result[11] = nodeId[1];
98
+ result[12] = nodeId[2];
99
+ result[13] = nodeId[3];
100
+ result[14] = nodeId[4];
101
+ result[15] = nodeId[5];
102
+ return uuidStringify(result);
103
+ }
104
+ //# sourceMappingURL=Uuid6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uuid6.js","sourceRoot":"","sources":["../src/Uuid6.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Layer, Schema } from 'effect';
4
+ import { DateTimes } from './DateTimes.js';
5
+ export declare const Uuid7: Schema.brand<typeof Schema.UUID, "@typed/id/UUID7">;
6
+ export type Uuid7 = Schema.Schema.Type<typeof Uuid7>;
7
+ export declare const isUuid7: (value: string) => value is Uuid7;
8
+ export type Uuid7Seed = {
9
+ readonly timestamp: number;
10
+ readonly seq: number;
11
+ readonly randomBytes: Uint8Array;
12
+ };
13
+ declare const Uuid7State_base: import("effect/Context").TagClass<Uuid7State, "Uuid7State", {
14
+ readonly next: Effect.Effect<Uuid7Seed>;
15
+ }> & Effect.Tag.Proxy<Uuid7State, {
16
+ readonly next: Effect.Effect<Uuid7Seed>;
17
+ }> & {
18
+ use: <X>(body: (_: {
19
+ readonly next: Effect.Effect<Uuid7Seed>;
20
+ }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | Uuid7State> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, Uuid7State> : Effect.Effect<X, never, Uuid7State>;
21
+ };
22
+ export declare class Uuid7State extends Uuid7State_base {
23
+ static Default: Layer.Layer<Uuid7State, never, GetRandomValues | DateTimes>;
24
+ }
25
+ export declare const makeUuid7: Effect.Effect<Uuid7, never, Uuid7State>;
26
+ export {};
package/dist/Uuid7.js ADDED
@@ -0,0 +1,71 @@
1
+ import * as Effect from 'effect/Effect';
2
+ import { GetRandomValues } from './GetRandomValues.js';
3
+ import { Layer, Schema } from 'effect';
4
+ import { DateTimes } from './DateTimes.js';
5
+ import { uuidStringify } from './UuidStringify.js';
6
+ export const Uuid7 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID7'));
7
+ export const isUuid7 = Schema.is(Uuid7);
8
+ export class Uuid7State extends Effect.Tag('Uuid7State')() {
9
+ static Default = Layer.effect(this, Effect.gen(function* () {
10
+ const { now } = yield* DateTimes;
11
+ const getRandomValues = yield* GetRandomValues;
12
+ const state = {
13
+ msecs: Number.NEGATIVE_INFINITY,
14
+ seq: 0,
15
+ };
16
+ function updateV7State(now, randomBytes) {
17
+ if (now > state.msecs) {
18
+ // Time has moved on! Pick a new random sequence number
19
+ state.seq =
20
+ (randomBytes[6] << 23) | (randomBytes[7] << 16) | (randomBytes[8] << 8) | randomBytes[9];
21
+ state.msecs = now;
22
+ }
23
+ else {
24
+ // Bump sequence counter w/ 32-bit rollover
25
+ state.seq = (state.seq + 1) | 0;
26
+ // In case of rollover, bump timestamp to preserve monotonicity. This is
27
+ // allowed by the RFC and should self-correct as the system clock catches
28
+ // up. See https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-9.4
29
+ if (state.seq === 0) {
30
+ state.msecs++;
31
+ }
32
+ }
33
+ }
34
+ return {
35
+ next: Effect.gen(function* () {
36
+ const randomBytes = yield* getRandomValues(16);
37
+ updateV7State(yield* now, randomBytes);
38
+ return { timestamp: state.msecs, seq: state.seq, randomBytes };
39
+ }),
40
+ };
41
+ }));
42
+ }
43
+ export const makeUuid7 = Effect.map(Uuid7State.next, uuid7FromSeed);
44
+ function uuid7FromSeed({ timestamp, seq, randomBytes }) {
45
+ const result = new Uint8Array(16);
46
+ // byte 0-5: timestamp (48 bits)
47
+ result[0] = (timestamp / 0x10000000000) & 0xff;
48
+ result[1] = (timestamp / 0x100000000) & 0xff;
49
+ result[2] = (timestamp / 0x1000000) & 0xff;
50
+ result[3] = (timestamp / 0x10000) & 0xff;
51
+ result[4] = (timestamp / 0x100) & 0xff;
52
+ result[5] = timestamp & 0xff;
53
+ // byte 6: `version` (4 bits) | sequence bits 28-31 (4 bits)
54
+ result[6] = 0x70 | ((seq >>> 28) & 0x0f);
55
+ // byte 7: sequence bits 20-27 (8 bits)
56
+ result[7] = (seq >>> 20) & 0xff;
57
+ // byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits)
58
+ result[8] = 0x80 | ((seq >>> 14) & 0x3f);
59
+ // byte 9: sequence bits 6-13 (8 bits)
60
+ result[9] = (seq >>> 6) & 0xff;
61
+ // byte 10: sequence bits 0-5 (6 bits) | random (2 bits)
62
+ result[10] = ((seq << 2) & 0xff) | (randomBytes[10] & 0x03);
63
+ // bytes 11-15: random (40 bits)
64
+ result[11] = randomBytes[11];
65
+ result[12] = randomBytes[12];
66
+ result[13] = randomBytes[13];
67
+ result[14] = randomBytes[14];
68
+ result[15] = randomBytes[15];
69
+ return uuidStringify(result);
70
+ }
71
+ //# sourceMappingURL=Uuid7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Uuid7.js","sourceRoot":"","sources":["../src/Uuid7.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAGtE,MAAM,CAAC,MAAM,OAAO,GAAsC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;AAQ1E,MAAM,OAAO,UAAW,SAAQ,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,EAKrD;IACD,MAAM,CAAC,OAAO,GAAgE,KAAK,CAAC,MAAM,CACxF,IAAI,EACJ,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,SAAS,CAAA;QAChC,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,eAAe,CAAA;QAE9C,MAAM,KAAK,GAAG;YACZ,KAAK,EAAE,MAAM,CAAC,iBAAiB;YAC/B,GAAG,EAAE,CAAC;SACP,CAAA;QAED,SAAS,aAAa,CAAC,GAAW,EAAE,WAAuB;YACzD,IAAI,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;gBACtB,uDAAuD;gBACvD,KAAK,CAAC,GAAG;oBACP,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAA;gBAC1F,KAAK,CAAC,KAAK,GAAG,GAAG,CAAA;YACnB,CAAC;iBAAM,CAAC;gBACN,2CAA2C;gBAC3C,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;gBAE/B,wEAAwE;gBACxE,yEAAyE;gBACzE,sEAAsE;gBACtE,IAAI,KAAK,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;oBACpB,KAAK,CAAC,KAAK,EAAE,CAAA;gBACf,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACxB,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;gBAC9C,aAAa,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;gBACtC,OAAO,EAAE,SAAS,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,WAAW,EAAE,CAAA;YAChE,CAAC,CAAC;SACH,CAAA;IACH,CAAC,CAAC,CACH,CAAA;;AAGH,MAAM,CAAC,MAAM,SAAS,GAA4C,MAAM,CAAC,GAAG,CAC1E,UAAU,CAAC,IAAI,EACf,aAAa,CACd,CAAA;AAED,SAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAa;IAC/D,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAEjC,gCAAgC;IAChC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC,GAAG,IAAI,CAAA;IAC9C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,IAAI,CAAA;IAC5C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,IAAI,CAAA;IAC1C,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,IAAI,CAAA;IACxC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,IAAI,CAAA;IACtC,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAAA;IAE5B,4DAA4D;IAC5D,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAExC,uCAAuC;IACvC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IAE/B,4DAA4D;IAC5D,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAExC,sCAAsC;IACtC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAA;IAE9B,wDAAwD;IACxD,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;IAE3D,gCAAgC;IAChC,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAC5B,MAAM,CAAC,EAAE,CAAC,GAAG,WAAW,CAAC,EAAE,CAAC,CAAA;IAE5B,OAAO,aAAa,CAAC,MAAM,CAAU,CAAA;AACvC,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function uuidStringify(seed: Uint8Array): string;
@@ -0,0 +1,29 @@
1
+ const byteToHex = [];
2
+ for (let i = 0; i < 256; ++i) {
3
+ byteToHex.push((i + 0x100).toString(16).slice(1));
4
+ }
5
+ export function uuidStringify(seed) {
6
+ return (
7
+ // biome-ignore lint/style/useTemplate: Faster than template literals
8
+ byteToHex[seed[0]] +
9
+ byteToHex[seed[1]] +
10
+ byteToHex[seed[2]] +
11
+ byteToHex[seed[3]] +
12
+ '-' +
13
+ byteToHex[seed[4]] +
14
+ byteToHex[seed[5]] +
15
+ '-' +
16
+ byteToHex[seed[6]] +
17
+ byteToHex[seed[7]] +
18
+ '-' +
19
+ byteToHex[seed[8]] +
20
+ byteToHex[seed[9]] +
21
+ '-' +
22
+ byteToHex[seed[10]] +
23
+ byteToHex[seed[11]] +
24
+ byteToHex[seed[12]] +
25
+ byteToHex[seed[13]] +
26
+ byteToHex[seed[14]] +
27
+ byteToHex[seed[15]]);
28
+ }
29
+ //# sourceMappingURL=UuidStringify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UuidStringify.js","sourceRoot":"","sources":["../src/UuidStringify.ts"],"names":[],"mappings":"AAAA,MAAM,SAAS,GAAkB,EAAE,CAAA;AAEnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;IAC7B,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,OAAO;IACL,qEAAqE;IACrE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,GAAG;QACH,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,GAAG;QACH,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,GAAG;QACH,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,GAAG;QACH,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnB,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CACpB,CAAA;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,4 +2,7 @@ export * from './DateTimes.js';
2
2
  export * from './GetRandomValues.js';
3
3
  export * from './NanoId.js';
4
4
  export * from './Ulid.js';
5
- export * from './Uuid.js';
5
+ export * from './Uuid4.js';
6
+ export * from './Uuid5.js';
7
+ export * from './Uuid6.js';
8
+ export * from './Uuid7.js';
package/dist/index.js CHANGED
@@ -2,5 +2,8 @@ export * from './DateTimes.js';
2
2
  export * from './GetRandomValues.js';
3
3
  export * from './NanoId.js';
4
4
  export * from './Ulid.js';
5
- export * from './Uuid.js';
5
+ export * from './Uuid4.js';
6
+ export * from './Uuid5.js';
7
+ export * from './Uuid6.js';
8
+ export * from './Uuid7.js';
6
9
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typed/id",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Common ID format generation for Effect",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -17,7 +17,7 @@ yarn add @typed/id effect
17
17
  - 🎯 Type-safe ID generation
18
18
  - 🔧 Built on top of Effect
19
19
  - 🎨 Multiple ID format support:
20
- - UUID (v4)
20
+ - UUID (v4, v5, v6, v7)
21
21
  - NanoID
22
22
  - ULID
23
23
  - ⚡ Efficient and secure random value generation
@@ -30,24 +30,55 @@ import { Effect } from 'effect'
30
30
  import {
31
31
  DateTimes,
32
32
  GetRandomValues,
33
- makeUuid,
33
+ makeUuid4,
34
+ makeUuid5,
35
+ makeUuid6,
36
+ makeUuid7,
37
+ Uuid6State,
38
+ Uuid7State,
39
+ Uuid5Namespace,
40
+ Sha1,
34
41
  makeNanoId,
35
42
  makeUlid
36
43
  } from '@typed/id'
37
44
 
38
- // Generate a UUID
39
- await makeUuid.pipe(
45
+ // Generate a UUID v4 (random)
46
+ await makeUuid4.pipe(
40
47
  Effect.provide(GetRandomValues.CryptoRandom),
41
- // Effect.provide(GetRandomValues.PseudoRandom),
42
48
  Effect.flatMap(Effect.log),
43
49
  Effect.runPromise
44
50
  )
45
51
  // Output: "550e8400-e29b-41d4-a716-446655440000"
46
52
 
53
+ // Generate a UUID v5 (namespace + name based)
54
+ await makeUuid5(Uuid5Namespace.URL, 'https://example.com').pipe(
55
+ Effect.provide(Sha1.Default),
56
+ Effect.flatMap(Effect.log),
57
+ Effect.runPromise
58
+ )
59
+ // Output: "2ed6657d-e927-568b-95e1-2665a8aea6a2"
60
+
61
+ // Generate a UUID v6 (reordered time-based)
62
+ await makeUuid6.pipe(
63
+ Effect.provide(Uuid6State.Default),
64
+ Effect.provide([GetRandomValues.CryptoRandom, DateTimes.Default]),
65
+ Effect.flatMap(Effect.log),
66
+ Effect.runPromise
67
+ )
68
+ // Output: "1ee6742c-8f9c-6000-b9d1-0242ac120002"
69
+
70
+ // Generate a UUID v7 (time-sortable)
71
+ await makeUuid7.pipe(
72
+ Effect.provide(Uuid7State.Default),
73
+ Effect.provide([GetRandomValues.CryptoRandom, DateTimes.Default]),
74
+ Effect.flatMap(Effect.log),
75
+ Effect.runPromise
76
+ )
77
+ // Output: "018e7768-c0b3-7000-8000-123456789abc"
78
+
47
79
  // Generate a NanoID
48
80
  await makeNanoId.pipe(
49
81
  Effect.provide(GetRandomValues.CryptoRandom),
50
- // Effect.provide(GetRandomValues.PseudoRandom),
51
82
  Effect.flatMap(Effect.log),
52
83
  Effect.runPromise
53
84
  )
@@ -55,12 +86,7 @@ await makeNanoId.pipe(
55
86
 
56
87
  // Generate a ULID
57
88
  await makeUlid.pipe(
58
- Effect.provide([
59
- GetRandomValues.CryptoRandom,
60
- // GetRandomValues.PseudoRandom,
61
- DateTimes.Default
62
- // DateTimes.Fixed(new Date(0))
63
- ]),
89
+ Effect.provide([GetRandomValues.CryptoRandom, DateTimes.Default]),
64
90
  Effect.flatMap(Effect.log),
65
91
  Effect.runPromise
66
92
  )
@@ -71,7 +97,18 @@ await makeUlid.pipe(
71
97
 
72
98
  ### UUID
73
99
 
74
- - `makeUuid`: Generates a v4 UUID
100
+ - `makeUuid4`: Generates a v4 UUID (random)
101
+ - `makeUuid5`: Generates a v5 UUID (SHA-1 hash of namespace + name)
102
+ - `makeUuid6`: Generates a v6 UUID (reordered time-based for better sorting)
103
+ - `makeUuid7`: Generates a v7 UUID (time-sortable)
104
+
105
+ ### UUID v5 Namespaces
106
+
107
+ Pre-defined namespaces for UUID v5 generation:
108
+ - `Uuid5Namespace.DNS`: For DNS-based UUIDs
109
+ - `Uuid5Namespace.URL`: For URL-based UUIDs
110
+ - `Uuid5Namespace.OID`: For OID-based UUIDs
111
+ - `Uuid5Namespace.X500`: For X.500 DN-based UUIDs
75
112
 
76
113
  ### NanoID
77
114
 
package/src/DateTimes.ts CHANGED
@@ -7,15 +7,15 @@ export class DateTimes extends Effect.Tag('DateTimes')<
7
7
  readonly date: Effect.Effect<Date>
8
8
  }
9
9
  >() {
10
- static readonly make = (now: Effect.Effect<number>) =>
11
- Effect.succeed({
10
+ static readonly make = (now: Effect.Effect<number>): Layer.Layer<DateTimes> =>
11
+ Layer.succeed(this, {
12
12
  now,
13
13
  date: now.pipe(Effect.map((millis) => new Date(millis))),
14
14
  })
15
15
 
16
- static readonly Default = this.make(Effect.clockWith((clock) => clock.currentTimeMillis))
16
+ static readonly Default: Layer.Layer<DateTimes> = this.make(Effect.clockWith((clock) => clock.currentTimeMillis))
17
17
 
18
- static readonly Fixed = (base: Date) =>
18
+ static readonly Fixed = (base: Date): Layer.Layer<DateTimes> =>
19
19
  Layer.effect(
20
20
  DateTimes,
21
21
  Effect.gen(function* () {
package/src/Uuid4.ts ADDED
@@ -0,0 +1,22 @@
1
+ import * as Effect from 'effect/Effect'
2
+ import { GetRandomValues } from './GetRandomValues.js'
3
+ import { Schema } from 'effect'
4
+ import { uuidStringify } from './UuidStringify.js'
5
+
6
+ export const Uuid4 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID4'))
7
+ export type Uuid4 = Schema.Schema.Type<typeof Uuid4>
8
+
9
+ export const isUuid4: (value: string) => value is Uuid4 = Schema.is(Uuid4)
10
+
11
+ export const makeUuid4: Effect.Effect<Uuid4, never, GetRandomValues> = Effect.map(
12
+ GetRandomValues.apply(16),
13
+ uuid4FromSeed,
14
+ )
15
+
16
+ function uuid4FromSeed(seed: Uint8Array): Uuid4 {
17
+ // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
18
+ seed[6] = (seed[6] & 0x0f) | 0x40
19
+ seed[8] = (seed[8] & 0x3f) | 0x80
20
+
21
+ return uuidStringify(seed) as Uuid4
22
+ }
package/src/Uuid5.ts ADDED
@@ -0,0 +1,77 @@
1
+ import * as Effect from 'effect/Effect'
2
+ import { Layer, Schema } from 'effect'
3
+ import { uuidStringify } from './UuidStringify.js'
4
+
5
+ export const Uuid5 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID5'))
6
+ export type Uuid5 = Schema.Schema.Type<typeof Uuid5>
7
+
8
+ export const isUuid5: (value: string) => value is Uuid5 = Schema.is(Uuid5)
9
+
10
+ export class Sha1 extends Effect.Tag('Sha1')<
11
+ Sha1,
12
+ {
13
+ readonly hash: (data: Uint8Array) => Effect.Effect<Uint8Array, never>
14
+ }
15
+ >() {
16
+ static readonly Default = Layer.succeed(this, {
17
+ hash: (data: Uint8Array) =>
18
+ Effect.promise(() =>
19
+ crypto.subtle.digest('SHA-1', data).then((hash) => new Uint8Array(hash)),
20
+ ),
21
+ })
22
+ }
23
+
24
+ export type Uuid5Namespace = Uint8Array
25
+
26
+ const textEncoder = new TextEncoder()
27
+
28
+ // Pre-defined namespaces from RFC 4122
29
+ export const Uuid5Namespace = {
30
+ DNS: new Uint8Array([
31
+ 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
32
+ ]),
33
+
34
+ URL: new Uint8Array([
35
+ 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
36
+ ]),
37
+
38
+ OID: new Uint8Array([
39
+ 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
40
+ ]),
41
+
42
+ X500: new Uint8Array([
43
+ 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
44
+ ]),
45
+ } as const
46
+
47
+ export function makeUuid5(
48
+ namespace: Uuid5Namespace,
49
+ name: string,
50
+ ): Effect.Effect<Uuid5, never, Sha1> {
51
+ return Effect.gen(function* () {
52
+ const sha1 = yield* Sha1
53
+
54
+ // Convert name to UTF-8 bytes
55
+ const nameBytes = textEncoder.encode(name)
56
+
57
+ // Concatenate namespace and name
58
+ const buffer = new Uint8Array(namespace.length + nameBytes.length)
59
+ buffer.set(namespace)
60
+ buffer.set(nameBytes, namespace.length)
61
+
62
+ // Hash the concatenated bytes
63
+ const hash = yield* sha1.hash(buffer)
64
+
65
+ // Format as UUID v5
66
+ const result = new Uint8Array(16)
67
+
68
+ // Copy first 16 bytes of the hash
69
+ result.set(hash.subarray(0, 16))
70
+
71
+ // Set version (5) and variant bits
72
+ result[6] = (result[6] & 0x0f) | 0x50 // version 5
73
+ result[8] = (result[8] & 0x3f) | 0x80 // variant 1
74
+
75
+ return uuidStringify(result) as Uuid5
76
+ })
77
+ }
package/src/Uuid6.ts ADDED
@@ -0,0 +1,141 @@
1
+ import * as Effect from 'effect/Effect'
2
+ import { GetRandomValues } from './GetRandomValues.js'
3
+ import { Layer, Schema } from 'effect'
4
+ import { DateTimes } from './DateTimes.js'
5
+ import { uuidStringify } from './UuidStringify.js'
6
+
7
+ export const Uuid6 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID6'))
8
+ export type Uuid6 = Schema.Schema.Type<typeof Uuid6>
9
+
10
+ export const isUuid6: (value: string) => value is Uuid6 = Schema.is(Uuid6)
11
+
12
+ export type Uuid6Seed = {
13
+ readonly msecs: number
14
+ readonly nsecs: number
15
+ readonly clockSeq: number
16
+ readonly nodeId: Uint8Array
17
+ }
18
+
19
+ export class Uuid6State extends Effect.Tag('Uuid6State')<
20
+ Uuid6State,
21
+ {
22
+ readonly next: Effect.Effect<Uuid6Seed>
23
+ }
24
+ >() {
25
+ static Default: Layer.Layer<Uuid6State, never, GetRandomValues | DateTimes> = Layer.effect(
26
+ this,
27
+ Effect.gen(function* () {
28
+ const { now } = yield* DateTimes
29
+ const getRandomValues = yield* GetRandomValues
30
+
31
+ const state = {
32
+ msecs: Number.NEGATIVE_INFINITY,
33
+ nsecs: 0,
34
+ clockSeq: -1,
35
+ nodeId: undefined as Uint8Array | undefined,
36
+ }
37
+
38
+ function updateState(currentTime: number, randomBytes: Uint8Array) {
39
+ if (currentTime === state.msecs) {
40
+ // Same msec-interval = simulate higher clock resolution by bumping `nsecs`
41
+ state.nsecs++
42
+
43
+ // Check for `nsecs` overflow (nsecs is capped at 10K intervals / msec)
44
+ if (state.nsecs >= 10000) {
45
+ state.nodeId = undefined
46
+ state.nsecs = 0
47
+ }
48
+ } else if (currentTime > state.msecs) {
49
+ // Reset nsec counter when clock advances to a new msec interval
50
+ state.nsecs = 0
51
+ } else if (currentTime < state.msecs) {
52
+ // Handle clock regression
53
+ state.nodeId = undefined
54
+ }
55
+
56
+ // Init node and clock sequence if needed
57
+ if (!state.nodeId) {
58
+ state.nodeId = randomBytes.slice(10, 16)
59
+ // Set multicast bit
60
+ state.nodeId[0] |= 0x01
61
+
62
+ // Clock sequence must be randomized
63
+ state.clockSeq = ((randomBytes[8] << 8) | randomBytes[9]) & 0x3fff
64
+ }
65
+
66
+ state.msecs = currentTime
67
+
68
+ return {
69
+ msecs: state.msecs,
70
+ nsecs: state.nsecs,
71
+ clockSeq: state.clockSeq,
72
+ nodeId: state.nodeId,
73
+ }
74
+ }
75
+
76
+ return {
77
+ next: Effect.gen(function* () {
78
+ const timestamp = yield* now
79
+ const randomBytes = yield* getRandomValues(16)
80
+ return updateState(timestamp, randomBytes)
81
+ }),
82
+ }
83
+ }),
84
+ )
85
+ }
86
+
87
+ export const makeUuid6: Effect.Effect<Uuid6, never, Uuid6State> = Effect.map(
88
+ Uuid6State.next,
89
+ uuid6FromSeed,
90
+ )
91
+
92
+ function uuid6FromSeed({ msecs, nsecs, clockSeq, nodeId }: Uuid6Seed): Uuid6 {
93
+ // First generate the fields as they would be in a v1 UUID
94
+ const result = new Uint8Array(16)
95
+
96
+ // Offset to Gregorian epoch
97
+ msecs += 12219292800000
98
+
99
+ // `time_low`
100
+ const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000
101
+ const time_low_bytes = new Uint8Array([
102
+ (tl >>> 24) & 0xff,
103
+ (tl >>> 16) & 0xff,
104
+ (tl >>> 8) & 0xff,
105
+ tl & 0xff,
106
+ ])
107
+
108
+ // `time_mid` and `time_high`
109
+ const tmh = ((msecs / 0x100000000) * 10000) & 0xfffffff
110
+ const time_mid_high_bytes = new Uint8Array([
111
+ (tmh >>> 8) & 0xff,
112
+ tmh & 0xff,
113
+ ((tmh >>> 24) & 0xf) | 0x10, // include version 1
114
+ (tmh >>> 16) & 0xff,
115
+ ])
116
+
117
+ result[0] = ((time_mid_high_bytes[2] & 0x0f) << 4) | ((time_mid_high_bytes[3] >> 4) & 0x0f)
118
+ result[1] = ((time_mid_high_bytes[3] & 0x0f) << 4) | ((time_mid_high_bytes[0] & 0xf0) >> 4)
119
+ result[2] = ((time_mid_high_bytes[0] & 0x0f) << 4) | ((time_mid_high_bytes[1] & 0xf0) >> 4)
120
+ result[3] = ((time_mid_high_bytes[1] & 0x0f) << 4) | ((time_low_bytes[0] & 0xf0) >> 4)
121
+ result[4] = ((time_low_bytes[0] & 0x0f) << 4) | ((time_low_bytes[1] & 0xf0) >> 4)
122
+ result[5] = ((time_low_bytes[1] & 0x0f) << 4) | ((time_low_bytes[2] & 0xf0) >> 4)
123
+ result[6] = 0x60 | (time_low_bytes[2] & 0x0f)
124
+ result[7] = time_low_bytes[3]
125
+
126
+ // clock_seq_hi_and_reserved
127
+ result[8] = (clockSeq >>> 8) | 0x80 // variant bits
128
+
129
+ // clock_seq_low
130
+ result[9] = clockSeq & 0xff
131
+
132
+ // node
133
+ result[10] = nodeId[0]
134
+ result[11] = nodeId[1]
135
+ result[12] = nodeId[2]
136
+ result[13] = nodeId[3]
137
+ result[14] = nodeId[4]
138
+ result[15] = nodeId[5]
139
+
140
+ return uuidStringify(result) as Uuid6
141
+ }
package/src/Uuid7.ts ADDED
@@ -0,0 +1,104 @@
1
+ import * as Effect from 'effect/Effect'
2
+ import { GetRandomValues } from './GetRandomValues.js'
3
+ import { Layer, Schema } from 'effect'
4
+ import { DateTimes } from './DateTimes.js'
5
+ import { uuidStringify } from './UuidStringify.js'
6
+
7
+ export const Uuid7 = Schema.UUID.pipe(Schema.brand('@typed/id/UUID7'))
8
+ export type Uuid7 = Schema.Schema.Type<typeof Uuid7>
9
+
10
+ export const isUuid7: (value: string) => value is Uuid7 = Schema.is(Uuid7)
11
+
12
+ export type Uuid7Seed = {
13
+ readonly timestamp: number
14
+ readonly seq: number
15
+ readonly randomBytes: Uint8Array
16
+ }
17
+
18
+ export class Uuid7State extends Effect.Tag('Uuid7State')<
19
+ Uuid7State,
20
+ {
21
+ readonly next: Effect.Effect<Uuid7Seed>
22
+ }
23
+ >() {
24
+ static Default: Layer.Layer<Uuid7State, never, GetRandomValues | DateTimes> = Layer.effect(
25
+ this,
26
+ Effect.gen(function* () {
27
+ const { now } = yield* DateTimes
28
+ const getRandomValues = yield* GetRandomValues
29
+
30
+ const state = {
31
+ msecs: Number.NEGATIVE_INFINITY,
32
+ seq: 0,
33
+ }
34
+
35
+ function updateV7State(now: number, randomBytes: Uint8Array) {
36
+ if (now > state.msecs) {
37
+ // Time has moved on! Pick a new random sequence number
38
+ state.seq =
39
+ (randomBytes[6] << 23) | (randomBytes[7] << 16) | (randomBytes[8] << 8) | randomBytes[9]
40
+ state.msecs = now
41
+ } else {
42
+ // Bump sequence counter w/ 32-bit rollover
43
+ state.seq = (state.seq + 1) | 0
44
+
45
+ // In case of rollover, bump timestamp to preserve monotonicity. This is
46
+ // allowed by the RFC and should self-correct as the system clock catches
47
+ // up. See https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-9.4
48
+ if (state.seq === 0) {
49
+ state.msecs++
50
+ }
51
+ }
52
+ }
53
+
54
+ return {
55
+ next: Effect.gen(function* () {
56
+ const randomBytes = yield* getRandomValues(16)
57
+ updateV7State(yield* now, randomBytes)
58
+ return { timestamp: state.msecs, seq: state.seq, randomBytes }
59
+ }),
60
+ }
61
+ }),
62
+ )
63
+ }
64
+
65
+ export const makeUuid7: Effect.Effect<Uuid7, never, Uuid7State> = Effect.map(
66
+ Uuid7State.next,
67
+ uuid7FromSeed,
68
+ )
69
+
70
+ function uuid7FromSeed({ timestamp, seq, randomBytes }: Uuid7Seed): Uuid7 {
71
+ const result = new Uint8Array(16)
72
+
73
+ // byte 0-5: timestamp (48 bits)
74
+ result[0] = (timestamp / 0x10000000000) & 0xff
75
+ result[1] = (timestamp / 0x100000000) & 0xff
76
+ result[2] = (timestamp / 0x1000000) & 0xff
77
+ result[3] = (timestamp / 0x10000) & 0xff
78
+ result[4] = (timestamp / 0x100) & 0xff
79
+ result[5] = timestamp & 0xff
80
+
81
+ // byte 6: `version` (4 bits) | sequence bits 28-31 (4 bits)
82
+ result[6] = 0x70 | ((seq >>> 28) & 0x0f)
83
+
84
+ // byte 7: sequence bits 20-27 (8 bits)
85
+ result[7] = (seq >>> 20) & 0xff
86
+
87
+ // byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits)
88
+ result[8] = 0x80 | ((seq >>> 14) & 0x3f)
89
+
90
+ // byte 9: sequence bits 6-13 (8 bits)
91
+ result[9] = (seq >>> 6) & 0xff
92
+
93
+ // byte 10: sequence bits 0-5 (6 bits) | random (2 bits)
94
+ result[10] = ((seq << 2) & 0xff) | (randomBytes[10] & 0x03)
95
+
96
+ // bytes 11-15: random (40 bits)
97
+ result[11] = randomBytes[11]
98
+ result[12] = randomBytes[12]
99
+ result[13] = randomBytes[13]
100
+ result[14] = randomBytes[14]
101
+ result[15] = randomBytes[15]
102
+
103
+ return uuidStringify(result) as Uuid7
104
+ }
@@ -0,0 +1,31 @@
1
+ const byteToHex: Array<string> = []
2
+
3
+ for (let i = 0; i < 256; ++i) {
4
+ byteToHex.push((i + 0x100).toString(16).slice(1))
5
+ }
6
+
7
+ export function uuidStringify(seed: Uint8Array): string {
8
+ return (
9
+ // biome-ignore lint/style/useTemplate: Faster than template literals
10
+ byteToHex[seed[0]] +
11
+ byteToHex[seed[1]] +
12
+ byteToHex[seed[2]] +
13
+ byteToHex[seed[3]] +
14
+ '-' +
15
+ byteToHex[seed[4]] +
16
+ byteToHex[seed[5]] +
17
+ '-' +
18
+ byteToHex[seed[6]] +
19
+ byteToHex[seed[7]] +
20
+ '-' +
21
+ byteToHex[seed[8]] +
22
+ byteToHex[seed[9]] +
23
+ '-' +
24
+ byteToHex[seed[10]] +
25
+ byteToHex[seed[11]] +
26
+ byteToHex[seed[12]] +
27
+ byteToHex[seed[13]] +
28
+ byteToHex[seed[14]] +
29
+ byteToHex[seed[15]]
30
+ )
31
+ }
package/src/id.test.ts CHANGED
@@ -1,6 +1,23 @@
1
1
  import { describe, expect, it } from '@effect/vitest'
2
- import { Effect } from 'effect'
3
- import { DateTimes, GetRandomValues, isUuid, makeNanoId, makeUlid, makeUuid } from './index.js'
2
+ import { Effect, flow } from 'effect'
3
+ import {
4
+ DateTimes,
5
+ GetRandomValues,
6
+ isUuid4,
7
+ isUuid5,
8
+ isUuid6,
9
+ isUuid7,
10
+ makeNanoId,
11
+ makeUlid,
12
+ makeUuid4,
13
+ makeUuid5,
14
+ makeUuid6,
15
+ makeUuid7,
16
+ Sha1,
17
+ Uuid5Namespace,
18
+ Uuid6State,
19
+ Uuid7State,
20
+ } from './index.js'
4
21
 
5
22
  const makeTestValues = (length: number) => {
6
23
  const values = new Uint8Array(length)
@@ -10,19 +27,56 @@ const makeTestValues = (length: number) => {
10
27
  return values
11
28
  }
12
29
 
13
- const provideTestValues = Effect.provide([
14
- GetRandomValues.layer((length) => Effect.succeed(makeTestValues(length))),
15
- DateTimes.Fixed(new Date(0)),
16
- ])
30
+ const provideTestValues = flow(
31
+ Effect.provide([Uuid6State.Default, Uuid7State.Default]),
32
+ Effect.provide(Sha1.Default),
33
+ Effect.provide([
34
+ GetRandomValues.layer((length) => Effect.succeed(makeTestValues(length))),
35
+ DateTimes.Fixed(new Date(0)),
36
+ ]),
37
+ )
17
38
 
18
39
  describe(__filename, () => {
19
- describe('Uuid', () => {
20
- it.effect('generates a UUID', () =>
40
+ describe('Uuid4', () => {
41
+ it.effect('generates a UUID v4', () =>
21
42
  Effect.gen(function* (_) {
22
- const id = yield* _(makeUuid)
23
- expect(id).toEqual('00010203-0405-0607-0809-0a0b0c0d0e0f')
43
+ const id = yield* _(makeUuid4)
44
+ expect(id).toMatchInlineSnapshot(`"00010203-0405-4607-8809-0a0b0c0d0e0f"`)
24
45
  expect(id.length).toEqual(36)
25
- expect(isUuid(id)).toEqual(true)
46
+ expect(isUuid4(id)).toEqual(true)
47
+ }).pipe(provideTestValues),
48
+ )
49
+ })
50
+
51
+ describe('Uuid5', () => {
52
+ it.effect('generates a UUID v5', () =>
53
+ Effect.gen(function* (_) {
54
+ const id = yield* _(makeUuid5(Uuid5Namespace.DNS, 'example.com'))
55
+ expect(id).toMatchInlineSnapshot(`"cfbff0d1-9375-5685-968c-48ce8b15ae17"`)
56
+ expect(id.length).toEqual(36)
57
+ expect(isUuid5(id)).toEqual(true)
58
+ }).pipe(provideTestValues),
59
+ )
60
+ })
61
+
62
+ describe('Uuid6', () => {
63
+ it.effect('generates a UUID v6', () =>
64
+ Effect.gen(function* (_) {
65
+ const id = yield* _(makeUuid6)
66
+ expect(id).toMatchInlineSnapshot(`"1b21dd21-3814-6000-8809-0b0b0c0d0e0f"`)
67
+ expect(id.length).toEqual(36)
68
+ expect(isUuid6(id)).toEqual(true)
69
+ }).pipe(provideTestValues),
70
+ )
71
+ })
72
+
73
+ describe('Uuid7', () => {
74
+ it.effect('generates a UUID v7', () =>
75
+ Effect.gen(function* (_) {
76
+ const id = yield* _(makeUuid7)
77
+ expect(id).toMatchInlineSnapshot(`"00000000-0000-7030-9c20-260b0c0d0e0f"`)
78
+ expect(id.length).toEqual(36)
79
+ expect(isUuid7(id)).toEqual(true)
26
80
  }).pipe(provideTestValues),
27
81
  )
28
82
  })
@@ -31,8 +85,7 @@ describe(__filename, () => {
31
85
  it.effect('generates a NanoId', () =>
32
86
  Effect.gen(function* (_) {
33
87
  const id = yield* _(makeNanoId)
34
-
35
- expect(id).toEqual('0123456789abcdefghijk')
88
+ expect(id).toMatchInlineSnapshot(`"0123456789abcdefghijk"`)
36
89
  expect(id.length).toEqual(21)
37
90
  }).pipe(provideTestValues),
38
91
  )
package/src/index.ts CHANGED
@@ -2,4 +2,7 @@ export * from './DateTimes.js'
2
2
  export * from './GetRandomValues.js'
3
3
  export * from './NanoId.js'
4
4
  export * from './Ulid.js'
5
- export * from './Uuid.js'
5
+ export * from './Uuid4.js'
6
+ export * from './Uuid5.js'
7
+ export * from './Uuid6.js'
8
+ export * from './Uuid7.js'
package/src/Uuid.ts DELETED
@@ -1,76 +0,0 @@
1
- import * as Effect from 'effect/Effect'
2
- import { GetRandomValues } from './GetRandomValues.js'
3
- import { Schema } from 'effect'
4
-
5
- export const isUuid: (value: string) => value is Uuid = Schema.is(Schema.UUID) as any
6
-
7
- export const Uuid = Schema.UUID.pipe(Schema.brand('@typed/id/UUID'))
8
- export type Uuid = Schema.Schema.Type<typeof Uuid>
9
-
10
- export type UuidSeed = readonly [
11
- zero: number,
12
- one: number,
13
- two: number,
14
- three: number,
15
- four: number,
16
- five: number,
17
- six: number,
18
- seven: number,
19
- eight: number,
20
- nine: number,
21
- ten: number,
22
- eleven: number,
23
- twelve: number,
24
- thirteen: number,
25
- fourteen: number,
26
- fifteen: number,
27
- ]
28
-
29
- export const makeUuidSeed: Effect.Effect<UuidSeed, never, GetRandomValues> = GetRandomValues.apply(
30
- 32,
31
- ) as any
32
-
33
- export const makeUuid: Effect.Effect<Uuid, never, GetRandomValues> = Effect.map(makeUuidSeed, uuid4)
34
-
35
- /**
36
- * Convert array of 16 byte values to UUID string format of the form:
37
- * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
38
- */
39
- const byteToHex: Array<string> = []
40
-
41
- for (let i = 0; i < 256; ++i) {
42
- byteToHex.push((i + 0x100).toString(16).slice(1))
43
- }
44
-
45
- export function uuid4(seed: UuidSeed): Uuid {
46
- // Note: Be careful editing this code! It's been tuned for performance
47
- // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
48
- //
49
- // Note to future-self: No, you can't remove the `toLowerCase()` call.
50
- // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351
51
- return (
52
- (
53
- // biome-ignore lint/style/useTemplate: <explanation>
54
- byteToHex[seed[0]] +
55
- byteToHex[seed[1]] +
56
- byteToHex[seed[2]] +
57
- byteToHex[seed[3]] +
58
- '-' +
59
- byteToHex[seed[4]] +
60
- byteToHex[seed[5]] +
61
- '-' +
62
- byteToHex[seed[6]] +
63
- byteToHex[seed[7]] +
64
- '-' +
65
- byteToHex[seed[8]] +
66
- byteToHex[seed[9]] +
67
- '-' +
68
- byteToHex[seed[10]] +
69
- byteToHex[seed[11]] +
70
- byteToHex[seed[12]] +
71
- byteToHex[seed[13]] +
72
- byteToHex[seed[14]] +
73
- byteToHex[seed[15]]
74
- ).toLowerCase() as Uuid
75
- )
76
- }