@typed/id 1.0.0-beta.3 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -65
- package/dist/Cuid.d.ts +128 -13
- package/dist/Cuid.d.ts.map +1 -1
- package/dist/Cuid.js +135 -41
- package/dist/DateTimes.d.ts +66 -3
- package/dist/DateTimes.d.ts.map +1 -1
- package/dist/DateTimes.js +72 -5
- package/dist/Ids.d.ts +173 -30
- package/dist/Ids.d.ts.map +1 -1
- package/dist/Ids.js +162 -19
- package/dist/Ksuid.d.ts +51 -1
- package/dist/Ksuid.d.ts.map +1 -1
- package/dist/Ksuid.js +59 -7
- package/dist/NanoId.d.ts +47 -0
- package/dist/NanoId.d.ts.map +1 -1
- package/dist/NanoId.js +48 -1
- package/dist/RandomValues.d.ts +63 -4
- package/dist/RandomValues.d.ts.map +1 -1
- package/dist/RandomValues.js +74 -10
- package/dist/Ulid.d.ts +49 -1
- package/dist/Ulid.d.ts.map +1 -1
- package/dist/Ulid.js +54 -4
- package/dist/Uuid4.d.ts +47 -0
- package/dist/Uuid4.d.ts.map +1 -1
- package/dist/Uuid4.js +48 -1
- package/dist/Uuid5.d.ts +178 -6
- package/dist/Uuid5.d.ts.map +1 -1
- package/dist/Uuid5.js +152 -15
- package/dist/Uuid7.d.ts +121 -15
- package/dist/Uuid7.d.ts.map +1 -1
- package/dist/Uuid7.js +124 -16
- package/dist/__tests__/helpers.d.ts +7 -0
- package/dist/__tests__/helpers.d.ts.map +1 -0
- package/dist/__tests__/helpers.js +19 -0
- package/dist/__tests__/public-contract.type-test.d.ts +2 -0
- package/dist/__tests__/public-contract.type-test.d.ts.map +1 -0
- package/dist/__tests__/public-contract.type-test.js +3 -0
- package/dist/_sha.d.ts +32 -0
- package/dist/_sha.d.ts.map +1 -1
- package/dist/_sha.js +32 -0
- package/dist/_uuid-stringify.d.ts +15 -0
- package/dist/_uuid-stringify.d.ts.map +1 -1
- package/dist/_uuid-stringify.js +15 -0
- package/package.json +65 -11
- package/src/Cuid.ts +0 -124
- package/src/DateTimes.ts +0 -36
- package/src/Id.test.ts +0 -230
- package/src/Ids.ts +0 -128
- package/src/Ksuid.ts +0 -74
- package/src/NanoId.ts +0 -33
- package/src/RandomValues.ts +0 -33
- package/src/Ulid.ts +0 -51
- package/src/Uuid4.ts +0 -24
- package/src/Uuid5.ts +0 -71
- package/src/Uuid7.ts +0 -104
- package/src/_sha.ts +0 -11
- package/src/_uuid-stringify.ts +0 -30
- package/src/index.ts +0 -10
package/dist/Uuid5.js
CHANGED
|
@@ -1,28 +1,105 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import { dual } from "effect/Function";
|
|
3
4
|
import * as Schema from "effect/Schema";
|
|
4
5
|
import { sha1 } from "./_sha.js";
|
|
5
6
|
import { uuidStringify } from "./_uuid-stringify.js";
|
|
7
|
+
/**
|
|
8
|
+
* Effect Schema and branded string type for RFC UUID version 5 values.
|
|
9
|
+
* @remarks
|
|
10
|
+
* ## Why
|
|
11
|
+
* The schema verifies UUID version and variant at transport boundaries before restoring the compile-time brand.
|
|
12
|
+
* ## Ownership and lifetime
|
|
13
|
+
* This module-level schema value acquires no resources and is shared; no runtime freezing guarantee is implied.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { Uuid5 } from "@typed/id/Uuid5"
|
|
17
|
+
* import { Schema } from "effect"
|
|
18
|
+
* const id = Schema.decodeUnknownSync(Uuid5)("21f7f8de-8051-5b89-8680-0195ef798b6a")
|
|
19
|
+
* ```
|
|
20
|
+
* @category Schemas
|
|
21
|
+
* @since 1.0.0
|
|
22
|
+
*/
|
|
6
23
|
export const Uuid5 = Schema.String.pipe(Schema.check(Schema.isUUID(5)), Schema.brand("@typed/id/UUID5"));
|
|
24
|
+
/**
|
|
25
|
+
* Tests whether a string is an RFC UUID version 5 value.
|
|
26
|
+
* @remarks
|
|
27
|
+
* ## Why
|
|
28
|
+
* Runtime refinement restores trust after serialization has erased the TypeScript brand.
|
|
29
|
+
* ## Ownership and lifetime
|
|
30
|
+
* This pure predicate acquires no resources and retains no input.
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { isUuid5 } from "@typed/id/Uuid5"
|
|
34
|
+
* isUuid5("21f7f8de-8051-5b89-8680-0195ef798b6a")
|
|
35
|
+
* ```
|
|
36
|
+
* @category Refinements
|
|
37
|
+
* @since 1.0.0
|
|
38
|
+
*/
|
|
7
39
|
export const isUuid5 = Schema.is(Uuid5);
|
|
8
40
|
const textEncoder = new TextEncoder();
|
|
9
41
|
// Pre-defined namespaces from RFC 4122
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
const DNS = new Uint8Array([
|
|
43
|
+
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
|
44
|
+
]);
|
|
45
|
+
const URL = new Uint8Array([
|
|
46
|
+
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
|
47
|
+
]);
|
|
48
|
+
const OID = new Uint8Array([
|
|
49
|
+
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
|
50
|
+
]);
|
|
51
|
+
const X500 = new Uint8Array([
|
|
52
|
+
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
|
53
|
+
]);
|
|
54
|
+
/**
|
|
55
|
+
* Standard RFC UUID version 5 namespaces, returned as fresh mutable copies.
|
|
56
|
+
* @remarks
|
|
57
|
+
* ## Why
|
|
58
|
+
* Copy-on-access protects canonical constants while allowing callers to own and safely modify their selected namespace bytes.
|
|
59
|
+
* ## Ownership and lifetime
|
|
60
|
+
* The module owns canonical bytes for its lifetime; every property access transfers a fresh 16-byte copy to the caller.
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { Uuid5Namespace } from "@typed/id/Uuid5"
|
|
64
|
+
* const namespace = Uuid5Namespace.DNS
|
|
65
|
+
* ```
|
|
66
|
+
* @category Namespaces
|
|
67
|
+
* @since 1.0.0
|
|
68
|
+
*/
|
|
69
|
+
export const Uuid5Namespace = Object.freeze({
|
|
70
|
+
get DNS() {
|
|
71
|
+
return DNS.slice(0);
|
|
72
|
+
},
|
|
73
|
+
get URL() {
|
|
74
|
+
return URL.slice(0);
|
|
75
|
+
},
|
|
76
|
+
get OID() {
|
|
77
|
+
return OID.slice(0);
|
|
78
|
+
},
|
|
79
|
+
get X500() {
|
|
80
|
+
return X500.slice(0);
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Derives a deterministic UUID version 5 from a UTF-8 name and 16-byte namespace.
|
|
85
|
+
* @remarks
|
|
86
|
+
* ## Why
|
|
87
|
+
* Determinism is exact for the same name bytes and namespace. Invalid namespace length fails in the typed channel with `IllegalArgumentError`; missing Web Crypto or a rejected SHA-1 digest is an Effect defect, not a typed error.
|
|
88
|
+
* ## Ownership and lifetime
|
|
89
|
+
* Each Effect acquires no persistent resources, reads but does not retain the namespace, and uses Web Crypto `subtle.digest` for the invocation.
|
|
90
|
+
* @example
|
|
91
|
+
* ```ts
|
|
92
|
+
* import { uuid5, Uuid5Namespace } from "@typed/id/Uuid5"
|
|
93
|
+
* const id = uuid5("example.com", Uuid5Namespace.DNS)
|
|
94
|
+
* ```
|
|
95
|
+
* @category Generators
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
*/
|
|
24
98
|
export const uuid5 = dual(2, function uuid5(name, namespace) {
|
|
25
99
|
return Effect.gen(function* () {
|
|
100
|
+
if (namespace.length !== 16) {
|
|
101
|
+
return yield* new Cause.IllegalArgumentError(`UUIDv5 namespace must contain exactly 16 bytes, received ${namespace.length}`);
|
|
102
|
+
}
|
|
26
103
|
// Convert name to UTF-8 bytes
|
|
27
104
|
const nameBytes = textEncoder.encode(name);
|
|
28
105
|
// Concatenate namespace and name
|
|
@@ -38,10 +115,70 @@ export const uuid5 = dual(2, function uuid5(name, namespace) {
|
|
|
38
115
|
// Set version (5) and variant bits
|
|
39
116
|
result[6] = (result[6] & 0x0f) | 0x50; // version 5
|
|
40
117
|
result[8] = (result[8] & 0x3f) | 0x80; // variant 1
|
|
41
|
-
return Uuid5.
|
|
118
|
+
return Uuid5.make(uuidStringify(result));
|
|
42
119
|
});
|
|
43
120
|
});
|
|
121
|
+
/**
|
|
122
|
+
* Derives deterministic UUID version 5 values in the standard DNS namespace.
|
|
123
|
+
* @remarks
|
|
124
|
+
* ## Why
|
|
125
|
+
* The pre-bound helper prevents accidental namespace selection drift for DNS identities.
|
|
126
|
+
* ## Ownership and lifetime
|
|
127
|
+
* This function acquires no persistent resources and uses a captured namespace copy for module lifetime.
|
|
128
|
+
* @example
|
|
129
|
+
* ```ts
|
|
130
|
+
* import { dnsUuid5 } from "@typed/id/Uuid5"
|
|
131
|
+
* const id = dnsUuid5("example.com")
|
|
132
|
+
* ```
|
|
133
|
+
* @category Generators
|
|
134
|
+
* @since 1.0.0
|
|
135
|
+
*/
|
|
44
136
|
export const dnsUuid5 = uuid5(Uuid5Namespace.DNS);
|
|
137
|
+
/**
|
|
138
|
+
* Derives deterministic UUID version 5 values in the standard URL namespace.
|
|
139
|
+
* @remarks
|
|
140
|
+
* ## Why
|
|
141
|
+
* The pre-bound helper prevents accidental namespace selection drift for URL identities.
|
|
142
|
+
* ## Ownership and lifetime
|
|
143
|
+
* This function acquires no persistent resources and uses a captured namespace copy for module lifetime.
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* import { urlUuid5 } from "@typed/id/Uuid5"
|
|
147
|
+
* const id = urlUuid5("https://example.com")
|
|
148
|
+
* ```
|
|
149
|
+
* @category Generators
|
|
150
|
+
* @since 1.0.0
|
|
151
|
+
*/
|
|
45
152
|
export const urlUuid5 = uuid5(Uuid5Namespace.URL);
|
|
153
|
+
/**
|
|
154
|
+
* Derives deterministic UUID version 5 values in the standard OID namespace.
|
|
155
|
+
* @remarks
|
|
156
|
+
* ## Why
|
|
157
|
+
* The pre-bound helper prevents accidental namespace selection drift for object identifiers.
|
|
158
|
+
* ## Ownership and lifetime
|
|
159
|
+
* This function acquires no persistent resources and uses a captured namespace copy for module lifetime.
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* import { oidUuid5 } from "@typed/id/Uuid5"
|
|
163
|
+
* const id = oidUuid5("1.3.6.1.4.1")
|
|
164
|
+
* ```
|
|
165
|
+
* @category Generators
|
|
166
|
+
* @since 1.0.0
|
|
167
|
+
*/
|
|
46
168
|
export const oidUuid5 = uuid5(Uuid5Namespace.OID);
|
|
169
|
+
/**
|
|
170
|
+
* Derives deterministic UUID version 5 values in the standard X.500 namespace.
|
|
171
|
+
* @remarks
|
|
172
|
+
* ## Why
|
|
173
|
+
* The pre-bound helper prevents accidental namespace selection drift for X.500 identities.
|
|
174
|
+
* ## Ownership and lifetime
|
|
175
|
+
* This function acquires no persistent resources and uses a captured namespace copy for module lifetime.
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { x500Uuid5 } from "@typed/id/Uuid5"
|
|
179
|
+
* const id = x500Uuid5("CN=example")
|
|
180
|
+
* ```
|
|
181
|
+
* @category Generators
|
|
182
|
+
* @since 1.0.0
|
|
183
|
+
*/
|
|
47
184
|
export const x500Uuid5 = uuid5(Uuid5Namespace.X500);
|
package/dist/Uuid7.d.ts
CHANGED
|
@@ -1,44 +1,150 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Layer from "effect/Layer";
|
|
3
4
|
import * as Schema from "effect/Schema";
|
|
4
|
-
import * as
|
|
5
|
+
import * as Context from "effect/Context";
|
|
5
6
|
import { DateTimes } from "./DateTimes.js";
|
|
6
7
|
import { RandomValues } from "./RandomValues.js";
|
|
8
|
+
/**
|
|
9
|
+
* Effect Schema and branded string type for RFC UUID version 7 values.
|
|
10
|
+
* @remarks
|
|
11
|
+
* ## Why
|
|
12
|
+
* The schema verifies version and variant at transport boundaries; generated ordering is local to one Uuid7State, not a distributed total order.
|
|
13
|
+
* ## Ownership and lifetime
|
|
14
|
+
* This module-level schema value acquires no resources and is shared; no runtime freezing guarantee is implied.
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { Uuid7 } from "@typed/id/Uuid7"
|
|
18
|
+
* import { Schema } from "effect"
|
|
19
|
+
* const id = Schema.decodeUnknownSync(Uuid7)("01890f2e-7d6c-7cc0-98c4-dc0c0c07398f")
|
|
20
|
+
* ```
|
|
21
|
+
* @category Schemas
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
*/
|
|
7
24
|
export declare const Uuid7: Schema.brand<Schema.String, "@typed/id/UUID7">;
|
|
8
25
|
export type Uuid7 = typeof Uuid7.Type;
|
|
26
|
+
/**
|
|
27
|
+
* Tests whether a string is an RFC UUID version 7 value.
|
|
28
|
+
* @remarks
|
|
29
|
+
* ## Why
|
|
30
|
+
* Runtime refinement restores trust after serialization has erased the TypeScript brand.
|
|
31
|
+
* ## Ownership and lifetime
|
|
32
|
+
* This pure predicate acquires no resources and retains no input.
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { isUuid7 } from "@typed/id/Uuid7"
|
|
36
|
+
* const valid = isUuid7("01890f2e-7d6c-7cc0-98c4-dc0c0c07398f")
|
|
37
|
+
* ```
|
|
38
|
+
* @category Refinements
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
*/
|
|
9
41
|
export declare const isUuid7: (value: string) => value is Uuid7;
|
|
42
|
+
/**
|
|
43
|
+
* The validated time, sequence, and entropy used to format one UUID version 7.
|
|
44
|
+
* @remarks
|
|
45
|
+
* ## Why
|
|
46
|
+
* An explicit seed documents the exact inputs to version and variant bit layout and makes generator state testable.
|
|
47
|
+
* ## Ownership and lifetime
|
|
48
|
+
* This data acquires no resources; the producing service owns sequence state and transfers a fresh random byte array.
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* import type { Uuid7Seed } from "@typed/id/Uuid7"
|
|
52
|
+
* const seed: Uuid7Seed = { timestamp: 0, seq: 0, randomBytes: new Uint8Array(16) as Uuid7Seed["randomBytes"] }
|
|
53
|
+
* ```
|
|
54
|
+
* @category Models
|
|
55
|
+
* @since 1.0.0
|
|
56
|
+
*/
|
|
10
57
|
export type Uuid7Seed = {
|
|
58
|
+
/** Validated millisecond timestamp encoded into the UUID. @since 1.0.0 */
|
|
11
59
|
readonly timestamp: number;
|
|
60
|
+
/** Unsigned 32-bit sequence integer in the range [0, 0xffffffff]. @since 1.0.0 */
|
|
12
61
|
readonly seq: number;
|
|
62
|
+
/** Fresh entropy buffer whose final bytes complete the UUID payload. @since 1.0.0 */
|
|
13
63
|
readonly randomBytes: Uint8Array & {
|
|
14
64
|
length: 16;
|
|
15
65
|
};
|
|
16
66
|
};
|
|
17
|
-
declare const Uuid7State_base:
|
|
18
|
-
|
|
19
|
-
seq: number;
|
|
20
|
-
randomBytes: Uint8Array<ArrayBufferLike> & {
|
|
21
|
-
length: 16;
|
|
22
|
-
};
|
|
23
|
-
}, never, never>> & {
|
|
24
|
-
readonly make: Effect.Effect<Effect.Effect<{
|
|
67
|
+
declare const Uuid7State_base: Context.ServiceClass<Uuid7State, "@typed/id/Uuid7State", {
|
|
68
|
+
next: Effect.Effect<{
|
|
25
69
|
timestamp: number;
|
|
26
70
|
seq: number;
|
|
27
71
|
randomBytes: Uint8Array<ArrayBufferLike> & {
|
|
28
|
-
length: 16;
|
|
72
|
+
readonly length: 16;
|
|
29
73
|
};
|
|
30
|
-
},
|
|
74
|
+
}, Cause.IllegalArgumentError, never>;
|
|
75
|
+
}> & {
|
|
76
|
+
readonly make: Effect.Effect<{
|
|
77
|
+
next: Effect.Effect<{
|
|
78
|
+
timestamp: number;
|
|
79
|
+
seq: number;
|
|
80
|
+
randomBytes: Uint8Array<ArrayBufferLike> & {
|
|
81
|
+
readonly length: 16;
|
|
82
|
+
};
|
|
83
|
+
}, Cause.IllegalArgumentError, never>;
|
|
84
|
+
}, never, DateTimes | RandomValues>;
|
|
31
85
|
};
|
|
86
|
+
/**
|
|
87
|
+
* Process-local Effect service that produces monotonic UUID version 7 seeds.
|
|
88
|
+
* @remarks
|
|
89
|
+
* ## Why
|
|
90
|
+
* Clock rollback and 32-bit sequence rollover are handled inside an explicit service, guaranteeing monotonicity only for one shared service instance.
|
|
91
|
+
* ## Ownership and lifetime
|
|
92
|
+
* Each Layer instance owns mutable timestamp and sequence state. A new process, worker, deployment, or Layer resets it; exact hydration identity must be serialized.
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { uuid7, Uuid7State } from "@typed/id/Uuid7"
|
|
96
|
+
* import { Effect } from "effect"
|
|
97
|
+
* const id = Effect.provide(uuid7, Uuid7State.Default)
|
|
98
|
+
* ```
|
|
99
|
+
* @category Services
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
*/
|
|
32
102
|
export declare class Uuid7State extends Uuid7State_base {
|
|
103
|
+
/**
|
|
104
|
+
* Reads the next validated seed from the current Uuid7State.
|
|
105
|
+
* @remarks
|
|
106
|
+
* ## Why
|
|
107
|
+
* The accessor exposes ordered state through Effect's service channel and reports timestamp exhaustion as `IllegalArgumentError`.
|
|
108
|
+
* ## Ownership and lifetime
|
|
109
|
+
* The Effect uses the state owned by its provided Layer and acquires no separate persistent resource.
|
|
110
|
+
* @category Services
|
|
111
|
+
* @since 1.0.0
|
|
112
|
+
*/
|
|
33
113
|
static readonly next: Effect.Effect<{
|
|
34
114
|
timestamp: number;
|
|
35
115
|
seq: number;
|
|
36
116
|
randomBytes: Uint8Array<ArrayBufferLike> & {
|
|
37
|
-
length: 16;
|
|
117
|
+
readonly length: 16;
|
|
38
118
|
};
|
|
39
|
-
},
|
|
40
|
-
|
|
119
|
+
}, Cause.IllegalArgumentError, Uuid7State>;
|
|
120
|
+
/**
|
|
121
|
+
* Provides Uuid7State from system time and Web Crypto entropy.
|
|
122
|
+
* @remarks
|
|
123
|
+
* ## Why
|
|
124
|
+
* The production default is explicit while remaining replaceable by deterministic service layers.
|
|
125
|
+
* ## Ownership and lifetime
|
|
126
|
+
* Layer acquisition creates one mutable sequence state owned by the surrounding Layer Scope.
|
|
127
|
+
* @category Layers
|
|
128
|
+
* @since 1.0.0
|
|
129
|
+
*/
|
|
130
|
+
static readonly Default: Layer.Layer<Uuid7State, never, never>;
|
|
41
131
|
}
|
|
42
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Generates one UUID version 7 from the current Uuid7State.
|
|
134
|
+
* @remarks
|
|
135
|
+
* ## Why
|
|
136
|
+
* Effectful generation makes the state boundary explicit and preserves `IllegalArgumentError` for invalid or exhausted timestamp space.
|
|
137
|
+
* ## Ownership and lifetime
|
|
138
|
+
* The Effect acquires no persistent resource and uses state owned by the provided Uuid7State Layer.
|
|
139
|
+
* @example
|
|
140
|
+
* ```ts
|
|
141
|
+
* import { uuid7, Uuid7State } from "@typed/id/Uuid7"
|
|
142
|
+
* import { Effect } from "effect"
|
|
143
|
+
* const id = Effect.provide(uuid7, Uuid7State.Default)
|
|
144
|
+
* ```
|
|
145
|
+
* @category Generators
|
|
146
|
+
* @since 1.0.0
|
|
147
|
+
*/
|
|
148
|
+
export declare const uuid7: Effect.Effect<Uuid7, Cause.IllegalArgumentError, Uuid7State>;
|
|
43
149
|
export {};
|
|
44
150
|
//# sourceMappingURL=Uuid7.d.ts.map
|
package/dist/Uuid7.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Uuid7.d.ts","sourceRoot":"","sources":["../src/Uuid7.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Uuid7.d.ts","sourceRoot":"","sources":["../src/Uuid7.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,gDAGjB,CAAC;AACF,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAEtC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,IAAI,KAAwB,CAAC;AAE3E;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,WAAW,EAAE,UAAU,GAAG;QAAE,MAAM,EAAE,EAAE,CAAA;KAAE,CAAC;CACnD,CAAC;;;;;;;;;;;;;;;;;;;;AAIF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAW,SAAQ,eA8D9B;IACA;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI;;;;;;+CAGjB;IAEH;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,wCAErB;CACH;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,oBAAoB,EAAE,UAAU,CAG9E,CAAC"}
|
package/dist/Uuid7.js
CHANGED
|
@@ -1,13 +1,62 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Layer from "effect/Layer";
|
|
3
4
|
import * as Schema from "effect/Schema";
|
|
4
|
-
import * as
|
|
5
|
+
import * as Context from "effect/Context";
|
|
5
6
|
import { uuidStringify } from "./_uuid-stringify.js";
|
|
6
7
|
import { DateTimes } from "./DateTimes.js";
|
|
7
8
|
import { RandomValues } from "./RandomValues.js";
|
|
9
|
+
/**
|
|
10
|
+
* Effect Schema and branded string type for RFC UUID version 7 values.
|
|
11
|
+
* @remarks
|
|
12
|
+
* ## Why
|
|
13
|
+
* The schema verifies version and variant at transport boundaries; generated ordering is local to one Uuid7State, not a distributed total order.
|
|
14
|
+
* ## Ownership and lifetime
|
|
15
|
+
* This module-level schema value acquires no resources and is shared; no runtime freezing guarantee is implied.
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { Uuid7 } from "@typed/id/Uuid7"
|
|
19
|
+
* import { Schema } from "effect"
|
|
20
|
+
* const id = Schema.decodeUnknownSync(Uuid7)("01890f2e-7d6c-7cc0-98c4-dc0c0c07398f")
|
|
21
|
+
* ```
|
|
22
|
+
* @category Schemas
|
|
23
|
+
* @since 1.0.0
|
|
24
|
+
*/
|
|
8
25
|
export const Uuid7 = Schema.String.pipe(Schema.check(Schema.isUUID(7)), Schema.brand("@typed/id/UUID7"));
|
|
26
|
+
/**
|
|
27
|
+
* Tests whether a string is an RFC UUID version 7 value.
|
|
28
|
+
* @remarks
|
|
29
|
+
* ## Why
|
|
30
|
+
* Runtime refinement restores trust after serialization has erased the TypeScript brand.
|
|
31
|
+
* ## Ownership and lifetime
|
|
32
|
+
* This pure predicate acquires no resources and retains no input.
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { isUuid7 } from "@typed/id/Uuid7"
|
|
36
|
+
* const valid = isUuid7("01890f2e-7d6c-7cc0-98c4-dc0c0c07398f")
|
|
37
|
+
* ```
|
|
38
|
+
* @category Refinements
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
*/
|
|
9
41
|
export const isUuid7 = Schema.is(Uuid7);
|
|
10
|
-
|
|
42
|
+
const maximumTimestamp = 2 ** 48 - 1;
|
|
43
|
+
/**
|
|
44
|
+
* Process-local Effect service that produces monotonic UUID version 7 seeds.
|
|
45
|
+
* @remarks
|
|
46
|
+
* ## Why
|
|
47
|
+
* Clock rollback and 32-bit sequence rollover are handled inside an explicit service, guaranteeing monotonicity only for one shared service instance.
|
|
48
|
+
* ## Ownership and lifetime
|
|
49
|
+
* Each Layer instance owns mutable timestamp and sequence state. A new process, worker, deployment, or Layer resets it; exact hydration identity must be serialized.
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import { uuid7, Uuid7State } from "@typed/id/Uuid7"
|
|
53
|
+
* import { Effect } from "effect"
|
|
54
|
+
* const id = Effect.provide(uuid7, Uuid7State.Default)
|
|
55
|
+
* ```
|
|
56
|
+
* @category Services
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export class Uuid7State extends Context.Service()("@typed/id/Uuid7State", {
|
|
11
60
|
make: Effect.gen(function* () {
|
|
12
61
|
const { now } = yield* DateTimes;
|
|
13
62
|
const getRandomValues = yield* RandomValues;
|
|
@@ -16,33 +65,92 @@ export class Uuid7State extends ServiceMap.Service()("@typed/id/Uuid7State", {
|
|
|
16
65
|
seq: 0,
|
|
17
66
|
};
|
|
18
67
|
function updateV7State(now, randomBytes) {
|
|
68
|
+
let msecs;
|
|
69
|
+
let seq;
|
|
19
70
|
if (now > state.msecs) {
|
|
20
71
|
// Time has moved on! Pick a new random sequence number
|
|
21
|
-
|
|
22
|
-
(randomBytes[6] <<
|
|
23
|
-
|
|
72
|
+
seq =
|
|
73
|
+
((randomBytes[6] << 24) |
|
|
74
|
+
(randomBytes[7] << 16) |
|
|
75
|
+
(randomBytes[8] << 8) |
|
|
76
|
+
randomBytes[9]) >>>
|
|
77
|
+
0;
|
|
78
|
+
msecs = now;
|
|
24
79
|
}
|
|
25
80
|
else {
|
|
26
81
|
// Bump sequence counter w/ 32-bit rollover
|
|
27
|
-
|
|
82
|
+
seq = (state.seq + 1) >>> 0;
|
|
83
|
+
msecs = state.msecs;
|
|
28
84
|
// In case of rollover, bump timestamp to preserve monotonicity. This is
|
|
29
85
|
// allowed by the RFC and should self-correct as the system clock catches
|
|
30
86
|
// up. See https://www.rfc-editor.org/rfc/rfc9562.html#section-6.2-9.4
|
|
31
|
-
if (
|
|
32
|
-
|
|
87
|
+
if (seq === 0) {
|
|
88
|
+
msecs++;
|
|
33
89
|
}
|
|
34
90
|
}
|
|
91
|
+
state.msecs = msecs;
|
|
92
|
+
state.seq = seq;
|
|
35
93
|
}
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
94
|
+
return {
|
|
95
|
+
next: Effect.gen(function* () {
|
|
96
|
+
const timestamp = yield* now;
|
|
97
|
+
if (!Number.isSafeInteger(timestamp) || timestamp < 0 || timestamp > maximumTimestamp) {
|
|
98
|
+
return yield* new Cause.IllegalArgumentError(`UUIDv7 timestamp must be a safe integer between 0 and ${maximumTimestamp}, received ${timestamp}`);
|
|
99
|
+
}
|
|
100
|
+
if (timestamp <= state.msecs &&
|
|
101
|
+
state.msecs === maximumTimestamp &&
|
|
102
|
+
state.seq === 0xffffffff) {
|
|
103
|
+
return yield* new Cause.IllegalArgumentError("UUIDv7 sequence rollover exceeds its 48-bit timestamp field");
|
|
104
|
+
}
|
|
105
|
+
const randomBytes = yield* getRandomValues(16);
|
|
106
|
+
updateV7State(timestamp, randomBytes);
|
|
107
|
+
return { timestamp: state.msecs, seq: state.seq, randomBytes };
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
41
110
|
}),
|
|
42
111
|
}) {
|
|
43
|
-
|
|
44
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Reads the next validated seed from the current Uuid7State.
|
|
114
|
+
* @remarks
|
|
115
|
+
* ## Why
|
|
116
|
+
* The accessor exposes ordered state through Effect's service channel and reports timestamp exhaustion as `IllegalArgumentError`.
|
|
117
|
+
* ## Ownership and lifetime
|
|
118
|
+
* The Effect uses the state owned by its provided Layer and acquires no separate persistent resource.
|
|
119
|
+
* @category Services
|
|
120
|
+
* @since 1.0.0
|
|
121
|
+
*/
|
|
122
|
+
static next = Effect.gen(function* () {
|
|
123
|
+
const { next } = yield* Uuid7State;
|
|
124
|
+
return yield* next;
|
|
125
|
+
});
|
|
126
|
+
/**
|
|
127
|
+
* Provides Uuid7State from system time and Web Crypto entropy.
|
|
128
|
+
* @remarks
|
|
129
|
+
* ## Why
|
|
130
|
+
* The production default is explicit while remaining replaceable by deterministic service layers.
|
|
131
|
+
* ## Ownership and lifetime
|
|
132
|
+
* Layer acquisition creates one mutable sequence state owned by the surrounding Layer Scope.
|
|
133
|
+
* @category Layers
|
|
134
|
+
* @since 1.0.0
|
|
135
|
+
*/
|
|
136
|
+
static Default = Layer.effect(Uuid7State, Uuid7State.make).pipe(Layer.provide([DateTimes.Default, RandomValues.Default]));
|
|
45
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Generates one UUID version 7 from the current Uuid7State.
|
|
140
|
+
* @remarks
|
|
141
|
+
* ## Why
|
|
142
|
+
* Effectful generation makes the state boundary explicit and preserves `IllegalArgumentError` for invalid or exhausted timestamp space.
|
|
143
|
+
* ## Ownership and lifetime
|
|
144
|
+
* The Effect acquires no persistent resource and uses state owned by the provided Uuid7State Layer.
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* import { uuid7, Uuid7State } from "@typed/id/Uuid7"
|
|
148
|
+
* import { Effect } from "effect"
|
|
149
|
+
* const id = Effect.provide(uuid7, Uuid7State.Default)
|
|
150
|
+
* ```
|
|
151
|
+
* @category Generators
|
|
152
|
+
* @since 1.0.0
|
|
153
|
+
*/
|
|
46
154
|
export const uuid7 = Effect.map(Uuid7State.next, uuid7FromSeed);
|
|
47
155
|
function uuid7FromSeed({ randomBytes, seq, timestamp }) {
|
|
48
156
|
const result = new Uint8Array(16);
|
|
@@ -69,5 +177,5 @@ function uuid7FromSeed({ randomBytes, seq, timestamp }) {
|
|
|
69
177
|
result[13] = randomBytes[13];
|
|
70
178
|
result[14] = randomBytes[14];
|
|
71
179
|
result[15] = randomBytes[15];
|
|
72
|
-
return Uuid7.
|
|
180
|
+
return Uuid7.make(uuidStringify(result));
|
|
73
181
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as Exit from "effect/Exit";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import { RandomValues } from "../RandomValues.js";
|
|
4
|
+
export declare const expectIllegalArgument: (exit: Exit.Exit<unknown, unknown>) => void;
|
|
5
|
+
export declare const seededRandomValues: (seed: string | number) => Layer.Layer<RandomValues>;
|
|
6
|
+
export declare const zeroRandomValues: Layer.Layer<RandomValues>;
|
|
7
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/__tests__/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,eAAO,MAAM,qBAAqB,SAAU,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,SAStE,CAAC;AAEF,eAAO,MAAM,kBAAkB,SAAU,MAAM,GAAG,MAAM,KAAG,KAAK,CAAC,KAAK,CAAC,YAAY,CAIhF,CAAC;AAEJ,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY,CAOtD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
import * as Random from "effect/Random";
|
|
6
|
+
import { expect } from "vitest";
|
|
7
|
+
import { RandomValues } from "../RandomValues.js";
|
|
8
|
+
export const expectIllegalArgument = (exit) => {
|
|
9
|
+
expect(Exit.isFailure(exit)).toBe(true);
|
|
10
|
+
if (Exit.isFailure(exit)) {
|
|
11
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
12
|
+
expect(failure._tag).toBe("Some");
|
|
13
|
+
if (failure._tag === "Some") {
|
|
14
|
+
expect(Cause.isIllegalArgumentError(failure.value)).toBe(true);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export const seededRandomValues = (seed) => Layer.effect(RandomValues, RandomValues.pipe(Effect.provide(RandomValues.Random), Random.withSeed(seed)));
|
|
19
|
+
export const zeroRandomValues = Layer.succeed(RandomValues, RandomValues.of(Effect.fn((length) => Effect.succeed(new Uint8Array(length)))));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-contract.type-test.d.ts","sourceRoot":"","sources":["../../src/__tests__/public-contract.type-test.ts"],"names":[],"mappings":""}
|
package/dist/_sha.d.ts
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
+
/**
|
|
3
|
+
* Digests bytes with Web Crypto SHA-1 and returns a fresh Uint8Array.
|
|
4
|
+
* @remarks
|
|
5
|
+
* ## Why
|
|
6
|
+
* This published low-level helper exposes the exact hash required by UUID version 5; SHA-1 here is identity derivation, not password or signature security. Missing Web Crypto and rejected `subtle.digest` promises are Effect defects because the typed error channel is `never`.
|
|
7
|
+
* ## Ownership and lifetime
|
|
8
|
+
* Each Effect owns one asynchronous Web Crypto request and transfers a fresh result buffer; it retains no input after completion.
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { sha1 } from "@typed/id/_sha"
|
|
12
|
+
* import { Effect } from "effect"
|
|
13
|
+
* const digest = Effect.runPromise(sha1(new TextEncoder().encode("name")))
|
|
14
|
+
* ```
|
|
15
|
+
* @category Hashing
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
2
18
|
export declare const sha1: (data: BufferSource) => Effect.Effect<Uint8Array<ArrayBuffer>, never, never>;
|
|
19
|
+
/**
|
|
20
|
+
* Digests bytes with Web Crypto SHA-512 and returns a fresh Uint8Array.
|
|
21
|
+
* @remarks
|
|
22
|
+
* ## Why
|
|
23
|
+
* This published low-level helper exposes the exact domain-separated hash used by CUID formatting without hiding platform requirements. Missing Web Crypto and rejected `subtle.digest` promises are Effect defects because the typed error channel is `never`.
|
|
24
|
+
* ## Ownership and lifetime
|
|
25
|
+
* Each Effect owns one asynchronous Web Crypto request and transfers a fresh result buffer; it retains no input after completion.
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { sha512 } from "@typed/id/_sha"
|
|
29
|
+
* import { Effect } from "effect"
|
|
30
|
+
* const digest = Effect.runPromise(sha512(new TextEncoder().encode("seed")))
|
|
31
|
+
* ```
|
|
32
|
+
* @category Hashing
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
*/
|
|
3
35
|
export declare const sha512: (data: BufferSource) => Effect.Effect<Uint8Array<ArrayBuffer>, never, never>;
|
|
4
36
|
//# sourceMappingURL=_sha.d.ts.map
|
package/dist/_sha.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_sha.d.ts","sourceRoot":"","sources":["../src/_sha.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,eAAO,MAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"_sha.d.ts","sourceRoot":"","sources":["../src/_sha.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,IAAI,SAAU,YAAY,yDAGpC,CAAC;AAEJ;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,MAAM,SAAU,YAAY,yDAGtC,CAAC"}
|
package/dist/_sha.js
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
1
|
import * as Effect from "effect/Effect";
|
|
2
|
+
/**
|
|
3
|
+
* Digests bytes with Web Crypto SHA-1 and returns a fresh Uint8Array.
|
|
4
|
+
* @remarks
|
|
5
|
+
* ## Why
|
|
6
|
+
* This published low-level helper exposes the exact hash required by UUID version 5; SHA-1 here is identity derivation, not password or signature security. Missing Web Crypto and rejected `subtle.digest` promises are Effect defects because the typed error channel is `never`.
|
|
7
|
+
* ## Ownership and lifetime
|
|
8
|
+
* Each Effect owns one asynchronous Web Crypto request and transfers a fresh result buffer; it retains no input after completion.
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { sha1 } from "@typed/id/_sha"
|
|
12
|
+
* import { Effect } from "effect"
|
|
13
|
+
* const digest = Effect.runPromise(sha1(new TextEncoder().encode("name")))
|
|
14
|
+
* ```
|
|
15
|
+
* @category Hashing
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
2
18
|
export const sha1 = (data) => Effect.promise(() => crypto.subtle.digest("SHA-1", data).then((buffer) => new Uint8Array(buffer)));
|
|
19
|
+
/**
|
|
20
|
+
* Digests bytes with Web Crypto SHA-512 and returns a fresh Uint8Array.
|
|
21
|
+
* @remarks
|
|
22
|
+
* ## Why
|
|
23
|
+
* This published low-level helper exposes the exact domain-separated hash used by CUID formatting without hiding platform requirements. Missing Web Crypto and rejected `subtle.digest` promises are Effect defects because the typed error channel is `never`.
|
|
24
|
+
* ## Ownership and lifetime
|
|
25
|
+
* Each Effect owns one asynchronous Web Crypto request and transfers a fresh result buffer; it retains no input after completion.
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { sha512 } from "@typed/id/_sha"
|
|
29
|
+
* import { Effect } from "effect"
|
|
30
|
+
* const digest = Effect.runPromise(sha512(new TextEncoder().encode("seed")))
|
|
31
|
+
* ```
|
|
32
|
+
* @category Hashing
|
|
33
|
+
* @since 1.0.0
|
|
34
|
+
*/
|
|
3
35
|
export const sha512 = (data) => Effect.promise(() => crypto.subtle.digest("SHA-512", data).then((buffer) => new Uint8Array(buffer)));
|