@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.
- package/dist/DateTimes.d.ts +3 -9
- package/dist/DateTimes.js +1 -1
- package/dist/DateTimes.js.map +1 -1
- package/dist/Uuid4.d.ts +7 -0
- package/dist/Uuid4.js +14 -0
- package/dist/Uuid4.js.map +1 -0
- package/dist/Uuid5.d.ts +26 -0
- package/dist/Uuid5.js +48 -0
- package/dist/Uuid5.js.map +1 -0
- package/dist/Uuid6.d.ts +27 -0
- package/dist/Uuid6.js +104 -0
- package/dist/Uuid6.js.map +1 -0
- package/dist/Uuid7.d.ts +26 -0
- package/dist/Uuid7.js +71 -0
- package/dist/Uuid7.js.map +1 -0
- package/dist/UuidStringify.d.ts +1 -0
- package/dist/UuidStringify.js +29 -0
- package/dist/UuidStringify.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +50 -13
- package/src/DateTimes.ts +4 -4
- package/src/Uuid4.ts +22 -0
- package/src/Uuid5.ts +77 -0
- package/src/Uuid6.ts +141 -0
- package/src/Uuid7.ts +104 -0
- package/src/UuidStringify.ts +31 -0
- package/src/id.test.ts +66 -13
- package/src/index.ts +4 -1
- package/src/Uuid.ts +0 -76
package/dist/DateTimes.d.ts
CHANGED
|
@@ -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>) =>
|
|
16
|
-
|
|
17
|
-
|
|
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
package/dist/DateTimes.js.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/Uuid4.d.ts
ADDED
|
@@ -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"}
|
package/dist/Uuid5.d.ts
ADDED
|
@@ -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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"}
|
package/dist/Uuid6.d.ts
ADDED
|
@@ -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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"}
|
package/dist/Uuid7.d.ts
ADDED
|
@@ -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":"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"}
|
|
@@ -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 './
|
|
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 './
|
|
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,
|
|
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
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
|
-
|
|
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
|
|
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
|
-
- `
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
14
|
-
|
|
15
|
-
|
|
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('
|
|
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* _(
|
|
23
|
-
expect(id).
|
|
43
|
+
const id = yield* _(makeUuid4)
|
|
44
|
+
expect(id).toMatchInlineSnapshot(`"00010203-0405-4607-8809-0a0b0c0d0e0f"`)
|
|
24
45
|
expect(id.length).toEqual(36)
|
|
25
|
-
expect(
|
|
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 './
|
|
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
|
-
}
|