@typed/id 0.15.0 → 0.17.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/Cuid.d.ts +27 -0
- package/dist/Cuid.js +86 -0
- package/dist/Cuid.js.map +1 -0
- package/dist/DateTimes.js +2 -1
- package/dist/DateTimes.js.map +1 -1
- package/dist/Ksuid.d.ts +7 -0
- package/dist/Ksuid.js +56 -0
- package/dist/Ksuid.js.map +1 -0
- package/dist/Uuid4.js +1 -1
- package/dist/Uuid4.js.map +1 -1
- package/dist/Uuid5.js +1 -1
- package/dist/Uuid5.js.map +1 -1
- package/dist/Uuid7.js +1 -1
- package/dist/Uuid7.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +55 -4
- package/src/Cuid.ts +135 -0
- package/src/DateTimes.ts +3 -1
- package/src/Ksuid.ts +81 -0
- package/src/Uuid4.ts +1 -1
- package/src/Uuid5.ts +1 -1
- package/src/Uuid7.ts +1 -1
- package/src/id.test.ts +61 -31
- package/src/index.ts +2 -0
package/dist/Cuid.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Effect, Layer, Schema } from 'effect';
|
|
2
|
+
import { DateTimes } from './DateTimes.js';
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js';
|
|
4
|
+
export declare const Cuid: Schema.brand<Schema.filter<Schema.Schema<string, string, never>>, "@typed/id/CUID">;
|
|
5
|
+
export type Cuid = Schema.Schema.Type<typeof Cuid>;
|
|
6
|
+
export declare const isCuid: (value: string) => value is Cuid;
|
|
7
|
+
export type CuidSeed = {
|
|
8
|
+
readonly timestamp: number;
|
|
9
|
+
readonly counter: number;
|
|
10
|
+
readonly random: Uint8Array;
|
|
11
|
+
readonly fingerprint: string;
|
|
12
|
+
};
|
|
13
|
+
declare const CuidState_base: import("effect/Context").TagClass<CuidState, "CuidState", {
|
|
14
|
+
readonly next: Effect.Effect<CuidSeed>;
|
|
15
|
+
}> & Effect.Tag.Proxy<CuidState, {
|
|
16
|
+
readonly next: Effect.Effect<CuidSeed>;
|
|
17
|
+
}> & {
|
|
18
|
+
use: <X>(body: (_: {
|
|
19
|
+
readonly next: Effect.Effect<CuidSeed>;
|
|
20
|
+
}) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | CuidState> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, import("effect/Cause").UnknownException, CuidState> : Effect.Effect<X, never, CuidState>;
|
|
21
|
+
};
|
|
22
|
+
export declare class CuidState extends CuidState_base {
|
|
23
|
+
static readonly layer: (envData: string) => Layer.Layer<CuidState, never, DateTimes | GetRandomValues>;
|
|
24
|
+
static readonly Default: Layer.Layer<CuidState, never, DateTimes | GetRandomValues>;
|
|
25
|
+
}
|
|
26
|
+
export declare const makeCuid: Effect.Effect<Cuid, never, DateTimes | GetRandomValues | CuidState>;
|
|
27
|
+
export {};
|
package/dist/Cuid.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Effect, Layer, Schema } from 'effect';
|
|
2
|
+
import { DateTimes } from './DateTimes.js';
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js';
|
|
4
|
+
// Constants
|
|
5
|
+
const DEFAULT_LENGTH = 24;
|
|
6
|
+
const BIG_LENGTH = 32;
|
|
7
|
+
const INITIAL_COUNT_MAX = 476782367;
|
|
8
|
+
// Schema
|
|
9
|
+
export const Cuid = Schema.String.pipe(Schema.pattern(/^[a-z][0-9a-z]+$/), Schema.brand('@typed/id/CUID'));
|
|
10
|
+
export const isCuid = Schema.is(Cuid);
|
|
11
|
+
// Utilities
|
|
12
|
+
const ALPHABET = Array.from({ length: 26 }, (_, i) => String.fromCharCode(i + 97));
|
|
13
|
+
const encoder = new TextEncoder();
|
|
14
|
+
function createEntropy(length, random) {
|
|
15
|
+
let entropy = '';
|
|
16
|
+
let offset = 0;
|
|
17
|
+
while (entropy.length < length) {
|
|
18
|
+
const value = random[offset];
|
|
19
|
+
entropy += Math.floor(value % 36).toString(36);
|
|
20
|
+
offset = (offset + 1) % random.length;
|
|
21
|
+
}
|
|
22
|
+
return entropy;
|
|
23
|
+
}
|
|
24
|
+
function hash(input) {
|
|
25
|
+
// Convert string to bytes
|
|
26
|
+
const data = encoder.encode(input);
|
|
27
|
+
// Create a hash using the Web Crypto API
|
|
28
|
+
return crypto.subtle.digest('SHA-512', data).then((buffer) => {
|
|
29
|
+
const view = new Uint8Array(buffer);
|
|
30
|
+
let value = 0n;
|
|
31
|
+
for (const byte of view) {
|
|
32
|
+
value = (value << 8n) + BigInt(byte);
|
|
33
|
+
}
|
|
34
|
+
// Drop the first character because it will bias the histogram to the left
|
|
35
|
+
return value.toString(36).slice(1);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// State Management
|
|
39
|
+
export class CuidState extends Effect.Tag('CuidState')() {
|
|
40
|
+
static layer = (envData) => Layer.effect(this, Effect.gen(function* () {
|
|
41
|
+
const { now } = yield* DateTimes;
|
|
42
|
+
const getRandomValues = yield* GetRandomValues;
|
|
43
|
+
const initialBytes = yield* getRandomValues(4);
|
|
44
|
+
const initialValue = Math.abs((initialBytes[0] << 24) |
|
|
45
|
+
(initialBytes[1] << 16) |
|
|
46
|
+
(initialBytes[2] << 8) |
|
|
47
|
+
initialBytes[3]) % INITIAL_COUNT_MAX;
|
|
48
|
+
// Create fingerprint from environment data
|
|
49
|
+
const fingerprint = yield* Effect.promise(() => hash(envData).then((h) => h.substring(0, BIG_LENGTH)));
|
|
50
|
+
let counter = initialValue;
|
|
51
|
+
return {
|
|
52
|
+
next: Effect.gen(function* () {
|
|
53
|
+
const timestamp = yield* now;
|
|
54
|
+
const random = yield* getRandomValues(32);
|
|
55
|
+
return {
|
|
56
|
+
timestamp,
|
|
57
|
+
counter: counter++,
|
|
58
|
+
random,
|
|
59
|
+
fingerprint,
|
|
60
|
+
};
|
|
61
|
+
}),
|
|
62
|
+
};
|
|
63
|
+
}));
|
|
64
|
+
static Default = this.layer('node');
|
|
65
|
+
}
|
|
66
|
+
// Core Functions
|
|
67
|
+
function cuidFromSeed({ timestamp, counter, random, fingerprint }) {
|
|
68
|
+
return Effect.gen(function* () {
|
|
69
|
+
// First letter is always a random lowercase letter from the seed
|
|
70
|
+
const firstLetter = ALPHABET[random[0] % ALPHABET.length];
|
|
71
|
+
// Convert components to base36
|
|
72
|
+
const time = timestamp.toString(36);
|
|
73
|
+
const count = counter.toString(36);
|
|
74
|
+
// Create entropy from remaining random bytes
|
|
75
|
+
const salt = createEntropy(4, random.slice(1));
|
|
76
|
+
// Hash all components together
|
|
77
|
+
const hashInput = `${time}${salt}${count}${fingerprint}`;
|
|
78
|
+
const hashed = yield* Effect.promise(() => hash(hashInput));
|
|
79
|
+
// Construct the final CUID
|
|
80
|
+
const id = `${firstLetter}${hashed.substring(0, DEFAULT_LENGTH - 1)}`;
|
|
81
|
+
return Cuid.make(id);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
// Public API
|
|
85
|
+
export const makeCuid = Effect.flatMap(CuidState.next, cuidFromSeed);
|
|
86
|
+
//# sourceMappingURL=Cuid.js.map
|
package/dist/Cuid.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Cuid.js","sourceRoot":"","sources":["../src/Cuid.ts"],"names":[],"mappings":"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"}
|
package/dist/DateTimes.js
CHANGED
|
@@ -7,10 +7,11 @@ export class DateTimes extends Effect.Tag('DateTimes')() {
|
|
|
7
7
|
static Default = this.make(Effect.clockWith((clock) => clock.currentTimeMillis));
|
|
8
8
|
static Fixed = (base) => Layer.effect(DateTimes, Effect.gen(function* () {
|
|
9
9
|
const clock = yield* Effect.clock;
|
|
10
|
+
const baseN = BigInt(base.getTime());
|
|
10
11
|
const startMillis = yield* clock.currentTimeMillis;
|
|
11
12
|
const now = clock.currentTimeMillis.pipe(Effect.map((millis) =>
|
|
12
13
|
// Use BigInt to avoid floating point precision issues which can break deterministic testing
|
|
13
|
-
Number(
|
|
14
|
+
Number(baseN + BigInt(millis) - BigInt(startMillis))));
|
|
14
15
|
return {
|
|
15
16
|
now,
|
|
16
17
|
date: now.pipe(Effect.map((millis) => new Date(millis))),
|
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,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,
|
|
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,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QACpC,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,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CACrD,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/Ksuid.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect';
|
|
2
|
+
import { DateTimes } from './DateTimes.js';
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js';
|
|
4
|
+
export declare const Ksuid: Schema.brand<Schema.filter<Schema.Schema<string, string, never>>, "@typed/id/KSUID">;
|
|
5
|
+
export type Ksuid = Schema.Schema.Type<typeof Ksuid>;
|
|
6
|
+
export declare const isKsuid: (value: string) => value is Ksuid;
|
|
7
|
+
export declare const makeKsuid: Effect.Effect<Ksuid, never, DateTimes | GetRandomValues>;
|
package/dist/Ksuid.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect';
|
|
2
|
+
import { DateTimes } from './DateTimes.js';
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js';
|
|
4
|
+
// Constants
|
|
5
|
+
const EPOCH = 14e11; // 2014-03-01T00:00:00Z
|
|
6
|
+
const TIMESTAMP_BYTES = 4;
|
|
7
|
+
const PAYLOAD_BYTES = 16;
|
|
8
|
+
const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES;
|
|
9
|
+
const STRING_LENGTH = 27;
|
|
10
|
+
// Schema
|
|
11
|
+
export const Ksuid = Schema.String.pipe(Schema.pattern(/^[0-9a-zA-Z]{27}$/), Schema.brand('@typed/id/KSUID'));
|
|
12
|
+
export const isKsuid = Schema.is(Ksuid);
|
|
13
|
+
// Utilities
|
|
14
|
+
const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
|
15
|
+
const base = BigInt(base62Chars.length);
|
|
16
|
+
function base62Encode(bytes) {
|
|
17
|
+
let number = 0n;
|
|
18
|
+
for (const byte of bytes) {
|
|
19
|
+
number = (number << 8n) + BigInt(byte);
|
|
20
|
+
}
|
|
21
|
+
const chars = new Array(STRING_LENGTH);
|
|
22
|
+
let i = chars.length;
|
|
23
|
+
while (i > 0) {
|
|
24
|
+
i--;
|
|
25
|
+
const remainder = Number(number % base);
|
|
26
|
+
chars[i] = base62Chars[remainder];
|
|
27
|
+
number = number / base;
|
|
28
|
+
}
|
|
29
|
+
return chars.join('');
|
|
30
|
+
}
|
|
31
|
+
// Core Functions
|
|
32
|
+
function ksuidFromSeed({ timestamp, payload }) {
|
|
33
|
+
// Create the combined bytes
|
|
34
|
+
const bytes = new Uint8Array(TOTAL_BYTES);
|
|
35
|
+
// Support for timestamps before the epoch, usually for testing
|
|
36
|
+
if (timestamp < EPOCH) {
|
|
37
|
+
timestamp += EPOCH;
|
|
38
|
+
}
|
|
39
|
+
// Write timestamp (4 bytes, big-endian)
|
|
40
|
+
const seconds = Math.floor((timestamp - EPOCH) / 1000);
|
|
41
|
+
bytes[0] = (seconds >>> 24) & 0xff;
|
|
42
|
+
bytes[1] = (seconds >>> 16) & 0xff;
|
|
43
|
+
bytes[2] = (seconds >>> 8) & 0xff;
|
|
44
|
+
bytes[3] = seconds & 0xff;
|
|
45
|
+
// Copy payload
|
|
46
|
+
bytes.set(payload, TIMESTAMP_BYTES);
|
|
47
|
+
// Encode as base62
|
|
48
|
+
return Ksuid.make(base62Encode(bytes));
|
|
49
|
+
}
|
|
50
|
+
// Public API
|
|
51
|
+
export const makeKsuid = Effect.gen(function* () {
|
|
52
|
+
const timestamp = yield* DateTimes.now;
|
|
53
|
+
const payload = yield* GetRandomValues.apply(PAYLOAD_BYTES);
|
|
54
|
+
return ksuidFromSeed({ timestamp, payload });
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=Ksuid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Ksuid.js","sourceRoot":"","sources":["../src/Ksuid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEtD,YAAY;AACZ,MAAM,KAAK,GAAG,KAAK,CAAA,CAAC,uBAAuB;AAC3C,MAAM,eAAe,GAAG,CAAC,CAAA;AACzB,MAAM,aAAa,GAAG,EAAE,CAAA;AACxB,MAAM,WAAW,GAAG,eAAe,GAAG,aAAa,CAAA;AACnD,MAAM,aAAa,GAAG,EAAE,CAAA;AAExB,SAAS;AACT,MAAM,CAAC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CACrC,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EACnC,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAChC,CAAA;AAGD,MAAM,CAAC,MAAM,OAAO,GAAsC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;AAQ1E,YAAY;AACZ,MAAM,WAAW,GAAG,gEAAgE,CAAA;AACpF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;AAEvC,SAAS,YAAY,CAAC,KAAiB;IACrC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,KAAK,GAAa,IAAI,KAAK,CAAC,aAAa,CAAC,CAAA;IAChD,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,CAAA;IAEpB,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACb,CAAC,EAAE,CAAA;QACH,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,SAAS,CAAC,CAAA;QACjC,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACxB,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AACvB,CAAC;AAED,iBAAiB;AACjB,SAAS,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,EAAa;IACtD,4BAA4B;IAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAA;IAEzC,+DAA+D;IAC/D,IAAI,SAAS,GAAG,KAAK,EAAE,CAAC;QACtB,SAAS,IAAI,KAAK,CAAA;IACpB,CAAC;IAED,wCAAwC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;IACtD,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IAClC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,KAAK,EAAE,CAAC,GAAG,IAAI,CAAA;IAClC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC,GAAG,IAAI,CAAA;IACjC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,IAAI,CAAA;IAEzB,eAAe;IACf,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;IAEnC,mBAAmB;IACnB,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAA;AACxC,CAAC;AAED,aAAa;AACb,MAAM,CAAC,MAAM,SAAS,GAA6D,MAAM,CAAC,GAAG,CAC3F,QAAQ,CAAC;IACP,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,GAAG,CAAA;IACtC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;IAC3D,OAAO,aAAa,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;AAC9C,CAAC,CACF,CAAA"}
|
package/dist/Uuid4.js
CHANGED
|
@@ -9,6 +9,6 @@ function uuid4FromSeed(seed) {
|
|
|
9
9
|
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
10
10
|
seed[6] = (seed[6] & 0x0f) | 0x40;
|
|
11
11
|
seed[8] = (seed[8] & 0x3f) | 0x80;
|
|
12
|
-
return uuidStringify(seed);
|
|
12
|
+
return Uuid4.make(uuidStringify(seed));
|
|
13
13
|
}
|
|
14
14
|
//# sourceMappingURL=Uuid4.js.map
|
package/dist/Uuid4.js.map
CHANGED
|
@@ -1 +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,
|
|
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,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAA;AACxC,CAAC"}
|
package/dist/Uuid5.js
CHANGED
|
@@ -42,7 +42,7 @@ export function makeUuid5(namespace, name) {
|
|
|
42
42
|
// Set version (5) and variant bits
|
|
43
43
|
result[6] = (result[6] & 0x0f) | 0x50; // version 5
|
|
44
44
|
result[8] = (result[8] & 0x3f) | 0x80; // variant 1
|
|
45
|
-
return uuidStringify(result);
|
|
45
|
+
return Uuid5.make(uuidStringify(result));
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
//# sourceMappingURL=Uuid5.js.map
|
package/dist/Uuid5.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Uuid5.js","sourceRoot":"","sources":["../src/Uuid5.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Uuid5.js","sourceRoot":"","sources":["../src/Uuid5.ts"],"names":[],"mappings":"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"}
|
package/dist/Uuid7.js
CHANGED
|
@@ -66,6 +66,6 @@ function uuid7FromSeed({ timestamp, seq, randomBytes }) {
|
|
|
66
66
|
result[13] = randomBytes[13];
|
|
67
67
|
result[14] = randomBytes[14];
|
|
68
68
|
result[15] = randomBytes[15];
|
|
69
|
-
return uuidStringify(result);
|
|
69
|
+
return Uuid7.make(uuidStringify(result));
|
|
70
70
|
}
|
|
71
71
|
//# sourceMappingURL=Uuid7.js.map
|
package/dist/Uuid7.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Uuid7.js","sourceRoot":"","sources":["../src/Uuid7.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Uuid7.js","sourceRoot":"","sources":["../src/Uuid7.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
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,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,sBAAsB,CAAA;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @typed/id
|
|
2
2
|
|
|
3
|
-
A TypeScript library providing common ID format generation using [Effect](https://effect.website/). This package includes implementations for UUID, NanoID, and
|
|
3
|
+
A TypeScript library providing common ID format generation using [Effect](https://effect.website/). This package includes implementations for UUID, NanoID, ULID, CUID2, and KSUID generation with a focus on type safety and functional programming principles.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -17,9 +17,11 @@ 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, v5,
|
|
20
|
+
- UUID (v4, v5, v7)
|
|
21
21
|
- NanoID
|
|
22
22
|
- ULID
|
|
23
|
+
- CUID2
|
|
24
|
+
- KSUID
|
|
23
25
|
- ⚡ Efficient and secure random value generation
|
|
24
26
|
- 📦 Zero dependencies (except Effect)
|
|
25
27
|
|
|
@@ -37,7 +39,10 @@ import {
|
|
|
37
39
|
Uuid5Namespace,
|
|
38
40
|
Sha1,
|
|
39
41
|
makeNanoId,
|
|
40
|
-
makeUlid
|
|
42
|
+
makeUlid,
|
|
43
|
+
makeCuid,
|
|
44
|
+
CuidState,
|
|
45
|
+
makeKsuid,
|
|
41
46
|
} from '@typed/id'
|
|
42
47
|
|
|
43
48
|
// Generate a UUID v4 (random)
|
|
@@ -80,6 +85,23 @@ await makeUlid.pipe(
|
|
|
80
85
|
Effect.runPromise
|
|
81
86
|
)
|
|
82
87
|
// Output: "01ARZ3NDEKTSV4RRFFQ69G5FAV"
|
|
88
|
+
|
|
89
|
+
// Generate a CUID
|
|
90
|
+
await makeCuid.pipe(
|
|
91
|
+
Effect.provide(CuidState.layer('my-environment')), // Provide environment fingerprint
|
|
92
|
+
Effect.provide([GetRandomValues.CryptoRandom, DateTimes.Default]),
|
|
93
|
+
Effect.flatMap(Effect.log),
|
|
94
|
+
Effect.runPromise
|
|
95
|
+
)
|
|
96
|
+
// Output: "clh3aqnd900003b64zpka3df"
|
|
97
|
+
|
|
98
|
+
// Generate a KSUID
|
|
99
|
+
await makeKsuid.pipe(
|
|
100
|
+
Effect.provide([GetRandomValues.CryptoRandom, DateTimes.Default]),
|
|
101
|
+
Effect.flatMap(Effect.log),
|
|
102
|
+
Effect.runPromise
|
|
103
|
+
)
|
|
104
|
+
// Output: "1jIGxyVFPeR4GkCcDPQU2bXhxy9"
|
|
83
105
|
```
|
|
84
106
|
|
|
85
107
|
## API
|
|
@@ -88,7 +110,6 @@ await makeUlid.pipe(
|
|
|
88
110
|
|
|
89
111
|
- `makeUuid4`: Generates a v4 UUID (random)
|
|
90
112
|
- `makeUuid5`: Generates a v5 UUID (SHA-1 hash of namespace + name)
|
|
91
|
-
- `makeUuid6`: Generates a v6 UUID (reordered time-based for better sorting)
|
|
92
113
|
- `makeUuid7`: Generates a v7 UUID (time-sortable)
|
|
93
114
|
|
|
94
115
|
### UUID v5 Namespaces
|
|
@@ -107,6 +128,36 @@ Pre-defined namespaces for UUID v5 generation:
|
|
|
107
128
|
|
|
108
129
|
- `makeUlid`: Generates a ULID (Universally Unique Lexicographically Sortable Identifier)
|
|
109
130
|
|
|
131
|
+
### CUID
|
|
132
|
+
|
|
133
|
+
- `makeCuid`: Generates a CUID2 (Collision-resistant Unique IDentifier)
|
|
134
|
+
- `CuidState.layer(envData)`: Creates a CUID state layer with environment fingerprint
|
|
135
|
+
- `envData`: A string identifying the environment (e.g., 'browser', 'node', 'mobile-ios')
|
|
136
|
+
- Used to help prevent collisions in distributed systems
|
|
137
|
+
- Cached and reused for efficiency
|
|
138
|
+
- Format: 24 characters, starting with a lowercase letter, followed by numbers and lowercase letters
|
|
139
|
+
- Properties:
|
|
140
|
+
- Sequential for database performance
|
|
141
|
+
- Secure from enumeration
|
|
142
|
+
- URL-safe
|
|
143
|
+
- Horizontally scalable
|
|
144
|
+
- Includes timestamp for time-based sorting
|
|
145
|
+
|
|
146
|
+
### KSUID
|
|
147
|
+
|
|
148
|
+
- `makeKsuid`: Generates a KSUID (K-Sortable Unique IDentifier)
|
|
149
|
+
- Format: 27 characters of base62 (0-9A-Za-z)
|
|
150
|
+
- Components:
|
|
151
|
+
- 32-bit timestamp (seconds since 2014-03-01)
|
|
152
|
+
- 128-bit random payload
|
|
153
|
+
- Properties:
|
|
154
|
+
- Time-sortable (lexicographically ordered by time)
|
|
155
|
+
- URL-safe (base62 encoded)
|
|
156
|
+
- Includes entropy for uniqueness
|
|
157
|
+
- Fixed size (27 characters)
|
|
158
|
+
- ~136 years of timestamp space from 2014
|
|
159
|
+
- No special character dependencies
|
|
160
|
+
|
|
110
161
|
## License
|
|
111
162
|
|
|
112
163
|
MIT
|
package/src/Cuid.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Effect, Layer, Schema } from 'effect'
|
|
2
|
+
import { DateTimes } from './DateTimes.js'
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js'
|
|
4
|
+
|
|
5
|
+
// Constants
|
|
6
|
+
const DEFAULT_LENGTH = 24
|
|
7
|
+
const BIG_LENGTH = 32
|
|
8
|
+
const INITIAL_COUNT_MAX = 476782367
|
|
9
|
+
|
|
10
|
+
// Schema
|
|
11
|
+
export const Cuid = Schema.String.pipe(
|
|
12
|
+
Schema.pattern(/^[a-z][0-9a-z]+$/),
|
|
13
|
+
Schema.brand('@typed/id/CUID'),
|
|
14
|
+
)
|
|
15
|
+
export type Cuid = Schema.Schema.Type<typeof Cuid>
|
|
16
|
+
|
|
17
|
+
export const isCuid: (value: string) => value is Cuid = Schema.is(Cuid)
|
|
18
|
+
|
|
19
|
+
// Types
|
|
20
|
+
export type CuidSeed = {
|
|
21
|
+
readonly timestamp: number
|
|
22
|
+
readonly counter: number
|
|
23
|
+
readonly random: Uint8Array
|
|
24
|
+
readonly fingerprint: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Utilities
|
|
28
|
+
const ALPHABET = Array.from({ length: 26 }, (_, i) => String.fromCharCode(i + 97))
|
|
29
|
+
const encoder = new TextEncoder()
|
|
30
|
+
|
|
31
|
+
function createEntropy(length: number, random: Uint8Array): string {
|
|
32
|
+
let entropy = ''
|
|
33
|
+
let offset = 0
|
|
34
|
+
|
|
35
|
+
while (entropy.length < length) {
|
|
36
|
+
const value = random[offset]
|
|
37
|
+
entropy += Math.floor(value % 36).toString(36)
|
|
38
|
+
offset = (offset + 1) % random.length
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return entropy
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function hash(input: string): Promise<string> {
|
|
45
|
+
// Convert string to bytes
|
|
46
|
+
const data = encoder.encode(input)
|
|
47
|
+
|
|
48
|
+
// Create a hash using the Web Crypto API
|
|
49
|
+
return crypto.subtle.digest('SHA-512', data).then((buffer) => {
|
|
50
|
+
const view = new Uint8Array(buffer)
|
|
51
|
+
let value = 0n
|
|
52
|
+
for (const byte of view) {
|
|
53
|
+
value = (value << 8n) + BigInt(byte)
|
|
54
|
+
}
|
|
55
|
+
// Drop the first character because it will bias the histogram to the left
|
|
56
|
+
return value.toString(36).slice(1)
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// State Management
|
|
61
|
+
export class CuidState extends Effect.Tag('CuidState')<
|
|
62
|
+
CuidState,
|
|
63
|
+
{
|
|
64
|
+
readonly next: Effect.Effect<CuidSeed>
|
|
65
|
+
}
|
|
66
|
+
>() {
|
|
67
|
+
static readonly layer = (
|
|
68
|
+
envData: string,
|
|
69
|
+
): Layer.Layer<CuidState, never, DateTimes | GetRandomValues> =>
|
|
70
|
+
Layer.effect(
|
|
71
|
+
this,
|
|
72
|
+
Effect.gen(function* () {
|
|
73
|
+
const { now } = yield* DateTimes
|
|
74
|
+
const getRandomValues = yield* GetRandomValues
|
|
75
|
+
const initialBytes = yield* getRandomValues(4)
|
|
76
|
+
const initialValue =
|
|
77
|
+
Math.abs(
|
|
78
|
+
(initialBytes[0] << 24) |
|
|
79
|
+
(initialBytes[1] << 16) |
|
|
80
|
+
(initialBytes[2] << 8) |
|
|
81
|
+
initialBytes[3],
|
|
82
|
+
) % INITIAL_COUNT_MAX
|
|
83
|
+
|
|
84
|
+
// Create fingerprint from environment data
|
|
85
|
+
const fingerprint = yield* Effect.promise(() =>
|
|
86
|
+
hash(envData).then((h) => h.substring(0, BIG_LENGTH)),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
let counter = initialValue
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
next: Effect.gen(function* () {
|
|
93
|
+
const timestamp = yield* now
|
|
94
|
+
const random = yield* getRandomValues(32)
|
|
95
|
+
return {
|
|
96
|
+
timestamp,
|
|
97
|
+
counter: counter++,
|
|
98
|
+
random,
|
|
99
|
+
fingerprint,
|
|
100
|
+
}
|
|
101
|
+
}),
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
static readonly Default = this.layer('node')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Core Functions
|
|
110
|
+
function cuidFromSeed({ timestamp, counter, random, fingerprint }: CuidSeed): Effect.Effect<Cuid> {
|
|
111
|
+
return Effect.gen(function* () {
|
|
112
|
+
// First letter is always a random lowercase letter from the seed
|
|
113
|
+
const firstLetter = ALPHABET[random[0] % ALPHABET.length]
|
|
114
|
+
|
|
115
|
+
// Convert components to base36
|
|
116
|
+
const time = timestamp.toString(36)
|
|
117
|
+
const count = counter.toString(36)
|
|
118
|
+
|
|
119
|
+
// Create entropy from remaining random bytes
|
|
120
|
+
const salt = createEntropy(4, random.slice(1))
|
|
121
|
+
|
|
122
|
+
// Hash all components together
|
|
123
|
+
const hashInput = `${time}${salt}${count}${fingerprint}`
|
|
124
|
+
const hashed = yield* Effect.promise(() => hash(hashInput))
|
|
125
|
+
|
|
126
|
+
// Construct the final CUID
|
|
127
|
+
const id = `${firstLetter}${hashed.substring(0, DEFAULT_LENGTH - 1)}`
|
|
128
|
+
|
|
129
|
+
return Cuid.make(id)
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Public API
|
|
134
|
+
export const makeCuid: Effect.Effect<Cuid, never, DateTimes | GetRandomValues | CuidState> =
|
|
135
|
+
Effect.flatMap(CuidState.next, cuidFromSeed)
|
package/src/DateTimes.ts
CHANGED
|
@@ -20,11 +20,12 @@ export class DateTimes extends Effect.Tag('DateTimes')<
|
|
|
20
20
|
DateTimes,
|
|
21
21
|
Effect.gen(function* () {
|
|
22
22
|
const clock = yield* Effect.clock
|
|
23
|
+
const baseN = BigInt(base.getTime())
|
|
23
24
|
const startMillis = yield* clock.currentTimeMillis
|
|
24
25
|
const now = clock.currentTimeMillis.pipe(
|
|
25
26
|
Effect.map((millis) =>
|
|
26
27
|
// Use BigInt to avoid floating point precision issues which can break deterministic testing
|
|
27
|
-
Number(
|
|
28
|
+
Number(baseN + BigInt(millis) - BigInt(startMillis)),
|
|
28
29
|
),
|
|
29
30
|
)
|
|
30
31
|
|
|
@@ -35,3 +36,4 @@ export class DateTimes extends Effect.Tag('DateTimes')<
|
|
|
35
36
|
}),
|
|
36
37
|
)
|
|
37
38
|
}
|
|
39
|
+
|
package/src/Ksuid.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Effect, Schema } from 'effect'
|
|
2
|
+
import { DateTimes } from './DateTimes.js'
|
|
3
|
+
import { GetRandomValues } from './GetRandomValues.js'
|
|
4
|
+
|
|
5
|
+
// Constants
|
|
6
|
+
const EPOCH = 14e11 // 2014-03-01T00:00:00Z
|
|
7
|
+
const TIMESTAMP_BYTES = 4
|
|
8
|
+
const PAYLOAD_BYTES = 16
|
|
9
|
+
const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES
|
|
10
|
+
const STRING_LENGTH = 27
|
|
11
|
+
|
|
12
|
+
// Schema
|
|
13
|
+
export const Ksuid = Schema.String.pipe(
|
|
14
|
+
Schema.pattern(/^[0-9a-zA-Z]{27}$/),
|
|
15
|
+
Schema.brand('@typed/id/KSUID'),
|
|
16
|
+
)
|
|
17
|
+
export type Ksuid = Schema.Schema.Type<typeof Ksuid>
|
|
18
|
+
|
|
19
|
+
export const isKsuid: (value: string) => value is Ksuid = Schema.is(Ksuid)
|
|
20
|
+
|
|
21
|
+
// Types
|
|
22
|
+
type KsuidSeed = {
|
|
23
|
+
readonly timestamp: number
|
|
24
|
+
readonly payload: Uint8Array
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Utilities
|
|
28
|
+
const base62Chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
|
29
|
+
const base = BigInt(base62Chars.length)
|
|
30
|
+
|
|
31
|
+
function base62Encode(bytes: Uint8Array): string {
|
|
32
|
+
let number = 0n
|
|
33
|
+
for (const byte of bytes) {
|
|
34
|
+
number = (number << 8n) + BigInt(byte)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const chars: string[] = new Array(STRING_LENGTH)
|
|
38
|
+
let i = chars.length
|
|
39
|
+
|
|
40
|
+
while (i > 0) {
|
|
41
|
+
i--
|
|
42
|
+
const remainder = Number(number % base)
|
|
43
|
+
chars[i] = base62Chars[remainder]
|
|
44
|
+
number = number / base
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return chars.join('')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Core Functions
|
|
51
|
+
function ksuidFromSeed({ timestamp, payload }: KsuidSeed): Ksuid {
|
|
52
|
+
// Create the combined bytes
|
|
53
|
+
const bytes = new Uint8Array(TOTAL_BYTES)
|
|
54
|
+
|
|
55
|
+
// Support for timestamps before the epoch, usually for testing
|
|
56
|
+
if (timestamp < EPOCH) {
|
|
57
|
+
timestamp += EPOCH
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Write timestamp (4 bytes, big-endian)
|
|
61
|
+
const seconds = Math.floor((timestamp - EPOCH) / 1000)
|
|
62
|
+
bytes[0] = (seconds >>> 24) & 0xff
|
|
63
|
+
bytes[1] = (seconds >>> 16) & 0xff
|
|
64
|
+
bytes[2] = (seconds >>> 8) & 0xff
|
|
65
|
+
bytes[3] = seconds & 0xff
|
|
66
|
+
|
|
67
|
+
// Copy payload
|
|
68
|
+
bytes.set(payload, TIMESTAMP_BYTES)
|
|
69
|
+
|
|
70
|
+
// Encode as base62
|
|
71
|
+
return Ksuid.make(base62Encode(bytes))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Public API
|
|
75
|
+
export const makeKsuid: Effect.Effect<Ksuid, never, DateTimes | GetRandomValues> = Effect.gen(
|
|
76
|
+
function* () {
|
|
77
|
+
const timestamp = yield* DateTimes.now
|
|
78
|
+
const payload = yield* GetRandomValues.apply(PAYLOAD_BYTES)
|
|
79
|
+
return ksuidFromSeed({ timestamp, payload })
|
|
80
|
+
},
|
|
81
|
+
)
|
package/src/Uuid4.ts
CHANGED
package/src/Uuid5.ts
CHANGED
package/src/Uuid7.ts
CHANGED
package/src/id.test.ts
CHANGED
|
@@ -1,46 +1,48 @@
|
|
|
1
1
|
import { describe, expect, it } from '@effect/vitest'
|
|
2
|
-
import { Effect, flow } from 'effect'
|
|
2
|
+
import { Effect, flow, TestClock } from 'effect'
|
|
3
3
|
import {
|
|
4
4
|
DateTimes,
|
|
5
5
|
GetRandomValues,
|
|
6
6
|
isUuid4,
|
|
7
7
|
isUuid5,
|
|
8
|
-
isUuid6,
|
|
9
8
|
isUuid7,
|
|
10
9
|
makeNanoId,
|
|
11
10
|
makeUlid,
|
|
12
11
|
makeUuid4,
|
|
13
12
|
makeUuid5,
|
|
14
|
-
makeUuid6,
|
|
15
13
|
makeUuid7,
|
|
16
14
|
Sha1,
|
|
17
15
|
Uuid5Namespace,
|
|
18
|
-
Uuid6State,
|
|
19
16
|
Uuid7State,
|
|
17
|
+
makeCuid,
|
|
18
|
+
isCuid,
|
|
19
|
+
CuidState,
|
|
20
|
+
makeKsuid,
|
|
21
|
+
isKsuid,
|
|
20
22
|
} from './index.js'
|
|
21
23
|
|
|
24
|
+
|
|
22
25
|
const makeTestValues = (length: number) => {
|
|
23
26
|
const values = new Uint8Array(length)
|
|
24
27
|
for (let i = 0; i < length; ++i) {
|
|
25
|
-
values[i] = i
|
|
28
|
+
values[i] = i % 256
|
|
26
29
|
}
|
|
27
30
|
return values
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
const provideTestValues = flow(
|
|
31
|
-
Effect.provide(
|
|
34
|
+
Effect.provide(CuidState.layer('test')),
|
|
35
|
+
Effect.provide(Uuid7State.Default),
|
|
32
36
|
Effect.provide(Sha1.Default),
|
|
33
|
-
Effect.provide(
|
|
34
|
-
|
|
35
|
-
DateTimes.Fixed(new Date(0)),
|
|
36
|
-
]),
|
|
37
|
+
Effect.provide(GetRandomValues.layer((length) => Effect.succeed(makeTestValues(length)))),
|
|
38
|
+
Effect.provide(DateTimes.Fixed(new Date(0))),
|
|
37
39
|
)
|
|
38
40
|
|
|
39
41
|
describe(__filename, () => {
|
|
40
42
|
describe('Uuid4', () => {
|
|
41
43
|
it.effect('generates a UUID v4', () =>
|
|
42
|
-
Effect.gen(function* (
|
|
43
|
-
const id = yield*
|
|
44
|
+
Effect.gen(function* () {
|
|
45
|
+
const id = yield* makeUuid4
|
|
44
46
|
expect(id).toMatchInlineSnapshot(`"00010203-0405-4607-8809-0a0b0c0d0e0f"`)
|
|
45
47
|
expect(id.length).toEqual(36)
|
|
46
48
|
expect(isUuid4(id)).toEqual(true)
|
|
@@ -50,8 +52,8 @@ describe(__filename, () => {
|
|
|
50
52
|
|
|
51
53
|
describe('Uuid5', () => {
|
|
52
54
|
it.effect('generates a UUID v5', () =>
|
|
53
|
-
Effect.gen(function* (
|
|
54
|
-
const id = yield*
|
|
55
|
+
Effect.gen(function* () {
|
|
56
|
+
const id = yield* makeUuid5(Uuid5Namespace.DNS, 'example.com')
|
|
55
57
|
expect(id).toMatchInlineSnapshot(`"cfbff0d1-9375-5685-968c-48ce8b15ae17"`)
|
|
56
58
|
expect(id.length).toEqual(36)
|
|
57
59
|
expect(isUuid5(id)).toEqual(true)
|
|
@@ -59,21 +61,10 @@ describe(__filename, () => {
|
|
|
59
61
|
)
|
|
60
62
|
})
|
|
61
63
|
|
|
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
64
|
describe('Uuid7', () => {
|
|
74
65
|
it.effect('generates a UUID v7', () =>
|
|
75
|
-
Effect.gen(function* (
|
|
76
|
-
const id = yield*
|
|
66
|
+
Effect.gen(function* () {
|
|
67
|
+
const id = yield* makeUuid7
|
|
77
68
|
expect(id).toMatchInlineSnapshot(`"00000000-0000-7030-9c20-260b0c0d0e0f"`)
|
|
78
69
|
expect(id.length).toEqual(36)
|
|
79
70
|
expect(isUuid7(id)).toEqual(true)
|
|
@@ -83,8 +74,8 @@ describe(__filename, () => {
|
|
|
83
74
|
|
|
84
75
|
describe('NanoId', () => {
|
|
85
76
|
it.effect('generates a NanoId', () =>
|
|
86
|
-
Effect.gen(function* (
|
|
87
|
-
const id = yield*
|
|
77
|
+
Effect.gen(function* () {
|
|
78
|
+
const id = yield* makeNanoId
|
|
88
79
|
expect(id).toMatchInlineSnapshot(`"0123456789abcdefghijk"`)
|
|
89
80
|
expect(id.length).toEqual(21)
|
|
90
81
|
}).pipe(provideTestValues),
|
|
@@ -93,11 +84,50 @@ describe(__filename, () => {
|
|
|
93
84
|
|
|
94
85
|
describe('Ulid', () => {
|
|
95
86
|
it.effect('generates a Ulid', () =>
|
|
96
|
-
Effect.gen(function* (
|
|
97
|
-
const id = yield*
|
|
87
|
+
Effect.gen(function* () {
|
|
88
|
+
const id = yield* makeUlid
|
|
98
89
|
expect(id).toMatchInlineSnapshot(`"00000000000123456789ABCDEF"`)
|
|
99
90
|
expect(id.length).toEqual(26)
|
|
100
91
|
}).pipe(provideTestValues),
|
|
101
92
|
)
|
|
102
93
|
})
|
|
94
|
+
|
|
95
|
+
describe('Cuid', () => {
|
|
96
|
+
it.effect('generates a CUID', () =>
|
|
97
|
+
Effect.gen(function* () {
|
|
98
|
+
const id = yield* makeCuid
|
|
99
|
+
expect(id.length).toEqual(24)
|
|
100
|
+
expect(isCuid(id)).toEqual(true)
|
|
101
|
+
expect(id).toMatchInlineSnapshot(`"ai17q5mkkp8w5f2cey3lyzu5"`)
|
|
102
|
+
|
|
103
|
+
// Generate another to ensure uniqueness
|
|
104
|
+
const id2 = yield* makeCuid
|
|
105
|
+
expect(id2).not.toEqual(id)
|
|
106
|
+
expect(isCuid(id2)).toEqual(true)
|
|
107
|
+
expect(id2).toMatchInlineSnapshot(`"abeo5wmlmnjxjrnjiidlfvzp"`)
|
|
108
|
+
}).pipe(provideTestValues),
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
describe('Ksuid', () => {
|
|
113
|
+
it.effect('generates a KSUID', () =>
|
|
114
|
+
Effect.gen(function* () {
|
|
115
|
+
console.log(yield* DateTimes.now)
|
|
116
|
+
const id = yield* makeKsuid
|
|
117
|
+
expect(id.length).toEqual(27)
|
|
118
|
+
expect(isKsuid(id)).toEqual(true)
|
|
119
|
+
expect(id).toMatchInlineSnapshot(`"00000000SYW7RiJxkEgOGusQGwp"`)
|
|
120
|
+
|
|
121
|
+
yield* TestClock.adjust(1000)
|
|
122
|
+
|
|
123
|
+
console.log(yield* DateTimes.now)
|
|
124
|
+
|
|
125
|
+
// Generate another to ensure uniqueness
|
|
126
|
+
const id2 = yield* makeKsuid
|
|
127
|
+
expect(id2).not.toEqual(id)
|
|
128
|
+
expect(isKsuid(id2)).toEqual(true)
|
|
129
|
+
expect(id2).toMatchInlineSnapshot(`"000007n4UlmTXBzjUOTX3nzfyYx"`)
|
|
130
|
+
}).pipe(provideTestValues),
|
|
131
|
+
)
|
|
132
|
+
})
|
|
103
133
|
})
|