uniku 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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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"}
@@ -17,9 +17,9 @@ type UuidV7Options = {
17
17
  */
18
18
  counter?: number;
19
19
  /**
20
- * Unsigned 32-bit sequence value.
20
+ * Deprecated alias for `counter`; unsigned 32-bit sequence value.
21
21
  *
22
- * @deprecated Use `counter` instead. Will be removed at v1-rc.
22
+ * @deprecated Use `counter` instead. Will be removed in `1.0.0-rc.1`.
23
23
  */
24
24
  seq?: number;
25
25
  };
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-BPebYihz.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};
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
@@ -1 +1 @@
1
- {"version":3,"file":"v7.mjs","names":[],"sources":["../../src/uuid/v7.ts"],"sourcesContent":["import { rng } from '../common/random'\nimport { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { formatUuid, formatUuidUnchecked, parseUuid } from './common/uuid'\n\nexport type UuidV7Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Several bytes will be overwritten with timestamp, version, and variant data.\n */\n random?: Uint8Array\n /**\n * Timestamp in milliseconds since Unix epoch.\n * Defaults to Date.now().\n */\n msecs?: number\n /**\n * Unsigned 32-bit counter value.\n */\n counter?: number\n /**\n * Unsigned 32-bit sequence value.\n *\n * @deprecated Use `counter` instead. Will be removed at v1-rc.\n */\n // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).\n seq?: number\n}\n\nexport type UuidV7 = {\n /** Generate a time-ordered UUID v7 string. */\n (): string\n /** Generate a UUID v7 with explicit options or write its 16 canonical bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(options: UuidV7Options | undefined, buf: TBuf, offset?: number): TBuf\n /** Generate a UUID v7 string with optional timestamp, counter, or random bytes. */\n (options?: UuidV7Options, buf?: undefined, offset?: number): string\n /** Convert a UUID v7 string to its canonical 16-byte representation. */\n toBytes(id: string): Uint8Array\n /** Convert 16 canonical UUID bytes to a UUID v7 string. */\n fromBytes(bytes: Uint8Array): string\n /** Read the embedded Unix timestamp in milliseconds. */\n timestamp(id: string): number\n /** Return whether a value is a syntactically valid UUID v7 string. */\n isValid(id: unknown): id is string\n /** The nil UUID (all zeros) */\n NIL: string\n /** The max UUID (all ones) */\n MAX: string\n}\n\nconst UUID_V7_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\nconst UUID_BYTES = 16\nconst MAX_MSECS = 0xffffffffffff\nconst MAX_SEQ = 0xffffffff\n\n// Reusable buffer for string output path - avoids allocation per call.\n// Safe because bytes are consumed synchronously by formatUuidUnchecked().\nconst reusableBuf = new Uint8Array(UUID_BYTES)\n\ntype V7State = {\n msecs: number\n seq: number\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same millisecond.\n *\n * IMPORTANT: This state persists across all uuidv7() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs` and `seq` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: V7State = { msecs: -Infinity, seq: 0 }\n\nfunction writeV7BytesUnchecked(rnds: Uint8Array, msecs: number, seq: number, buf: Uint8Array, offset: number): void {\n // Keep this inline on the UUID v7 hot path. Using writeTimestamp48() here\n // benchmarked slower than the direct byte writes.\n buf[offset++] = (msecs / 0x10000000000) & 0xff\n buf[offset++] = (msecs / 0x100000000) & 0xff\n buf[offset++] = (msecs / 0x1000000) & 0xff\n buf[offset++] = (msecs / 0x10000) & 0xff\n buf[offset++] = (msecs / 0x100) & 0xff\n buf[offset++] = msecs & 0xff\n\n // Set version (7) and variant (10xx), then pack sequence and random tail bytes.\n buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f)\n buf[offset++] = (seq >>> 20) & 0xff\n buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f)\n buf[offset++] = (seq >>> 6) & 0xff\n // Lower seq bits plus 2 random bits to complete the 128-bit payload.\n buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03)\n buf[offset++] = rnds[11]\n buf[offset++] = rnds[12]\n buf[offset++] = rnds[13]\n buf[offset++] = rnds[14]\n buf[offset++] = rnds[15]\n}\n\nfunction writeV7Bytes(rnds: Uint8Array, msecs: number, seq: number, buf: Uint8Array, offset: number): void {\n if (!isWritableRange(buf, offset, UUID_BYTES)) {\n throw new BufferError(\n 'BUFFER_OUT_OF_BOUNDS',\n `UUID byte range ${offset}:${offset + UUID_BYTES - 1} is out of buffer bounds`,\n { strategy: 'uuid' },\n )\n }\n writeV7BytesUnchecked(rnds, msecs, seq, buf, offset)\n}\n\nfunction v7WithOptions<TBuf extends Uint8Array = Uint8Array>(\n options: UuidV7Options,\n buf?: TBuf,\n offset = 0,\n): string | TBuf {\n const optMsecs = options.msecs\n if (optMsecs !== undefined && !isIntegerInRange(optMsecs, 0, MAX_MSECS)) {\n throw new InvalidInputError('TIMESTAMP_OUT_OF_RANGE', `Timestamp must be an integer between 0 and ${MAX_MSECS}`, {\n strategy: 'uuid',\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: 'uuid',\n })\n }\n const optSeq = options.counter ?? options.seq\n if (optSeq !== undefined && !isIntegerInRange(optSeq, 0, MAX_SEQ)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be an integer between 0 and ${MAX_SEQ}`, {\n strategy: 'uuid',\n })\n }\n const optRandom = options.random\n if (optRandom && optRandom.length < UUID_BYTES) {\n throw new InvalidInputError('RANDOM_BYTES_TOO_SHORT', `Random bytes length must be >= ${UUID_BYTES}`, {\n strategy: 'uuid',\n })\n }\n\n const rnds = optRandom ?? rng()\n const msecs = optMsecs ?? Date.now()\n // Derive a 31-bit sequence if not provided by the caller, matching the default hot path.\n const seq = optSeq ?? (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n\n if (buf) {\n writeV7Bytes(rnds, msecs, seq, buf, offset)\n return buf\n }\n writeV7BytesUnchecked(rnds, msecs, seq, reusableBuf, 0)\n return formatUuidUnchecked(reusableBuf)\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v7(options?: UuidV7Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v7<TBuf extends Uint8Array = Uint8Array>(options: UuidV7Options | undefined, buf: TBuf, offset?: number): TBuf\nfunction v7<TBuf extends Uint8Array = Uint8Array>(options?: UuidV7Options, buf?: TBuf, offset?: number): string | TBuf {\n if (options) {\n return v7WithOptions(options, buf, offset)\n }\n\n // HOT PATH: Inline state management and byte generation for best performance\n const now = Date.now()\n const rnds = rng()\n\n // Update state (inlined for performance)\n if (now > state.msecs) {\n state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n state.msecs = now\n } else {\n state.seq = (state.seq + 1) | 0\n if (state.seq < 0) {\n state.seq = 0\n state.msecs++\n }\n }\n\n if (buf) {\n writeV7Bytes(rnds, state.msecs, state.seq, buf, offset ?? 0)\n return buf\n }\n writeV7BytesUnchecked(rnds, state.msecs, state.seq, reusableBuf, 0)\n return formatUuidUnchecked(reusableBuf)\n}\n\nfunction timestamp(id: string): number {\n const bytes = parseUuid(id)\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\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && UUID_V7_REGEX.test(id)\n}\n\n/**\n * Generate a UUID v7 string or write the bytes into a buffer.\n *\n * UUID v7 is a time-ordered UUID that embeds a Unix timestamp in milliseconds,\n * making IDs naturally sortable by creation time. Ideal for database primary keys\n * where chronological ordering improves index performance.\n *\n * @example\n * ```ts\n * import { uuidv7 } from 'uniku/uuid/v7'\n *\n * const id = uuidv7()\n * // => \"018e5e5c-7c8a-7000-8000-000000000000\"\n *\n * // Extract timestamp\n * const ts = uuidv7.timestamp(id)\n * console.log(new Date(ts))\n *\n * // Validate\n * uuidv7.isValid(id) // true\n *\n * // Convert to/from bytes\n * const bytes = uuidv7.toBytes(id)\n * const restored = uuidv7.fromBytes(bytes)\n * ```\n */\nexport const uuidv7: UuidV7 = Object.assign(v7, {\n toBytes: parseUuid,\n fromBytes: formatUuid,\n timestamp,\n isValid,\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":"8PAkDA,MAAM,EAAgB,yEAEhB,EAAY,eACZ,EAAU,WAIV,EAAc,IAAI,WAAW,EAAU,EAevC,EAAiB,CAAE,MAAO,KAAW,IAAK,CAAE,EAElD,SAAS,EAAsB,EAAkB,EAAe,EAAa,EAAiB,EAAsB,CAGlH,EAAI,KAAa,EAAQ,cAAiB,IAC1C,EAAI,KAAa,EAAQ,WAAe,IACxC,EAAI,KAAa,EAAQ,SAAa,IACtC,EAAI,KAAa,EAAQ,MAAW,IACpC,EAAI,KAAa,EAAQ,IAAS,IAClC,EAAI,KAAY,EAAQ,IAGxB,EAAI,KAAY,IAAS,IAAQ,GAAM,GACvC,EAAI,KAAa,IAAQ,GAAM,IAC/B,EAAI,KAAY,IAAS,IAAQ,GAAM,GACvC,EAAI,KAAa,IAAQ,EAAK,IAE9B,EAAI,KAAc,GAAO,EAAK,IAAS,EAAK,IAAM,EAClD,EAAI,KAAY,EAAK,IACrB,EAAI,KAAY,EAAK,IACrB,EAAI,KAAY,EAAK,IACrB,EAAI,KAAY,EAAK,IACrB,EAAI,KAAY,EAAK,GACvB,CAEA,SAAS,EAAa,EAAkB,EAAe,EAAa,EAAiB,EAAsB,CACzG,GAAI,CAAC,EAAgB,EAAK,EAAQ,EAAU,EAC1C,MAAM,IAAI,EACR,uBACA,mBAAmB,EAAO,GAAG,EAAS,GAAa,EAAE,0BACrD,CAAE,SAAU,MAAO,CACrB,EAEF,EAAsB,EAAM,EAAO,EAAK,EAAK,CAAM,CACrD,CAEA,SAAS,EACP,EACA,EACA,EAAS,EACM,CACf,IAAM,EAAW,EAAQ,MACzB,GAAI,IAAa,IAAA,IAAa,CAAC,EAAiB,EAAU,EAAG,CAAS,EACpE,MAAM,IAAI,EAAkB,yBAA0B,8CAA8C,IAAa,CAC/G,SAAU,MACZ,CAAC,EAEH,GAAI,EAAQ,UAAY,IAAA,IAAa,EAAQ,MAAQ,IAAA,GACnD,MAAM,IAAI,EAAkB,sBAAuB,gDAAiD,CAClG,SAAU,MACZ,CAAC,EAEH,IAAM,EAAS,EAAQ,SAAW,EAAQ,IAC1C,GAAI,IAAW,IAAA,IAAa,CAAC,EAAiB,EAAQ,EAAG,CAAO,EAC9D,MAAM,IAAI,EAAkB,uBAAwB,4CAA4C,IAAW,CACzG,SAAU,MACZ,CAAC,EAEH,IAAM,EAAY,EAAQ,OAC1B,GAAI,GAAa,EAAU,OAAS,GAClC,MAAM,IAAI,EAAkB,yBAA0B,oCAAgD,CACpG,SAAU,MACZ,CAAC,EAGH,IAAM,EAAO,GAAa,EAAI,EACxB,EAAQ,GAAY,KAAK,IAAI,EAE7B,EAAM,GAAW,EAAK,IAAM,GAAO,EAAK,IAAM,GAAO,EAAK,IAAM,EAAK,EAAK,GAOhF,OALI,GACF,EAAa,EAAM,EAAO,EAAK,EAAK,CAAM,EACnC,IAET,EAAsB,EAAM,EAAO,EAAK,EAAa,CAAC,EAC/C,EAAoB,CAAW,EACxC,CAUA,SAAS,EAAyC,EAAyB,EAAY,EAAgC,CACrH,GAAI,EACF,OAAO,EAAc,EAAS,EAAK,CAAM,EAI3C,IAAM,EAAM,KAAK,IAAI,EACf,EAAO,EAAI,EAmBjB,OAhBI,EAAM,EAAM,OACd,EAAM,IAAO,EAAK,IAAM,GAAO,EAAK,IAAM,GAAO,EAAK,IAAM,EAAK,EAAK,GACtE,EAAM,MAAQ,IAEd,EAAM,IAAO,EAAM,IAAM,EAAK,EAC1B,EAAM,IAAM,IACd,EAAM,IAAM,EACZ,EAAM,UAIN,GACF,EAAa,EAAM,EAAM,MAAO,EAAM,IAAK,EAAK,GAAU,CAAC,EACpD,IAET,EAAsB,EAAM,EAAM,MAAO,EAAM,IAAK,EAAa,CAAC,EAC3D,EAAoB,CAAW,EACxC,CAEA,SAAS,EAAU,EAAoB,CACrC,IAAM,EAAQ,EAAU,CAAE,EACtB,EAAQ,EACZ,IAAK,IAAI,EAAI,EAAG,EAAI,EAAG,GAAK,EAC1B,EAAQ,EAAQ,IAAM,EAAM,GAE9B,OAAO,CACT,CAEA,SAAS,EAAQ,EAA2B,CAC1C,OAAO,OAAO,GAAO,UAAY,EAAc,KAAK,CAAE,CACxD,CA4BA,MAAa,EAAiB,OAAO,OAAO,EAAI,CAC9C,QAAS,EACT,UAAW,EACX,YACA,UACA,IAAK,uCACL,IAAK,sCACP,CAAC"}
1
+ {"version":3,"file":"v7.mjs","names":[],"sources":["../../src/uuid/v7.ts"],"sourcesContent":["import { rng } from '../common/random'\nimport { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { formatUuid, formatUuidUnchecked, parseUuid } from './common/uuid'\n\nexport type UuidV7Options = {\n /**\n * 16 bytes of random data to use for UUID generation.\n * Note: Several bytes will be overwritten with timestamp, version, and variant data.\n */\n random?: Uint8Array\n /**\n * Timestamp in milliseconds since Unix epoch.\n * Defaults to Date.now().\n */\n msecs?: number\n /**\n * Unsigned 32-bit counter value.\n */\n counter?: number\n /**\n * Deprecated alias for `counter`; unsigned 32-bit sequence value.\n *\n * @deprecated Use `counter` instead. Will be removed in `1.0.0-rc.1`.\n */\n // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).\n seq?: number\n}\n\nexport type UuidV7 = {\n /** Generate a time-ordered UUID v7 string. */\n (): string\n /** Generate a UUID v7 with explicit options or write its 16 canonical bytes into a caller-owned buffer. */\n <TBuf extends Uint8Array = Uint8Array>(options: UuidV7Options | undefined, buf: TBuf, offset?: number): TBuf\n /** Generate a UUID v7 string with optional timestamp, counter, or random bytes. */\n (options?: UuidV7Options, buf?: undefined, offset?: number): string\n /** Convert a UUID v7 string to its canonical 16-byte representation. */\n toBytes(id: string): Uint8Array\n /** Convert 16 canonical UUID bytes to a UUID v7 string. */\n fromBytes(bytes: Uint8Array): string\n /** Read the embedded Unix timestamp in milliseconds. */\n timestamp(id: string): number\n /** Return whether a value is a syntactically valid UUID v7 string. */\n isValid(id: unknown): id is string\n /** The nil UUID (all zeros) */\n NIL: string\n /** The max UUID (all ones) */\n MAX: string\n}\n\nconst UUID_V7_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\nconst UUID_BYTES = 16\nconst MAX_MSECS = 0xffffffffffff\nconst MAX_SEQ = 0xffffffff\n\n// Reusable buffer for string output path - avoids allocation per call.\n// Safe because bytes are consumed synchronously by formatUuidUnchecked().\nconst reusableBuf = new Uint8Array(UUID_BYTES)\n\ntype V7State = {\n msecs: number\n seq: number\n}\n\n/**\n * Module-level state for maintaining monotonic ordering within the same millisecond.\n *\n * IMPORTANT: This state persists across all uuidv7() calls in the module's lifetime.\n * - In serverless/edge functions with warm starts, state persists between invocations.\n * - For isolated state, pass explicit `msecs` and `seq` via options.\n * - Tests should mock Date.now() or provide explicit options for deterministic behavior.\n */\nconst state: V7State = { msecs: -Infinity, seq: 0 }\n\nfunction writeV7BytesUnchecked(rnds: Uint8Array, msecs: number, seq: number, buf: Uint8Array, offset: number): void {\n // Keep this inline on the UUID v7 hot path. Using writeTimestamp48() here\n // benchmarked slower than the direct byte writes.\n buf[offset++] = (msecs / 0x10000000000) & 0xff\n buf[offset++] = (msecs / 0x100000000) & 0xff\n buf[offset++] = (msecs / 0x1000000) & 0xff\n buf[offset++] = (msecs / 0x10000) & 0xff\n buf[offset++] = (msecs / 0x100) & 0xff\n buf[offset++] = msecs & 0xff\n\n // Set version (7) and variant (10xx), then pack sequence and random tail bytes.\n buf[offset++] = 0x70 | ((seq >>> 28) & 0x0f)\n buf[offset++] = (seq >>> 20) & 0xff\n buf[offset++] = 0x80 | ((seq >>> 14) & 0x3f)\n buf[offset++] = (seq >>> 6) & 0xff\n // Lower seq bits plus 2 random bits to complete the 128-bit payload.\n buf[offset++] = ((seq << 2) & 0xff) | (rnds[10] & 0x03)\n buf[offset++] = rnds[11]\n buf[offset++] = rnds[12]\n buf[offset++] = rnds[13]\n buf[offset++] = rnds[14]\n buf[offset++] = rnds[15]\n}\n\nfunction writeV7Bytes(rnds: Uint8Array, msecs: number, seq: number, buf: Uint8Array, offset: number): void {\n if (!isWritableRange(buf, offset, UUID_BYTES)) {\n throw new BufferError(\n 'BUFFER_OUT_OF_BOUNDS',\n `UUID byte range ${offset}:${offset + UUID_BYTES - 1} is out of buffer bounds`,\n { strategy: 'uuid' },\n )\n }\n writeV7BytesUnchecked(rnds, msecs, seq, buf, offset)\n}\n\nfunction v7WithOptions<TBuf extends Uint8Array = Uint8Array>(\n options: UuidV7Options,\n buf?: TBuf,\n offset = 0,\n): string | TBuf {\n const optMsecs = options.msecs\n if (optMsecs !== undefined && !isIntegerInRange(optMsecs, 0, MAX_MSECS)) {\n throw new InvalidInputError('TIMESTAMP_OUT_OF_RANGE', `Timestamp must be an integer between 0 and ${MAX_MSECS}`, {\n strategy: 'uuid',\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: 'uuid',\n })\n }\n const optSeq = options.counter ?? options.seq\n if (optSeq !== undefined && !isIntegerInRange(optSeq, 0, MAX_SEQ)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be an integer between 0 and ${MAX_SEQ}`, {\n strategy: 'uuid',\n })\n }\n const optRandom = options.random\n if (optRandom && optRandom.length < UUID_BYTES) {\n throw new InvalidInputError('RANDOM_BYTES_TOO_SHORT', `Random bytes length must be >= ${UUID_BYTES}`, {\n strategy: 'uuid',\n })\n }\n\n const rnds = optRandom ?? rng()\n const msecs = optMsecs ?? Date.now()\n // Derive a 31-bit sequence if not provided by the caller, matching the default hot path.\n const seq = optSeq ?? (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n\n if (buf) {\n writeV7Bytes(rnds, msecs, seq, buf, offset)\n return buf\n }\n writeV7BytesUnchecked(rnds, msecs, seq, reusableBuf, 0)\n return formatUuidUnchecked(reusableBuf)\n}\n\n/*\n * Overload: no buffer => return a UUID string.\n */\nfunction v7(options?: UuidV7Options, buf?: undefined, offset?: number): string\n/*\n * Overload: caller provides a buffer slice to fill with UUID bytes.\n */\nfunction v7<TBuf extends Uint8Array = Uint8Array>(options: UuidV7Options | undefined, buf: TBuf, offset?: number): TBuf\nfunction v7<TBuf extends Uint8Array = Uint8Array>(options?: UuidV7Options, buf?: TBuf, offset?: number): string | TBuf {\n if (options) {\n return v7WithOptions(options, buf, offset)\n }\n\n // HOT PATH: Inline state management and byte generation for best performance\n const now = Date.now()\n const rnds = rng()\n\n // Update state (inlined for performance)\n if (now > state.msecs) {\n state.seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9]\n state.msecs = now\n } else {\n state.seq = (state.seq + 1) | 0\n if (state.seq < 0) {\n state.seq = 0\n state.msecs++\n }\n }\n\n if (buf) {\n writeV7Bytes(rnds, state.msecs, state.seq, buf, offset ?? 0)\n return buf\n }\n writeV7BytesUnchecked(rnds, state.msecs, state.seq, reusableBuf, 0)\n return formatUuidUnchecked(reusableBuf)\n}\n\nfunction timestamp(id: string): number {\n const bytes = parseUuid(id)\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\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && UUID_V7_REGEX.test(id)\n}\n\n/**\n * Generate a UUID v7 string or write the bytes into a buffer.\n *\n * UUID v7 is a time-ordered UUID that embeds a Unix timestamp in milliseconds,\n * making IDs naturally sortable by creation time. Ideal for database primary keys\n * where chronological ordering improves index performance.\n *\n * @example\n * ```ts\n * import { uuidv7 } from 'uniku/uuid/v7'\n *\n * const id = uuidv7()\n * // => \"018e5e5c-7c8a-7000-8000-000000000000\"\n *\n * // Extract timestamp\n * const ts = uuidv7.timestamp(id)\n * console.log(new Date(ts))\n *\n * // Validate\n * uuidv7.isValid(id) // true\n *\n * // Convert to/from bytes\n * const bytes = uuidv7.toBytes(id)\n * const restored = uuidv7.fromBytes(bytes)\n * ```\n */\nexport const uuidv7: UuidV7 = Object.assign(v7, {\n toBytes: parseUuid,\n fromBytes: formatUuid,\n timestamp,\n isValid,\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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"}
@@ -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"}
@@ -10,9 +10,9 @@ type XidOptions = {
10
10
  */
11
11
  msecs?: number;
12
12
  /**
13
- * Unix timestamp in seconds.
13
+ * Deprecated alias for `msecs`; Unix timestamp in seconds.
14
14
  *
15
- * @deprecated Use `msecs` instead. Will be removed at v1-rc.
15
+ * @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.
16
16
  */
17
17
  secs?: number; /** 24-bit counter. Explicit values do not consume shared state. */
18
18
  counter?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"xid.mjs","names":["XID_BYTES"],"sources":["../../src/xid/base32hex.ts","../../src/xid/xid.ts"],"sourcesContent":["import { BufferError, ParseError } from '../errors'\n\nconst ENCODING = '0123456789abcdefghijklmnopqrstuv'\nconst ENCODING_CODES = Uint8Array.from(ENCODING, (character) => character.charCodeAt(0))\nconst XID_BYTES = 12\nconst XID_LENGTH = 20\nconst ENCODED = new Array<number>(XID_LENGTH)\nconst COUNTER_SUFFIX = new Array<number>(6)\n\nconst DECODING = new Uint8Array(65536)\nDECODING.fill(255)\nfor (let i = 0; i < ENCODING.length; i += 1) {\n DECODING[ENCODING.charCodeAt(i)] = i\n}\n\n/** Encode the 12 canonical XID bytes as lowercase base32hex. */\nexport function encodeBase32Hex(bytes: Uint8Array): string {\n if (bytes.length !== XID_BYTES) {\n throw new BufferError('BYTES_INVALID_LENGTH', `XID bytes must be exactly ${XID_BYTES} bytes, got ${bytes.length}`, {\n strategy: 'xid',\n })\n }\n\n ENCODED[0] = ENCODING_CODES[bytes[0] >> 3]\n ENCODED[1] = ENCODING_CODES[((bytes[0] << 2) | (bytes[1] >> 6)) & 0x1f]\n ENCODED[2] = ENCODING_CODES[(bytes[1] >> 1) & 0x1f]\n ENCODED[3] = ENCODING_CODES[((bytes[1] << 4) | (bytes[2] >> 4)) & 0x1f]\n ENCODED[4] = ENCODING_CODES[((bytes[2] << 1) | (bytes[3] >> 7)) & 0x1f]\n ENCODED[5] = ENCODING_CODES[(bytes[3] >> 2) & 0x1f]\n ENCODED[6] = ENCODING_CODES[((bytes[3] << 3) | (bytes[4] >> 5)) & 0x1f]\n ENCODED[7] = ENCODING_CODES[bytes[4] & 0x1f]\n ENCODED[8] = ENCODING_CODES[bytes[5] >> 3]\n ENCODED[9] = ENCODING_CODES[((bytes[5] << 2) | (bytes[6] >> 6)) & 0x1f]\n ENCODED[10] = ENCODING_CODES[(bytes[6] >> 1) & 0x1f]\n ENCODED[11] = ENCODING_CODES[((bytes[6] << 4) | (bytes[7] >> 4)) & 0x1f]\n ENCODED[12] = ENCODING_CODES[((bytes[7] << 1) | (bytes[8] >> 7)) & 0x1f]\n ENCODED[13] = ENCODING_CODES[(bytes[8] >> 2) & 0x1f]\n ENCODED[14] = ENCODING_CODES[((bytes[8] << 3) | (bytes[9] >> 5)) & 0x1f]\n ENCODED[15] = ENCODING_CODES[bytes[9] & 0x1f]\n ENCODED[16] = ENCODING_CODES[bytes[10] >> 3]\n ENCODED[17] = ENCODING_CODES[((bytes[10] << 2) | (bytes[11] >> 6)) & 0x1f]\n ENCODED[18] = ENCODING_CODES[(bytes[11] >> 1) & 0x1f]\n ENCODED[19] = ENCODING_CODES[(bytes[11] << 4) & 0x1f]\n return String.fromCharCode(...ENCODED)\n}\n\n/** Encode the six XID characters affected by the 24-bit counter. */\nexport function encodeCounterSuffix(lastIdentityByte: number, counter: number): string {\n const high = counter >>> 16\n const middle = (counter >>> 8) & 0xff\n const low = counter & 0xff\n COUNTER_SUFFIX[0] = ENCODING_CODES[((lastIdentityByte << 3) | (high >> 5)) & 0x1f]\n COUNTER_SUFFIX[1] = ENCODING_CODES[high & 0x1f]\n COUNTER_SUFFIX[2] = ENCODING_CODES[middle >> 3]\n COUNTER_SUFFIX[3] = ENCODING_CODES[((middle << 2) | (low >> 6)) & 0x1f]\n COUNTER_SUFFIX[4] = ENCODING_CODES[(low >> 1) & 0x1f]\n COUNTER_SUFFIX[5] = ENCODING_CODES[(low << 4) & 0x1f]\n return String.fromCharCode(...COUNTER_SUFFIX)\n}\n\n/** Decode a canonical lowercase base32hex XID string to its 12 bytes. */\nexport function decodeBase32Hex(id: string): Uint8Array {\n if (id.length !== XID_LENGTH) {\n throw new ParseError('INVALID_LENGTH', `XID string must be ${XID_LENGTH} characters, got ${id.length}`, {\n strategy: 'xid',\n })\n }\n\n const values = new Uint8Array(XID_LENGTH)\n for (let i = 0; i < XID_LENGTH; i += 1) {\n const value = DECODING[id.charCodeAt(i)]\n if (value === 255) {\n throw new ParseError('INVALID_CHAR', `Invalid XID character: ${id[i]}`, { strategy: 'xid' })\n }\n values[i] = value\n }\n\n if (values[19] !== 0 && values[19] !== 16) {\n throw new ParseError('NON_CANONICAL', 'XID trailing bits must be canonically encoded', { strategy: 'xid' })\n }\n\n return new Uint8Array([\n (values[0] << 3) | (values[1] >> 2),\n (values[1] << 6) | (values[2] << 1) | (values[3] >> 4),\n (values[3] << 4) | (values[4] >> 1),\n (values[4] << 7) | (values[5] << 2) | (values[6] >> 3),\n (values[6] << 5) | values[7],\n (values[8] << 3) | (values[9] >> 2),\n (values[9] << 6) | (values[10] << 1) | (values[11] >> 4),\n (values[11] << 4) | (values[12] >> 1),\n (values[12] << 7) | (values[13] << 2) | (values[14] >> 3),\n (values[14] << 5) | values[15],\n (values[16] << 3) | (values[17] >> 2),\n (values[17] << 6) | (values[18] << 1) | (values[19] >> 4),\n ])\n}\n","import { writeTimestamp32 } from '../common/bytes'\nimport { randomBytes, randomUint32 } from '../common/random'\nimport { resolveTimestampSecs } from '../common/timestamp'\nimport { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeBase32Hex, encodeBase32Hex, encodeCounterSuffix } from './base32hex'\n\nconst XID_BYTES = 12\nconst MACHINE_ID_BYTES = 3\nconst MAX_SECS = 0xffffffff\nconst MAX_PROCESS_ID = 0xffff\nconst MAX_COUNTER = 0xffffff\nconst XID_REGEX = /^[0-9a-v]{19}[0g]$/\nconst stringBuffer = new Uint8Array(XID_BYTES)\nlet cachedPrefixSecs = -1\nlet cachedPrefix = ''\n\nexport type XidOptions = {\n /** First three bytes used as the XID machine identity. */\n machineId?: Uint8Array\n /** 16-bit process identity. */\n processId?: number\n /**\n * Unix timestamp in milliseconds. Defaults to Date.now().\n * XID stores whole seconds, so sub-second precision is truncated.\n */\n msecs?: number\n /**\n * Unix timestamp in seconds.\n *\n * @deprecated Use `msecs` instead. Will be removed at v1-rc.\n */\n // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).\n secs?: number\n /** 24-bit counter. Explicit values do not consume shared state. */\n counter?: number\n}\n\nexport type Xid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: XidOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: unknown): id is string\n NIL: string\n MAX: string\n}\n\ntype XidState = {\n machineId: Uint8Array | undefined\n processId: number | undefined\n counter: number | undefined\n}\n\nconst state: XidState = { machineId: undefined, processId: undefined, counter: undefined }\n\n/** Copy pooled bytes before another random draw can refill their backing pool. */\nfunction freshRandom(count: number): Uint8Array {\n return randomBytes(count).slice()\n}\n\nfunction initializeIdentity(): void {\n if (state.machineId === undefined) state.machineId = freshRandom(MACHINE_ID_BYTES)\n if (state.processId === undefined) state.processId = randomUint32() & MAX_PROCESS_ID\n}\n\nfunction nextCounter(): number {\n if (state.counter === undefined) state.counter = randomUint32() & MAX_COUNTER\n state.counter = (state.counter + 1) & MAX_COUNTER\n return state.counter\n}\n\nfunction writeXidBytesUnchecked(\n secs: number,\n machineId: Uint8Array,\n processId: number,\n counter: number,\n buf: Uint8Array,\n offset: number,\n): void {\n writeTimestamp32(buf, offset, secs)\n buf.set(machineId.subarray(0, MACHINE_ID_BYTES), offset + 4)\n buf[offset + 7] = processId >>> 8\n buf[offset + 8] = processId & 0xff\n buf[offset + 9] = counter >>> 16\n buf[offset + 10] = (counter >>> 8) & 0xff\n buf[offset + 11] = counter & 0xff\n}\n\nfunction validateOptions(options: XidOptions): void {\n if (options.machineId !== undefined && options.machineId.length < MACHINE_ID_BYTES) {\n throw new InvalidInputError(\n 'MACHINE_ID_BYTES_TOO_SHORT',\n `Machine ID bytes length must be >= ${MACHINE_ID_BYTES} for XID`,\n { strategy: 'xid' },\n )\n }\n if (options.processId !== undefined && !isIntegerInRange(options.processId, 0, MAX_PROCESS_ID)) {\n throw new InvalidInputError('PROCESS_ID_OUT_OF_RANGE', `Process ID must be between 0 and ${MAX_PROCESS_ID}`, {\n strategy: 'xid',\n })\n }\n if (options.counter !== undefined && !isIntegerInRange(options.counter, 0, MAX_COUNTER)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be between 0 and ${MAX_COUNTER}`, {\n strategy: 'xid',\n })\n }\n}\n\nfunction xidFn(options?: XidOptions, buf?: undefined, offset?: number): string\nfunction xidFn<TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf\nfunction xidFn<TBuf extends Uint8Array = Uint8Array>(options?: XidOptions, buf?: TBuf, offset = 0): string | TBuf {\n if (options === undefined && buf === undefined) {\n initializeIdentity()\n const secs = Math.floor(Date.now() / 1000)\n const counter = nextCounter()\n if (secs !== cachedPrefixSecs) {\n writeXidBytesUnchecked(secs, state.machineId!, state.processId!, counter, stringBuffer, 0)\n cachedPrefix = encodeBase32Hex(stringBuffer).slice(0, 14)\n cachedPrefixSecs = secs\n }\n return cachedPrefix + encodeCounterSuffix(state.processId! & 0xff, counter)\n }\n\n if (options !== undefined) validateOptions(options)\n\n const resolvedSecs = options === undefined ? undefined : resolveTimestampSecs(options, 0, MAX_SECS, 'xid')\n const secs = resolvedSecs ?? Math.floor(Date.now() / 1000)\n if (options?.machineId === undefined || options.processId === undefined) {\n initializeIdentity()\n }\n const machineId = options?.machineId ?? state.machineId!\n const processId = options?.processId ?? state.processId!\n const counter = options?.counter ?? nextCounter()\n\n if (buf !== undefined) {\n if (!isWritableRange(buf, offset, XID_BYTES)) {\n throw new BufferError('BUFFER_OUT_OF_BOUNDS', `XID byte range ${offset}:${offset + 11} is out of buffer bounds`, {\n strategy: 'xid',\n })\n }\n writeXidBytesUnchecked(secs, machineId, processId, counter, buf, offset)\n return buf\n }\n\n writeXidBytesUnchecked(secs, machineId, processId, counter, stringBuffer, 0)\n return encodeBase32Hex(stringBuffer)\n}\n\nfunction toBytes(id: string): Uint8Array {\n return decodeBase32Hex(id)\n}\n\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeBase32Hex(bytes)\n}\n\nfunction timestamp(id: string): number {\n const bytes = decodeBase32Hex(id)\n return (((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0) * 1000\n}\n\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && XID_REGEX.test(id)\n}\n\n/**\n * Generate a 20-character rs/xid-compatible identifier.\n *\n * XID embeds seconds, a lazily-random per-runtime identity, and a shared\n * always-incrementing counter. In Cloudflare Workers, time is frozen during a\n * request, so the counter preserves ordering for IDs made in that request.\n */\nexport const xid: Xid = Object.assign(xidFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n NIL: '0'.repeat(20),\n MAX: encodeBase32Hex(new Uint8Array(12).fill(0xff)),\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
1
+ {"version":3,"file":"xid.mjs","names":["XID_BYTES"],"sources":["../../src/xid/base32hex.ts","../../src/xid/xid.ts"],"sourcesContent":["import { BufferError, ParseError } from '../errors'\n\nconst ENCODING = '0123456789abcdefghijklmnopqrstuv'\nconst ENCODING_CODES = Uint8Array.from(ENCODING, (character) => character.charCodeAt(0))\nconst XID_BYTES = 12\nconst XID_LENGTH = 20\nconst ENCODED = new Array<number>(XID_LENGTH)\nconst COUNTER_SUFFIX = new Array<number>(6)\n\nconst DECODING = new Uint8Array(65536)\nDECODING.fill(255)\nfor (let i = 0; i < ENCODING.length; i += 1) {\n DECODING[ENCODING.charCodeAt(i)] = i\n}\n\n/** Encode the 12 canonical XID bytes as lowercase base32hex. */\nexport function encodeBase32Hex(bytes: Uint8Array): string {\n if (bytes.length !== XID_BYTES) {\n throw new BufferError('BYTES_INVALID_LENGTH', `XID bytes must be exactly ${XID_BYTES} bytes, got ${bytes.length}`, {\n strategy: 'xid',\n })\n }\n\n ENCODED[0] = ENCODING_CODES[bytes[0] >> 3]\n ENCODED[1] = ENCODING_CODES[((bytes[0] << 2) | (bytes[1] >> 6)) & 0x1f]\n ENCODED[2] = ENCODING_CODES[(bytes[1] >> 1) & 0x1f]\n ENCODED[3] = ENCODING_CODES[((bytes[1] << 4) | (bytes[2] >> 4)) & 0x1f]\n ENCODED[4] = ENCODING_CODES[((bytes[2] << 1) | (bytes[3] >> 7)) & 0x1f]\n ENCODED[5] = ENCODING_CODES[(bytes[3] >> 2) & 0x1f]\n ENCODED[6] = ENCODING_CODES[((bytes[3] << 3) | (bytes[4] >> 5)) & 0x1f]\n ENCODED[7] = ENCODING_CODES[bytes[4] & 0x1f]\n ENCODED[8] = ENCODING_CODES[bytes[5] >> 3]\n ENCODED[9] = ENCODING_CODES[((bytes[5] << 2) | (bytes[6] >> 6)) & 0x1f]\n ENCODED[10] = ENCODING_CODES[(bytes[6] >> 1) & 0x1f]\n ENCODED[11] = ENCODING_CODES[((bytes[6] << 4) | (bytes[7] >> 4)) & 0x1f]\n ENCODED[12] = ENCODING_CODES[((bytes[7] << 1) | (bytes[8] >> 7)) & 0x1f]\n ENCODED[13] = ENCODING_CODES[(bytes[8] >> 2) & 0x1f]\n ENCODED[14] = ENCODING_CODES[((bytes[8] << 3) | (bytes[9] >> 5)) & 0x1f]\n ENCODED[15] = ENCODING_CODES[bytes[9] & 0x1f]\n ENCODED[16] = ENCODING_CODES[bytes[10] >> 3]\n ENCODED[17] = ENCODING_CODES[((bytes[10] << 2) | (bytes[11] >> 6)) & 0x1f]\n ENCODED[18] = ENCODING_CODES[(bytes[11] >> 1) & 0x1f]\n ENCODED[19] = ENCODING_CODES[(bytes[11] << 4) & 0x1f]\n return String.fromCharCode(...ENCODED)\n}\n\n/** Encode the six XID characters affected by the 24-bit counter. */\nexport function encodeCounterSuffix(lastIdentityByte: number, counter: number): string {\n const high = counter >>> 16\n const middle = (counter >>> 8) & 0xff\n const low = counter & 0xff\n COUNTER_SUFFIX[0] = ENCODING_CODES[((lastIdentityByte << 3) | (high >> 5)) & 0x1f]\n COUNTER_SUFFIX[1] = ENCODING_CODES[high & 0x1f]\n COUNTER_SUFFIX[2] = ENCODING_CODES[middle >> 3]\n COUNTER_SUFFIX[3] = ENCODING_CODES[((middle << 2) | (low >> 6)) & 0x1f]\n COUNTER_SUFFIX[4] = ENCODING_CODES[(low >> 1) & 0x1f]\n COUNTER_SUFFIX[5] = ENCODING_CODES[(low << 4) & 0x1f]\n return String.fromCharCode(...COUNTER_SUFFIX)\n}\n\n/** Decode a canonical lowercase base32hex XID string to its 12 bytes. */\nexport function decodeBase32Hex(id: string): Uint8Array {\n if (id.length !== XID_LENGTH) {\n throw new ParseError('INVALID_LENGTH', `XID string must be ${XID_LENGTH} characters, got ${id.length}`, {\n strategy: 'xid',\n })\n }\n\n const values = new Uint8Array(XID_LENGTH)\n for (let i = 0; i < XID_LENGTH; i += 1) {\n const value = DECODING[id.charCodeAt(i)]\n if (value === 255) {\n throw new ParseError('INVALID_CHAR', `Invalid XID character: ${id[i]}`, { strategy: 'xid' })\n }\n values[i] = value\n }\n\n if (values[19] !== 0 && values[19] !== 16) {\n throw new ParseError('NON_CANONICAL', 'XID trailing bits must be canonically encoded', { strategy: 'xid' })\n }\n\n return new Uint8Array([\n (values[0] << 3) | (values[1] >> 2),\n (values[1] << 6) | (values[2] << 1) | (values[3] >> 4),\n (values[3] << 4) | (values[4] >> 1),\n (values[4] << 7) | (values[5] << 2) | (values[6] >> 3),\n (values[6] << 5) | values[7],\n (values[8] << 3) | (values[9] >> 2),\n (values[9] << 6) | (values[10] << 1) | (values[11] >> 4),\n (values[11] << 4) | (values[12] >> 1),\n (values[12] << 7) | (values[13] << 2) | (values[14] >> 3),\n (values[14] << 5) | values[15],\n (values[16] << 3) | (values[17] >> 2),\n (values[17] << 6) | (values[18] << 1) | (values[19] >> 4),\n ])\n}\n","import { writeTimestamp32 } from '../common/bytes'\nimport { randomBytes, randomUint32 } from '../common/random'\nimport { resolveTimestampSecs } from '../common/timestamp'\nimport { isIntegerInRange, isWritableRange } from '../common/validation'\nimport { BufferError, InvalidInputError } from '../errors'\nimport { decodeBase32Hex, encodeBase32Hex, encodeCounterSuffix } from './base32hex'\n\nconst XID_BYTES = 12\nconst MACHINE_ID_BYTES = 3\nconst MAX_SECS = 0xffffffff\nconst MAX_PROCESS_ID = 0xffff\nconst MAX_COUNTER = 0xffffff\nconst XID_REGEX = /^[0-9a-v]{19}[0g]$/\nconst stringBuffer = new Uint8Array(XID_BYTES)\nlet cachedPrefixSecs = -1\nlet cachedPrefix = ''\n\nexport type XidOptions = {\n /** First three bytes used as the XID machine identity. */\n machineId?: Uint8Array\n /** 16-bit process identity. */\n processId?: number\n /**\n * Unix timestamp in milliseconds. Defaults to Date.now().\n * XID stores whole seconds, so sub-second precision is truncated.\n */\n msecs?: number\n /**\n * Deprecated alias for `msecs`; Unix timestamp in seconds.\n *\n * @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.\n */\n // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).\n secs?: number\n /** 24-bit counter. Explicit values do not consume shared state. */\n counter?: number\n}\n\nexport type Xid = {\n (): string\n <TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf\n (options?: XidOptions, buf?: undefined, offset?: number): string\n toBytes(id: string): Uint8Array\n fromBytes(bytes: Uint8Array): string\n timestamp(id: string): number\n isValid(id: unknown): id is string\n NIL: string\n MAX: string\n}\n\ntype XidState = {\n machineId: Uint8Array | undefined\n processId: number | undefined\n counter: number | undefined\n}\n\nconst state: XidState = { machineId: undefined, processId: undefined, counter: undefined }\n\n/** Copy pooled bytes before another random draw can refill their backing pool. */\nfunction freshRandom(count: number): Uint8Array {\n return randomBytes(count).slice()\n}\n\nfunction initializeIdentity(): void {\n if (state.machineId === undefined) state.machineId = freshRandom(MACHINE_ID_BYTES)\n if (state.processId === undefined) state.processId = randomUint32() & MAX_PROCESS_ID\n}\n\nfunction nextCounter(): number {\n if (state.counter === undefined) state.counter = randomUint32() & MAX_COUNTER\n state.counter = (state.counter + 1) & MAX_COUNTER\n return state.counter\n}\n\nfunction writeXidBytesUnchecked(\n secs: number,\n machineId: Uint8Array,\n processId: number,\n counter: number,\n buf: Uint8Array,\n offset: number,\n): void {\n writeTimestamp32(buf, offset, secs)\n buf.set(machineId.subarray(0, MACHINE_ID_BYTES), offset + 4)\n buf[offset + 7] = processId >>> 8\n buf[offset + 8] = processId & 0xff\n buf[offset + 9] = counter >>> 16\n buf[offset + 10] = (counter >>> 8) & 0xff\n buf[offset + 11] = counter & 0xff\n}\n\nfunction validateOptions(options: XidOptions): void {\n if (options.machineId !== undefined && options.machineId.length < MACHINE_ID_BYTES) {\n throw new InvalidInputError(\n 'MACHINE_ID_BYTES_TOO_SHORT',\n `Machine ID bytes length must be >= ${MACHINE_ID_BYTES} for XID`,\n { strategy: 'xid' },\n )\n }\n if (options.processId !== undefined && !isIntegerInRange(options.processId, 0, MAX_PROCESS_ID)) {\n throw new InvalidInputError('PROCESS_ID_OUT_OF_RANGE', `Process ID must be between 0 and ${MAX_PROCESS_ID}`, {\n strategy: 'xid',\n })\n }\n if (options.counter !== undefined && !isIntegerInRange(options.counter, 0, MAX_COUNTER)) {\n throw new InvalidInputError('COUNTER_OUT_OF_RANGE', `Counter must be between 0 and ${MAX_COUNTER}`, {\n strategy: 'xid',\n })\n }\n}\n\nfunction xidFn(options?: XidOptions, buf?: undefined, offset?: number): string\nfunction xidFn<TBuf extends Uint8Array = Uint8Array>(options: XidOptions | undefined, buf: TBuf, offset?: number): TBuf\nfunction xidFn<TBuf extends Uint8Array = Uint8Array>(options?: XidOptions, buf?: TBuf, offset = 0): string | TBuf {\n if (options === undefined && buf === undefined) {\n initializeIdentity()\n const secs = Math.floor(Date.now() / 1000)\n const counter = nextCounter()\n if (secs !== cachedPrefixSecs) {\n writeXidBytesUnchecked(secs, state.machineId!, state.processId!, counter, stringBuffer, 0)\n cachedPrefix = encodeBase32Hex(stringBuffer).slice(0, 14)\n cachedPrefixSecs = secs\n }\n return cachedPrefix + encodeCounterSuffix(state.processId! & 0xff, counter)\n }\n\n if (options !== undefined) validateOptions(options)\n\n const resolvedSecs = options === undefined ? undefined : resolveTimestampSecs(options, 0, MAX_SECS, 'xid')\n const secs = resolvedSecs ?? Math.floor(Date.now() / 1000)\n if (options?.machineId === undefined || options.processId === undefined) {\n initializeIdentity()\n }\n const machineId = options?.machineId ?? state.machineId!\n const processId = options?.processId ?? state.processId!\n const counter = options?.counter ?? nextCounter()\n\n if (buf !== undefined) {\n if (!isWritableRange(buf, offset, XID_BYTES)) {\n throw new BufferError('BUFFER_OUT_OF_BOUNDS', `XID byte range ${offset}:${offset + 11} is out of buffer bounds`, {\n strategy: 'xid',\n })\n }\n writeXidBytesUnchecked(secs, machineId, processId, counter, buf, offset)\n return buf\n }\n\n writeXidBytesUnchecked(secs, machineId, processId, counter, stringBuffer, 0)\n return encodeBase32Hex(stringBuffer)\n}\n\nfunction toBytes(id: string): Uint8Array {\n return decodeBase32Hex(id)\n}\n\nfunction fromBytes(bytes: Uint8Array): string {\n return encodeBase32Hex(bytes)\n}\n\nfunction timestamp(id: string): number {\n const bytes = decodeBase32Hex(id)\n return (((bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]) >>> 0) * 1000\n}\n\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && XID_REGEX.test(id)\n}\n\n/**\n * Generate a 20-character rs/xid-compatible identifier.\n *\n * XID embeds seconds, a lazily-random per-runtime identity, and a shared\n * always-incrementing counter. In Cloudflare Workers, time is frozen during a\n * request, so the counter preserves ordering for IDs made in that request.\n */\nexport const xid: Xid = Object.assign(xidFn, {\n toBytes,\n fromBytes,\n timestamp,\n isValid,\n NIL: '0'.repeat(20),\n MAX: encodeBase32Hex(new Uint8Array(12).fill(0xff)),\n})\n\nexport { BufferError, InvalidInputError, ParseError, UniqueIdError } from '../errors'\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "uniku",
4
- "version": "0.5.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.0.0-beta.93",
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.0.0-beta.93",
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": {
@@ -13,11 +13,11 @@ export type TimestampSecsOptions = {
13
13
  */
14
14
  msecs?: number
15
15
  /**
16
- * Timestamp in seconds since the Unix epoch.
16
+ * Deprecated alias for `msecs`; timestamp in seconds since the Unix epoch.
17
17
  *
18
- * @deprecated Use `msecs` instead. Will be removed at v1-rc.
18
+ * @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.
19
19
  */
20
- // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
20
+ // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
21
21
  secs?: number
22
22
  }
23
23
 
@@ -28,7 +28,7 @@ export type TimestampSecsOptions = {
28
28
  * `msecs` is the unified input and is truncated to whole seconds; `secs` is
29
29
  * the deprecated pre-v1 alias. Passing both is an error.
30
30
  *
31
- * TODO(v1-rc): drop the `secs` branch once the alias is removed.
31
+ * TODO(v1-rc.1): drop the `secs` branch once the alias is removed.
32
32
  */
33
33
  export function resolveTimestampSecs(
34
34
  options: TimestampSecsOptions,
@@ -44,11 +44,11 @@ export type KsuidOptions = {
44
44
  */
45
45
  msecs?: number
46
46
  /**
47
- * Timestamp in seconds since the Unix epoch.
47
+ * Deprecated alias for `msecs`; timestamp in seconds since the Unix epoch.
48
48
  *
49
- * @deprecated Use `msecs` instead. Will be removed at v1-rc.
49
+ * @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.
50
50
  */
51
- // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
51
+ // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
52
52
  secs?: number
53
53
  }
54
54
 
@@ -55,11 +55,11 @@ export type NanoidOptions = {
55
55
  */
56
56
  length?: number
57
57
  /**
58
- * Length of generated ID.
58
+ * Deprecated alias for `length`.
59
59
  *
60
- * @deprecated Use `length` instead. Will be removed at v1-rc.
60
+ * @deprecated Use `length` instead. Will be removed in `1.0.0-rc.1`.
61
61
  */
62
- // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
62
+ // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
63
63
  size?: number
64
64
  }
65
65
 
@@ -38,11 +38,11 @@ export type ObjectIdOptions = {
38
38
  */
39
39
  msecs?: number
40
40
  /**
41
- * Timestamp in seconds since the Unix epoch.
41
+ * Deprecated alias for `msecs`; timestamp in seconds since the Unix epoch.
42
42
  *
43
- * @deprecated Use `msecs` instead. Will be removed at v1-rc.
43
+ * @deprecated Use `msecs` instead. Will be removed in `1.0.0-rc.1`.
44
44
  */
45
- // TODO(v1-rc): remove this alias (tracked in docs/STABILITY.md).
45
+ // TODO(v1-rc.1): remove this alias (tracked in docs/STABILITY.md).
46
46
  secs?: number
47
47
  /**
48
48
  * 24-bit counter value (0 to 0xFFFFFF).
@@ -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 UUID v7 suffix to its canonical 16-byte representation. */
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 canonical UUID v7 bytes. */
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 v7 string. */
25
+ /** Convert a TypeID to its UUID string. */
25
26
  toUuid(id: string): string
26
- /** Build a TypeID from a prefix and UUID v7 string. */
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 UUID v7 suffix. */
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 = uuidV7BytesFromSuffix(suffix)
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 formatTypeidFromUuidV7Bytes(prefix: string, bytes: Uint8Array): string {
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 formatTypeidFromUuidV7Bytes(prefix, bytes)
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 formatTypeidFromUuidV7Bytes(prefix, bytes)
294
+ return formatTypeid(prefix, bytes)
317
295
  }
318
296
 
319
297
  function typeidToUuid(id: string): string {
320
- return uuidv7.fromBytes(typeidToBytes(id))
298
+ return formatUuidUnchecked(typeidToBytes(id))
321
299
  }
322
300
 
323
301
  function typeidFromUuid(prefix: string, uuid: string): string {
324
- return formatTypeidFromUuidV7Bytes(prefix, uuidV7BytesFromUuid(uuid))
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 v7, producing sortable domain identifiers
358
- * such as `user_01h2xcejqtf2nbrexx3vqjhp41`.
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