uniku 0.3.1 → 0.4.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/README.md +32 -0
- package/build/cuid2/cuid2.d.mts +5 -5
- package/build/cuid2/cuid2.d.mts.map +1 -1
- package/build/cuid2/cuid2.mjs.map +1 -1
- package/build/generators.d.mts +1 -1
- package/build/generators.d.mts.map +1 -1
- package/build/generators.mjs +1 -1
- package/build/generators.mjs.map +1 -1
- package/build/ksuid/ksuid.d.mts +6 -6
- package/build/ksuid/ksuid.d.mts.map +1 -1
- package/build/ksuid/ksuid.mjs.map +1 -1
- package/build/nanoid/nanoid.d.mts +2 -2
- package/build/nanoid/nanoid.mjs.map +1 -1
- package/build/objectid/objectid.d.mts +6 -6
- package/build/objectid/objectid.d.mts.map +1 -1
- package/build/objectid/objectid.mjs.map +1 -1
- package/build/tsid/tsid.d.mts +8 -8
- package/build/tsid/tsid.d.mts.map +1 -1
- package/build/tsid/tsid.mjs.map +1 -1
- package/build/typeid/typeid.d.mts +8 -8
- package/build/typeid/typeid.d.mts.map +1 -1
- package/build/typeid/typeid.mjs.map +1 -1
- package/build/ulid/ulid.d.mts +10 -6
- package/build/ulid/ulid.d.mts.map +1 -1
- package/build/ulid/ulid.mjs.map +1 -1
- package/build/uuid/v4.d.mts +5 -5
- package/build/uuid/v4.d.mts.map +1 -1
- package/build/uuid/v4.mjs.map +1 -1
- package/build/uuid/v7.d.mts +10 -6
- package/build/uuid/v7.d.mts.map +1 -1
- package/build/uuid/v7.mjs.map +1 -1
- package/build/xid/xid.d.mts +31 -0
- package/build/xid/xid.d.mts.map +1 -0
- package/build/xid/xid.mjs +2 -0
- package/build/xid/xid.mjs.map +1 -0
- package/package.json +11 -3
- package/src/cuid2/cuid2.ts +6 -4
- package/src/generators.ts +1 -1
- package/src/ksuid/ksuid.ts +7 -0
- package/src/nanoid/nanoid.ts +3 -3
- package/src/objectid/objectid.ts +7 -0
- package/src/tsid/tsid.ts +9 -0
- package/src/typeid/typeid.ts +9 -0
- package/src/ulid/ulid.ts +11 -0
- package/src/uuid/v4.ts +6 -0
- package/src/uuid/v7.ts +11 -0
- package/src/xid/base32hex.ts +79 -0
- package/src/xid/xid.ts +158 -0
package/src/tsid/tsid.ts
CHANGED
|
@@ -62,14 +62,23 @@ export type TsidOptions = {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export type Tsid = {
|
|
65
|
+
/** Generate a time-sorted TSID bigint. */
|
|
65
66
|
(): bigint
|
|
67
|
+
/** Generate a TSID with explicit options or write its 8 canonical bytes into a caller-owned buffer. */
|
|
66
68
|
<TBuf extends Uint8Array = Uint8Array>(options: TsidOptions | undefined, buf: TBuf, offset?: number): TBuf
|
|
69
|
+
/** Generate a TSID bigint with optional timestamp, epoch, node, or counter controls. */
|
|
67
70
|
(options?: TsidOptions, buf?: undefined, offset?: number): bigint
|
|
71
|
+
/** Convert a TSID bigint to its canonical 8-byte representation. */
|
|
68
72
|
toBytes(id: bigint): Uint8Array
|
|
73
|
+
/** Convert 8 canonical TSID bytes to a TSID bigint. */
|
|
69
74
|
fromBytes(bytes: Uint8Array): bigint
|
|
75
|
+
/** Convert a TSID bigint to its canonical 13-character string. */
|
|
70
76
|
toString(id: bigint): string
|
|
77
|
+
/** Convert a canonical TSID string to a bigint. */
|
|
71
78
|
fromString(str: string): bigint
|
|
79
|
+
/** Read a TSID timestamp in milliseconds, using the supplied or default epoch. */
|
|
72
80
|
timestamp(id: bigint, epoch?: number): number
|
|
81
|
+
/** Return whether a value is a valid 64-bit TSID bigint. */
|
|
73
82
|
isValid(id: unknown): id is bigint
|
|
74
83
|
/** The nil TSID (all zeros) */
|
|
75
84
|
NIL: bigint
|
package/src/typeid/typeid.ts
CHANGED
|
@@ -5,14 +5,23 @@ import { uuidv7 } from '../uuid/v7'
|
|
|
5
5
|
export type TypeidOptions = UuidV7Options
|
|
6
6
|
|
|
7
7
|
export type Typeid = {
|
|
8
|
+
/** Generate a TypeID from a lowercase entity prefix and a UUID v7 suffix. */
|
|
8
9
|
(prefix: string, options?: TypeidOptions): string
|
|
10
|
+
/** Convert a TypeID's UUID v7 suffix to its canonical 16-byte representation. */
|
|
9
11
|
toBytes(id: string): Uint8Array
|
|
12
|
+
/** Build a TypeID from a prefix and canonical UUID v7 bytes. */
|
|
10
13
|
fromBytes(prefix: string, bytes: Uint8Array): string
|
|
14
|
+
/** Convert a TypeID to its UUID v7 string. */
|
|
11
15
|
toUuid(id: string): string
|
|
16
|
+
/** Build a TypeID from a prefix and UUID v7 string. */
|
|
12
17
|
fromUuid(prefix: string, uuid: string): string
|
|
18
|
+
/** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. */
|
|
13
19
|
timestamp(id: string): number
|
|
20
|
+
/** Read a TypeID's entity prefix. */
|
|
14
21
|
prefix(id: string): string
|
|
22
|
+
/** Read a TypeID's UUID v7 suffix. */
|
|
15
23
|
suffix(id: string): string
|
|
24
|
+
/** Return whether a value is a syntactically valid TypeID. */
|
|
16
25
|
isValid(id: unknown): id is string
|
|
17
26
|
}
|
|
18
27
|
|
package/src/ulid/ulid.ts
CHANGED
|
@@ -18,12 +18,19 @@ export type UlidOptions = {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export type Ulid = {
|
|
21
|
+
/** Generate a time-ordered ULID string. */
|
|
21
22
|
(): string
|
|
23
|
+
/** Generate a ULID with explicit options or write its 16 canonical bytes into a caller-owned buffer. */
|
|
22
24
|
<TBuf extends Uint8Array = Uint8Array>(options: UlidOptions | undefined, buf: TBuf, offset?: number): TBuf
|
|
25
|
+
/** Generate a ULID string with optional timestamp or random bytes. */
|
|
23
26
|
(options?: UlidOptions, buf?: undefined, offset?: number): string
|
|
27
|
+
/** Convert a ULID string to its canonical 16-byte representation. */
|
|
24
28
|
toBytes(id: string): Uint8Array
|
|
29
|
+
/** Convert 16 canonical ULID bytes to a ULID string. */
|
|
25
30
|
fromBytes(bytes: Uint8Array): string
|
|
31
|
+
/** Read the embedded Unix timestamp in milliseconds. */
|
|
26
32
|
timestamp(id: string): number
|
|
33
|
+
/** Return whether a value is a syntactically valid ULID string. */
|
|
27
34
|
isValid(id: unknown): id is string
|
|
28
35
|
/** The nil ULID (all zeros) */
|
|
29
36
|
NIL: string
|
|
@@ -183,6 +190,10 @@ function isValid(id: unknown): id is string {
|
|
|
183
190
|
return typeof id === 'string' && ULID_REGEX.test(id)
|
|
184
191
|
}
|
|
185
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Generate a ULID string or write its 16 canonical bytes into a buffer.
|
|
195
|
+
* ULIDs are URL-safe, carry a millisecond timestamp, and sort by creation time.
|
|
196
|
+
*/
|
|
186
197
|
export const ulid: Ulid = Object.assign(ulidFn, {
|
|
187
198
|
toBytes: (id: string) => decodeToBytes(id),
|
|
188
199
|
fromBytes: (bytes: Uint8Array) => bytesToUlid(bytes),
|
package/src/uuid/v4.ts
CHANGED
|
@@ -15,11 +15,17 @@ export type UuidV4Options = {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export type UuidV4 = {
|
|
18
|
+
/** Generate a random UUID v4 string. */
|
|
18
19
|
(): string
|
|
20
|
+
/** Generate a UUID v4 with explicit options or write its 16 canonical bytes into a caller-owned buffer. */
|
|
19
21
|
<TBuf extends Uint8Array = Uint8Array>(options: UuidV4Options | undefined, buf: TBuf, offset?: number): TBuf
|
|
22
|
+
/** Generate a UUID v4 string with optional deterministic random bytes. */
|
|
20
23
|
(options?: UuidV4Options, buf?: undefined, offset?: number): string
|
|
24
|
+
/** Convert a UUID v4 string to its canonical 16-byte representation. */
|
|
21
25
|
toBytes(id: string): Uint8Array
|
|
26
|
+
/** Convert 16 canonical UUID bytes to a UUID v4 string. */
|
|
22
27
|
fromBytes(bytes: Uint8Array): string
|
|
28
|
+
/** Return whether a value is a syntactically valid UUID v4 string. */
|
|
23
29
|
isValid(id: unknown): id is string
|
|
24
30
|
/** The nil UUID (all zeros) */
|
|
25
31
|
NIL: string
|
package/src/uuid/v7.ts
CHANGED
|
@@ -9,6 +9,10 @@ export type UuidV7Options = {
|
|
|
9
9
|
* Note: Several bytes will be overwritten with timestamp, version, and variant data.
|
|
10
10
|
*/
|
|
11
11
|
random?: Uint8Array
|
|
12
|
+
/**
|
|
13
|
+
* Timestamp in milliseconds since Unix epoch.
|
|
14
|
+
* Defaults to Date.now().
|
|
15
|
+
*/
|
|
12
16
|
msecs?: number
|
|
13
17
|
/**
|
|
14
18
|
* Unsigned 32-bit sequence value.
|
|
@@ -17,12 +21,19 @@ export type UuidV7Options = {
|
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export type UuidV7 = {
|
|
24
|
+
/** Generate a time-ordered UUID v7 string. */
|
|
20
25
|
(): string
|
|
26
|
+
/** Generate a UUID v7 with explicit options or write its 16 canonical bytes into a caller-owned buffer. */
|
|
21
27
|
<TBuf extends Uint8Array = Uint8Array>(options: UuidV7Options | undefined, buf: TBuf, offset?: number): TBuf
|
|
28
|
+
/** Generate a UUID v7 string with optional timestamp, sequence, or random bytes. */
|
|
22
29
|
(options?: UuidV7Options, buf?: undefined, offset?: number): string
|
|
30
|
+
/** Convert a UUID v7 string to its canonical 16-byte representation. */
|
|
23
31
|
toBytes(id: string): Uint8Array
|
|
32
|
+
/** Convert 16 canonical UUID bytes to a UUID v7 string. */
|
|
24
33
|
fromBytes(bytes: Uint8Array): string
|
|
34
|
+
/** Read the embedded Unix timestamp in milliseconds. */
|
|
25
35
|
timestamp(id: string): number
|
|
36
|
+
/** Return whether a value is a syntactically valid UUID v7 string. */
|
|
26
37
|
isValid(id: unknown): id is string
|
|
27
38
|
/** The nil UUID (all zeros) */
|
|
28
39
|
NIL: string
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BufferError, ParseError } from '../errors'
|
|
2
|
+
|
|
3
|
+
const ENCODING = '0123456789abcdefghijklmnopqrstuv'
|
|
4
|
+
const XID_BYTES = 12
|
|
5
|
+
const XID_LENGTH = 20
|
|
6
|
+
|
|
7
|
+
const DECODING = new Uint8Array(65536)
|
|
8
|
+
DECODING.fill(255)
|
|
9
|
+
for (let i = 0; i < ENCODING.length; i += 1) {
|
|
10
|
+
DECODING[ENCODING.charCodeAt(i)] = i
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Encode the 12 canonical XID bytes as lowercase base32hex. */
|
|
14
|
+
export function encodeBase32Hex(bytes: Uint8Array): string {
|
|
15
|
+
if (bytes.length !== XID_BYTES) {
|
|
16
|
+
throw new BufferError(
|
|
17
|
+
'XID_BYTES_INVALID_LENGTH',
|
|
18
|
+
`XID bytes must be exactly ${XID_BYTES} bytes, got ${bytes.length}`,
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
ENCODING[bytes[0] >> 3] +
|
|
24
|
+
ENCODING[((bytes[0] << 2) | (bytes[1] >> 6)) & 0x1f] +
|
|
25
|
+
ENCODING[(bytes[1] >> 1) & 0x1f] +
|
|
26
|
+
ENCODING[((bytes[1] << 4) | (bytes[2] >> 4)) & 0x1f] +
|
|
27
|
+
ENCODING[((bytes[2] << 1) | (bytes[3] >> 7)) & 0x1f] +
|
|
28
|
+
ENCODING[(bytes[3] >> 2) & 0x1f] +
|
|
29
|
+
ENCODING[((bytes[3] << 3) | (bytes[4] >> 5)) & 0x1f] +
|
|
30
|
+
ENCODING[bytes[4] & 0x1f] +
|
|
31
|
+
ENCODING[bytes[5] >> 3] +
|
|
32
|
+
ENCODING[((bytes[5] << 2) | (bytes[6] >> 6)) & 0x1f] +
|
|
33
|
+
ENCODING[(bytes[6] >> 1) & 0x1f] +
|
|
34
|
+
ENCODING[((bytes[6] << 4) | (bytes[7] >> 4)) & 0x1f] +
|
|
35
|
+
ENCODING[((bytes[7] << 1) | (bytes[8] >> 7)) & 0x1f] +
|
|
36
|
+
ENCODING[(bytes[8] >> 2) & 0x1f] +
|
|
37
|
+
ENCODING[((bytes[8] << 3) | (bytes[9] >> 5)) & 0x1f] +
|
|
38
|
+
ENCODING[bytes[9] & 0x1f] +
|
|
39
|
+
ENCODING[bytes[10] >> 3] +
|
|
40
|
+
ENCODING[((bytes[10] << 2) | (bytes[11] >> 6)) & 0x1f] +
|
|
41
|
+
ENCODING[(bytes[11] >> 1) & 0x1f] +
|
|
42
|
+
ENCODING[(bytes[11] << 4) & 0x1f]
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Decode a canonical lowercase base32hex XID string to its 12 bytes. */
|
|
47
|
+
export function decodeBase32Hex(id: string): Uint8Array {
|
|
48
|
+
if (id.length !== XID_LENGTH) {
|
|
49
|
+
throw new ParseError('XID_INVALID_LENGTH', `XID string must be ${XID_LENGTH} characters, got ${id.length}`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const values = new Uint8Array(XID_LENGTH)
|
|
53
|
+
for (let i = 0; i < XID_LENGTH; i += 1) {
|
|
54
|
+
const value = DECODING[id.charCodeAt(i)]
|
|
55
|
+
if (value === 255) {
|
|
56
|
+
throw new ParseError('XID_INVALID_CHAR', `Invalid XID character: ${id[i]}`)
|
|
57
|
+
}
|
|
58
|
+
values[i] = value
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (values[19] !== 0 && values[19] !== 16) {
|
|
62
|
+
throw new ParseError('XID_NON_CANONICAL', 'XID trailing bits must be canonically encoded')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return new Uint8Array([
|
|
66
|
+
(values[0] << 3) | (values[1] >> 2),
|
|
67
|
+
(values[1] << 6) | (values[2] << 1) | (values[3] >> 4),
|
|
68
|
+
(values[3] << 4) | (values[4] >> 1),
|
|
69
|
+
(values[4] << 7) | (values[5] << 2) | (values[6] >> 3),
|
|
70
|
+
(values[6] << 5) | values[7],
|
|
71
|
+
(values[8] << 3) | (values[9] >> 2),
|
|
72
|
+
(values[9] << 6) | (values[10] << 1) | (values[11] >> 4),
|
|
73
|
+
(values[11] << 4) | (values[12] >> 1),
|
|
74
|
+
(values[12] << 7) | (values[13] << 2) | (values[14] >> 3),
|
|
75
|
+
(values[14] << 5) | values[15],
|
|
76
|
+
(values[16] << 3) | (values[17] >> 2),
|
|
77
|
+
(values[17] << 6) | (values[18] << 1) | (values[19] >> 4),
|
|
78
|
+
])
|
|
79
|
+
}
|
package/src/xid/xid.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { writeTimestamp32 } from '../common/bytes'
|
|
2
|
+
import { randomBytes, randomUint32 } from '../common/random'
|
|
3
|
+
import { isIntegerInRange, isWritableRange } from '../common/validation'
|
|
4
|
+
import { BufferError, InvalidInputError } from '../errors'
|
|
5
|
+
import { decodeBase32Hex, encodeBase32Hex } from './base32hex'
|
|
6
|
+
|
|
7
|
+
const XID_BYTES = 12
|
|
8
|
+
const MACHINE_ID_BYTES = 3
|
|
9
|
+
const MAX_SECS = 0xffffffff
|
|
10
|
+
const MAX_PROCESS_ID = 0xffff
|
|
11
|
+
const MAX_COUNTER = 0xffffff
|
|
12
|
+
const XID_REGEX = /^[0-9a-v]{19}[0g]$/
|
|
13
|
+
|
|
14
|
+
export type XidOptions = {
|
|
15
|
+
/** First three bytes used as the XID machine identity. */
|
|
16
|
+
machineId?: Uint8Array
|
|
17
|
+
/** 16-bit process identity. */
|
|
18
|
+
processId?: number
|
|
19
|
+
/** Unix timestamp in seconds. Defaults to the current second. */
|
|
20
|
+
secs?: number
|
|
21
|
+
/** 24-bit counter. Explicit values do not consume shared state. */
|
|
22
|
+
counter?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type Xid = {
|
|
26
|
+
(): string
|
|
27
|
+
<TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf
|
|
28
|
+
(options?: XidOptions, buf?: undefined, offset?: number): string
|
|
29
|
+
toBytes(id: string): Uint8Array
|
|
30
|
+
fromBytes(bytes: Uint8Array): string
|
|
31
|
+
timestamp(id: string): number
|
|
32
|
+
isValid(id: unknown): id is string
|
|
33
|
+
NIL: string
|
|
34
|
+
MAX: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type XidState = {
|
|
38
|
+
machineId: Uint8Array | undefined
|
|
39
|
+
processId: number | undefined
|
|
40
|
+
counter: number | undefined
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const state: XidState = { machineId: undefined, processId: undefined, counter: undefined }
|
|
44
|
+
|
|
45
|
+
/** Copy pooled bytes before another random draw can refill their backing pool. */
|
|
46
|
+
function freshRandom(count: number): Uint8Array {
|
|
47
|
+
return randomBytes(count).slice()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function initializeIdentity(): void {
|
|
51
|
+
if (state.machineId === undefined) state.machineId = freshRandom(MACHINE_ID_BYTES)
|
|
52
|
+
if (state.processId === undefined) state.processId = randomUint32() & MAX_PROCESS_ID
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function nextCounter(): number {
|
|
56
|
+
if (state.counter === undefined) state.counter = randomUint32() & MAX_COUNTER
|
|
57
|
+
state.counter = (state.counter + 1) & MAX_COUNTER
|
|
58
|
+
return state.counter
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function writeXidBytesUnchecked(
|
|
62
|
+
secs: number,
|
|
63
|
+
machineId: Uint8Array,
|
|
64
|
+
processId: number,
|
|
65
|
+
counter: number,
|
|
66
|
+
buf: Uint8Array,
|
|
67
|
+
offset: number,
|
|
68
|
+
): void {
|
|
69
|
+
writeTimestamp32(buf, offset, secs)
|
|
70
|
+
buf.set(machineId.subarray(0, MACHINE_ID_BYTES), offset + 4)
|
|
71
|
+
buf[offset + 7] = processId >>> 8
|
|
72
|
+
buf[offset + 8] = processId & 0xff
|
|
73
|
+
buf[offset + 9] = counter >>> 16
|
|
74
|
+
buf[offset + 10] = (counter >>> 8) & 0xff
|
|
75
|
+
buf[offset + 11] = counter & 0xff
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateOptions(options: XidOptions): void {
|
|
79
|
+
if (options.machineId !== undefined && options.machineId.length < MACHINE_ID_BYTES) {
|
|
80
|
+
throw new InvalidInputError(
|
|
81
|
+
'XID_MACHINE_ID_BYTES_TOO_SHORT',
|
|
82
|
+
`Machine ID bytes length must be >= ${MACHINE_ID_BYTES} for XID`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
if (options.processId !== undefined && !isIntegerInRange(options.processId, 0, MAX_PROCESS_ID)) {
|
|
86
|
+
throw new InvalidInputError('XID_PROCESS_ID_OUT_OF_RANGE', `Process ID must be between 0 and ${MAX_PROCESS_ID}`)
|
|
87
|
+
}
|
|
88
|
+
if (options.secs !== undefined && !isIntegerInRange(options.secs, 0, MAX_SECS)) {
|
|
89
|
+
throw new InvalidInputError('XID_TIMESTAMP_OUT_OF_RANGE', `Timestamp must be between 0 and ${MAX_SECS}`)
|
|
90
|
+
}
|
|
91
|
+
if (options.counter !== undefined && !isIntegerInRange(options.counter, 0, MAX_COUNTER)) {
|
|
92
|
+
throw new InvalidInputError('XID_COUNTER_OUT_OF_RANGE', `Counter must be between 0 and ${MAX_COUNTER}`)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function xidFn(options?: XidOptions, buf?: undefined, offset?: number): string
|
|
97
|
+
function xidFn<TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf
|
|
98
|
+
function xidFn<TBuf extends Uint8Array = Uint8Array>(options?: XidOptions, buf?: TBuf, offset = 0): string | TBuf {
|
|
99
|
+
if (options !== undefined) validateOptions(options)
|
|
100
|
+
|
|
101
|
+
const secs = options?.secs ?? Math.floor(Date.now() / 1000)
|
|
102
|
+
if (options?.machineId === undefined || options.processId === undefined) {
|
|
103
|
+
initializeIdentity()
|
|
104
|
+
}
|
|
105
|
+
const machineId = options?.machineId ?? state.machineId!
|
|
106
|
+
const processId = options?.processId ?? state.processId!
|
|
107
|
+
const counter = options?.counter ?? nextCounter()
|
|
108
|
+
|
|
109
|
+
if (buf !== undefined) {
|
|
110
|
+
if (!isWritableRange(buf, offset, XID_BYTES)) {
|
|
111
|
+
throw new BufferError(
|
|
112
|
+
'XID_BUFFER_OUT_OF_BOUNDS',
|
|
113
|
+
`XID byte range ${offset}:${offset + 11} is out of buffer bounds`,
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
writeXidBytesUnchecked(secs, machineId, processId, counter, buf, offset)
|
|
117
|
+
return buf
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const bytes = new Uint8Array(XID_BYTES)
|
|
121
|
+
writeXidBytesUnchecked(secs, machineId, processId, counter, bytes, 0)
|
|
122
|
+
return encodeBase32Hex(bytes)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function toBytes(id: string): Uint8Array {
|
|
126
|
+
return decodeBase32Hex(id)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function fromBytes(bytes: Uint8Array): string {
|
|
130
|
+
return encodeBase32Hex(bytes)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function timestamp(id: string): number {
|
|
134
|
+
const bytes = decodeBase32Hex(id)
|
|
135
|
+
return (((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0) * 1000
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isValid(id: unknown): id is string {
|
|
139
|
+
return typeof id === 'string' && XID_REGEX.test(id)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Generate a 20-character rs/xid-compatible identifier.
|
|
144
|
+
*
|
|
145
|
+
* XID embeds seconds, a lazily-random per-runtime identity, and a shared
|
|
146
|
+
* always-incrementing counter. In Cloudflare Workers, time is frozen during a
|
|
147
|
+
* request, so the counter preserves ordering for IDs made in that request.
|
|
148
|
+
*/
|
|
149
|
+
export const xid: Xid = Object.assign(xidFn, {
|
|
150
|
+
toBytes,
|
|
151
|
+
fromBytes,
|
|
152
|
+
timestamp,
|
|
153
|
+
isValid,
|
|
154
|
+
NIL: '0'.repeat(20),
|
|
155
|
+
MAX: encodeBase32Hex(new Uint8Array(12).fill(0xff)),
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
export { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'
|