uniku 0.6.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 +6 -3
- 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.mjs +1 -1
- package/build/uuid-Q8CMAbBu.mjs +2 -0
- package/build/uuid-Q8CMAbBu.mjs.map +1 -0
- package/package.json +11 -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/build/uuid-BPebYihz.mjs +0 -2
- package/build/uuid-BPebYihz.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://jkomyno.github.io/uniku/)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Eleven ID strategies behind one consistent, type-safe API. The fastest implementation of each benchmarked format in our [current CI benchmark](https://jkomyno.github.io/uniku/docs/guides/performance/).
|
|
10
10
|
|
|
11
11
|
> **uniku** */uˈniːku/*: Maltese for "unique"
|
|
12
12
|
|
|
@@ -21,8 +21,8 @@ const id = uuidv7()
|
|
|
21
21
|
|
|
22
22
|
## Why `uniku`?
|
|
23
23
|
|
|
24
|
-
- **Fastest across the benchmark suite.** Every strategy beats its dedicated npm alternative in the current isolated-process CI benchmark, from 1.2× for UUID v4 and ObjectID to 116× for ULID.
|
|
25
|
-
- **
|
|
24
|
+
- **Fastest across the benchmark suite.** Every benchmarked strategy beats its dedicated npm alternative in the current isolated-process CI benchmark, from 1.2× for UUID v4 and ObjectID to 116× for ULID.
|
|
25
|
+
- **Eleven strategies, one type-safe API.** UUID v4/v5/v7, ULID, TypeID, CUID v2, Nanoid, KSUID, ObjectID, XID, and TSID each expose a callable generator with matching typed helpers.
|
|
26
26
|
- **Portable by default.** Runs on Node.js, Bun, Deno, browsers, edge runtimes, and Cloudflare Workers using `globalThis.crypto`.
|
|
27
27
|
- **Tree-shakeable entry points.** Import only the generator you use; the package root is intentionally not exported.
|
|
28
28
|
- **Useful at system boundaries.** Validate unknown input, convert canonical binary formats to bytes, or write directly into a caller-owned buffer.
|
|
@@ -82,6 +82,7 @@ result === destination
|
|
|
82
82
|
| --- | --- |
|
|
83
83
|
| `uuid/v7`, `ulid`, `ksuid`, `objectid`, `xid` | Callable, buffer write, `toBytes()`, `fromBytes()`, `timestamp()`, `isValid()`, `NIL`, `MAX` |
|
|
84
84
|
| `uuid/v4` | Callable, buffer write, `toBytes()`, `fromBytes()`, `isValid()`, `NIL`, `MAX` |
|
|
85
|
+
| `uuid/v5` | Name-based callable, buffer write, `namespace()`, `toBytes()`, `fromBytes()`, `isValid()`, `DNS`, `URL`, `OID`, `X500`, `NIL`, `MAX` |
|
|
85
86
|
| `typeid` | Prefix-first callable, buffer write, `toBytes()`, `fromBytes()`, `toUuid()`, `fromUuid()`, `timestamp()`, `prefix()`, `suffix()`, `isValid()` |
|
|
86
87
|
| `tsid` | `bigint` primary value, buffer write, `toBytes()`, `fromBytes()`, `toString()`, `fromString()`, `timestamp()`, `isValid()`, `NIL`, `MAX` |
|
|
87
88
|
| `cuid/v2`, `nanoid` | Callable and `isValid()` only |
|
|
@@ -92,6 +93,7 @@ There are no barrel exports:
|
|
|
92
93
|
|
|
93
94
|
```ts
|
|
94
95
|
import { uuidv4 } from 'uniku/uuid/v4'
|
|
96
|
+
import { uuidv5 } from 'uniku/uuid/v5'
|
|
95
97
|
import { ulid } from 'uniku/ulid'
|
|
96
98
|
import { typeid } from 'uniku/typeid'
|
|
97
99
|
import { cuidv2 } from 'uniku/cuid/v2'
|
|
@@ -114,6 +116,7 @@ import { tsid } from 'uniku/tsid'
|
|
|
114
116
|
| Go rs/xid compatibility | XID | Matches rs/xid's text and binary representation |
|
|
115
117
|
| Native `BIGINT` storage | TSID | A sortable 64-bit integer rather than a UUID-shaped string |
|
|
116
118
|
| Broad standards compatibility | UUID v4 | The conventional random UUID format |
|
|
119
|
+
| An ID derived from something you already have | UUID v5 | The same name and namespace always give the same UUID |
|
|
117
120
|
|
|
118
121
|
The [choosing guide](https://jkomyno.github.io/uniku/docs/guides/choosing-an-id/) covers ordering, timestamp leakage, storage boundaries, and format-specific trade-offs.
|
|
119
122
|
|
|
@@ -6,22 +6,25 @@ type TypeidOptions = UuidV7Options;
|
|
|
6
6
|
type Typeid = {
|
|
7
7
|
/** Generate a TypeID from a lowercase entity prefix and a UUID v7 suffix. */(prefix: string, options?: TypeidOptions): string; /** Generate a TypeID with explicit options or write its 16 canonical UUID v7 bytes into a caller-owned buffer. */
|
|
8
8
|
<TBuf extends Uint8Array = Uint8Array>(prefix: string, options: TypeidOptions | undefined, buf: TBuf, offset?: number): TBuf; /** Generate a TypeID string with optional timestamp, counter, or random bytes. */
|
|
9
|
-
(prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string; /** Convert a TypeID's
|
|
10
|
-
toBytes(id: string): Uint8Array; /** Build a TypeID from a prefix and
|
|
11
|
-
fromBytes(prefix: string, bytes: Uint8Array): string; /** Convert a TypeID to its UUID
|
|
12
|
-
toUuid(id: string): string; /** Build a TypeID from a prefix and UUID
|
|
13
|
-
fromUuid(prefix: string, uuid: string): string; /** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. */
|
|
9
|
+
(prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string; /** Convert a TypeID's suffix to its 16 UUID bytes. */
|
|
10
|
+
toBytes(id: string): Uint8Array; /** Build a TypeID from a prefix and 16 UUID bytes of any version. */
|
|
11
|
+
fromBytes(prefix: string, bytes: Uint8Array): string; /** Convert a TypeID to its UUID string. */
|
|
12
|
+
toUuid(id: string): string; /** Build a TypeID from a prefix and a UUID string of any version. */
|
|
13
|
+
fromUuid(prefix: string, uuid: string): string; /** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. Throws for a TypeID that does not wrap a UUID v7. */
|
|
14
14
|
timestamp(id: string): number; /** Read a TypeID's entity prefix. */
|
|
15
|
-
prefix(id: string): string; /** Read a TypeID's
|
|
16
|
-
suffix(id: string): string; /** Return whether a value is a syntactically valid TypeID. */
|
|
15
|
+
prefix(id: string): string; /** Read a TypeID's base32 suffix. */
|
|
16
|
+
suffix(id: string): string; /** Return whether a value is a syntactically valid TypeID, whatever UUID version it wraps. */
|
|
17
17
|
isValid(id: unknown): id is string;
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
20
|
* Generate a TypeID string backed by UUID v7.
|
|
21
21
|
*
|
|
22
22
|
* TypeID combines a lowercase snake_case type prefix with a 26-character
|
|
23
|
-
* modified base32 encoding of a UUID
|
|
24
|
-
*
|
|
23
|
+
* modified base32 encoding of a UUID, producing domain identifiers such as
|
|
24
|
+
* `user_01h2xcejqtf2nbrexx3vqjhp41`. Generated TypeIDs always wrap a UUID v7,
|
|
25
|
+
* so they sort by creation time. As the TypeID specification recommends, the
|
|
26
|
+
* conversion and validation functions accept any 128-bit value, such as a
|
|
27
|
+
* name-based UUID v5.
|
|
25
28
|
*
|
|
26
29
|
* @example
|
|
27
30
|
* ```ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeid.d.mts","names":[],"sources":["../../src/typeid/typeid.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"typeid.d.mts","names":[],"sources":["../../src/typeid/typeid.ts"],"mappings":";;;;KAMY,aAAA,GAAgB,aAAA;AAAA,KAEhB,MAAA;gFAET,MAAA,UAAgB,OAAA,GAAU,aAAA,WAJjB;EAAA,cAMI,UAAA,GAAa,UAAA,EACzB,MAAA,UACA,OAAA,EAAS,aAAA,cACT,GAAA,EAAK,IAAA,EACL,MAAA,YACC,IAAA,EAXuB;EAAA,CAazB,MAAA,UAAgB,OAAA,GAAU,aAAA,EAAe,GAAA,cAAiB,MAAA,oBAX7D;EAaE,OAAA,CAAQ,EAAA,WAAa,UAAA;EAErB,SAAA,CAAU,MAAA,UAAgB,KAAA,EAAO,UAAA;EAEjC,MAAA,CAAO,EAAA;EAEP,QAAA,CAAS,MAAA,UAAgB,IAAA;EAEzB,SAAA,CAAU,EAAA;EAEV,MAAA,CAAO,EAAA,mBAR0B;EAUjC,MAAA,CAAO,EAAA;EAEP,OAAA,CAAQ,EAAA,YAAc,EAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;cA4TX,MAAA,EAAQ,MAAA"}
|
package/build/typeid/typeid.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{n as e,t}from"../validation-CTNpXm94.mjs";import{BufferError as n,InvalidInputError as r,ParseError as i,UniqueIdError as a}from"../errors.mjs";import{
|
|
1
|
+
import{n as e,t}from"../validation-CTNpXm94.mjs";import{BufferError as n,InvalidInputError as r,ParseError as i,UniqueIdError as a}from"../errors.mjs";import{i as o,n as s}from"../uuid-Q8CMAbBu.mjs";import{uuidv7 as c}from"../uuid/v7.mjs";const l=0xffffffffffff,u=4294967295,d=`0123456789abcdefghjkmnpqrstvwxyz`,f=Array.from({length:128},()=>-1);for(let e=0;e<32;e+=1)f[d.charCodeAt(e)]=e;function p(e){if(e.length!==0){if(e.length>63)throw new r(`PREFIX_TOO_LONG`,`TypeID prefix must be at most 63 characters`,{strategy:`typeid`});for(let t=0;t<e.length;t+=1){let n=e.charCodeAt(t),i=n>=97&&n<=122;if(!i&&n!==95)throw new r(`PREFIX_INVALID_CHAR`,`TypeID prefix must contain only lowercase ASCII letters and underscores`,{strategy:`typeid`});if((t===0||t===e.length-1)&&!i)throw new r(`PREFIX_INVALID_BOUNDARY`,`TypeID prefix must start and end with a-z`,{strategy:`typeid`})}}}function m(e,t){let n=e.charCodeAt(t),r=n<f.length?f[n]:-1;if(r===-1)throw new i(`INVALID_CHAR`,`TypeID suffix contains invalid character at position ${t}`,{strategy:`typeid`});return r}function h(e){if(e.length!==26)throw new i(`INVALID_LENGTH`,`TypeID suffix must be 26 characters, got ${e.length}`,{strategy:`typeid`});if(e[0]>`7`)throw new i(`VALUE_OUT_OF_RANGE`,`TypeID suffix must encode a 128-bit value`,{strategy:`typeid`})}function g(e){if(e.length!==16)throw new r(`BYTES_INVALID_LENGTH`,`UUID bytes must be 16 bytes, got ${e.length}`,{strategy:`typeid`});return d[(e[0]&224)>>5]+d[e[0]&31]+d[(e[1]&248)>>3]+d[(e[1]&7)<<2|(e[2]&192)>>6]+d[(e[2]&62)>>1]+d[(e[2]&1)<<4|(e[3]&240)>>4]+d[(e[3]&15)<<1|(e[4]&128)>>7]+d[(e[4]&124)>>2]+d[(e[4]&3)<<3|(e[5]&224)>>5]+d[e[5]&31]+d[(e[6]&248)>>3]+d[(e[6]&7)<<2|(e[7]&192)>>6]+d[(e[7]&62)>>1]+d[(e[7]&1)<<4|(e[8]&240)>>4]+d[(e[8]&15)<<1|(e[9]&128)>>7]+d[(e[9]&124)>>2]+d[(e[9]&3)<<3|(e[10]&224)>>5]+d[e[10]&31]+d[(e[11]&248)>>3]+d[(e[11]&7)<<2|(e[12]&192)>>6]+d[(e[12]&62)>>1]+d[(e[12]&1)<<4|(e[13]&240)>>4]+d[(e[13]&15)<<1|(e[14]&128)>>7]+d[(e[14]&124)>>2]+d[(e[14]&3)<<3|(e[15]&224)>>5]+d[e[15]&31]}function _(e){h(e);let t=Array(26);for(let n=0;n<26;n+=1)t[n]=m(e,n);let n=new Uint8Array(16);return n[0]=t[0]<<5|t[1],n[1]=t[2]<<3|t[3]>>2,n[2]=(t[3]&3)<<6|t[4]<<1|t[5]>>4,n[3]=(t[5]&15)<<4|t[6]>>1,n[4]=(t[6]&1)<<7|t[7]<<2|t[8]>>3,n[5]=(t[8]&7)<<5|t[9],n[6]=t[10]<<3|t[11]>>2,n[7]=(t[11]&3)<<6|t[12]<<1|t[13]>>4,n[8]=(t[13]&15)<<4|t[14]>>1,n[9]=(t[14]&1)<<7|t[15]<<2|t[16]>>3,n[10]=(t[16]&7)<<5|t[17],n[11]=t[18]<<3|t[19]>>2,n[12]=(t[19]&3)<<6|t[20]<<1|t[21]>>4,n[13]=(t[21]&15)<<4|t[22]>>1,n[14]=(t[22]&1)<<7|t[23]<<2|t[24]>>3,n[15]=(t[24]&7)<<5|t[25],n}function v(e){let t=e.lastIndexOf(`_`);if(t===0)throw new i(`INVALID_FORMAT`,`TypeID must not start with "_"`,{strategy:`typeid`});let n=t===-1?``:e.slice(0,t),r=t===-1?e:e.slice(t+1);return p(n),{prefix:n,suffix:r,bytes:_(r)}}function y(e,t){return e===``?t:`${e}_${t}`}function b(e,t){return p(e),y(e,g(t))}function x(e){if(e[6]>>4!=7||(e[8]&192)!=128)throw new r(`UUID_NOT_V7`,`TypeID must wrap a UUID v7 to have a timestamp`,{strategy:`typeid`});let t=0;for(let n=0;n<6;n+=1)t=t*256+e[n];return t}function S(e){let n=e.msecs;if(n!==void 0&&!t(n,0,l))throw new r(`TIMESTAMP_OUT_OF_RANGE`,`Timestamp must be an integer between 0 and ${l}`,{strategy:`typeid`});if(e.counter!==void 0&&e.seq!==void 0)throw new r(`CONFLICTING_OPTIONS`,"Pass only one of `counter` or `seq`, not both",{strategy:`typeid`});let i=e.counter??e.seq;if(i!==void 0&&!t(i,0,u))throw new r(`COUNTER_OUT_OF_RANGE`,`Counter must be an integer between 0 and ${u}`,{strategy:`typeid`});let a=e.random;if(a&&a.length<16)throw new r(`RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 16`,{strategy:`typeid`})}function C(t,r,i,a=0){if(r!==void 0&&S(r),i){if(!e(i,a,16))throw new n(`BUFFER_OUT_OF_BOUNDS`,`TypeID byte range ${a}:${a+16-1} is out of buffer bounds`,{strategy:`typeid`});return p(t),c(r,i,a)}return b(t,c(r,new Uint8Array(16)))}function w(e){return v(e).bytes}function T(e,t){return b(e,t)}function E(e){return s(w(e))}function D(e,t){return b(e,o(t,`typeid`))}function O(e){return x(v(e).bytes)}function k(e){return v(e).prefix}function A(e){return v(e).suffix}function j(e){if(typeof e!=`string`)return!1;try{v(e)}catch{return!1}return!0}const M=Object.assign(C,{toBytes:w,fromBytes:T,toUuid:E,fromUuid:D,timestamp:O,prefix:k,suffix:A,isValid:j});export{n as BufferError,r as InvalidInputError,i as ParseError,a as UniqueIdError,M as typeid};
|
|
2
2
|
//# sourceMappingURL=typeid.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeid.mjs","names":[],"sources":["../../src/typeid/typeid.ts"],"sourcesContent":["import { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError, ParseError } from '../errors'\nimport type { UuidV7Options } from '../uuid/v7'\nimport { uuidv7 } from '../uuid/v7'\n\nexport type TypeidOptions = UuidV7Options\n\nexport type Typeid = {\n /** Generate a TypeID from a lowercase entity prefix and a UUID v7 suffix. */\n (prefix: string, options?: TypeidOptions): string\n /** Generate a TypeID with explicit options or write its 16 canonical UUID v7 bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options: TypeidOptions | undefined,\n buf: TBuf,\n offset?: number,\n ): TBuf\n /** Generate a TypeID string with optional timestamp, counter, or random bytes. */\n (prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string\n /** Convert a TypeID's UUID v7 suffix to its canonical 16-byte representation. */\n toBytes(id: string): Uint8Array\n /** Build a TypeID from a prefix and canonical UUID v7 bytes. */\n fromBytes(prefix: string, bytes: Uint8Array): string\n /** Convert a TypeID to its UUID v7 string. */\n toUuid(id: string): string\n /** Build a TypeID from a prefix and UUID v7 string. */\n fromUuid(prefix: string, uuid: string): string\n /** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. */\n timestamp(id: string): number\n /** Read a TypeID's entity prefix. */\n prefix(id: string): string\n /** Read a TypeID's UUID v7 suffix. */\n suffix(id: string): string\n /** Return whether a value is a syntactically valid TypeID. */\n isValid(id: unknown): id is string\n}\n\nconst TYPEID_SUFFIX_LENGTH = 26\nconst TYPEID_UUID_BYTE_LENGTH = 16\n// 48-bit UUID v7 timestamp capacity, mirrored from uuid/v7 alongside the\n// 32-bit counter capacity, so option validation is attributed to the typeid\n// boundary instead of leaking `strategy: 'uuid'` through delegation.\nconst MAX_MSECS = 0xffffffffffff\nconst MAX_COUNTER = 0xffffffff\nconst TYPEID_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\nconst BASE32_DECODE: number[] = Array.from({ length: 128 }, () => -1)\nfor (let i = 0; i < TYPEID_ALPHABET.length; i += 1) {\n BASE32_DECODE[TYPEID_ALPHABET.charCodeAt(i)] = i\n}\n\ntype ParsedTypeid = {\n prefix: string\n suffix: string\n bytes: Uint8Array\n}\n\nfunction assertValidPrefix(prefix: string): void {\n if (prefix.length === 0) {\n return\n }\n\n if (prefix.length > 63) {\n throw new InvalidInputError('PREFIX_TOO_LONG', 'TypeID prefix must be at most 63 characters', {\n strategy: 'typeid',\n })\n }\n\n for (let i = 0; i < prefix.length; i += 1) {\n const code = prefix.charCodeAt(i)\n const isLowercaseLetter = code >= 97 && code <= 122\n const isUnderscore = code === 95\n\n if (!isLowercaseLetter && !isUnderscore) {\n throw new InvalidInputError(\n 'PREFIX_INVALID_CHAR',\n 'TypeID prefix must contain only lowercase ASCII letters and underscores',\n { strategy: 'typeid' },\n )\n }\n\n if ((i === 0 || i === prefix.length - 1) && !isLowercaseLetter) {\n throw new InvalidInputError('PREFIX_INVALID_BOUNDARY', 'TypeID prefix must start and end with a-z', {\n strategy: 'typeid',\n })\n }\n }\n}\n\nfunction decodeSuffixValue(suffix: string, index: number): number {\n const code = suffix.charCodeAt(index)\n const value = code < BASE32_DECODE.length ? BASE32_DECODE[code] : -1\n\n if (value === -1) {\n throw new ParseError('INVALID_CHAR', `TypeID suffix contains invalid character at position ${index}`, {\n strategy: 'typeid',\n })\n }\n\n return value\n}\n\nfunction assertValidSuffixShape(suffix: string): void {\n if (suffix.length !== TYPEID_SUFFIX_LENGTH) {\n throw new ParseError('INVALID_LENGTH', `TypeID suffix must be 26 characters, got ${suffix.length}`, {\n strategy: 'typeid',\n })\n }\n\n if (suffix[0] > '7') {\n throw new ParseError('VALUE_OUT_OF_RANGE', 'TypeID suffix must encode a 128-bit value', {\n strategy: 'typeid',\n })\n }\n}\n\nfunction encodeBytesToSuffix(bytes: Uint8Array): string {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`, {\n strategy: 'typeid',\n })\n }\n\n return (\n TYPEID_ALPHABET[(bytes[0] & 0xe0) >> 5] +\n TYPEID_ALPHABET[bytes[0] & 0x1f] +\n TYPEID_ALPHABET[(bytes[1] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[1] & 0x07) << 2) | ((bytes[2] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[2] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[2] & 0x01) << 4) | ((bytes[3] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[3] & 0x0f) << 1) | ((bytes[4] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[4] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[4] & 0x03) << 3) | ((bytes[5] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[5] & 0x1f] +\n TYPEID_ALPHABET[(bytes[6] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[6] & 0x07) << 2) | ((bytes[7] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[7] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[7] & 0x01) << 4) | ((bytes[8] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[8] & 0x0f) << 1) | ((bytes[9] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[9] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[9] & 0x03) << 3) | ((bytes[10] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[10] & 0x1f] +\n TYPEID_ALPHABET[(bytes[11] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[11] & 0x07) << 2) | ((bytes[12] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[12] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[12] & 0x01) << 4) | ((bytes[13] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[13] & 0x0f) << 1) | ((bytes[14] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[14] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[14] & 0x03) << 3) | ((bytes[15] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[15] & 0x1f]\n )\n}\n\nfunction decodeSuffixToBytes(suffix: string): Uint8Array {\n assertValidSuffixShape(suffix)\n\n const values = new Array<number>(TYPEID_SUFFIX_LENGTH)\n for (let i = 0; i < TYPEID_SUFFIX_LENGTH; i += 1) {\n values[i] = decodeSuffixValue(suffix, i)\n }\n\n const bytes = new Uint8Array(TYPEID_UUID_BYTE_LENGTH)\n bytes[0] = (values[0] << 5) | values[1]\n bytes[1] = (values[2] << 3) | (values[3] >> 2)\n bytes[2] = ((values[3] & 0x03) << 6) | (values[4] << 1) | (values[5] >> 4)\n bytes[3] = ((values[5] & 0x0f) << 4) | (values[6] >> 1)\n bytes[4] = ((values[6] & 0x01) << 7) | (values[7] << 2) | (values[8] >> 3)\n bytes[5] = ((values[8] & 0x07) << 5) | values[9]\n bytes[6] = (values[10] << 3) | (values[11] >> 2)\n bytes[7] = ((values[11] & 0x03) << 6) | (values[12] << 1) | (values[13] >> 4)\n bytes[8] = ((values[13] & 0x0f) << 4) | (values[14] >> 1)\n bytes[9] = ((values[14] & 0x01) << 7) | (values[15] << 2) | (values[16] >> 3)\n bytes[10] = ((values[16] & 0x07) << 5) | values[17]\n bytes[11] = (values[18] << 3) | (values[19] >> 2)\n bytes[12] = ((values[19] & 0x03) << 6) | (values[20] << 1) | (values[21] >> 4)\n bytes[13] = ((values[21] & 0x0f) << 4) | (values[22] >> 1)\n bytes[14] = ((values[22] & 0x01) << 7) | (values[23] << 2) | (values[24] >> 3)\n bytes[15] = ((values[24] & 0x07) << 5) | values[25]\n\n return bytes\n}\n\nfunction assertUuidV7Bytes(bytes: Uint8Array): void {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`, {\n strategy: 'typeid',\n })\n }\n\n if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {\n throw new InvalidInputError('UUID_NOT_V7', 'TypeID UUID bytes must encode a UUID v7 value', {\n strategy: 'typeid',\n })\n }\n}\n\nfunction uuidV7BytesFromSuffix(suffix: string): Uint8Array {\n const bytes = decodeSuffixToBytes(suffix)\n assertUuidV7Bytes(bytes)\n return bytes\n}\n\nfunction uuidV7BytesFromUuid(uuid: string): Uint8Array {\n if (!uuidv7.isValid(uuid)) {\n throw new InvalidInputError('UUID_NOT_V7', 'TypeID can only wrap UUID v7 values', { strategy: 'typeid' })\n }\n\n return uuidv7.toBytes(uuid)\n}\n\nfunction parseTypeid(id: string): ParsedTypeid {\n const separatorIndex = id.lastIndexOf('_')\n\n if (separatorIndex === 0) {\n throw new ParseError('INVALID_FORMAT', 'TypeID must not start with \"_\"', { strategy: 'typeid' })\n }\n\n const prefix = separatorIndex === -1 ? '' : id.slice(0, separatorIndex)\n const suffix = separatorIndex === -1 ? id : id.slice(separatorIndex + 1)\n\n assertValidPrefix(prefix)\n const bytes = uuidV7BytesFromSuffix(suffix)\n\n return { prefix, suffix, bytes }\n}\n\nfunction joinPrefixAndSuffix(prefix: string, suffix: string): string {\n return prefix === '' ? suffix : `${prefix}_${suffix}`\n}\n\nfunction formatTypeidFromUuidV7Bytes(prefix: string, bytes: Uint8Array): string {\n assertValidPrefix(prefix)\n assertUuidV7Bytes(bytes)\n return joinPrefixAndSuffix(prefix, encodeBytesToSuffix(bytes))\n}\n\nfunction timestampFromUuidV7Bytes(bytes: Uint8Array): number {\n let msecs = 0\n for (let i = 0; i < 6; i += 1) {\n msecs = msecs * 256 + bytes[i]\n }\n return msecs\n}\n\n/**\n * Mirror uuid/v7's option validation at the typeid boundary, so failures are\n * attributed to `strategy: 'typeid'` instead of leaking `strategy: 'uuid'`\n * through delegation.\n */\nfunction validateTypeidOptions(options: TypeidOptions): void {\n const msecs = options.msecs\n if (msecs !== undefined && !isIntegerInRange(msecs, 0, MAX_MSECS)) {\n throw new InvalidInputError('TIMESTAMP_OUT_OF_RANGE', `Timestamp must be an integer between 0 and ${MAX_MSECS}`, {\n strategy: 'typeid',\n })\n }\n\n if (options.counter !== undefined && options.seq !== undefined) {\n throw new InvalidInputError('CONFLICTING_OPTIONS', 'Pass only one of `counter` or `seq`, not both', {\n strategy: 'typeid',\n })\n }\n const counter = options.counter ?? options.seq\n if (counter !== undefined && !isIntegerInRange(counter, 0, MAX_COUNTER)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be an integer between 0 and ${MAX_COUNTER}`, {\n strategy: 'typeid',\n })\n }\n\n const random = options.random\n if (random && random.length < TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('RANDOM_BYTES_TOO_SHORT', `Random bytes length must be >= ${TYPEID_UUID_BYTE_LENGTH}`, {\n strategy: 'typeid',\n })\n }\n}\n\nfunction typeidFn(prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string\nfunction typeidFn<TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options: TypeidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction typeidFn<TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options?: TypeidOptions,\n buf?: TBuf,\n offset = 0,\n): string | TBuf {\n if (options !== undefined) {\n validateTypeidOptions(options)\n }\n\n if (buf) {\n if (!isWritableRange(buf, offset, TYPEID_UUID_BYTE_LENGTH)) {\n throw new BufferError(\n 'BUFFER_OUT_OF_BOUNDS',\n `TypeID byte range ${offset}:${offset + TYPEID_UUID_BYTE_LENGTH - 1} is out of buffer bounds`,\n { strategy: 'typeid' },\n )\n }\n assertValidPrefix(prefix)\n return uuidv7(options, buf, offset)\n }\n\n const bytes = uuidv7(options, new Uint8Array(TYPEID_UUID_BYTE_LENGTH))\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToBytes(id: string): Uint8Array {\n return parseTypeid(id).bytes\n}\n\nfunction typeidFromBytes(prefix: string, bytes: Uint8Array): string {\n return formatTypeidFromUuidV7Bytes(prefix, bytes)\n}\n\nfunction typeidToUuid(id: string): string {\n return uuidv7.fromBytes(typeidToBytes(id))\n}\n\nfunction typeidFromUuid(prefix: string, uuid: string): string {\n return formatTypeidFromUuidV7Bytes(prefix, uuidV7BytesFromUuid(uuid))\n}\n\nfunction timestamp(id: string): number {\n return timestampFromUuidV7Bytes(parseTypeid(id).bytes)\n}\n\nfunction prefix(id: string): string {\n return parseTypeid(id).prefix\n}\n\nfunction suffix(id: string): string {\n return parseTypeid(id).suffix\n}\n\nfunction isValid(id: unknown): id is string {\n if (typeof id !== 'string') {\n return false\n }\n\n try {\n parseTypeid(id)\n } catch {\n return false\n }\n\n return true\n}\n\n/**\n * Generate a TypeID string backed by UUID v7.\n *\n * TypeID combines a lowercase snake_case type prefix with a 26-character\n * modified base32 encoding of a UUID v7, producing sortable domain identifiers\n * such as `user_01h2xcejqtf2nbrexx3vqjhp41`.\n *\n * @example\n * ```ts\n * import { typeid } from 'uniku/typeid'\n *\n * const id = typeid('user')\n * // => \"user_01h2xcejqtf2nbrexx3vqjhp41\"\n *\n * const uuid = typeid.toUuid(id)\n * const restored = typeid.fromUuid('user', uuid)\n * ```\n */\nexport const typeid: Typeid = Object.assign(typeidFn, {\n toBytes: typeidToBytes,\n fromBytes: typeidFromBytes,\n toUuid: typeidToUuid,\n fromUuid: typeidFromUuid,\n timestamp,\n prefix,\n suffix,\n isValid,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"typeid.mjs","names":[],"sources":["../../src/typeid/typeid.ts"],"sourcesContent":["import { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError, ParseError } from '../errors'\nimport { formatUuidUnchecked, parseUuidFor } from '../uuid/common/uuid'\nimport type { UuidV7Options } from '../uuid/v7'\nimport { uuidv7 } from '../uuid/v7'\n\nexport type TypeidOptions = UuidV7Options\n\nexport type Typeid = {\n /** Generate a TypeID from a lowercase entity prefix and a UUID v7 suffix. */\n (prefix: string, options?: TypeidOptions): string\n /** Generate a TypeID with explicit options or write its 16 canonical UUID v7 bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options: TypeidOptions | undefined,\n buf: TBuf,\n offset?: number,\n ): TBuf\n /** Generate a TypeID string with optional timestamp, counter, or random bytes. */\n (prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string\n /** Convert a TypeID's suffix to its 16 UUID bytes. */\n toBytes(id: string): Uint8Array\n /** Build a TypeID from a prefix and 16 UUID bytes of any version. */\n fromBytes(prefix: string, bytes: Uint8Array): string\n /** Convert a TypeID to its UUID string. */\n toUuid(id: string): string\n /** Build a TypeID from a prefix and a UUID string of any version. */\n fromUuid(prefix: string, uuid: string): string\n /** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. Throws for a TypeID that does not wrap a UUID v7. */\n timestamp(id: string): number\n /** Read a TypeID's entity prefix. */\n prefix(id: string): string\n /** Read a TypeID's base32 suffix. */\n suffix(id: string): string\n /** Return whether a value is a syntactically valid TypeID, whatever UUID version it wraps. */\n isValid(id: unknown): id is string\n}\n\nconst TYPEID_SUFFIX_LENGTH = 26\nconst TYPEID_UUID_BYTE_LENGTH = 16\n// 48-bit UUID v7 timestamp capacity, mirrored from uuid/v7 alongside the\n// 32-bit counter capacity, so option validation is attributed to the typeid\n// boundary instead of leaking `strategy: 'uuid'` through delegation.\nconst MAX_MSECS = 0xffffffffffff\nconst MAX_COUNTER = 0xffffffff\nconst TYPEID_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz'\n\nconst BASE32_DECODE: number[] = Array.from({ length: 128 }, () => -1)\nfor (let i = 0; i < TYPEID_ALPHABET.length; i += 1) {\n BASE32_DECODE[TYPEID_ALPHABET.charCodeAt(i)] = i\n}\n\ntype ParsedTypeid = {\n prefix: string\n suffix: string\n bytes: Uint8Array\n}\n\nfunction assertValidPrefix(prefix: string): void {\n if (prefix.length === 0) {\n return\n }\n\n if (prefix.length > 63) {\n throw new InvalidInputError('PREFIX_TOO_LONG', 'TypeID prefix must be at most 63 characters', {\n strategy: 'typeid',\n })\n }\n\n for (let i = 0; i < prefix.length; i += 1) {\n const code = prefix.charCodeAt(i)\n const isLowercaseLetter = code >= 97 && code <= 122\n const isUnderscore = code === 95\n\n if (!isLowercaseLetter && !isUnderscore) {\n throw new InvalidInputError(\n 'PREFIX_INVALID_CHAR',\n 'TypeID prefix must contain only lowercase ASCII letters and underscores',\n { strategy: 'typeid' },\n )\n }\n\n if ((i === 0 || i === prefix.length - 1) && !isLowercaseLetter) {\n throw new InvalidInputError('PREFIX_INVALID_BOUNDARY', 'TypeID prefix must start and end with a-z', {\n strategy: 'typeid',\n })\n }\n }\n}\n\nfunction decodeSuffixValue(suffix: string, index: number): number {\n const code = suffix.charCodeAt(index)\n const value = code < BASE32_DECODE.length ? BASE32_DECODE[code] : -1\n\n if (value === -1) {\n throw new ParseError('INVALID_CHAR', `TypeID suffix contains invalid character at position ${index}`, {\n strategy: 'typeid',\n })\n }\n\n return value\n}\n\nfunction assertValidSuffixShape(suffix: string): void {\n if (suffix.length !== TYPEID_SUFFIX_LENGTH) {\n throw new ParseError('INVALID_LENGTH', `TypeID suffix must be 26 characters, got ${suffix.length}`, {\n strategy: 'typeid',\n })\n }\n\n if (suffix[0] > '7') {\n throw new ParseError('VALUE_OUT_OF_RANGE', 'TypeID suffix must encode a 128-bit value', {\n strategy: 'typeid',\n })\n }\n}\n\nfunction encodeBytesToSuffix(bytes: Uint8Array): string {\n if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`, {\n strategy: 'typeid',\n })\n }\n\n return (\n TYPEID_ALPHABET[(bytes[0] & 0xe0) >> 5] +\n TYPEID_ALPHABET[bytes[0] & 0x1f] +\n TYPEID_ALPHABET[(bytes[1] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[1] & 0x07) << 2) | ((bytes[2] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[2] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[2] & 0x01) << 4) | ((bytes[3] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[3] & 0x0f) << 1) | ((bytes[4] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[4] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[4] & 0x03) << 3) | ((bytes[5] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[5] & 0x1f] +\n TYPEID_ALPHABET[(bytes[6] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[6] & 0x07) << 2) | ((bytes[7] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[7] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[7] & 0x01) << 4) | ((bytes[8] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[8] & 0x0f) << 1) | ((bytes[9] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[9] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[9] & 0x03) << 3) | ((bytes[10] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[10] & 0x1f] +\n TYPEID_ALPHABET[(bytes[11] & 0xf8) >> 3] +\n TYPEID_ALPHABET[((bytes[11] & 0x07) << 2) | ((bytes[12] & 0xc0) >> 6)] +\n TYPEID_ALPHABET[(bytes[12] & 0x3e) >> 1] +\n TYPEID_ALPHABET[((bytes[12] & 0x01) << 4) | ((bytes[13] & 0xf0) >> 4)] +\n TYPEID_ALPHABET[((bytes[13] & 0x0f) << 1) | ((bytes[14] & 0x80) >> 7)] +\n TYPEID_ALPHABET[(bytes[14] & 0x7c) >> 2] +\n TYPEID_ALPHABET[((bytes[14] & 0x03) << 3) | ((bytes[15] & 0xe0) >> 5)] +\n TYPEID_ALPHABET[bytes[15] & 0x1f]\n )\n}\n\nfunction decodeSuffixToBytes(suffix: string): Uint8Array {\n assertValidSuffixShape(suffix)\n\n const values = new Array<number>(TYPEID_SUFFIX_LENGTH)\n for (let i = 0; i < TYPEID_SUFFIX_LENGTH; i += 1) {\n values[i] = decodeSuffixValue(suffix, i)\n }\n\n const bytes = new Uint8Array(TYPEID_UUID_BYTE_LENGTH)\n bytes[0] = (values[0] << 5) | values[1]\n bytes[1] = (values[2] << 3) | (values[3] >> 2)\n bytes[2] = ((values[3] & 0x03) << 6) | (values[4] << 1) | (values[5] >> 4)\n bytes[3] = ((values[5] & 0x0f) << 4) | (values[6] >> 1)\n bytes[4] = ((values[6] & 0x01) << 7) | (values[7] << 2) | (values[8] >> 3)\n bytes[5] = ((values[8] & 0x07) << 5) | values[9]\n bytes[6] = (values[10] << 3) | (values[11] >> 2)\n bytes[7] = ((values[11] & 0x03) << 6) | (values[12] << 1) | (values[13] >> 4)\n bytes[8] = ((values[13] & 0x0f) << 4) | (values[14] >> 1)\n bytes[9] = ((values[14] & 0x01) << 7) | (values[15] << 2) | (values[16] >> 3)\n bytes[10] = ((values[16] & 0x07) << 5) | values[17]\n bytes[11] = (values[18] << 3) | (values[19] >> 2)\n bytes[12] = ((values[19] & 0x03) << 6) | (values[20] << 1) | (values[21] >> 4)\n bytes[13] = ((values[21] & 0x0f) << 4) | (values[22] >> 1)\n bytes[14] = ((values[22] & 0x01) << 7) | (values[23] << 2) | (values[24] >> 3)\n bytes[15] = ((values[24] & 0x07) << 5) | values[25]\n\n return bytes\n}\n\nfunction parseTypeid(id: string): ParsedTypeid {\n const separatorIndex = id.lastIndexOf('_')\n\n if (separatorIndex === 0) {\n throw new ParseError('INVALID_FORMAT', 'TypeID must not start with \"_\"', { strategy: 'typeid' })\n }\n\n const prefix = separatorIndex === -1 ? '' : id.slice(0, separatorIndex)\n const suffix = separatorIndex === -1 ? id : id.slice(separatorIndex + 1)\n\n assertValidPrefix(prefix)\n const bytes = decodeSuffixToBytes(suffix)\n\n return { prefix, suffix, bytes }\n}\n\nfunction joinPrefixAndSuffix(prefix: string, suffix: string): string {\n return prefix === '' ? suffix : `${prefix}_${suffix}`\n}\n\nfunction formatTypeid(prefix: string, bytes: Uint8Array): string {\n assertValidPrefix(prefix)\n return joinPrefixAndSuffix(prefix, encodeBytesToSuffix(bytes))\n}\n\nfunction timestampFromUuidV7Bytes(bytes: Uint8Array): number {\n if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {\n throw new InvalidInputError('UUID_NOT_V7', 'TypeID must wrap a UUID v7 to have a timestamp', {\n strategy: 'typeid',\n })\n }\n\n let msecs = 0\n for (let i = 0; i < 6; i += 1) {\n msecs = msecs * 256 + bytes[i]\n }\n return msecs\n}\n\n/**\n * Mirror uuid/v7's option validation at the typeid boundary, so failures are\n * attributed to `strategy: 'typeid'` instead of leaking `strategy: 'uuid'`\n * through delegation.\n */\nfunction validateTypeidOptions(options: TypeidOptions): void {\n const msecs = options.msecs\n if (msecs !== undefined && !isIntegerInRange(msecs, 0, MAX_MSECS)) {\n throw new InvalidInputError('TIMESTAMP_OUT_OF_RANGE', `Timestamp must be an integer between 0 and ${MAX_MSECS}`, {\n strategy: 'typeid',\n })\n }\n\n if (options.counter !== undefined && options.seq !== undefined) {\n throw new InvalidInputError('CONFLICTING_OPTIONS', 'Pass only one of `counter` or `seq`, not both', {\n strategy: 'typeid',\n })\n }\n const counter = options.counter ?? options.seq\n if (counter !== undefined && !isIntegerInRange(counter, 0, MAX_COUNTER)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be an integer between 0 and ${MAX_COUNTER}`, {\n strategy: 'typeid',\n })\n }\n\n const random = options.random\n if (random && random.length < TYPEID_UUID_BYTE_LENGTH) {\n throw new InvalidInputError('RANDOM_BYTES_TOO_SHORT', `Random bytes length must be >= ${TYPEID_UUID_BYTE_LENGTH}`, {\n strategy: 'typeid',\n })\n }\n}\n\nfunction typeidFn(prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string\nfunction typeidFn<TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options: TypeidOptions | undefined,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction typeidFn<TBuf extends Uint8Array = Uint8Array>(\n prefix: string,\n options?: TypeidOptions,\n buf?: TBuf,\n offset = 0,\n): string | TBuf {\n if (options !== undefined) {\n validateTypeidOptions(options)\n }\n\n if (buf) {\n if (!isWritableRange(buf, offset, TYPEID_UUID_BYTE_LENGTH)) {\n throw new BufferError(\n 'BUFFER_OUT_OF_BOUNDS',\n `TypeID byte range ${offset}:${offset + TYPEID_UUID_BYTE_LENGTH - 1} is out of buffer bounds`,\n { strategy: 'typeid' },\n )\n }\n assertValidPrefix(prefix)\n return uuidv7(options, buf, offset)\n }\n\n const bytes = uuidv7(options, new Uint8Array(TYPEID_UUID_BYTE_LENGTH))\n return formatTypeid(prefix, bytes)\n}\n\nfunction typeidToBytes(id: string): Uint8Array {\n return parseTypeid(id).bytes\n}\n\nfunction typeidFromBytes(prefix: string, bytes: Uint8Array): string {\n return formatTypeid(prefix, bytes)\n}\n\nfunction typeidToUuid(id: string): string {\n return formatUuidUnchecked(typeidToBytes(id))\n}\n\nfunction typeidFromUuid(prefix: string, uuid: string): string {\n return formatTypeid(prefix, parseUuidFor(uuid, 'typeid'))\n}\n\nfunction timestamp(id: string): number {\n return timestampFromUuidV7Bytes(parseTypeid(id).bytes)\n}\n\nfunction prefix(id: string): string {\n return parseTypeid(id).prefix\n}\n\nfunction suffix(id: string): string {\n return parseTypeid(id).suffix\n}\n\nfunction isValid(id: unknown): id is string {\n if (typeof id !== 'string') {\n return false\n }\n\n try {\n parseTypeid(id)\n } catch {\n return false\n }\n\n return true\n}\n\n/**\n * Generate a TypeID string backed by UUID v7.\n *\n * TypeID combines a lowercase snake_case type prefix with a 26-character\n * modified base32 encoding of a UUID, producing domain identifiers such as\n * `user_01h2xcejqtf2nbrexx3vqjhp41`. Generated TypeIDs always wrap a UUID v7,\n * so they sort by creation time. As the TypeID specification recommends, the\n * conversion and validation functions accept any 128-bit value, such as a\n * name-based UUID v5.\n *\n * @example\n * ```ts\n * import { typeid } from 'uniku/typeid'\n *\n * const id = typeid('user')\n * // => \"user_01h2xcejqtf2nbrexx3vqjhp41\"\n *\n * const uuid = typeid.toUuid(id)\n * const restored = typeid.fromUuid('user', uuid)\n * ```\n */\nexport const typeid: Typeid = Object.assign(typeidFn, {\n toBytes: typeidToBytes,\n fromBytes: typeidFromBytes,\n toUuid: typeidToUuid,\n fromUuid: typeidFromUuid,\n timestamp,\n prefix,\n suffix,\n isValid,\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"+OAsCA,MAKM,EAAY,eACZ,EAAc,WACd,EAAkB,mCAElB,EAA0B,MAAM,KAAK,CAAE,OAAQ,GAAI,MAAS,EAAE,EACpE,IAAK,IAAI,EAAI,EAAG,EAAI,GAAwB,GAAK,EAC/C,EAAc,EAAgB,WAAW,CAAC,GAAK,EASjD,SAAS,EAAkB,EAAsB,CAC3C,KAAO,SAAW,EAItB,IAAI,EAAO,OAAS,GAClB,MAAM,IAAI,EAAkB,kBAAmB,8CAA+C,CAC5F,SAAU,QACZ,CAAC,EAGH,IAAK,IAAI,EAAI,EAAG,EAAI,EAAO,OAAQ,GAAK,EAAG,CACzC,IAAM,EAAO,EAAO,WAAW,CAAC,EAC1B,EAAoB,GAAQ,IAAM,GAAQ,IAGhD,GAAI,CAAC,GAFgB,IAAS,GAG5B,MAAM,IAAI,EACR,sBACA,0EACA,CAAE,SAAU,QAAS,CACvB,EAGF,IAAK,IAAM,GAAK,IAAM,EAAO,OAAS,IAAM,CAAC,EAC3C,MAAM,IAAI,EAAkB,0BAA2B,4CAA6C,CAClG,SAAU,QACZ,CAAC,CAEL,CArBG,CAsBL,CAEA,SAAS,EAAkB,EAAgB,EAAuB,CAChE,IAAM,EAAO,EAAO,WAAW,CAAK,EAC9B,EAAQ,EAAO,EAAc,OAAS,EAAc,GAAQ,GAElE,GAAI,IAAU,GACZ,MAAM,IAAI,EAAW,eAAgB,wDAAwD,IAAS,CACpG,SAAU,QACZ,CAAC,EAGH,OAAO,CACT,CAEA,SAAS,EAAuB,EAAsB,CACpD,GAAI,EAAO,SAAW,GACpB,MAAM,IAAI,EAAW,iBAAkB,4CAA4C,EAAO,SAAU,CAClG,SAAU,QACZ,CAAC,EAGH,GAAI,EAAO,GAAK,IACd,MAAM,IAAI,EAAW,qBAAsB,4CAA6C,CACtF,SAAU,QACZ,CAAC,CAEL,CAEA,SAAS,EAAoB,EAA2B,CACtD,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EAAkB,uBAAwB,oCAAoC,EAAM,SAAU,CACtG,SAAU,QACZ,CAAC,EAGH,OACE,GAAiB,EAAM,GAAK,MAAS,GACrC,EAAgB,EAAM,GAAK,IAC3B,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,KAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAkB,EAAM,GAAK,KAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,EAAgB,EAAM,GAAK,IAC3B,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,KAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAkB,EAAM,GAAK,KAAS,GAAO,EAAM,GAAK,MAAS,GACjE,GAAiB,EAAM,GAAK,MAAS,GACrC,GAAkB,EAAM,GAAK,IAAS,GAAO,EAAM,IAAM,MAAS,GAClE,EAAgB,EAAM,IAAM,IAC5B,GAAiB,EAAM,IAAM,MAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAiB,EAAM,IAAM,KAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAkB,EAAM,IAAM,KAAS,GAAO,EAAM,IAAM,MAAS,GACnE,GAAiB,EAAM,IAAM,MAAS,GACtC,GAAkB,EAAM,IAAM,IAAS,GAAO,EAAM,IAAM,MAAS,GACnE,EAAgB,EAAM,IAAM,GAEhC,CAEA,SAAS,EAAoB,EAA4B,CACvD,EAAuB,CAAM,EAE7B,IAAM,EAAa,MAAc,EAAoB,EACrD,IAAK,IAAI,EAAI,EAAG,EAAI,GAAsB,GAAK,EAC7C,EAAO,GAAK,EAAkB,EAAQ,CAAC,EAGzC,IAAM,EAAQ,IAAI,WAAW,EAAuB,EAkBpD,MAjBA,GAAM,GAAM,EAAO,IAAM,EAAK,EAAO,GACrC,EAAM,GAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EAC5C,EAAM,IAAO,EAAO,GAAK,IAAS,EAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EACxE,EAAM,IAAO,EAAO,GAAK,KAAS,EAAM,EAAO,IAAM,EACrD,EAAM,IAAO,EAAO,GAAK,IAAS,EAAM,EAAO,IAAM,EAAM,EAAO,IAAM,EACxE,EAAM,IAAO,EAAO,GAAK,IAAS,EAAK,EAAO,GAC9C,EAAM,GAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC9C,EAAM,IAAO,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC3E,EAAM,IAAO,EAAO,IAAM,KAAS,EAAM,EAAO,KAAO,EACvD,EAAM,IAAO,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC3E,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAK,EAAO,IAChD,EAAM,IAAO,EAAO,KAAO,EAAM,EAAO,KAAO,EAC/C,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC5E,EAAM,KAAQ,EAAO,IAAM,KAAS,EAAM,EAAO,KAAO,EACxD,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAM,EAAO,KAAO,EAAM,EAAO,KAAO,EAC5E,EAAM,KAAQ,EAAO,IAAM,IAAS,EAAK,EAAO,IAEzC,CACT,CAEA,SAAS,EAAY,EAA0B,CAC7C,IAAM,EAAiB,EAAG,YAAY,GAAG,EAEzC,GAAI,IAAmB,EACrB,MAAM,IAAI,EAAW,iBAAkB,iCAAkC,CAAE,SAAU,QAAS,CAAC,EAGjG,IAAM,EAAS,IAAmB,GAAK,GAAK,EAAG,MAAM,EAAG,CAAc,EAChE,EAAS,IAAmB,GAAK,EAAK,EAAG,MAAM,EAAiB,CAAC,EAKvE,OAHA,EAAkB,CAAM,EAGjB,CAAE,SAAQ,SAAQ,MAFX,EAAoB,CAEL,CAAE,CACjC,CAEA,SAAS,EAAoB,EAAgB,EAAwB,CACnE,OAAO,IAAW,GAAK,EAAS,GAAG,EAAO,GAAG,GAC/C,CAEA,SAAS,EAAa,EAAgB,EAA2B,CAE/D,OADA,EAAkB,CAAM,EACjB,EAAoB,EAAQ,EAAoB,CAAK,CAAC,CAC/D,CAEA,SAAS,EAAyB,EAA2B,CAC3D,GAAI,EAAM,IAAM,GAAM,IAAM,EAAM,GAAK,MAAU,IAC/C,MAAM,IAAI,EAAkB,cAAe,iDAAkD,CAC3F,SAAU,QACZ,CAAC,EAGH,IAAI,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,GAAK,EAC1B,EAAQ,EAAQ,IAAM,EAAM,GAE9B,OAAO,CACT,CAOA,SAAS,EAAsB,EAA8B,CAC3D,IAAM,EAAQ,EAAQ,MACtB,GAAI,IAAU,IAAA,IAAa,CAAC,EAAiB,EAAO,EAAG,CAAS,EAC9D,MAAM,IAAI,EAAkB,yBAA0B,8CAA8C,IAAa,CAC/G,SAAU,QACZ,CAAC,EAGH,GAAI,EAAQ,UAAY,IAAA,IAAa,EAAQ,MAAQ,IAAA,GACnD,MAAM,IAAI,EAAkB,sBAAuB,gDAAiD,CAClG,SAAU,QACZ,CAAC,EAEH,IAAM,EAAU,EAAQ,SAAW,EAAQ,IAC3C,GAAI,IAAY,IAAA,IAAa,CAAC,EAAiB,EAAS,EAAG,CAAW,EACpE,MAAM,IAAI,EAAkB,uBAAwB,4CAA4C,IAAe,CAC7G,SAAU,QACZ,CAAC,EAGH,IAAM,EAAS,EAAQ,OACvB,GAAI,GAAU,EAAO,OAAS,GAC5B,MAAM,IAAI,EAAkB,yBAA0B,oCAA6D,CACjH,SAAU,QACZ,CAAC,CAEL,CASA,SAAS,EACP,EACA,EACA,EACA,EAAS,EACM,CAKf,GAJI,IAAY,IAAA,IACd,EAAsB,CAAO,EAG3B,EAAK,CACP,GAAI,CAAC,EAAgB,EAAK,EAAQ,EAAuB,EACvD,MAAM,IAAI,EACR,uBACA,qBAAqB,EAAO,GAAG,EAAS,GAA0B,EAAE,0BACpE,CAAE,SAAU,QAAS,CACvB,EAGF,OADA,EAAkB,CAAM,EACjB,EAAO,EAAS,EAAK,CAAM,CACpC,CAGA,OAAO,EAAa,EADN,EAAO,EAAS,IAAI,WAAW,EAAuB,CACpC,CAAC,CACnC,CAEA,SAAS,EAAc,EAAwB,CAC7C,OAAO,EAAY,CAAE,CAAC,CAAC,KACzB,CAEA,SAAS,EAAgB,EAAgB,EAA2B,CAClE,OAAO,EAAa,EAAQ,CAAK,CACnC,CAEA,SAAS,EAAa,EAAoB,CACxC,OAAO,EAAoB,EAAc,CAAE,CAAC,CAC9C,CAEA,SAAS,EAAe,EAAgB,EAAsB,CAC5D,OAAO,EAAa,EAAQ,EAAa,EAAM,QAAQ,CAAC,CAC1D,CAEA,SAAS,EAAU,EAAoB,CACrC,OAAO,EAAyB,EAAY,CAAE,CAAC,CAAC,KAAK,CACvD,CAEA,SAAS,EAAO,EAAoB,CAClC,OAAO,EAAY,CAAE,CAAC,CAAC,MACzB,CAEA,SAAS,EAAO,EAAoB,CAClC,OAAO,EAAY,CAAE,CAAC,CAAC,MACzB,CAEA,SAAS,EAAQ,EAA2B,CAC1C,GAAI,OAAO,GAAO,SAChB,MAAO,GAGT,GAAI,CACF,EAAY,CAAE,CAChB,MAAQ,CACN,MAAO,EACT,CAEA,MAAO,EACT,CAuBA,MAAa,EAAiB,OAAO,OAAO,EAAU,CACpD,QAAS,EACT,UAAW,EACX,OAAQ,EACR,SAAU,EACV,YACA,SACA,SACA,SACF,CAAC"}
|
package/build/uuid/v4.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"../random-Chp-Nkzi.mjs";import{n as t}from"../validation-CTNpXm94.mjs";import{BufferError as n,InvalidInputError as r,ParseError as i,UniqueIdError as a}from"../errors.mjs";import{n as o,r as s,t as c}from"../uuid-
|
|
1
|
+
import{r as e}from"../random-Chp-Nkzi.mjs";import{n as t}from"../validation-CTNpXm94.mjs";import{BufferError as n,InvalidInputError as r,ParseError as i,UniqueIdError as a}from"../errors.mjs";import{n as o,r as s,t as c}from"../uuid-Q8CMAbBu.mjs";const l=globalThis.crypto.randomUUID.bind(globalThis.crypto),u=new Uint8Array(16),d=/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function f(e,t,n){for(let r=0;r<16;r+=1)t[n+r]=e[r];t[n+6]=t[n+6]&15|64,t[n+8]=t[n+8]&63|128}function p(e,t,n){return!t&&!e?l():m(e,t,n)}function m(i,a,s){let c=i?.random;if(c&&c.length<16)throw new r(`RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 16`,{strategy:`uuid`});let l=a?s??0:0;if(a&&!t(a,l,16))throw new n(`BUFFER_OUT_OF_BOUNDS`,`UUID byte range ${l}:${l+16-1} is out of buffer bounds`,{strategy:`uuid`});let d=a??u;return f(c??e(),d,l),a??o(d)}function h(e){return typeof e==`string`&&d.test(e)}const g=Object.assign(p,{toBytes:s,fromBytes:c,isValid:h,NIL:`00000000-0000-0000-0000-000000000000`,MAX:`ffffffff-ffff-ffff-ffff-ffffffffffff`});export{n as BufferError,r as InvalidInputError,i as ParseError,a as UniqueIdError,g as uuidv4};
|
|
2
2
|
//# sourceMappingURL=v4.mjs.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { a as ParseError, i as InvalidInputError, o as UniqueIdError, t as BufferError } from "../errors-Da-8dh4J.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/uuid/v5.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The input a UUID v5 is derived from: a string, hashed as UTF-8 without Unicode
|
|
6
|
+
* normalization, or raw bytes.
|
|
7
|
+
*/
|
|
8
|
+
type UuidV5Name = string | Uint8Array;
|
|
9
|
+
/** A namespace UUID, as a UUID string or its 16 canonical bytes. */
|
|
10
|
+
type UuidV5Namespace = string | Uint8Array;
|
|
11
|
+
/** Derives UUID v5 values for one namespace. Returned by `uuidv5.namespace()`. */
|
|
12
|
+
type UuidV5Deriver = {
|
|
13
|
+
/** Derive the UUID v5 string for a name. */(name: UuidV5Name, buf?: undefined, offset?: number): string; /** Derive the UUID v5 for a name and write its 16 canonical bytes into a caller-owned buffer. */
|
|
14
|
+
<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf: TBuf, offset?: number): TBuf;
|
|
15
|
+
};
|
|
16
|
+
type UuidV5 = {
|
|
17
|
+
/** Derive the UUID v5 string for a name within a namespace. */(name: UuidV5Name, namespace: UuidV5Namespace, buf?: undefined, offset?: number): string; /** Derive the UUID v5 for a name within a namespace and write its 16 canonical bytes into a caller-owned buffer. */
|
|
18
|
+
<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, namespace: UuidV5Namespace, buf: TBuf, offset?: number): TBuf; /** Validate and parse a namespace once, and return a function that derives IDs within it. */
|
|
19
|
+
namespace(namespace: UuidV5Namespace): UuidV5Deriver; /** Convert a UUID v5 string to its canonical 16-byte representation. */
|
|
20
|
+
toBytes(id: string): Uint8Array; /** Convert 16 canonical UUID bytes to a UUID v5 string. */
|
|
21
|
+
fromBytes(bytes: Uint8Array): string; /** Return whether a value is a syntactically valid UUID v5 string. */
|
|
22
|
+
isValid(id: unknown): id is string; /** The RFC 9562 namespace for fully qualified domain names. */
|
|
23
|
+
DNS: string; /** The RFC 9562 namespace for URLs. */
|
|
24
|
+
URL: string; /** The RFC 9562 namespace for ISO object identifiers (OIDs). */
|
|
25
|
+
OID: string; /** The RFC 9562 namespace for X.500 distinguished names. */
|
|
26
|
+
X500: string; /** The nil UUID (all zeros) */
|
|
27
|
+
NIL: string; /** The max UUID (all ones) */
|
|
28
|
+
MAX: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Derive a UUID v5 from a name and a namespace, or write its bytes into a buffer.
|
|
32
|
+
*
|
|
33
|
+
* UUID v5 (RFC 9562 section 5.5) is name-based: it hashes the namespace and
|
|
34
|
+
* name with SHA-1, so the same inputs give the same ID in every process, on
|
|
35
|
+
* every runtime, with no state to keep. Use it to derive a stable ID from an
|
|
36
|
+
* identifier you already hold.
|
|
37
|
+
*
|
|
38
|
+
* String names are hashed as UTF-8 without Unicode normalization. A UUID v5 is
|
|
39
|
+
* an identifier, not a secret: anyone who knows or can guess the name and
|
|
40
|
+
* namespace can compute it, and SHA-1 lets someone who controls the names craft
|
|
41
|
+
* two that collide.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { uuidv5 } from 'uniku/uuid/v5'
|
|
46
|
+
*
|
|
47
|
+
* const id = uuidv5('www.example.com', uuidv5.DNS)
|
|
48
|
+
* // => "2ed6657d-e927-568b-95e1-2665a8aea6a2"
|
|
49
|
+
*
|
|
50
|
+
* // Parse a namespace once and derive many IDs within it
|
|
51
|
+
* const sessionId = uuidv5.namespace('1b671a64-40d5-491e-99b0-da01ff1f3341')
|
|
52
|
+
* sessionId('tool-a:3f2c')
|
|
53
|
+
*
|
|
54
|
+
* // Validate
|
|
55
|
+
* uuidv5.isValid(id) // true
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare const uuidv5: UuidV5;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { BufferError, InvalidInputError, ParseError, UniqueIdError, UuidV5, UuidV5Deriver, UuidV5Name, UuidV5Namespace, uuidv5 };
|
|
61
|
+
//# sourceMappingURL=v5.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v5.d.mts","names":[],"sources":["../../src/uuid/v5.ts"],"mappings":";;;;;;AAiBA;KAAY,UAAA,YAAsB,UAAA;;KAGtB,eAAA,YAA2B,UAAA;AAHL;AAAA,KAMtB,aAAA;EAHA,6CAKT,IAAA,EAAM,UAAA,EAAY,GAAA,cAAiB,MAAA,oBALC;EAAA,cAOvB,UAAA,GAAa,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;AAAA;AAAA,KAG5E,MAAA;kEAET,IAAA,EAAM,UAAA,EAAY,SAAA,EAAW,eAAA,EAAiB,GAAA,cAAiB,MAAA;gBAElD,UAAA,GAAa,UAAA,EAAY,IAAA,EAAM,UAAA,EAAY,SAAA,EAAW,eAAA,EAAiB,GAAA,EAAK,IAAA,EAAM,MAAA,YAAkB,IAAA;EAElH,SAAA,CAAU,SAAA,EAAW,eAAA,GAAkB,aAAA;EAEvC,OAAA,CAAQ,EAAA,WAAa,UAAA,EAXiE;EAatF,SAAA,CAAU,KAAA,EAAO,UAAA;EAEjB,OAAA,CAAQ,EAAA,YAAc,EAAA;EAEtB,GAAA;EAEA,GAAA;EAEA,GAAA;EAEA,IAAA;EAEA,GAAA,UAzBsF;EA2BtF,GAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4IW,MAAA,EAAQ,MAAA"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{n as e}from"../validation-CTNpXm94.mjs";import{BufferError as t,InvalidInputError as n,ParseError as r,UniqueIdError as i}from"../errors.mjs";import{n as a,r as o,t as s}from"../uuid-Q8CMAbBu.mjs";const c=new Int32Array(80);function l(e){return(Math.floor((e+8)/64)+1)*64}function u(e,t,n,r){let i=l(t);e[t]=128,e.fill(0,t+1,i-8);let a=Math.floor(t/536870912),o=t<<3>>>0;e[i-8]=a>>>24,e[i-7]=a>>>16,e[i-6]=a>>>8,e[i-5]=a,e[i-4]=o>>>24,e[i-3]=o>>>16,e[i-2]=o>>>8,e[i-1]=o;let s=1732584193,u=-271733879,f=-1732584194,p=271733878,m=-1009589776;for(let t=0;t<i;t+=64){for(let n=0;n<16;n+=1){let r=t+n*4;c[n]=e[r]<<24|e[r+1]<<16|e[r+2]<<8|e[r+3]}for(let e=16;e<80;e+=1){let t=c[e-3]^c[e-8]^c[e-14]^c[e-16];c[e]=t<<1|t>>>31}let n=s,r=u,i=f,a=p,o=m;for(let e=0;e<80;e+=1){let t,s;e<20?(t=r&i|~r&a,s=1518500249):e<40?(t=r^i^a,s=1859775393):e<60?(t=r&i|r&a|i&a,s=-1894007588):(t=r^i^a,s=-899497514);let l=(n<<5|n>>>27)+t+o+s+c[e]|0;o=a,a=i,i=r<<30|r>>>2,r=n,n=l}s=s+n|0,u=u+r|0,f=f+i|0,p=p+a|0,m=m+o|0}d(n,r,s),d(n,r+4,u),d(n,r+8,f),d(n,r+12,p)}function d(e,t,n){e[t]=n>>>24,e[t+1]=n>>>16,e[t+2]=n>>>8,e[t+3]=n}const f=new Uint8Array(512),p=f.subarray(16),m=new Uint8Array(16),h=new TextEncoder,g=/^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;function _(e){if(typeof e==`string`)return o(e);if(e.length!==16)throw new n(`BYTES_INVALID_LENGTH`,`UUID v5 namespace must be exactly 16 bytes, got ${e.length}`,{strategy:`uuid`});return e}function v(e,t,n,r){let i,a;if(typeof t==`string`){let e=l(16+t.length*3);i=e<=512?f:new Uint8Array(e);let n=i===f?p:i.subarray(16);a=16+h.encodeInto(t,n).written}else{a=16+t.length;let e=l(a);i=e<=512?f:new Uint8Array(e),i.set(t,16)}i.set(e),u(i,a,n,r),n[r+6]=n[r+6]&15|80,n[r+8]=n[r+8]&63|128}function y(n,r,i,o){if(!i)return v(n,r,m,0),a(m);let s=o??0;if(!e(i,s,16))throw new t(`BUFFER_OUT_OF_BOUNDS`,`UUID byte range ${s}:${s+16-1} is out of buffer bounds`,{strategy:`uuid`});return v(n,r,i,s),i}function b(e,t,n,r){return y(_(t),e,n,r)}function x(e){let t=typeof e==`string`?o(e):new Uint8Array(_(e));function n(e,n,r){return y(t,e,n,r)}return n}function S(e){return typeof e==`string`&&g.test(e)}const C=Object.assign(b,{namespace:x,toBytes:o,fromBytes:s,isValid:S,DNS:`6ba7b810-9dad-11d1-80b4-00c04fd430c8`,URL:`6ba7b811-9dad-11d1-80b4-00c04fd430c8`,OID:`6ba7b812-9dad-11d1-80b4-00c04fd430c8`,X500:`6ba7b814-9dad-11d1-80b4-00c04fd430c8`,NIL:`00000000-0000-0000-0000-000000000000`,MAX:`ffffffff-ffff-ffff-ffff-ffffffffffff`});export{t as BufferError,n as InvalidInputError,r as ParseError,i as UniqueIdError,C as uuidv5};
|
|
2
|
+
//# sourceMappingURL=v5.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v5.mjs","names":[],"sources":["../../src/uuid/common/sha1.ts","../../src/uuid/v5.ts"],"sourcesContent":["/**\n * Minimal synchronous SHA-1 (FIPS 180-4) for name-based UUIDs.\n *\n * Web Crypto's `digest()` is async and every uniku generator is synchronous,\n * so `uuid/v5` carries its own SHA-1 instead of returning a promise or adding a\n * dependency. It serves identity only, never as a security primitive.\n */\n\nconst W = new Int32Array(80)\n\n/** Size of the buffer SHA-1 needs to pad a `length`-byte message: a multiple of 64 with room for 9 more bytes. */\nexport function sha1PaddedLength(length: number): number {\n return (Math.floor((length + 8) / 64) + 1) * 64\n}\n\n/**\n * Hash `msg[0, length)` and write the first 16 digest bytes to `out[offset, offset + 16)`.\n *\n * `msg` is caller-owned scratch space of at least `sha1PaddedLength(length)` bytes.\n * Bytes from `length` on are overwritten with padding, so stale contents are fine.\n */\nexport function sha1Digest128Into(msg: Uint8Array, length: number, out: Uint8Array, offset: number): void {\n const padded = sha1PaddedLength(length)\n msg[length] = 0x80\n msg.fill(0, length + 1, padded - 8)\n\n const bitsHigh = Math.floor(length / 0x20000000)\n const bitsLow = (length << 3) >>> 0\n msg[padded - 8] = bitsHigh >>> 24\n msg[padded - 7] = bitsHigh >>> 16\n msg[padded - 6] = bitsHigh >>> 8\n msg[padded - 5] = bitsHigh\n msg[padded - 4] = bitsLow >>> 24\n msg[padded - 3] = bitsLow >>> 16\n msg[padded - 2] = bitsLow >>> 8\n msg[padded - 1] = bitsLow\n\n let h0 = 0x67452301\n let h1 = 0xefcdab89 | 0\n let h2 = 0x98badcfe | 0\n let h3 = 0x10325476\n let h4 = 0xc3d2e1f0 | 0\n\n for (let block = 0; block < padded; block += 64) {\n for (let i = 0; i < 16; i += 1) {\n const j = block + i * 4\n W[i] = (msg[j] << 24) | (msg[j + 1] << 16) | (msg[j + 2] << 8) | msg[j + 3]\n }\n for (let i = 16; i < 80; i += 1) {\n const x = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]\n W[i] = (x << 1) | (x >>> 31)\n }\n\n let a = h0\n let b = h1\n let c = h2\n let d = h3\n let e = h4\n\n for (let i = 0; i < 80; i += 1) {\n let f: number\n let k: number\n if (i < 20) {\n f = (b & c) | (~b & d)\n k = 0x5a827999\n } else if (i < 40) {\n f = b ^ c ^ d\n k = 0x6ed9eba1\n } else if (i < 60) {\n f = (b & c) | (b & d) | (c & d)\n k = 0x8f1bbcdc | 0\n } else {\n f = b ^ c ^ d\n k = 0xca62c1d6 | 0\n }\n const t = (((a << 5) | (a >>> 27)) + f + e + k + W[i]) | 0\n e = d\n d = c\n c = (b << 30) | (b >>> 2)\n b = a\n a = t\n }\n\n h0 = (h0 + a) | 0\n h1 = (h1 + b) | 0\n h2 = (h2 + c) | 0\n h3 = (h3 + d) | 0\n h4 = (h4 + e) | 0\n }\n\n writeWord(out, offset, h0)\n writeWord(out, offset + 4, h1)\n writeWord(out, offset + 8, h2)\n writeWord(out, offset + 12, h3)\n}\n\nfunction writeWord(out: Uint8Array, offset: number, word: number): void {\n out[offset] = word >>> 24\n out[offset + 1] = word >>> 16\n out[offset + 2] = word >>> 8\n out[offset + 3] = word\n}\n","import { isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { sha1Digest128Into, sha1PaddedLength } from './common/sha1'\nimport { formatUuid, formatUuidUnchecked, parseUuid } from './common/uuid'\n\nconst UUID_BYTES = 16\n// Names that fit hash in this reused buffer; larger ones get a one-off allocation.\nconst SCRATCH_BYTES = 512\nconst scratch = new Uint8Array(SCRATCH_BYTES)\nconst scratchName = scratch.subarray(UUID_BYTES)\nconst reusableBuf = new Uint8Array(UUID_BYTES)\nconst encoder = /*@__PURE__*/ new TextEncoder()\n\n/**\n * The input a UUID v5 is derived from: a string, hashed as UTF-8 without Unicode\n * normalization, or raw bytes.\n */\nexport type UuidV5Name = string | Uint8Array\n\n/** A namespace UUID, as a UUID string or its 16 canonical bytes. */\nexport type UuidV5Namespace = string | Uint8Array\n\n/** Derives UUID v5 values for one namespace. Returned by `uuidv5.namespace()`. */\nexport type UuidV5Deriver = {\n /** Derive the UUID v5 string for a name. */\n (name: UuidV5Name, buf?: undefined, offset?: number): string\n /** Derive the UUID v5 for a name and write its 16 canonical bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf: TBuf, offset?: number): TBuf\n}\n\nexport type UuidV5 = {\n /** Derive the UUID v5 string for a name within a namespace. */\n (name: UuidV5Name, namespace: UuidV5Namespace, buf?: undefined, offset?: number): string\n /** Derive the UUID v5 for a name within a namespace and write its 16 canonical bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, namespace: UuidV5Namespace, buf: TBuf, offset?: number): TBuf\n /** Validate and parse a namespace once, and return a function that derives IDs within it. */\n namespace(namespace: UuidV5Namespace): UuidV5Deriver\n /** Convert a UUID v5 string to its canonical 16-byte representation. */\n toBytes(id: string): Uint8Array\n /** Convert 16 canonical UUID bytes to a UUID v5 string. */\n fromBytes(bytes: Uint8Array): string\n /** Return whether a value is a syntactically valid UUID v5 string. */\n isValid(id: unknown): id is string\n /** The RFC 9562 namespace for fully qualified domain names. */\n DNS: string\n /** The RFC 9562 namespace for URLs. */\n URL: string\n /** The RFC 9562 namespace for ISO object identifiers (OIDs). */\n OID: string\n /** The RFC 9562 namespace for X.500 distinguished names. */\n X500: string\n /** The nil UUID (all zeros) */\n NIL: string\n /** The max UUID (all ones) */\n MAX: string\n}\n\nconst 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\n\nfunction parseNamespace(namespace: UuidV5Namespace): Uint8Array {\n if (typeof namespace === 'string') {\n return parseUuid(namespace)\n }\n\n if (namespace.length !== UUID_BYTES) {\n throw new InvalidInputError(\n 'BYTES_INVALID_LENGTH',\n `UUID v5 namespace must be exactly ${UUID_BYTES} bytes, got ${namespace.length}`,\n { strategy: 'uuid' },\n )\n }\n\n return namespace\n}\n\nfunction writeV5BytesUnchecked(namespace: Uint8Array, name: UuidV5Name, out: Uint8Array, offset: number): void {\n let msg: Uint8Array\n let length: number\n\n if (typeof name === 'string') {\n // UTF-8 needs at most 3 bytes per UTF-16 code unit.\n const capacity = sha1PaddedLength(UUID_BYTES + name.length * 3)\n msg = capacity <= SCRATCH_BYTES ? scratch : new Uint8Array(capacity)\n const nameRegion = msg === scratch ? scratchName : msg.subarray(UUID_BYTES)\n length = UUID_BYTES + encoder.encodeInto(name, nameRegion).written\n } else {\n length = UUID_BYTES + name.length\n const capacity = sha1PaddedLength(length)\n msg = capacity <= SCRATCH_BYTES ? scratch : new Uint8Array(capacity)\n msg.set(name, UUID_BYTES)\n }\n\n // Name and namespace are copied before `out` is written, so either may alias it.\n msg.set(namespace)\n sha1Digest128Into(msg, length, out, offset)\n\n // Set RFC 9562 version (5) and variant (10xx) bits.\n out[offset + 6] = (out[offset + 6] & 0x0f) | 0x50\n out[offset + 8] = (out[offset + 8] & 0x3f) | 0x80\n}\n\nfunction derive<TBuf extends Uint8Array = Uint8Array>(\n namespace: Uint8Array,\n name: UuidV5Name,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n if (!buf) {\n writeV5BytesUnchecked(namespace, name, reusableBuf, 0)\n return formatUuidUnchecked(reusableBuf)\n }\n\n const outputOffset = offset ?? 0\n if (!isWritableRange(buf, outputOffset, UUID_BYTES)) {\n throw new BufferError(\n 'BUFFER_OUT_OF_BOUNDS',\n `UUID byte range ${outputOffset}:${outputOffset + UUID_BYTES - 1} is out of buffer bounds`,\n { strategy: 'uuid' },\n )\n }\n\n writeV5BytesUnchecked(namespace, name, buf, outputOffset)\n return buf\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v5(name: UuidV5Name, namespace: UuidV5Namespace, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v5<TBuf extends Uint8Array = Uint8Array>(\n name: UuidV5Name,\n namespace: UuidV5Namespace,\n buf: TBuf,\n offset?: number,\n): TBuf\nfunction v5<TBuf extends Uint8Array = Uint8Array>(\n name: UuidV5Name,\n namespace: UuidV5Namespace,\n buf?: TBuf,\n offset?: number,\n): string | TBuf {\n return derive(parseNamespace(namespace), name, buf, offset)\n}\n\nfunction namespace(namespace: UuidV5Namespace): UuidV5Deriver {\n // Copy caller bytes so later mutation cannot change derived IDs. `new Uint8Array()` always copies,\n // unlike `.slice()` on a Node.js Buffer, which returns a view of the same memory.\n const namespaceBytes =\n typeof namespace === 'string' ? parseUuid(namespace) : new Uint8Array(parseNamespace(namespace))\n\n function deriver(name: UuidV5Name, buf?: undefined, offset?: number): string\n function deriver<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf: TBuf, offset?: number): TBuf\n function deriver<TBuf extends Uint8Array = Uint8Array>(name: UuidV5Name, buf?: TBuf, offset?: number): string | TBuf {\n return derive(namespaceBytes, name, buf, offset)\n }\n\n return deriver\n}\n\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && UUID_V5_REGEX.test(id)\n}\n\n/**\n * Derive a UUID v5 from a name and a namespace, or write its bytes into a buffer.\n *\n * UUID v5 (RFC 9562 section 5.5) is name-based: it hashes the namespace and\n * name with SHA-1, so the same inputs give the same ID in every process, on\n * every runtime, with no state to keep. Use it to derive a stable ID from an\n * identifier you already hold.\n *\n * String names are hashed as UTF-8 without Unicode normalization. A UUID v5 is\n * an identifier, not a secret: anyone who knows or can guess the name and\n * namespace can compute it, and SHA-1 lets someone who controls the names craft\n * two that collide.\n *\n * @example\n * ```ts\n * import { uuidv5 } from 'uniku/uuid/v5'\n *\n * const id = uuidv5('www.example.com', uuidv5.DNS)\n * // => \"2ed6657d-e927-568b-95e1-2665a8aea6a2\"\n *\n * // Parse a namespace once and derive many IDs within it\n * const sessionId = uuidv5.namespace('1b671a64-40d5-491e-99b0-da01ff1f3341')\n * sessionId('tool-a:3f2c')\n *\n * // Validate\n * uuidv5.isValid(id) // true\n * ```\n */\nexport const uuidv5: UuidV5 = Object.assign(v5, {\n namespace,\n toBytes: parseUuid,\n fromBytes: formatUuid,\n isValid,\n DNS: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',\n URL: '6ba7b811-9dad-11d1-80b4-00c04fd430c8',\n OID: '6ba7b812-9dad-11d1-80b4-00c04fd430c8',\n X500: '6ba7b814-9dad-11d1-80b4-00c04fd430c8',\n NIL: '00000000-0000-0000-0000-000000000000',\n MAX: 'ffffffff-ffff-ffff-ffff-ffffffffffff',\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
|
package/build/uuid/v7.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"../random-Chp-Nkzi.mjs";import{n as t,t as n}from"../validation-CTNpXm94.mjs";import{BufferError as r,InvalidInputError as i,ParseError as a,UniqueIdError as o}from"../errors.mjs";import{n as s,r as c,t as l}from"../uuid-
|
|
1
|
+
import{r as e}from"../random-Chp-Nkzi.mjs";import{n as t,t as n}from"../validation-CTNpXm94.mjs";import{BufferError as r,InvalidInputError as i,ParseError as a,UniqueIdError as o}from"../errors.mjs";import{n as s,r as c,t as l}from"../uuid-Q8CMAbBu.mjs";const u=/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,d=0xffffffffffff,f=4294967295,p=new Uint8Array(16),m={msecs:-1/0,seq:0};function h(e,t,n,r,i){r[i++]=t/1099511627776&255,r[i++]=t/4294967296&255,r[i++]=t/16777216&255,r[i++]=t/65536&255,r[i++]=t/256&255,r[i++]=t&255,r[i++]=112|n>>>28&15,r[i++]=n>>>20&255,r[i++]=128|n>>>14&63,r[i++]=n>>>6&255,r[i++]=n<<2&255|e[10]&3,r[i++]=e[11],r[i++]=e[12],r[i++]=e[13],r[i++]=e[14],r[i++]=e[15]}function g(e,n,i,a,o){if(!t(a,o,16))throw new r(`BUFFER_OUT_OF_BOUNDS`,`UUID byte range ${o}:${o+16-1} is out of buffer bounds`,{strategy:`uuid`});h(e,n,i,a,o)}function _(t,r,a=0){let o=t.msecs;if(o!==void 0&&!n(o,0,d))throw new i(`TIMESTAMP_OUT_OF_RANGE`,`Timestamp must be an integer between 0 and ${d}`,{strategy:`uuid`});if(t.counter!==void 0&&t.seq!==void 0)throw new i(`CONFLICTING_OPTIONS`,"Pass only one of `counter` or `seq`, not both",{strategy:`uuid`});let c=t.counter??t.seq;if(c!==void 0&&!n(c,0,f))throw new i(`COUNTER_OUT_OF_RANGE`,`Counter must be an integer between 0 and ${f}`,{strategy:`uuid`});let l=t.random;if(l&&l.length<16)throw new i(`RANDOM_BYTES_TOO_SHORT`,`Random bytes length must be >= 16`,{strategy:`uuid`});let u=l??e(),m=o??Date.now(),_=c??u[6]<<23|u[7]<<16|u[8]<<8|u[9];return r?(g(u,m,_,r,a),r):(h(u,m,_,p,0),s(p))}function v(t,n,r){if(t)return _(t,n,r);let i=Date.now(),a=e();return i>m.msecs?(m.seq=a[6]<<23|a[7]<<16|a[8]<<8|a[9],m.msecs=i):(m.seq=m.seq+1|0,m.seq<0&&(m.seq=0,m.msecs++)),n?(g(a,m.msecs,m.seq,n,r??0),n):(h(a,m.msecs,m.seq,p,0),s(p))}function y(e){let t=c(e),n=0;for(let e=0;e<6;e+=1)n=n*256+t[e];return n}function b(e){return typeof e==`string`&&u.test(e)}const x=Object.assign(v,{toBytes:c,fromBytes:l,timestamp:y,isValid:b,NIL:`00000000-0000-0000-0000-000000000000`,MAX:`ffffffff-ffff-ffff-ffff-ffffffffffff`});export{r as BufferError,i as InvalidInputError,a as ParseError,o as UniqueIdError,x as uuidv7};
|
|
2
2
|
//# sourceMappingURL=v7.mjs.map
|
|
@@ -0,0 +1,2 @@
|
|
|
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){return l(e,`uuid`)}function l(e,a){if(e.length!==36)throw new t(`INVALID_LENGTH`,`UUID string must be 36 characters, got ${e.length}`,{strategy:a});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:a});let o=new Uint8Array(16);for(let s=0;s<36;s+=1){let c=r[s];if(c===-1)continue;let l=n(e.charCodeAt(s));if(l===-1)throw new t(`INVALID_CHAR`,`UUID string contains invalid hex character at position ${s}`,{strategy:a});i[s]?o[c]=l<<4:o[c]|=l}return o}export{l as i,s as n,c as r,o as t};
|
|
2
|
+
//# sourceMappingURL=uuid-Q8CMAbBu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uuid-Q8CMAbBu.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 type { IdGenerator } from '../../generators'\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 return parseUuidFor(value, 'uuid')\n}\n\n/** Parse a UUID string of any version, attributing errors to `strategy`. */\nexport function parseUuidFor(value: string, strategy: IdGenerator): 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,\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 },\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,\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,CCnBA,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,OAAO,EAAa,EAAO,MAAM,CACnC,CAGA,SAAgB,EAAa,EAAe,EAAmC,CAC7E,GAAI,EAAM,SAAW,GACnB,MAAM,IAAI,EAAW,iBAAkB,0CAA0C,EAAM,SAAU,CAC/F,UACF,CAAC,EAIH,GAAI,EAAM,KAAO,KAAO,EAAM,MAAQ,KAAO,EAAM,MAAQ,KAAO,EAAM,MAAQ,IAC9E,MAAM,IAAI,EACR,iBACA,6EAA6E,EAAM,GACnF,CAAE,UAAS,CACb,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,UACF,CAAC,EAGC,EAAkB,GACpB,EAAM,GAAW,GAAU,EAE3B,EAAM,IAAY,CAEtB,CAEA,OAAO,CACT"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"private": false,
|
|
3
3
|
"name": "uniku",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.7.0",
|
|
5
5
|
"description": "Minimal, tree-shakeable unique ID generators for every JavaScript runtime",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Alberto Schiabel",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"keywords": [
|
|
22
22
|
"uuid",
|
|
23
23
|
"uuidv4",
|
|
24
|
+
"uuidv5",
|
|
24
25
|
"uuidv7",
|
|
25
26
|
"ulid",
|
|
26
27
|
"typeid",
|
|
@@ -66,6 +67,11 @@
|
|
|
66
67
|
"import": "./build/uuid/v4.mjs",
|
|
67
68
|
"default": "./build/uuid/v4.mjs"
|
|
68
69
|
},
|
|
70
|
+
"./uuid/v5": {
|
|
71
|
+
"types": "./build/uuid/v5.d.mts",
|
|
72
|
+
"import": "./build/uuid/v5.mjs",
|
|
73
|
+
"default": "./build/uuid/v5.mjs"
|
|
74
|
+
},
|
|
69
75
|
"./uuid/v7": {
|
|
70
76
|
"types": "./build/uuid/v7.d.mts",
|
|
71
77
|
"import": "./build/uuid/v7.mjs",
|
|
@@ -132,18 +138,20 @@
|
|
|
132
138
|
"@noble/hashes": "^2.2.0"
|
|
133
139
|
},
|
|
134
140
|
"devDependencies": {
|
|
135
|
-
"@effect/platform-bun": "4.
|
|
141
|
+
"@effect/platform-bun": "4.1.3",
|
|
136
142
|
"@owpz/ksuid": "^25.7.20",
|
|
137
143
|
"@paralleldrive/cuid2": "^3.3.0",
|
|
138
144
|
"@types/bun": "^1.3.14",
|
|
145
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
139
146
|
"bson": "^7.3.1",
|
|
140
|
-
"effect": "4.
|
|
147
|
+
"effect": "4.1.3",
|
|
141
148
|
"nanoid": "^5.1.16",
|
|
142
149
|
"nexid": "^2.1.0",
|
|
143
150
|
"tsid-ts": "^0.0.9",
|
|
144
151
|
"typeid-js": "1.2.0",
|
|
145
152
|
"ulid": "3.0.2",
|
|
146
153
|
"uuid": "14.0.1",
|
|
154
|
+
"vitest": "^4.1.10",
|
|
147
155
|
"xid-ts": "^1.3.0"
|
|
148
156
|
},
|
|
149
157
|
"scripts": {
|
package/src/typeid/typeid.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isIntegerInRange, isWritableRange } from '../common/validation'
|
|
2
2
|
import { BufferError, InvalidInputError, ParseError } from '../errors'
|
|
3
|
+
import { formatUuidUnchecked, parseUuidFor } from '../uuid/common/uuid'
|
|
3
4
|
import type { UuidV7Options } from '../uuid/v7'
|
|
4
5
|
import { uuidv7 } from '../uuid/v7'
|
|
5
6
|
|
|
@@ -17,21 +18,21 @@ export type Typeid = {
|
|
|
17
18
|
): TBuf
|
|
18
19
|
/** Generate a TypeID string with optional timestamp, counter, or random bytes. */
|
|
19
20
|
(prefix: string, options?: TypeidOptions, buf?: undefined, offset?: number): string
|
|
20
|
-
/** Convert a TypeID's
|
|
21
|
+
/** Convert a TypeID's suffix to its 16 UUID bytes. */
|
|
21
22
|
toBytes(id: string): Uint8Array
|
|
22
|
-
/** Build a TypeID from a prefix and
|
|
23
|
+
/** Build a TypeID from a prefix and 16 UUID bytes of any version. */
|
|
23
24
|
fromBytes(prefix: string, bytes: Uint8Array): string
|
|
24
|
-
/** Convert a TypeID to its UUID
|
|
25
|
+
/** Convert a TypeID to its UUID string. */
|
|
25
26
|
toUuid(id: string): string
|
|
26
|
-
/** Build a TypeID from a prefix and UUID
|
|
27
|
+
/** Build a TypeID from a prefix and a UUID string of any version. */
|
|
27
28
|
fromUuid(prefix: string, uuid: string): string
|
|
28
|
-
/** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. */
|
|
29
|
+
/** Read the UUID v7 timestamp embedded in a TypeID, in milliseconds. Throws for a TypeID that does not wrap a UUID v7. */
|
|
29
30
|
timestamp(id: string): number
|
|
30
31
|
/** Read a TypeID's entity prefix. */
|
|
31
32
|
prefix(id: string): string
|
|
32
|
-
/** Read a TypeID's
|
|
33
|
+
/** Read a TypeID's base32 suffix. */
|
|
33
34
|
suffix(id: string): string
|
|
34
|
-
/** Return whether a value is a syntactically valid TypeID. */
|
|
35
|
+
/** Return whether a value is a syntactically valid TypeID, whatever UUID version it wraps. */
|
|
35
36
|
isValid(id: unknown): id is string
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -180,34 +181,6 @@ function decodeSuffixToBytes(suffix: string): Uint8Array {
|
|
|
180
181
|
return bytes
|
|
181
182
|
}
|
|
182
183
|
|
|
183
|
-
function assertUuidV7Bytes(bytes: Uint8Array): void {
|
|
184
|
-
if (bytes.length !== TYPEID_UUID_BYTE_LENGTH) {
|
|
185
|
-
throw new InvalidInputError('BYTES_INVALID_LENGTH', `UUID bytes must be 16 bytes, got ${bytes.length}`, {
|
|
186
|
-
strategy: 'typeid',
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {
|
|
191
|
-
throw new InvalidInputError('UUID_NOT_V7', 'TypeID UUID bytes must encode a UUID v7 value', {
|
|
192
|
-
strategy: 'typeid',
|
|
193
|
-
})
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function uuidV7BytesFromSuffix(suffix: string): Uint8Array {
|
|
198
|
-
const bytes = decodeSuffixToBytes(suffix)
|
|
199
|
-
assertUuidV7Bytes(bytes)
|
|
200
|
-
return bytes
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function uuidV7BytesFromUuid(uuid: string): Uint8Array {
|
|
204
|
-
if (!uuidv7.isValid(uuid)) {
|
|
205
|
-
throw new InvalidInputError('UUID_NOT_V7', 'TypeID can only wrap UUID v7 values', { strategy: 'typeid' })
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
return uuidv7.toBytes(uuid)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
184
|
function parseTypeid(id: string): ParsedTypeid {
|
|
212
185
|
const separatorIndex = id.lastIndexOf('_')
|
|
213
186
|
|
|
@@ -219,7 +192,7 @@ function parseTypeid(id: string): ParsedTypeid {
|
|
|
219
192
|
const suffix = separatorIndex === -1 ? id : id.slice(separatorIndex + 1)
|
|
220
193
|
|
|
221
194
|
assertValidPrefix(prefix)
|
|
222
|
-
const bytes =
|
|
195
|
+
const bytes = decodeSuffixToBytes(suffix)
|
|
223
196
|
|
|
224
197
|
return { prefix, suffix, bytes }
|
|
225
198
|
}
|
|
@@ -228,13 +201,18 @@ function joinPrefixAndSuffix(prefix: string, suffix: string): string {
|
|
|
228
201
|
return prefix === '' ? suffix : `${prefix}_${suffix}`
|
|
229
202
|
}
|
|
230
203
|
|
|
231
|
-
function
|
|
204
|
+
function formatTypeid(prefix: string, bytes: Uint8Array): string {
|
|
232
205
|
assertValidPrefix(prefix)
|
|
233
|
-
assertUuidV7Bytes(bytes)
|
|
234
206
|
return joinPrefixAndSuffix(prefix, encodeBytesToSuffix(bytes))
|
|
235
207
|
}
|
|
236
208
|
|
|
237
209
|
function timestampFromUuidV7Bytes(bytes: Uint8Array): number {
|
|
210
|
+
if (bytes[6] >> 4 !== 7 || (bytes[8] & 0xc0) !== 0x80) {
|
|
211
|
+
throw new InvalidInputError('UUID_NOT_V7', 'TypeID must wrap a UUID v7 to have a timestamp', {
|
|
212
|
+
strategy: 'typeid',
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
|
|
238
216
|
let msecs = 0
|
|
239
217
|
for (let i = 0; i < 6; i += 1) {
|
|
240
218
|
msecs = msecs * 256 + bytes[i]
|
|
@@ -305,7 +283,7 @@ function typeidFn<TBuf extends Uint8Array = Uint8Array>(
|
|
|
305
283
|
}
|
|
306
284
|
|
|
307
285
|
const bytes = uuidv7(options, new Uint8Array(TYPEID_UUID_BYTE_LENGTH))
|
|
308
|
-
return
|
|
286
|
+
return formatTypeid(prefix, bytes)
|
|
309
287
|
}
|
|
310
288
|
|
|
311
289
|
function typeidToBytes(id: string): Uint8Array {
|
|
@@ -313,15 +291,15 @@ function typeidToBytes(id: string): Uint8Array {
|
|
|
313
291
|
}
|
|
314
292
|
|
|
315
293
|
function typeidFromBytes(prefix: string, bytes: Uint8Array): string {
|
|
316
|
-
return
|
|
294
|
+
return formatTypeid(prefix, bytes)
|
|
317
295
|
}
|
|
318
296
|
|
|
319
297
|
function typeidToUuid(id: string): string {
|
|
320
|
-
return
|
|
298
|
+
return formatUuidUnchecked(typeidToBytes(id))
|
|
321
299
|
}
|
|
322
300
|
|
|
323
301
|
function typeidFromUuid(prefix: string, uuid: string): string {
|
|
324
|
-
return
|
|
302
|
+
return formatTypeid(prefix, parseUuidFor(uuid, 'typeid'))
|
|
325
303
|
}
|
|
326
304
|
|
|
327
305
|
function timestamp(id: string): number {
|
|
@@ -354,8 +332,11 @@ function isValid(id: unknown): id is string {
|
|
|
354
332
|
* Generate a TypeID string backed by UUID v7.
|
|
355
333
|
*
|
|
356
334
|
* TypeID combines a lowercase snake_case type prefix with a 26-character
|
|
357
|
-
* modified base32 encoding of a UUID
|
|
358
|
-
*
|
|
335
|
+
* modified base32 encoding of a UUID, producing domain identifiers such as
|
|
336
|
+
* `user_01h2xcejqtf2nbrexx3vqjhp41`. Generated TypeIDs always wrap a UUID v7,
|
|
337
|
+
* so they sort by creation time. As the TypeID specification recommends, the
|
|
338
|
+
* conversion and validation functions accept any 128-bit value, such as a
|
|
339
|
+
* name-based UUID v5.
|
|
359
340
|
*
|
|
360
341
|
* @example
|
|
361
342
|
* ```ts
|
|
@@ -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/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"}
|