uniku 0.0.3 → 0.0.4

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 CHANGED
@@ -24,6 +24,7 @@ Minimal, tree-shakeable UUID and ULID utilities for every JavaScript runtime.
24
24
  - UUID v7 ([RFC 9562](../../rfcs/rfc9562.txt), time-ordered, monotonic sequencing)
25
25
  - ULID ([spec](../../rfcs/ulid.txt), time-ordered, monotonic sequencing)
26
26
  - CUID2 ([spec](../../rfcs/cuid2.txt), secure, non-time-ordered)
27
+ - Nanoid ([spec](../../rfcs/nanoid.txt), URL-friendly, custom alphabet support)
27
28
 
28
29
  ## Installation
29
30
 
@@ -105,6 +106,27 @@ if (cuid2.isValid(maybeId)) {
105
106
  }
106
107
  ```
107
108
 
109
+ ### Nanoid (URL-friendly, custom alphabet)
110
+
111
+ ```ts
112
+ import { nanoid } from 'uniku/nanoid'
113
+
114
+ const id = nanoid()
115
+ // => "V1StGXR8_Z5jdHi6B-myT"
116
+
117
+ // Custom size
118
+ const shortId = nanoid(10)
119
+ // => "IRFa-VaY2b"
120
+
121
+ // Custom alphabet and size via options
122
+ const hexId = nanoid({ alphabet: '0123456789abcdef', size: 12 })
123
+ // => "4f90d13a42bc"
124
+
125
+ // Validation (default alphabet only)
126
+ nanoid.isValid(id)
127
+ // => true
128
+ ```
129
+
108
130
  ## API Reference
109
131
 
110
132
  ### `uuidv4` (from `uniku/uuid/v4`)
@@ -166,6 +188,22 @@ cuid2.isValid(id: unknown): id is string
166
188
 
167
189
  Note: Unlike UUID and ULID, CUID2 does not provide `toBytes`/`fromBytes` because it is a string-native format with no canonical binary representation.
168
190
 
191
+ ### `nanoid` (from `uniku/nanoid`)
192
+
193
+ ```ts
194
+ nanoid(): string
195
+ nanoid(size: number): string
196
+ nanoid(options: NanoidOptions): string
197
+ nanoid.isValid(id: unknown): id is string
198
+ ```
199
+
200
+ **Options:**
201
+ - `size?: number` - ID length (default 21, max 2048)
202
+ - `alphabet?: string` - Custom alphabet (2-256 printable ASCII characters)
203
+ - `random?: Uint8Array` - Custom random bytes for testing
204
+
205
+ Note: `isValid()` only validates IDs against the default URL-safe alphabet (`A-Za-z0-9_-`). IDs generated with custom alphabets cannot be validated with this method.
206
+
169
207
  ## Documentation
170
208
 
171
209
  For advanced usage, examples, and contributing guidelines, see the [full documentation on GitHub](https://github.com/jkomyno/uniku).
@@ -0,0 +1,39 @@
1
+ //#region src/nanoid/nanoid.d.ts
2
+ /** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */
3
+ declare const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
4
+ type NanoidOptions = {
5
+ /**
6
+ * Random bytes for deterministic output (testing).
7
+ * Must provide enough bytes for rejection sampling (~size * 2).
8
+ */
9
+ random?: Uint8Array;
10
+ /**
11
+ * Custom alphabet to use. Default: URL-safe A-Za-z0-9_-
12
+ * Must be 2-256 printable ASCII characters (32-126) with no duplicates.
13
+ */
14
+ alphabet?: string;
15
+ /**
16
+ * Length of generated ID. Default: 21. Maximum: 2048.
17
+ */
18
+ size?: number;
19
+ };
20
+ type Nanoid = {
21
+ /** Generate nanoid with default settings */
22
+ (): string;
23
+ /** Generate nanoid with custom size */
24
+ (size: number): string;
25
+ /** Generate nanoid with options */
26
+ (options: NanoidOptions): string;
27
+ /**
28
+ * Validate a nanoid string against the default URL-safe alphabet.
29
+ * Note: Does not validate IDs generated with custom alphabets.
30
+ */
31
+ isValid(id: unknown): id is string;
32
+ };
33
+ /**
34
+ * Generate a URL-friendly unique string ID.
35
+ */
36
+ declare const nanoid: Nanoid;
37
+ //#endregion
38
+ export { Nanoid, NanoidOptions, URL_ALPHABET, nanoid };
39
+ //# sourceMappingURL=nanoid.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nanoid.d.mts","names":["nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":[],"mappings":";;AACa,cAAA,YAAA,GAAA,kEAAA;AAMD,KAAA,aAAA,GAAA;EAiBZ;AAsHA;;;WAlIW;;;;;;;;;;;KAYC,MAAA;;;;;;YAMA;;;;;;;;;;cAgHCA,QAAQ"}
@@ -0,0 +1,73 @@
1
+ //#region src/nanoid/nanoid.ts
2
+ /** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */
3
+ const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
4
+ const DEFAULT_SIZE = 21;
5
+ const MAX_SIZE = 2048;
6
+ const NANOID_REGEX = /^[A-Za-z0-9_-]+$/;
7
+ /**
8
+ * Validate alphabet: 2-256 printable ASCII chars, no duplicates
9
+ */
10
+ function validateAlphabet(alphabet) {
11
+ if (alphabet.length < 2) throw new Error("Alphabet must contain at least 2 characters");
12
+ if (alphabet.length > 256) throw new Error("Alphabet must not exceed 256 characters");
13
+ const seen = /* @__PURE__ */ new Set();
14
+ for (const char of alphabet) {
15
+ const code = char.charCodeAt(0);
16
+ if (code < 32 || code > 126) throw new Error("Alphabet must contain only printable ASCII characters (32-126)");
17
+ if (seen.has(char)) throw new Error(`Duplicate character in alphabet: "${char}"`);
18
+ seen.add(char);
19
+ }
20
+ }
21
+ /**
22
+ * Validate size parameter
23
+ */
24
+ function validateSize(size) {
25
+ if (!Number.isInteger(size) || size < 0) throw new RangeError("Size must be a non-negative integer");
26
+ if (size > MAX_SIZE) throw new RangeError(`Size must not exceed ${MAX_SIZE}`);
27
+ }
28
+ function nanoidFn(sizeOrOptions) {
29
+ let size = DEFAULT_SIZE;
30
+ let alphabet = URL_ALPHABET;
31
+ let randomBytes;
32
+ if (typeof sizeOrOptions === "number") size = sizeOrOptions;
33
+ else if (sizeOrOptions) {
34
+ size = sizeOrOptions.size ?? DEFAULT_SIZE;
35
+ alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET;
36
+ randomBytes = sizeOrOptions.random;
37
+ if (sizeOrOptions.alphabet) validateAlphabet(alphabet);
38
+ }
39
+ validateSize(size);
40
+ if (size === 0) return "";
41
+ const mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
42
+ const step = Math.ceil(1.6 * mask * size / alphabet.length);
43
+ let id = "";
44
+ let randomOffset = 0;
45
+ while (id.length < size) {
46
+ let bytes;
47
+ if (randomBytes) {
48
+ if (randomBytes.length - randomOffset < step) throw new Error(`Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`);
49
+ bytes = randomBytes.subarray(randomOffset, randomOffset + step);
50
+ randomOffset += step;
51
+ } else bytes = globalThis.crypto.getRandomValues(new Uint8Array(step));
52
+ for (let i = 0; i < bytes.length && id.length < size; i++) {
53
+ const index = bytes[i] & mask;
54
+ if (index < alphabet.length) id += alphabet[index];
55
+ }
56
+ }
57
+ return id;
58
+ }
59
+ /**
60
+ * Validate a nanoid string against the default URL-safe alphabet.
61
+ * Note: Does not validate IDs generated with custom alphabets.
62
+ */
63
+ function isValid(id) {
64
+ return typeof id === "string" && id.length > 0 && NANOID_REGEX.test(id);
65
+ }
66
+ /**
67
+ * Generate a URL-friendly unique string ID.
68
+ */
69
+ const nanoid = Object.assign(nanoidFn, { isValid });
70
+
71
+ //#endregion
72
+ export { URL_ALPHABET, nanoid };
73
+ //# sourceMappingURL=nanoid.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nanoid.mjs","names":["randomBytes: Uint8Array | undefined","bytes: Uint8Array","nanoid: Nanoid"],"sources":["../../src/nanoid/nanoid.ts"],"sourcesContent":["/** Default URL-safe alphabet (64 characters): A-Z, a-z, 0-9, underscore, hyphen */\nexport const URL_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'\n\nconst DEFAULT_SIZE = 21\nconst MAX_SIZE = 2048\nconst NANOID_REGEX = /^[A-Za-z0-9_-]+$/\n\nexport type NanoidOptions = {\n /**\n * Random bytes for deterministic output (testing).\n * Must provide enough bytes for rejection sampling (~size * 2).\n */\n random?: Uint8Array\n /**\n * Custom alphabet to use. Default: URL-safe A-Za-z0-9_-\n * Must be 2-256 printable ASCII characters (32-126) with no duplicates.\n */\n alphabet?: string\n /**\n * Length of generated ID. Default: 21. Maximum: 2048.\n */\n size?: number\n}\n\nexport type Nanoid = {\n /** Generate nanoid with default settings */\n (): string\n /** Generate nanoid with custom size */\n (size: number): string\n /** Generate nanoid with options */\n (options: NanoidOptions): string\n /**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\n isValid(id: unknown): id is string\n}\n\n/**\n * Validate alphabet: 2-256 printable ASCII chars, no duplicates\n */\nfunction validateAlphabet(alphabet: string): void {\n if (alphabet.length < 2) {\n throw new Error('Alphabet must contain at least 2 characters')\n }\n if (alphabet.length > 256) {\n throw new Error('Alphabet must not exceed 256 characters')\n }\n const seen = new Set<string>()\n for (const char of alphabet) {\n const code = char.charCodeAt(0)\n if (code < 32 || code > 126) {\n throw new Error('Alphabet must contain only printable ASCII characters (32-126)')\n }\n if (seen.has(char)) {\n throw new Error(`Duplicate character in alphabet: \"${char}\"`)\n }\n seen.add(char)\n }\n}\n\n/**\n * Validate size parameter\n */\nfunction validateSize(size: number): void {\n if (!Number.isInteger(size) || size < 0) {\n throw new RangeError('Size must be a non-negative integer')\n }\n if (size > MAX_SIZE) {\n throw new RangeError(`Size must not exceed ${MAX_SIZE}`)\n }\n}\n\n// Overloads\nfunction nanoidFn(): string\nfunction nanoidFn(size: number): string\nfunction nanoidFn(options: NanoidOptions): string\nfunction nanoidFn(sizeOrOptions?: number | NanoidOptions): string {\n let size = DEFAULT_SIZE\n let alphabet = URL_ALPHABET\n let randomBytes: Uint8Array | undefined\n\n if (typeof sizeOrOptions === 'number') {\n size = sizeOrOptions\n } else if (sizeOrOptions) {\n size = sizeOrOptions.size ?? DEFAULT_SIZE\n alphabet = sizeOrOptions.alphabet ?? URL_ALPHABET\n randomBytes = sizeOrOptions.random\n if (sizeOrOptions.alphabet) {\n validateAlphabet(alphabet)\n }\n }\n\n validateSize(size)\n\n if (size === 0) return ''\n\n // Calculate mask: smallest power-of-2 minus 1 that covers alphabet size\n const mask = (2 << (31 - Math.clz32((alphabet.length - 1) | 1))) - 1\n // Calculate step: random bytes per batch (1.6x accounts for rejection)\n const step = Math.ceil((1.6 * mask * size) / alphabet.length)\n\n let id = ''\n let randomOffset = 0\n\n while (id.length < size) {\n let bytes: Uint8Array\n if (randomBytes) {\n if (randomBytes.length - randomOffset < step) {\n throw new Error(\n `Insufficient random bytes: need at least ${step} more, have ${randomBytes.length - randomOffset}`,\n )\n }\n bytes = randomBytes.subarray(randomOffset, randomOffset + step)\n randomOffset += step\n } else {\n bytes = globalThis.crypto.getRandomValues(new Uint8Array(step))\n }\n\n for (let i = 0; i < bytes.length && id.length < size; i++) {\n const index = bytes[i] & mask\n if (index < alphabet.length) {\n id += alphabet[index]\n }\n // Otherwise reject and continue (no modulo bias)\n }\n }\n\n return id\n}\n\n/**\n * Validate a nanoid string against the default URL-safe alphabet.\n * Note: Does not validate IDs generated with custom alphabets.\n */\nfunction isValid(id: unknown): id is string {\n return typeof id === 'string' && id.length > 0 && NANOID_REGEX.test(id)\n}\n\n/**\n * Generate a URL-friendly unique string ID.\n */\nexport const nanoid: Nanoid = Object.assign(nanoidFn, {\n isValid,\n})\n"],"mappings":";;AACA,MAAa,eAAe;AAE5B,MAAM,eAAe;AACrB,MAAM,WAAW;AACjB,MAAM,eAAe;;;;AAoCrB,SAAS,iBAAiB,UAAwB;AAChD,KAAI,SAAS,SAAS,EACpB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,KAAI,SAAS,SAAS,IACpB,OAAM,IAAI,MAAM,0CAA0C;CAE5D,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,OAAO,KAAK,WAAW,EAAE;AAC/B,MAAI,OAAO,MAAM,OAAO,IACtB,OAAM,IAAI,MAAM,iEAAiE;AAEnF,MAAI,KAAK,IAAI,KAAK,CAChB,OAAM,IAAI,MAAM,qCAAqC,KAAK,GAAG;AAE/D,OAAK,IAAI,KAAK;;;;;;AAOlB,SAAS,aAAa,MAAoB;AACxC,KAAI,CAAC,OAAO,UAAU,KAAK,IAAI,OAAO,EACpC,OAAM,IAAI,WAAW,sCAAsC;AAE7D,KAAI,OAAO,SACT,OAAM,IAAI,WAAW,wBAAwB,WAAW;;AAQ5D,SAAS,SAAS,eAAgD;CAChE,IAAI,OAAO;CACX,IAAI,WAAW;CACf,IAAIA;AAEJ,KAAI,OAAO,kBAAkB,SAC3B,QAAO;UACE,eAAe;AACxB,SAAO,cAAc,QAAQ;AAC7B,aAAW,cAAc,YAAY;AACrC,gBAAc,cAAc;AAC5B,MAAI,cAAc,SAChB,kBAAiB,SAAS;;AAI9B,cAAa,KAAK;AAElB,KAAI,SAAS,EAAG,QAAO;CAGvB,MAAM,QAAQ,KAAM,KAAK,KAAK,MAAO,SAAS,SAAS,IAAK,EAAE,IAAK;CAEnE,MAAM,OAAO,KAAK,KAAM,MAAM,OAAO,OAAQ,SAAS,OAAO;CAE7D,IAAI,KAAK;CACT,IAAI,eAAe;AAEnB,QAAO,GAAG,SAAS,MAAM;EACvB,IAAIC;AACJ,MAAI,aAAa;AACf,OAAI,YAAY,SAAS,eAAe,KACtC,OAAM,IAAI,MACR,4CAA4C,KAAK,cAAc,YAAY,SAAS,eACrF;AAEH,WAAQ,YAAY,SAAS,cAAc,eAAe,KAAK;AAC/D,mBAAgB;QAEhB,SAAQ,WAAW,OAAO,gBAAgB,IAAI,WAAW,KAAK,CAAC;AAGjE,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,UAAU,GAAG,SAAS,MAAM,KAAK;GACzD,MAAM,QAAQ,MAAM,KAAK;AACzB,OAAI,QAAQ,SAAS,OACnB,OAAM,SAAS;;;AAMrB,QAAO;;;;;;AAOT,SAAS,QAAQ,IAA2B;AAC1C,QAAO,OAAO,OAAO,YAAY,GAAG,SAAS,KAAK,aAAa,KAAK,GAAG;;;;;AAMzE,MAAaC,SAAiB,OAAO,OAAO,UAAU,EACpD,SACD,CAAC"}
@@ -14,4 +14,4 @@ function rng() {
14
14
 
15
15
  //#endregion
16
16
  export { rng as t };
17
- //# sourceMappingURL=random-kKYJ4YeW.mjs.map
17
+ //# sourceMappingURL=random-Dul5R6oJ.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"random-kKYJ4YeW.mjs","names":[],"sources":["../src/uuid/common/random.ts"],"sourcesContent":["export function randomBytes(out: Uint8Array): void {\n const crypto = globalThis.crypto\n crypto.getRandomValues(out)\n}\n\n/**\n * Generate 16 bytes of cryptographically strong random data.\n * Used internally by UUID generators.\n */\nexport function rng(): Uint8Array {\n const bytes = new Uint8Array(16)\n randomBytes(bytes)\n return bytes\n}\n"],"mappings":";AAAA,SAAgB,YAAY,KAAuB;AAEjD,CADe,WAAW,OACnB,gBAAgB,IAAI;;;;;;AAO7B,SAAgB,MAAkB;CAChC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAY,MAAM;AAClB,QAAO"}
1
+ {"version":3,"file":"random-Dul5R6oJ.mjs","names":[],"sources":["../src/uuid/common/random.ts"],"sourcesContent":["export function randomBytes(out: Uint8Array): void {\n const crypto = globalThis.crypto\n crypto.getRandomValues(out)\n}\n\n/**\n * Generate 16 bytes of cryptographically strong random data.\n * Used internally by UUID generators.\n */\nexport function rng(): Uint8Array {\n const bytes = new Uint8Array(16)\n randomBytes(bytes)\n return bytes\n}\n"],"mappings":";AAAA,SAAgB,YAAY,KAAuB;AAEjD,CADe,WAAW,OACnB,gBAAgB,IAAI;;;;;;AAO7B,SAAgB,MAAkB;CAChC,MAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,aAAY,MAAM;AAClB,QAAO"}
@@ -1,4 +1,4 @@
1
- import { t as rng } from "../random-kKYJ4YeW.mjs";
1
+ import { t as rng } from "../random-Dul5R6oJ.mjs";
2
2
 
3
3
  //#region src/ulid/common/crockford.ts
4
4
  const ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
package/build/uuid/v4.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { t as rng } from "../random-kKYJ4YeW.mjs";
2
- import { n as parseUuid, t as formatUuid } from "../uuid-DWH0cHB2.mjs";
1
+ import { t as rng } from "../random-Dul5R6oJ.mjs";
2
+ import { n as parseUuid, t as formatUuid } from "../uuid-cJ5hOOIR.mjs";
3
3
 
4
4
  //#region src/uuid/v4.ts
5
5
  const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
package/build/uuid/v7.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { t as rng } from "../random-kKYJ4YeW.mjs";
2
- import { n as parseUuid, t as formatUuid } from "../uuid-DWH0cHB2.mjs";
1
+ import { t as rng } from "../random-Dul5R6oJ.mjs";
2
+ import { n as parseUuid, t as formatUuid } from "../uuid-cJ5hOOIR.mjs";
3
3
 
4
4
  //#region src/uuid/v7.ts
5
5
  const 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;
@@ -46,4 +46,4 @@ function parseUuid(value) {
46
46
 
47
47
  //#endregion
48
48
  export { parseUuid as n, formatUuid as t };
49
- //# sourceMappingURL=uuid-DWH0cHB2.mjs.map
49
+ //# sourceMappingURL=uuid-cJ5hOOIR.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"uuid-DWH0cHB2.mjs","names":[],"sources":["../src/uuid/common/hex.ts","../src/uuid/common/uuid.ts"],"sourcesContent":["// Pre-computed lookup table for byte-to-hex conversion (0x00 -> \"00\", 0xff -> \"ff\")\nconst HEX_TABLE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))\n\nexport function bytesToHex(bytes: Uint8Array): string {\n // Build string directly, without intermediate array allocation\n let hex = ''\n for (let i = 0; i < bytes.length; i += 1) {\n hex += HEX_TABLE[bytes[i]]\n }\n return hex\n}\n\n// 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\nfunction 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\nexport function hexToBytes(hex: string, out: Uint8Array): void {\n if (hex.length !== out.length * 2) {\n throw new Error(`hex string must be ${out.length * 2} chars, got ${hex.length}`)\n }\n\n for (let i = 0; i < out.length; i += 1) {\n const hi = hexValue(hex.charCodeAt(i * 2))\n const lo = hexValue(hex.charCodeAt(i * 2 + 1))\n if (hi === -1 || lo === -1) {\n throw new Error('hex string contains invalid characters')\n }\n out[i] = (hi << 4) | lo\n }\n}\n","import { bytesToHex, hexToBytes } from './hex'\n\nconst UUID_BYTE_LENGTH = 16\nconst UUID_STRING_LENGTH = 36\n\nexport function formatUuid(bytes: Uint8Array): string {\n // precondition: bytes.length === UUID_BYTE_LENGTH\n const hex = bytesToHex(bytes)\n\n // Build UUID string with dashes at positions 8, 13, 18, 23\n // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\nexport function parseUuid(value: string): Uint8Array {\n if (value.length !== UUID_STRING_LENGTH) {\n throw new Error('uuid string must be 36 characters')\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 Error('uuid string has invalid separators')\n }\n\n const hex = value.slice(0, 8) + value.slice(9, 13) + value.slice(14, 18) + value.slice(19, 23) + value.slice(24)\n\n const bytes = new Uint8Array(UUID_BYTE_LENGTH)\n hexToBytes(hex, bytes)\n return bytes\n}\n"],"mappings":";AACA,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,GAAG,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAExF,SAAgB,WAAW,OAA2B;CAEpD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,QAAO,UAAU,MAAM;AAEzB,QAAO;;AAIT,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,SAAS,SAAS,MAAsB;AACtC,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO;AAEhB,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,QAAO;;AAGT,SAAgB,WAAW,KAAa,KAAuB;AAC7D,KAAI,IAAI,WAAW,IAAI,SAAS,EAC9B,OAAM,IAAI,MAAM,sBAAsB,IAAI,SAAS,EAAE,cAAc,IAAI,SAAS;AAGlF,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;EACtC,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,EAAE,CAAC;EAC1C,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;AAC9C,MAAI,OAAO,MAAM,OAAO,GACtB,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,KAAM,MAAM,IAAK;;;;;;AC1CzB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAE3B,SAAgB,WAAW,OAA2B;CAEpD,MAAM,MAAM,WAAW,MAAM;AAI7B,QAAO,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,GAAG;;AAG1G,SAAgB,UAAU,OAA2B;AACnD,KAAI,MAAM,WAAW,mBACnB,OAAM,IAAI,MAAM,oCAAoC;AAItD,KAAI,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAC9E,OAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,GAAG;CAEhH,MAAM,QAAQ,IAAI,WAAW,iBAAiB;AAC9C,YAAW,KAAK,MAAM;AACtB,QAAO"}
1
+ {"version":3,"file":"uuid-cJ5hOOIR.mjs","names":[],"sources":["../src/uuid/common/hex.ts","../src/uuid/common/uuid.ts"],"sourcesContent":["// Pre-computed lookup table for byte-to-hex conversion (0x00 -> \"00\", 0xff -> \"ff\")\nconst HEX_TABLE = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))\n\nexport function bytesToHex(bytes: Uint8Array): string {\n // Build string directly, without intermediate array allocation\n let hex = ''\n for (let i = 0; i < bytes.length; i += 1) {\n hex += HEX_TABLE[bytes[i]]\n }\n return hex\n}\n\n// 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\nfunction 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\nexport function hexToBytes(hex: string, out: Uint8Array): void {\n if (hex.length !== out.length * 2) {\n throw new Error(`hex string must be ${out.length * 2} chars, got ${hex.length}`)\n }\n\n for (let i = 0; i < out.length; i += 1) {\n const hi = hexValue(hex.charCodeAt(i * 2))\n const lo = hexValue(hex.charCodeAt(i * 2 + 1))\n if (hi === -1 || lo === -1) {\n throw new Error('hex string contains invalid characters')\n }\n out[i] = (hi << 4) | lo\n }\n}\n","import { bytesToHex, hexToBytes } from './hex'\n\nconst UUID_BYTE_LENGTH = 16\nconst UUID_STRING_LENGTH = 36\n\nexport function formatUuid(bytes: Uint8Array): string {\n // precondition: bytes.length === UUID_BYTE_LENGTH\n const hex = bytesToHex(bytes)\n\n // Build UUID string with dashes at positions 8, 13, 18, 23\n // Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n}\n\nexport function parseUuid(value: string): Uint8Array {\n if (value.length !== UUID_STRING_LENGTH) {\n throw new Error('uuid string must be 36 characters')\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 Error('uuid string has invalid separators')\n }\n\n const hex = value.slice(0, 8) + value.slice(9, 13) + value.slice(14, 18) + value.slice(19, 23) + value.slice(24)\n\n const bytes = new Uint8Array(UUID_BYTE_LENGTH)\n hexToBytes(hex, bytes)\n return bytes\n}\n"],"mappings":";AACA,MAAM,YAAY,MAAM,KAAK,EAAE,QAAQ,KAAK,GAAG,GAAG,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAExF,SAAgB,WAAW,OAA2B;CAEpD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,EACrC,QAAO,UAAU,MAAM;AAEzB,QAAO;;AAIT,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,UAAU;AAEhB,SAAS,SAAS,MAAsB;AACtC,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO;AAEhB,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,KAAI,QAAQ,WAAW,QAAQ,QAC7B,QAAO,OAAO,UAAU;AAE1B,QAAO;;AAGT,SAAgB,WAAW,KAAa,KAAuB;AAC7D,KAAI,IAAI,WAAW,IAAI,SAAS,EAC9B,OAAM,IAAI,MAAM,sBAAsB,IAAI,SAAS,EAAE,cAAc,IAAI,SAAS;AAGlF,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG;EACtC,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,EAAE,CAAC;EAC1C,MAAM,KAAK,SAAS,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;AAC9C,MAAI,OAAO,MAAM,OAAO,GACtB,OAAM,IAAI,MAAM,yCAAyC;AAE3D,MAAI,KAAM,MAAM,IAAK;;;;;;AC1CzB,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAE3B,SAAgB,WAAW,OAA2B;CAEpD,MAAM,MAAM,WAAW,MAAM;AAI7B,QAAO,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,MAAM,GAAG;;AAG1G,SAAgB,UAAU,OAA2B;AACnD,KAAI,MAAM,WAAW,mBACnB,OAAM,IAAI,MAAM,oCAAoC;AAItD,KAAI,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ,IAC9E,OAAM,IAAI,MAAM,qCAAqC;CAGvD,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,IAAI,GAAG,GAAG,MAAM,MAAM,GAAG;CAEhH,MAAM,QAAQ,IAAI,WAAW,iBAAiB;AAC9C,YAAW,KAAK,MAAM;AACtB,QAAO"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "private": false,
3
3
  "name": "uniku",
4
- "version": "0.0.3",
4
+ "version": "0.0.4",
5
5
  "description": "Minimal, tree-shakeable UUID utilities for every JavaScript runtime",
6
6
  "author": {
7
7
  "name": "Alberto Schiabel",
@@ -24,6 +24,7 @@
24
24
  "uuidv7",
25
25
  "ulid",
26
26
  "cuid2",
27
+ "nanoid",
27
28
  "unique",
28
29
  "id",
29
30
  "identifier",
@@ -84,6 +85,14 @@
84
85
  },
85
86
  "default": "./build/cuid2/cuid2.mjs"
86
87
  },
88
+ "./nanoid": {
89
+ "types": "./build/nanoid/nanoid.d.mts",
90
+ "import": {
91
+ "@jkomyno/source": "./src/nanoid/nanoid.ts",
92
+ "default": "./build/nanoid/nanoid.mjs"
93
+ },
94
+ "default": "./build/nanoid/nanoid.mjs"
95
+ },
87
96
  "./package.json": "./package.json"
88
97
  },
89
98
  "dependencies": {