uniku 0.5.0 → 0.7.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 +48 -9
- package/build/ksuid/ksuid.d.mts +2 -2
- 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 +2 -2
- package/build/objectid/objectid.mjs.map +1 -1
- package/build/timestamp-ChrSuQCR.mjs.map +1 -1
- package/build/typeid/typeid.d.mts +12 -9
- package/build/typeid/typeid.d.mts.map +1 -1
- package/build/typeid/typeid.mjs +1 -1
- package/build/typeid/typeid.mjs.map +1 -1
- package/build/uuid/v4.mjs +1 -1
- package/build/uuid/v5.d.mts +61 -0
- package/build/uuid/v5.d.mts.map +1 -0
- package/build/uuid/v5.mjs +2 -0
- package/build/uuid/v5.mjs.map +1 -0
- package/build/uuid/v7.d.mts +2 -2
- package/build/uuid/v7.mjs +1 -1
- package/build/uuid/v7.mjs.map +1 -1
- package/build/uuid-Q8CMAbBu.mjs +2 -0
- package/build/uuid-Q8CMAbBu.mjs.map +1 -0
- package/build/xid/xid.d.mts +2 -2
- package/build/xid/xid.mjs.map +1 -1
- package/package.json +11 -3
- package/src/common/timestamp.ts +4 -4
- package/src/ksuid/ksuid.ts +3 -3
- package/src/nanoid/nanoid.ts +3 -3
- package/src/objectid/objectid.ts +3 -3
- package/src/typeid/typeid.ts +25 -44
- package/src/uuid/common/sha1.ts +102 -0
- package/src/uuid/common/uuid.ts +9 -3
- package/src/uuid/v5.ts +208 -0
- package/src/uuid/v7.ts +3 -3
- package/src/xid/xid.ts +3 -3
- package/build/uuid-BPebYihz.mjs +0 -2
- package/build/uuid-BPebYihz.mjs.map +0 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal synchronous SHA-1 (FIPS 180-4) for name-based UUIDs.
|
|
3
|
+
*
|
|
4
|
+
* Web Crypto's `digest()` is async and every uniku generator is synchronous,
|
|
5
|
+
* so `uuid/v5` carries its own SHA-1 instead of returning a promise or adding a
|
|
6
|
+
* dependency. It serves identity only, never as a security primitive.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const W = new Int32Array(80)
|
|
10
|
+
|
|
11
|
+
/** Size of the buffer SHA-1 needs to pad a `length`-byte message: a multiple of 64 with room for 9 more bytes. */
|
|
12
|
+
export function sha1PaddedLength(length: number): number {
|
|
13
|
+
return (Math.floor((length + 8) / 64) + 1) * 64
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hash `msg[0, length)` and write the first 16 digest bytes to `out[offset, offset + 16)`.
|
|
18
|
+
*
|
|
19
|
+
* `msg` is caller-owned scratch space of at least `sha1PaddedLength(length)` bytes.
|
|
20
|
+
* Bytes from `length` on are overwritten with padding, so stale contents are fine.
|
|
21
|
+
*/
|
|
22
|
+
export function sha1Digest128Into(msg: Uint8Array, length: number, out: Uint8Array, offset: number): void {
|
|
23
|
+
const padded = sha1PaddedLength(length)
|
|
24
|
+
msg[length] = 0x80
|
|
25
|
+
msg.fill(0, length + 1, padded - 8)
|
|
26
|
+
|
|
27
|
+
const bitsHigh = Math.floor(length / 0x20000000)
|
|
28
|
+
const bitsLow = (length << 3) >>> 0
|
|
29
|
+
msg[padded - 8] = bitsHigh >>> 24
|
|
30
|
+
msg[padded - 7] = bitsHigh >>> 16
|
|
31
|
+
msg[padded - 6] = bitsHigh >>> 8
|
|
32
|
+
msg[padded - 5] = bitsHigh
|
|
33
|
+
msg[padded - 4] = bitsLow >>> 24
|
|
34
|
+
msg[padded - 3] = bitsLow >>> 16
|
|
35
|
+
msg[padded - 2] = bitsLow >>> 8
|
|
36
|
+
msg[padded - 1] = bitsLow
|
|
37
|
+
|
|
38
|
+
let h0 = 0x67452301
|
|
39
|
+
let h1 = 0xefcdab89 | 0
|
|
40
|
+
let h2 = 0x98badcfe | 0
|
|
41
|
+
let h3 = 0x10325476
|
|
42
|
+
let h4 = 0xc3d2e1f0 | 0
|
|
43
|
+
|
|
44
|
+
for (let block = 0; block < padded; block += 64) {
|
|
45
|
+
for (let i = 0; i < 16; i += 1) {
|
|
46
|
+
const j = block + i * 4
|
|
47
|
+
W[i] = (msg[j] << 24) | (msg[j + 1] << 16) | (msg[j + 2] << 8) | msg[j + 3]
|
|
48
|
+
}
|
|
49
|
+
for (let i = 16; i < 80; i += 1) {
|
|
50
|
+
const x = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]
|
|
51
|
+
W[i] = (x << 1) | (x >>> 31)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let a = h0
|
|
55
|
+
let b = h1
|
|
56
|
+
let c = h2
|
|
57
|
+
let d = h3
|
|
58
|
+
let e = h4
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < 80; i += 1) {
|
|
61
|
+
let f: number
|
|
62
|
+
let k: number
|
|
63
|
+
if (i < 20) {
|
|
64
|
+
f = (b & c) | (~b & d)
|
|
65
|
+
k = 0x5a827999
|
|
66
|
+
} else if (i < 40) {
|
|
67
|
+
f = b ^ c ^ d
|
|
68
|
+
k = 0x6ed9eba1
|
|
69
|
+
} else if (i < 60) {
|
|
70
|
+
f = (b & c) | (b & d) | (c & d)
|
|
71
|
+
k = 0x8f1bbcdc | 0
|
|
72
|
+
} else {
|
|
73
|
+
f = b ^ c ^ d
|
|
74
|
+
k = 0xca62c1d6 | 0
|
|
75
|
+
}
|
|
76
|
+
const t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) | 0
|
|
77
|
+
e = d
|
|
78
|
+
d = c
|
|
79
|
+
c = (b << 30) | (b >>> 2)
|
|
80
|
+
b = a
|
|
81
|
+
a = t
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
h0 = (h0 + a) | 0
|
|
85
|
+
h1 = (h1 + b) | 0
|
|
86
|
+
h2 = (h2 + c) | 0
|
|
87
|
+
h3 = (h3 + d) | 0
|
|
88
|
+
h4 = (h4 + e) | 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
writeWord(out, offset, h0)
|
|
92
|
+
writeWord(out, offset + 4, h1)
|
|
93
|
+
writeWord(out, offset + 8, h2)
|
|
94
|
+
writeWord(out, offset + 12, h3)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function writeWord(out: Uint8Array, offset: number, word: number): void {
|
|
98
|
+
out[offset] = word >>> 24
|
|
99
|
+
out[offset + 1] = word >>> 16
|
|
100
|
+
out[offset + 2] = word >>> 8
|
|
101
|
+
out[offset + 3] = word
|
|
102
|
+
}
|
package/src/uuid/common/uuid.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BufferError, ParseError } from '../../errors'
|
|
2
|
+
import type { IdGenerator } from '../../generators'
|
|
2
3
|
import { hexValue } from './hex'
|
|
3
4
|
|
|
4
5
|
const UUID_BYTE_LENGTH = 16
|
|
@@ -135,9 +136,14 @@ export function formatUuidUnchecked(bytes: Uint8Array): string {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
export function parseUuid(value: string): Uint8Array {
|
|
139
|
+
return parseUuidFor(value, 'uuid')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Parse a UUID string of any version, attributing errors to `strategy`. */
|
|
143
|
+
export function parseUuidFor(value: string, strategy: IdGenerator): Uint8Array {
|
|
138
144
|
if (value.length !== UUID_STRING_LENGTH) {
|
|
139
145
|
throw new ParseError('INVALID_LENGTH', `UUID string must be 36 characters, got ${value.length}`, {
|
|
140
|
-
strategy
|
|
146
|
+
strategy,
|
|
141
147
|
})
|
|
142
148
|
}
|
|
143
149
|
|
|
@@ -146,7 +152,7 @@ export function parseUuid(value: string): Uint8Array {
|
|
|
146
152
|
throw new ParseError(
|
|
147
153
|
'INVALID_FORMAT',
|
|
148
154
|
`UUID string has invalid separators at positions 8, 13, 18, 23. Received: "${value}"`,
|
|
149
|
-
{ strategy
|
|
155
|
+
{ strategy },
|
|
150
156
|
)
|
|
151
157
|
}
|
|
152
158
|
|
|
@@ -161,7 +167,7 @@ export function parseUuid(value: string): Uint8Array {
|
|
|
161
167
|
const nibble = hexValue(value.charCodeAt(i))
|
|
162
168
|
if (nibble === -1) {
|
|
163
169
|
throw new ParseError('INVALID_CHAR', `UUID string contains invalid hex character at position ${i}`, {
|
|
164
|
-
strategy
|
|
170
|
+
strategy,
|
|
165
171
|
})
|
|
166
172
|
}
|
|
167
173
|
|
package/src/uuid/v5.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { isWritableRange } from '../common/validation'
|
|
2
|
+
import { BufferError, InvalidInputError } from '../errors'
|
|
3
|
+
import { sha1Digest128Into, sha1PaddedLength } from './common/sha1'
|
|
4
|
+
import { formatUuid, formatUuidUnchecked, parseUuid } from './common/uuid'
|
|
5
|
+
|
|
6
|
+
const UUID_BYTES = 16
|
|
7
|
+
// Names that fit hash in this reused buffer; larger ones get a one-off allocation.
|
|
8
|
+
const SCRATCH_BYTES = 512
|
|
9
|
+
const scratch = new Uint8Array(SCRATCH_BYTES)
|
|
10
|
+
const scratchName = scratch.subarray(UUID_BYTES)
|
|
11
|
+
const reusableBuf = new Uint8Array(UUID_BYTES)
|
|
12
|
+
const encoder = /*@__PURE__*/ new TextEncoder()
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The input a UUID v5 is derived from: a string, hashed as UTF-8 without Unicode
|
|
16
|
+
* normalization, or raw bytes.
|
|
17
|
+
*/
|
|
18
|
+
export type UuidV5Name = string | Uint8Array
|
|
19
|
+
|
|
20
|
+
/** A namespace UUID, as a UUID string or its 16 canonical bytes. */
|
|
21
|
+
export type UuidV5Namespace = string | Uint8Array
|
|
22
|
+
|
|
23
|
+
/** Derives UUID v5 values for one namespace. Returned by `uuidv5.namespace()`. */
|
|
24
|
+
export type UuidV5Deriver = {
|
|
25
|
+
/** Derive the UUID v5 string for a name. */
|
|
26
|
+
(name: UuidV5Name, buf?: undefined, offset?: number): string
|
|
27
|
+
/** Derive the UUID v5 for a name and write its 16 canonical bytes into a caller-owned buffer. */
|
|
28
|
+
<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf: TBuf, offset?: number): TBuf
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type UuidV5 = {
|
|
32
|
+
/** Derive the UUID v5 string for a name within a namespace. */
|
|
33
|
+
(name: UuidV5Name, namespace: UuidV5Namespace, buf?: undefined, offset?: number): string
|
|
34
|
+
/** Derive the UUID v5 for a name within a namespace and write its 16 canonical bytes into a caller-owned buffer. */
|
|
35
|
+
<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, namespace: UuidV5Namespace, buf: TBuf, offset?: number): TBuf
|
|
36
|
+
/** Validate and parse a namespace once, and return a function that derives IDs within it. */
|
|
37
|
+
namespace(namespace: UuidV5Namespace): UuidV5Deriver
|
|
38
|
+
/** Convert a UUID v5 string to its canonical 16-byte representation. */
|
|
39
|
+
toBytes(id: string): Uint8Array
|
|
40
|
+
/** Convert 16 canonical UUID bytes to a UUID v5 string. */
|
|
41
|
+
fromBytes(bytes: Uint8Array): string
|
|
42
|
+
/** Return whether a value is a syntactically valid UUID v5 string. */
|
|
43
|
+
isValid(id: unknown): id is string
|
|
44
|
+
/** The RFC 9562 namespace for fully qualified domain names. */
|
|
45
|
+
DNS: string
|
|
46
|
+
/** The RFC 9562 namespace for URLs. */
|
|
47
|
+
URL: string
|
|
48
|
+
/** The RFC 9562 namespace for ISO object identifiers (OIDs). */
|
|
49
|
+
OID: string
|
|
50
|
+
/** The RFC 9562 namespace for X.500 distinguished names. */
|
|
51
|
+
X500: string
|
|
52
|
+
/** The nil UUID (all zeros) */
|
|
53
|
+
NIL: string
|
|
54
|
+
/** The max UUID (all ones) */
|
|
55
|
+
MAX: string
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const UUID_V5_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
|
59
|
+
|
|
60
|
+
function parseNamespace(namespace: UuidV5Namespace): Uint8Array {
|
|
61
|
+
if (typeof namespace === 'string') {
|
|
62
|
+
return parseUuid(namespace)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (namespace.length !== UUID_BYTES) {
|
|
66
|
+
throw new InvalidInputError(
|
|
67
|
+
'BYTES_INVALID_LENGTH',
|
|
68
|
+
`UUID v5 namespace must be exactly ${UUID_BYTES} bytes, got ${namespace.length}`,
|
|
69
|
+
{ strategy: 'uuid' },
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return namespace
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function writeV5BytesUnchecked(namespace: Uint8Array, name: UuidV5Name, out: Uint8Array, offset: number): void {
|
|
77
|
+
let msg: Uint8Array
|
|
78
|
+
let length: number
|
|
79
|
+
|
|
80
|
+
if (typeof name === 'string') {
|
|
81
|
+
// UTF-8 needs at most 3 bytes per UTF-16 code unit.
|
|
82
|
+
const capacity = sha1PaddedLength(UUID_BYTES + name.length * 3)
|
|
83
|
+
msg = capacity <= SCRATCH_BYTES ? scratch : new Uint8Array(capacity)
|
|
84
|
+
const nameRegion = msg === scratch ? scratchName : msg.subarray(UUID_BYTES)
|
|
85
|
+
length = UUID_BYTES + encoder.encodeInto(name, nameRegion).written
|
|
86
|
+
} else {
|
|
87
|
+
length = UUID_BYTES + name.length
|
|
88
|
+
const capacity = sha1PaddedLength(length)
|
|
89
|
+
msg = capacity <= SCRATCH_BYTES ? scratch : new Uint8Array(capacity)
|
|
90
|
+
msg.set(name, UUID_BYTES)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Name and namespace are copied before `out` is written, so either may alias it.
|
|
94
|
+
msg.set(namespace)
|
|
95
|
+
sha1Digest128Into(msg, length, out, offset)
|
|
96
|
+
|
|
97
|
+
// Set RFC 9562 version (5) and variant (10xx) bits.
|
|
98
|
+
out[offset + 6] = (out[offset + 6] & 0x0f) | 0x50
|
|
99
|
+
out[offset + 8] = (out[offset + 8] & 0x3f) | 0x80
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function derive<TBuf extends Uint8Array = Uint8Array>(
|
|
103
|
+
namespace: Uint8Array,
|
|
104
|
+
name: UuidV5Name,
|
|
105
|
+
buf?: TBuf,
|
|
106
|
+
offset?: number,
|
|
107
|
+
): string | TBuf {
|
|
108
|
+
if (!buf) {
|
|
109
|
+
writeV5BytesUnchecked(namespace, name, reusableBuf, 0)
|
|
110
|
+
return formatUuidUnchecked(reusableBuf)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const outputOffset = offset ?? 0
|
|
114
|
+
if (!isWritableRange(buf, outputOffset, UUID_BYTES)) {
|
|
115
|
+
throw new BufferError(
|
|
116
|
+
'BUFFER_OUT_OF_BOUNDS',
|
|
117
|
+
`UUID byte range ${outputOffset}:${outputOffset + UUID_BYTES - 1} is out of buffer bounds`,
|
|
118
|
+
{ strategy: 'uuid' },
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
writeV5BytesUnchecked(namespace, name, buf, outputOffset)
|
|
123
|
+
return buf
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* Overload: no buffer => return a UUID string.
|
|
128
|
+
*/
|
|
129
|
+
function v5(name: UuidV5Name, namespace: UuidV5Namespace, buf?: undefined, offset?: number): string
|
|
130
|
+
/*
|
|
131
|
+
* Overload: caller provides a buffer slice to fill with UUID bytes.
|
|
132
|
+
*/
|
|
133
|
+
function v5<TBuf extends Uint8Array = Uint8Array>(
|
|
134
|
+
name: UuidV5Name,
|
|
135
|
+
namespace: UuidV5Namespace,
|
|
136
|
+
buf: TBuf,
|
|
137
|
+
offset?: number,
|
|
138
|
+
): TBuf
|
|
139
|
+
function v5<TBuf extends Uint8Array = Uint8Array>(
|
|
140
|
+
name: UuidV5Name,
|
|
141
|
+
namespace: UuidV5Namespace,
|
|
142
|
+
buf?: TBuf,
|
|
143
|
+
offset?: number,
|
|
144
|
+
): string | TBuf {
|
|
145
|
+
return derive(parseNamespace(namespace), name, buf, offset)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function namespace(namespace: UuidV5Namespace): UuidV5Deriver {
|
|
149
|
+
// Copy caller bytes so later mutation cannot change derived IDs. `new Uint8Array()` always copies,
|
|
150
|
+
// unlike `.slice()` on a Node.js Buffer, which returns a view of the same memory.
|
|
151
|
+
const namespaceBytes =
|
|
152
|
+
typeof namespace === 'string' ? parseUuid(namespace) : new Uint8Array(parseNamespace(namespace))
|
|
153
|
+
|
|
154
|
+
function deriver(name: UuidV5Name, buf?: undefined, offset?: number): string
|
|
155
|
+
function deriver<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf: TBuf, offset?: number): TBuf
|
|
156
|
+
function deriver<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf?: TBuf, offset?: number): string | TBuf {
|
|
157
|
+
return derive(namespaceBytes, name, buf, offset)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return deriver
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isValid(id: unknown): id is string {
|
|
164
|
+
return typeof id === 'string' && UUID_V5_REGEX.test(id)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Derive a UUID v5 from a name and a namespace, or write its bytes into a buffer.
|
|
169
|
+
*
|
|
170
|
+
* UUID v5 (RFC 9562 section 5.5) is name-based: it hashes the namespace and
|
|
171
|
+
* name with SHA-1, so the same inputs give the same ID in every process, on
|
|
172
|
+
* every runtime, with no state to keep. Use it to derive a stable ID from an
|
|
173
|
+
* identifier you already hold.
|
|
174
|
+
*
|
|
175
|
+
* String names are hashed as UTF-8 without Unicode normalization. A UUID v5 is
|
|
176
|
+
* an identifier, not a secret: anyone who knows or can guess the name and
|
|
177
|
+
* namespace can compute it, and SHA-1 lets someone who controls the names craft
|
|
178
|
+
* two that collide.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* import { uuidv5 } from 'uniku/uuid/v5'
|
|
183
|
+
*
|
|
184
|
+
* const id = uuidv5('www.example.com', uuidv5.DNS)
|
|
185
|
+
* // => "2ed6657d-e927-568b-95e1-2665a8aea6a2"
|
|
186
|
+
*
|
|
187
|
+
* // Parse a namespace once and derive many IDs within it
|
|
188
|
+
* const sessionId = uuidv5.namespace('1b671a64-40d5-491e-99b0-da01ff1f3341')
|
|
189
|
+
* sessionId('tool-a:3f2c')
|
|
190
|
+
*
|
|
191
|
+
* // Validate
|
|
192
|
+
* uuidv5.isValid(id) // true
|
|
193
|
+
* ```
|
|
194
|
+
*/
|
|
195
|
+
export const uuidv5: UuidV5 = Object.assign(v5, {
|
|
196
|
+
namespace,
|
|
197
|
+
toBytes: parseUuid,
|
|
198
|
+
fromBytes: formatUuid,
|
|
199
|
+
isValid,
|
|
200
|
+
DNS: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
|
|
201
|
+
URL: '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
|
|
202
|
+
OID: '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
|
|
203
|
+
X500: '6ba7b814-9dad-11d1-80b4-00c04fd430c8',
|
|
204
|
+
NIL: '00000000-0000-0000-0000-000000000000',
|
|
205
|
+
MAX: 'ffffffff-ffff-ffff-ffff-ffffffffffff',
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
export { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'
|
package/src/uuid/v7.ts
CHANGED
|
@@ -19,11 +19,11 @@ export type UuidV7Options = {
|
|
|
19
19
|
*/
|
|
20
20
|
counter?: number
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Deprecated alias for `counter`; unsigned 32-bit sequence value.
|
|
23
23
|
*
|
|
24
|
-
* @deprecated Use `counter` instead. Will be removed
|
|
24
|
+
* @deprecated Use `counter` instead. Will be removed in `1.0.0-rc.1`.
|
|
25
25
|
*/
|
|
26
|
-
// TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
|
|
26
|
+
// TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
|
|
27
27
|
seq?: number
|
|
28
28
|
}
|
|
29
29
|
|
package/src/xid/xid.ts
CHANGED
|
@@ -26,11 +26,11 @@ export type XidOptions = {
|
|
|
26
26
|
*/
|
|
27
27
|
msecs?: number
|
|
28
28
|
/**
|
|
29
|
-
* Unix timestamp in seconds.
|
|
29
|
+
* Deprecated alias for `msecs`; Unix timestamp in seconds.
|
|
30
30
|
*
|
|
31
|
-
* @deprecated Use `msecs` instead. Will be removed
|
|
31
|
+
* @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.
|
|
32
32
|
*/
|
|
33
|
-
// TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
|
|
33
|
+
// TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
|
|
34
34
|
secs?: number
|
|
35
35
|
/** 24-bit counter. Explicit values do not consume shared state. */
|
|
36
36
|
counter?: number
|
package/build/uuid-BPebYihz.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{BufferError as e,ParseError as t}from"./errors.mjs";function n(e){return e>=48&&e<=57?e-48:e>=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:-1}const r=[0,0,1,1,2,2,3,3,-1,4,4,5,5,-1,6,6,7,7,-1,8,8,9,9,-1,10,10,11,11,12,12,13,13,14,14,15,15],i=[!0,!1,!0,!1,!0,!1,!0,!1,!1,!0,!1,!0,!1,!1,!0,!1,!0,!1,!1,!0,!1,!0,!1,!1,!0,!1,!0,!1,!0,!1,!0,!1,!0,!1,!0,!1],a=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,`0`));function o(t){if(t.length!==16)throw new e(`BYTES_INVALID_LENGTH`,`UUID bytes must be exactly 16 bytes, got ${t.length}`,{strategy:`uuid`});return s(t)}function s(e){return a[e[0]]+a[e[1]]+a[e[2]]+a[e[3]]+`-`+a[e[4]]+a[e[5]]+`-`+a[e[6]]+a[e[7]]+`-`+a[e[8]]+a[e[9]]+`-`+a[e[10]]+a[e[11]]+a[e[12]]+a[e[13]]+a[e[14]]+a[e[15]]}function c(e){if(e.length!==36)throw new t(`INVALID_LENGTH`,`UUID string must be 36 characters, got ${e.length}`,{strategy:`uuid`});if(e[8]!==`-`||e[13]!==`-`||e[18]!==`-`||e[23]!==`-`)throw new t(`INVALID_FORMAT`,`UUID string has invalid separators at positions 8, 13, 18, 23. Received: "${e}"`,{strategy:`uuid`});let a=new Uint8Array(16);for(let o=0;o<36;o+=1){let s=r[o];if(s===-1)continue;let c=n(e.charCodeAt(o));if(c===-1)throw new t(`INVALID_CHAR`,`UUID string contains invalid hex character at position ${o}`,{strategy:`uuid`});i[o]?a[s]=c<<4:a[s]|=c}return a}export{s as n,c as r,o as t};
|
|
2
|
-
//# sourceMappingURL=uuid-BPebYihz.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"uuid-BPebYihz.mjs","names":[],"sources":["../src/uuid/common/hex.ts","../src/uuid/common/uuid.ts"],"sourcesContent":["// ASCII code ranges for hex character validation\nconst ASCII_0 = 48 // '0'\nconst ASCII_9 = 57 // '9'\nconst ASCII_A = 65 // 'A'\nconst ASCII_F = 70 // 'F'\nconst ASCII_a = 97 // 'a'\nconst ASCII_f = 102 // 'f'\n\n/**\n * Convert a hex character ASCII code to its numeric value (0-15).\n * Returns -1 for invalid hex characters.\n */\nexport function hexValue(code: number): number {\n if (code >= ASCII_0 && code <= ASCII_9) {\n return code - ASCII_0\n }\n if (code >= ASCII_A && code <= ASCII_F) {\n return code - ASCII_A + 10\n }\n if (code >= ASCII_a && code <= ASCII_f) {\n return code - ASCII_a + 10\n }\n return -1 // Invalid hex character\n}\n","import { BufferError, ParseError } from '../../errors'\nimport { hexValue } from './hex'\n\nconst UUID_BYTE_LENGTH = 16\nconst UUID_STRING_LENGTH = 36\n\n// Mapping from UUID string position to byte index.\n// -1 indicates a dash position that should be skipped.\n// This avoids intermediate string allocations during parsing.\nconst UUID_CHAR_TO_BYTE: number[] = [\n 0,\n 0,\n 1,\n 1,\n 2,\n 2,\n 3,\n 3, // chars 0-7 → bytes 0-3\n -1, // char 8 is '-'\n 4,\n 4,\n 5,\n 5, // chars 9-12 → bytes 4-5\n -1, // char 13 is '-'\n 6,\n 6,\n 7,\n 7, // chars 14-17 → bytes 6-7\n -1, // char 18 is '-'\n 8,\n 8,\n 9,\n 9, // chars 19-22 → bytes 8-9\n -1, // char 23 is '-'\n 10,\n 10,\n 11,\n 11,\n 12,\n 12,\n 13,\n 13,\n 14,\n 14,\n 15,\n 15, // chars 24-35 → bytes 10-15\n]\n\n// Whether each position is the high nibble (true) or low nibble (false)\nconst UUID_CHAR_IS_HIGH: boolean[] = [\n true,\n false,\n true,\n false,\n true,\n false,\n true,\n false, // chars 0-7\n false, // dash (ignored)\n true,\n false,\n true,\n false, // chars 9-12\n false, // dash\n true,\n false,\n true,\n false, // chars 14-17\n false, // dash\n true,\n false,\n true,\n false, // chars 19-22\n false, // dash\n true,\n false,\n true,\n false,\n true,\n false,\n true,\n false,\n true,\n false,\n true,\n false, // chars 24-35\n]\n\n// Pre-computed lookup table for byte-to-hex conversion (0x00 -> \"00\", 0xff -> \"ff\")\n//\n// Note: this table must remain defined in the same module as `formatUuid`, otherwise the v8 optimizer\n// will cause a performance drop of ~36%.\nconst HEX_TABLE: string[] = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))\n\nexport function formatUuid(bytes: Uint8Array): string {\n if (bytes.length !== UUID_BYTE_LENGTH) {\n throw new BufferError(\n 'BYTES_INVALID_LENGTH',\n `UUID bytes must be exactly ${UUID_BYTE_LENGTH} bytes, got ${bytes.length}`,\n {\n strategy: 'uuid',\n },\n )\n }\n\n return formatUuidUnchecked(bytes)\n}\n\nexport function formatUuidUnchecked(bytes: Uint8Array): string {\n // Direct string concatenation - optimized for V8's string builder.\n // This approach avoids loop overhead and intermediate allocations.\n // See: https://github.com/uuidjs/uuid/pull/434\n return (\n HEX_TABLE[bytes[0]] +\n HEX_TABLE[bytes[1]] +\n HEX_TABLE[bytes[2]] +\n HEX_TABLE[bytes[3]] +\n '-' +\n HEX_TABLE[bytes[4]] +\n HEX_TABLE[bytes[5]] +\n '-' +\n HEX_TABLE[bytes[6]] +\n HEX_TABLE[bytes[7]] +\n '-' +\n HEX_TABLE[bytes[8]] +\n HEX_TABLE[bytes[9]] +\n '-' +\n HEX_TABLE[bytes[10]] +\n HEX_TABLE[bytes[11]] +\n HEX_TABLE[bytes[12]] +\n HEX_TABLE[bytes[13]] +\n HEX_TABLE[bytes[14]] +\n HEX_TABLE[bytes[15]]\n )\n}\n\nexport function parseUuid(value: string): Uint8Array {\n if (value.length !== UUID_STRING_LENGTH) {\n throw new ParseError('INVALID_LENGTH', `UUID string must be 36 characters, got ${value.length}`, {\n strategy: 'uuid',\n })\n }\n\n // Validate separator positions directly (more efficient than full loop)\n if (value[8] !== '-' || value[13] !== '-' || value[18] !== '-' || value[23] !== '-') {\n throw new ParseError(\n 'INVALID_FORMAT',\n `UUID string has invalid separators at positions 8, 13, 18, 23. Received: \"${value}\"`,\n { strategy: 'uuid' },\n )\n }\n\n // Parse bytes directly from UUID string without intermediate string allocations.\n // This avoids the 9 allocations (5 slices + 4 concatenations) of the naive approach.\n const bytes = new Uint8Array(UUID_BYTE_LENGTH)\n\n for (let i = 0; i < UUID_STRING_LENGTH; i += 1) {\n const byteIdx = UUID_CHAR_TO_BYTE[i]\n if (byteIdx === -1) continue // Skip dash positions\n\n const nibble = hexValue(value.charCodeAt(i))\n if (nibble === -1) {\n throw new ParseError('INVALID_CHAR', `UUID string contains invalid hex character at position ${i}`, {\n strategy: 'uuid',\n })\n }\n\n if (UUID_CHAR_IS_HIGH[i]) {\n bytes[byteIdx] = nibble << 4\n } else {\n bytes[byteIdx] |= nibble\n }\n }\n\n return bytes\n}\n"],"mappings":"2DAYA,SAAgB,EAAS,EAAsB,CAU7C,OATI,GAAQ,IAAW,GAAQ,GACtB,EAAO,GAEZ,GAAQ,IAAW,GAAQ,GACtB,EAAO,GAAU,GAEtB,GAAQ,IAAW,GAAQ,IACtB,EAAO,GAAU,GAEnB,EACT,CCpBA,MAMM,EAA8B,CAClC,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,GACA,EACA,EACA,EACA,EACA,GACA,EACA,EACA,EACA,EACA,GACA,EACA,EACA,EACA,EACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,EACF,EAGM,EAA+B,CACnC,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,GACA,EACF,EAMM,EAAsB,MAAM,KAAK,CAAE,OAAQ,GAAI,GAAI,EAAG,IAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,EAEjG,SAAgB,EAAW,EAA2B,CACpD,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EACR,uBACA,4CAA6D,EAAM,SACnE,CACE,SAAU,MACZ,CACF,EAGF,OAAO,EAAoB,CAAK,CAClC,CAEA,SAAgB,EAAoB,EAA2B,CAI7D,OACE,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,IACA,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,IACA,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,IACA,EAAU,EAAM,IAChB,EAAU,EAAM,IAChB,IACA,EAAU,EAAM,KAChB,EAAU,EAAM,KAChB,EAAU,EAAM,KAChB,EAAU,EAAM,KAChB,EAAU,EAAM,KAChB,EAAU,EAAM,IAEpB,CAEA,SAAgB,EAAU,EAA2B,CACnD,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EAAW,iBAAkB,0CAA0C,EAAM,SAAU,CAC/F,SAAU,MACZ,CAAC,EAIH,GAAI,EAAM,KAAO,KAAO,EAAM,MAAQ,KAAO,EAAM,MAAQ,KAAO,EAAM,MAAQ,IAC9E,MAAM,IAAI,EACR,iBACA,6EAA6E,EAAM,GACnF,CAAE,SAAU,MAAO,CACrB,EAKF,IAAM,EAAQ,IAAI,WAAW,EAAgB,EAE7C,IAAK,IAAI,EAAI,EAAG,EAAI,GAAoB,GAAK,EAAG,CAC9C,IAAM,EAAU,EAAkB,GAClC,GAAI,IAAY,GAAI,SAEpB,IAAM,EAAS,EAAS,EAAM,WAAW,CAAC,CAAC,EAC3C,GAAI,IAAW,GACb,MAAM,IAAI,EAAW,eAAgB,0DAA0D,IAAK,CAClG,SAAU,MACZ,CAAC,EAGC,EAAkB,GACpB,EAAM,GAAW,GAAU,EAE3B,EAAM,IAAY,CAEtB,CAEA,OAAO,CACT"}
|