sigilid 1.0.0 → 1.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
@@ -13,7 +13,7 @@ import { generateId } from "sigilid";
13
13
  generateId(); // "K7gkJ_q3vR2nL8xH5eM0w"
14
14
  ```
15
15
 
16
- For full documentation, see the [repository README](https://github.com/sigilid/sigilid#readme).
16
+ For full documentation, see the [repository README](https://github.com/moritzmyrz/sigilid#readme).
17
17
 
18
18
  ## Install
19
19
 
@@ -23,14 +23,14 @@ npm install sigilid
23
23
 
24
24
  ## Subpath exports
25
25
 
26
- | Import | Description |
27
- |---|---|
28
- | `sigilid` | Secure random ID generator |
26
+ | Import | Description |
27
+ | -------------------- | --------------------------- |
28
+ | `sigilid` | Secure random ID generator |
29
29
  | `sigilid/non-secure` | Math.random-based generator |
30
- | `sigilid/prefix` | Prefixed IDs (`usr_abc123`) |
31
- | `sigilid/typed` | Branded TypeScript ID types |
32
- | `sigilid/validate` | Validation helpers |
33
- | `sigilid/alphabet` | Custom alphabet factory |
30
+ | `sigilid/prefix` | Prefixed IDs (`usr_abc123`) |
31
+ | `sigilid/typed` | Branded TypeScript ID types |
32
+ | `sigilid/validate` | Validation helpers |
33
+ | `sigilid/alphabet` | Custom alphabet factory |
34
34
 
35
35
  ## License
36
36
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":"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","file":"alphabet.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet, validateAlphabetString } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Validates a custom alphabet string.\n *\n * Throws if the alphabet:\n * - is not a string\n * - has fewer than 2 characters\n * - has more than 256 characters\n * - contains duplicate characters\n *\n * @example\n * import { validateAlphabet } from \"sigilid/alphabet\";\n * validateAlphabet(\"abc123\"); // ok\n * validateAlphabet(\"aab\"); // throws: duplicate character\n */\nexport function validateAlphabet(alphabet: string): void {\n validateAlphabetString(alphabet);\n}\n\n/**\n * Creates a secure ID generator bound to a custom alphabet.\n *\n * The alphabet is validated once at creation time. The returned object\n * has a single `generate(length?)` method.\n *\n * @param alphabet - A string of unique characters to draw from.\n * @returns An object with a `generate(length?: number): string` method.\n *\n * @example\n * import { createAlphabet } from \"sigilid/alphabet\";\n * const hex = createAlphabet(\"0123456789abcdef\");\n * hex.generate(32); // \"3f2a8c1d...\" (32 hex characters)\n */\nexport function createAlphabet(alphabet: string): { generate(length?: number): string } {\n validateAlphabetString(alphabet);\n return {\n generate(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(alphabet, length, randomBytes);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":"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","file":"alphabet.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet, validateAlphabetString } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Validates a custom alphabet string.\n *\n * Throws if the alphabet:\n * - is not a string\n * - has fewer than 2 characters\n * - has more than 256 characters\n * - contains duplicate characters\n *\n * @example\n * import { validateAlphabet } from \"sigilid/alphabet\";\n * validateAlphabet(\"abc123\"); // ok\n * validateAlphabet(\"aab\"); // throws: duplicate character\n */\nexport function validateAlphabet(alphabet: string): void {\n validateAlphabetString(alphabet);\n}\n\n/**\n * Creates a secure ID generator bound to a custom alphabet.\n *\n * The alphabet is validated once at creation time. The returned object\n * has a single `generate(length?)` method.\n *\n * @param alphabet - A string of unique characters to draw from.\n * @returns An object with a `generate(length?: number): string` method.\n *\n * @example\n * import { createAlphabet } from \"sigilid/alphabet\";\n * const hex = createAlphabet(\"0123456789abcdef\");\n * hex.generate(32); // \"3f2a8c1d...\" (32 hex characters)\n */\nexport function createAlphabet(alphabet: string): { generate(length?: number): string } {\n validateAlphabetString(alphabet);\n return {\n generate(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(alphabet, length, randomBytes);\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":"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","file":"alphabet.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet, validateAlphabetString } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Validates a custom alphabet string.\n *\n * Throws if the alphabet:\n * - is not a string\n * - has fewer than 2 characters\n * - has more than 256 characters\n * - contains duplicate characters\n *\n * @example\n * import { validateAlphabet } from \"sigilid/alphabet\";\n * validateAlphabet(\"abc123\"); // ok\n * validateAlphabet(\"aab\"); // throws: duplicate character\n */\nexport function validateAlphabet(alphabet: string): void {\n validateAlphabetString(alphabet);\n}\n\n/**\n * Creates a secure ID generator bound to a custom alphabet.\n *\n * The alphabet is validated once at creation time. The returned object\n * has a single `generate(length?)` method.\n *\n * @param alphabet - A string of unique characters to draw from.\n * @returns An object with a `generate(length?: number): string` method.\n *\n * @example\n * import { createAlphabet } from \"sigilid/alphabet\";\n * const hex = createAlphabet(\"0123456789abcdef\");\n * hex.generate(32); // \"3f2a8c1d...\" (32 hex characters)\n */\nexport function createAlphabet(alphabet: string): { generate(length?: number): string } {\n validateAlphabetString(alphabet);\n return {\n generate(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(alphabet, length, randomBytes);\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":"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","file":"alphabet.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet, validateAlphabetString } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Validates a custom alphabet string.\n *\n * Throws if the alphabet:\n * - is not a string\n * - has fewer than 2 characters\n * - has more than 256 characters\n * - contains duplicate characters\n *\n * @example\n * import { validateAlphabet } from \"sigilid/alphabet\";\n * validateAlphabet(\"abc123\"); // ok\n * validateAlphabet(\"aab\"); // throws: duplicate character\n */\nexport function validateAlphabet(alphabet: string): void {\n validateAlphabetString(alphabet);\n}\n\n/**\n * Creates a secure ID generator bound to a custom alphabet.\n *\n * The alphabet is validated once at creation time. The returned object\n * has a single `generate(length?)` method.\n *\n * @param alphabet - A string of unique characters to draw from.\n * @returns An object with a `generate(length?: number): string` method.\n *\n * @example\n * import { createAlphabet } from \"sigilid/alphabet\";\n * const hex = createAlphabet(\"0123456789abcdef\");\n * hex.generate(32); // \"3f2a8c1d...\" (32 hex characters)\n */\nexport function createAlphabet(alphabet: string): { generate(length?: number): string } {\n validateAlphabetString(alphabet);\n return {\n generate(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(alphabet, length, randomBytes);\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;;;ACNO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB;AAEhC,IAAM,cAAA,GAAiB,EAAA;AAYhB,SAAS,UAAA,CAAW,SAAiB,cAAA,EAAwB;AAClE,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACnE","file":"index.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;;;ACNO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB;AAEhC,IAAM,cAAA,GAAiB,EAAA;AAYhB,SAAS,UAAA,CAAW,SAAiB,cAAA,EAAwB;AAClE,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACnE","file":"index.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;;;ACNO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB;AAEhC,IAAM,cAAA,GAAiB,EAAA;AAYhB,SAAS,UAAA,CAAW,SAAiB,cAAA,EAAwB;AAClE,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACnE","file":"index.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;;;ACNO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB;AAEhC,IAAM,cAAA,GAAiB,EAAA;AAYhB,SAAS,UAAA,CAAW,SAAiB,cAAA,EAAwB;AAClE,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACnE","file":"index.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/prefix.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AAeX,SAAS,kBAAA,CAAmB,MAAA,EAAgB,MAAA,GAAiB,cAAA,EAAwB;AAC1F,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AACnC;AAkBO,SAAS,uBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACH;AAEd,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AAAA,EACnC,CAAA;AACF","file":"prefix.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * Generates a prefixed, cryptographically secure ID.\n *\n * The format is `{prefix}_{id}`, e.g. `usr_K7gkJ_q3vR2nL8xH5eM0w`.\n *\n * @param prefix - Alphanumeric prefix starting with a letter, e.g. `\"usr\"`.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A string in the form `{prefix}_{id}`.\n *\n * @example\n * import { generatePrefixedId } from \"sigilid/prefix\";\n * const userId = generatePrefixedId(\"usr\"); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generatePrefixedId(prefix: string, length: number = DEFAULT_LENGTH): string {\n assertPrefix(prefix);\n assertLength(length);\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n}\n\n/**\n * Returns a factory function that generates prefixed IDs with fixed settings.\n *\n * Useful when you generate IDs for a specific entity type throughout your\n * codebase and want to avoid repeating the prefix at every call site.\n *\n * @param prefix - Alphanumeric prefix starting with a letter.\n * @param length - Length of the random portion. Defaults to 21.\n * @returns A zero-argument function that returns a new prefixed ID each call.\n *\n * @example\n * import { createPrefixedGenerator } from \"sigilid/prefix\";\n * const userId = createPrefixedGenerator(\"usr\");\n * userId(); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n * userId(); // \"usr_Xp9mN2qL5vR8nK3eJ7cHw\"\n */\nexport function createPrefixedGenerator(\n prefix: string,\n length: number = DEFAULT_LENGTH,\n): () => string {\n // Validate eagerly so callers get errors at factory creation, not later.\n assertPrefix(prefix);\n assertLength(length);\n return () => {\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/prefix.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AAeX,SAAS,kBAAA,CAAmB,MAAA,EAAgB,MAAA,GAAiB,cAAA,EAAwB;AAC1F,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AACnC;AAkBO,SAAS,uBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACH;AAEd,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AAAA,EACnC,CAAA;AACF","file":"prefix.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * Generates a prefixed, cryptographically secure ID.\n *\n * The format is `{prefix}_{id}`, e.g. `usr_K7gkJ_q3vR2nL8xH5eM0w`.\n *\n * @param prefix - Alphanumeric prefix starting with a letter, e.g. `\"usr\"`.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A string in the form `{prefix}_{id}`.\n *\n * @example\n * import { generatePrefixedId } from \"sigilid/prefix\";\n * const userId = generatePrefixedId(\"usr\"); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generatePrefixedId(prefix: string, length: number = DEFAULT_LENGTH): string {\n assertPrefix(prefix);\n assertLength(length);\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n}\n\n/**\n * Returns a factory function that generates prefixed IDs with fixed settings.\n *\n * Useful when you generate IDs for a specific entity type throughout your\n * codebase and want to avoid repeating the prefix at every call site.\n *\n * @param prefix - Alphanumeric prefix starting with a letter.\n * @param length - Length of the random portion. Defaults to 21.\n * @returns A zero-argument function that returns a new prefixed ID each call.\n *\n * @example\n * import { createPrefixedGenerator } from \"sigilid/prefix\";\n * const userId = createPrefixedGenerator(\"usr\");\n * userId(); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n * userId(); // \"usr_Xp9mN2qL5vR8nK3eJ7cHw\"\n */\nexport function createPrefixedGenerator(\n prefix: string,\n length: number = DEFAULT_LENGTH,\n): () => string {\n // Validate eagerly so callers get errors at factory creation, not later.\n assertPrefix(prefix);\n assertLength(length);\n return () => {\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/prefix.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AAeX,SAAS,kBAAA,CAAmB,MAAA,EAAgB,MAAA,GAAiB,cAAA,EAAwB;AAC1F,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AACnC;AAkBO,SAAS,uBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACH;AAEd,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AAAA,EACnC,CAAA;AACF","file":"prefix.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * Generates a prefixed, cryptographically secure ID.\n *\n * The format is `{prefix}_{id}`, e.g. `usr_K7gkJ_q3vR2nL8xH5eM0w`.\n *\n * @param prefix - Alphanumeric prefix starting with a letter, e.g. `\"usr\"`.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A string in the form `{prefix}_{id}`.\n *\n * @example\n * import { generatePrefixedId } from \"sigilid/prefix\";\n * const userId = generatePrefixedId(\"usr\"); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generatePrefixedId(prefix: string, length: number = DEFAULT_LENGTH): string {\n assertPrefix(prefix);\n assertLength(length);\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n}\n\n/**\n * Returns a factory function that generates prefixed IDs with fixed settings.\n *\n * Useful when you generate IDs for a specific entity type throughout your\n * codebase and want to avoid repeating the prefix at every call site.\n *\n * @param prefix - Alphanumeric prefix starting with a letter.\n * @param length - Length of the random portion. Defaults to 21.\n * @returns A zero-argument function that returns a new prefixed ID each call.\n *\n * @example\n * import { createPrefixedGenerator } from \"sigilid/prefix\";\n * const userId = createPrefixedGenerator(\"usr\");\n * userId(); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n * userId(); // \"usr_Xp9mN2qL5vR8nK3eJ7cHw\"\n */\nexport function createPrefixedGenerator(\n prefix: string,\n length: number = DEFAULT_LENGTH,\n): () => string {\n // Validate eagerly so callers get errors at factory creation, not later.\n assertPrefix(prefix);\n assertLength(length);\n return () => {\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/prefix.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AAeX,SAAS,kBAAA,CAAmB,MAAA,EAAgB,MAAA,GAAiB,cAAA,EAAwB;AAC1F,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,EAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AACnC;AAkBO,SAAS,uBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACH;AAEd,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,OAAO,MAAM;AACX,IAAA,MAAM,EAAA,GAAK,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACrE,IAAA,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,GAAG,EAAE,CAAA,CAAA;AAAA,EACnC,CAAA;AACF","file":"prefix.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * Generates a prefixed, cryptographically secure ID.\n *\n * The format is `{prefix}_{id}`, e.g. `usr_K7gkJ_q3vR2nL8xH5eM0w`.\n *\n * @param prefix - Alphanumeric prefix starting with a letter, e.g. `\"usr\"`.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A string in the form `{prefix}_{id}`.\n *\n * @example\n * import { generatePrefixedId } from \"sigilid/prefix\";\n * const userId = generatePrefixedId(\"usr\"); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generatePrefixedId(prefix: string, length: number = DEFAULT_LENGTH): string {\n assertPrefix(prefix);\n assertLength(length);\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n}\n\n/**\n * Returns a factory function that generates prefixed IDs with fixed settings.\n *\n * Useful when you generate IDs for a specific entity type throughout your\n * codebase and want to avoid repeating the prefix at every call site.\n *\n * @param prefix - Alphanumeric prefix starting with a letter.\n * @param length - Length of the random portion. Defaults to 21.\n * @returns A zero-argument function that returns a new prefixed ID each call.\n *\n * @example\n * import { createPrefixedGenerator } from \"sigilid/prefix\";\n * const userId = createPrefixedGenerator(\"usr\");\n * userId(); // \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n * userId(); // \"usr_Xp9mN2qL5vR8nK3eJ7cHw\"\n */\nexport function createPrefixedGenerator(\n prefix: string,\n length: number = DEFAULT_LENGTH,\n): () => string {\n // Validate eagerly so callers get errors at factory creation, not later.\n assertPrefix(prefix);\n assertLength(length);\n return () => {\n const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n return `${prefix}${SEPARATOR}${id}`;\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/typed.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AA+BX,SAAS,OAAyB,KAAA,EAAwB;AAC/D,EAAA,OAAO,KAAA;AACT;AAmBO,SAAS,oBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACF;AACf,EAAA,IAAI,MAAA,KAAW,MAAA,EAAW,YAAA,CAAa,MAAM,CAAA;AAC7C,EAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,EAAA,OAAO,MAAe;AACpB,IAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACtE,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF","file":"typed.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * A nominal type brand. Attach this to a string type to create a distinct\n * branded string that is not assignable from plain `string`.\n *\n * @example\n * type UserId = Brand<string, \"UserId\">;\n */\nexport type Brand<T, B> = T & { readonly __brand: B };\n\n/**\n * A branded string type for an entity named `T`.\n * Equivalent to `Brand<string, T>`, but more ergonomic for ID types.\n *\n * @example\n * type UserId = IdOf<\"User\">;\n * type PostId = IdOf<\"Post\">;\n */\nexport type IdOf<T extends string> = Brand<string, T>;\n\n/**\n * Casts a plain string to a branded `IdOf<T>`.\n *\n * This is a type-only operation at runtime — no validation is performed.\n * Use `assertValidId` from `\"sigilid/validate\"` first if you need runtime safety.\n *\n * @example\n * import { castId } from \"sigilid/typed\";\n * const id = castId<\"User\">(\"usr_abc123\");\n */\nexport function castId<T extends string>(value: string): IdOf<T> {\n return value as IdOf<T>;\n}\n\n/**\n * Returns a factory function that generates cryptographically secure branded IDs.\n *\n * The type parameter `T` sets the brand. If a prefix is provided, the format\n * is `{prefix}_{id}`, otherwise it is a plain random string.\n *\n * Validation happens at factory creation, not per-call.\n *\n * @param prefix - Optional alphanumeric prefix.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A zero-argument function returning `IdOf<T>`.\n *\n * @example\n * import { createTypedGenerator } from \"sigilid/typed\";\n * const userId = createTypedGenerator<\"User\">(\"usr\");\n * const id = userId(); // IdOf<\"User\"> = \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function createTypedGenerator<T extends string>(\n prefix?: string,\n length: number = DEFAULT_LENGTH,\n): () => IdOf<T> {\n if (prefix !== undefined) assertPrefix(prefix);\n assertLength(length);\n\n return (): IdOf<T> => {\n const raw = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n const value = prefix !== undefined ? `${prefix}${SEPARATOR}${raw}` : raw;\n return value as IdOf<T>;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/typed.ts"],"names":[],"mappings":";;;AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AA+BX,SAAS,OAAyB,KAAA,EAAwB;AAC/D,EAAA,OAAO,KAAA;AACT;AAmBO,SAAS,oBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACF;AACf,EAAA,IAAI,MAAA,KAAW,MAAA,EAAW,YAAA,CAAa,MAAM,CAAA;AAC7C,EAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,EAAA,OAAO,MAAe;AACpB,IAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACtE,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF","file":"typed.cjs","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * A nominal type brand. Attach this to a string type to create a distinct\n * branded string that is not assignable from plain `string`.\n *\n * @example\n * type UserId = Brand<string, \"UserId\">;\n */\nexport type Brand<T, B> = T & { readonly __brand: B };\n\n/**\n * A branded string type for an entity named `T`.\n * Equivalent to `Brand<string, T>`, but more ergonomic for ID types.\n *\n * @example\n * type UserId = IdOf<\"User\">;\n * type PostId = IdOf<\"Post\">;\n */\nexport type IdOf<T extends string> = Brand<string, T>;\n\n/**\n * Casts a plain string to a branded `IdOf<T>`.\n *\n * This is a type-only operation at runtime — no validation is performed.\n * Use `assertValidId` from `\"sigilid/validate\"` first if you need runtime safety.\n *\n * @example\n * import { castId } from \"sigilid/typed\";\n * const id = castId<\"User\">(\"usr_abc123\");\n */\nexport function castId<T extends string>(value: string): IdOf<T> {\n return value as IdOf<T>;\n}\n\n/**\n * Returns a factory function that generates cryptographically secure branded IDs.\n *\n * The type parameter `T` sets the brand. If a prefix is provided, the format\n * is `{prefix}_{id}`, otherwise it is a plain random string.\n *\n * Validation happens at factory creation, not per-call.\n *\n * @param prefix - Optional alphanumeric prefix.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A zero-argument function returning `IdOf<T>`.\n *\n * @example\n * import { createTypedGenerator } from \"sigilid/typed\";\n * const userId = createTypedGenerator<\"User\">(\"usr\");\n * const id = userId(); // IdOf<\"User\"> = \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function createTypedGenerator<T extends string>(\n prefix?: string,\n length: number = DEFAULT_LENGTH,\n): () => IdOf<T> {\n if (prefix !== undefined) assertPrefix(prefix);\n assertLength(length);\n\n return (): IdOf<T> => {\n const raw = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n const value = prefix !== undefined ? `${prefix}${SEPARATOR}${raw}` : raw;\n return value as IdOf<T>;\n };\n}\n"]}
package/dist/typed.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/typed.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AA+BX,SAAS,OAAyB,KAAA,EAAwB;AAC/D,EAAA,OAAO,KAAA;AACT;AAmBO,SAAS,oBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACF;AACf,EAAA,IAAI,MAAA,KAAW,MAAA,EAAW,YAAA,CAAa,MAAM,CAAA;AAC7C,EAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,EAAA,OAAO,MAAe;AACpB,IAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACtE,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF","file":"typed.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 18.17+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * A nominal type brand. Attach this to a string type to create a distinct\n * branded string that is not assignable from plain `string`.\n *\n * @example\n * type UserId = Brand<string, \"UserId\">;\n */\nexport type Brand<T, B> = T & { readonly __brand: B };\n\n/**\n * A branded string type for an entity named `T`.\n * Equivalent to `Brand<string, T>`, but more ergonomic for ID types.\n *\n * @example\n * type UserId = IdOf<\"User\">;\n * type PostId = IdOf<\"Post\">;\n */\nexport type IdOf<T extends string> = Brand<string, T>;\n\n/**\n * Casts a plain string to a branded `IdOf<T>`.\n *\n * This is a type-only operation at runtime — no validation is performed.\n * Use `assertValidId` from `\"sigilid/validate\"` first if you need runtime safety.\n *\n * @example\n * import { castId } from \"sigilid/typed\";\n * const id = castId<\"User\">(\"usr_abc123\");\n */\nexport function castId<T extends string>(value: string): IdOf<T> {\n return value as IdOf<T>;\n}\n\n/**\n * Returns a factory function that generates cryptographically secure branded IDs.\n *\n * The type parameter `T` sets the brand. If a prefix is provided, the format\n * is `{prefix}_{id}`, otherwise it is a plain random string.\n *\n * Validation happens at factory creation, not per-call.\n *\n * @param prefix - Optional alphanumeric prefix.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A zero-argument function returning `IdOf<T>`.\n *\n * @example\n * import { createTypedGenerator } from \"sigilid/typed\";\n * const userId = createTypedGenerator<\"User\">(\"usr\");\n * const id = userId(); // IdOf<\"User\"> = \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function createTypedGenerator<T extends string>(\n prefix?: string,\n length: number = DEFAULT_LENGTH,\n): () => IdOf<T> {\n if (prefix !== undefined) assertPrefix(prefix);\n assertLength(length);\n\n return (): IdOf<T> => {\n const raw = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n const value = prefix !== undefined ? `${prefix}${SEPARATOR}${raw}` : raw;\n return value as IdOf<T>;\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/index.ts","../src/typed.ts"],"names":[],"mappings":";AAiCO,SAAS,oBAAA,CACd,QAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,MAAM,eAAe,QAAA,CAAS,MAAA;AAG9B,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,OAAO,IAAA,GAAO,YAAA,EAAc,IAAA,GAAQ,IAAA,IAAQ,CAAA,GAAK,CAAA;AAGjD,EAAA,MAAM,mBAAmB,IAAA,CAAK,IAAA,CAAM,GAAA,GAAM,IAAA,GAAO,SAAU,YAAY,CAAA;AACvE,EAAA,IAAI,MAAA,GAAS,EAAA;AAEb,EAAA,OAAO,MAAA,CAAO,SAAS,MAAA,EAAQ;AAC7B,IAAA,MAAM,KAAA,GAAQ,SAAS,gBAAgB,CAAA;AACvC,IAAA,KAAA,IAAS,CAAA,GAAI,GAAG,CAAA,GAAI,KAAA,CAAM,UAAU,MAAA,CAAO,MAAA,GAAS,QAAQ,CAAA,EAAA,EAAK;AAC/D,MAAA,MAAM,IAAA,GAAQ,KAAA,CAAM,CAAC,CAAA,GAAe,IAAA;AACpC,MAAA,IAAI,OAAO,YAAA,EAAc;AACvB,QAAA,MAAA,IAAU,SAAS,IAAI,CAAA;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA;AACT;;;AC3DA,IAAM,UAAA,GAAa,CAAA;AACnB,IAAM,UAAA,GAAa,GAAA;AAEZ,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,OAAO,SAAA,CAAU,MAAM,KAAK,MAAA,GAAS,UAAA,IAAc,SAAS,UAAA,EAAY;AAC3E,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,qCAAA,EAAwC,UAAU,CAAA,KAAA,EAAQ,UAAU,SAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACF;AAEO,SAAS,aAAa,MAAA,EAAsB;AACjD,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,WAAW,CAAA,EAAG;AACrD,IAAA,MAAM,IAAI,UAAU,mCAAmC,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,CAAC,wBAAA,CAAyB,IAAA,CAAK,MAAM,CAAA,EAAG;AAC1C,IAAA,MAAM,IAAI,UAAU,qEAAqE,CAAA;AAAA,EAC3F;AACF;;;ACfO,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACCO,IAAM,gBAAA,GAAmB,kEAAA;;;ACHhC,IAAM,cAAA,GAAiB,EAAA;AACvB,IAAM,SAAA,GAAY,GAAA;AA+BX,SAAS,OAAyB,KAAA,EAAwB;AAC/D,EAAA,OAAO,KAAA;AACT;AAmBO,SAAS,oBAAA,CACd,MAAA,EACA,MAAA,GAAiB,cAAA,EACF;AACf,EAAA,IAAI,MAAA,KAAW,MAAA,EAAW,YAAA,CAAa,MAAM,CAAA;AAC7C,EAAA,YAAA,CAAa,MAAM,CAAA;AAEnB,EAAA,OAAO,MAAe;AACpB,IAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,gBAAA,EAAkB,MAAA,EAAQ,WAAW,CAAA;AACtE,IAAA,MAAM,KAAA,GAAQ,WAAW,MAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAG,SAAS,CAAA,EAAG,GAAG,CAAA,CAAA,GAAK,GAAA;AACrE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF","file":"typed.js","sourcesContent":["const MIN_ALPHABET_SIZE = 2;\nconst MAX_ALPHABET_SIZE = 256;\n\nexport function validateAlphabetString(alphabet: string): void {\n if (typeof alphabet !== \"string\") {\n throw new TypeError(\"Alphabet must be a string\");\n }\n if (alphabet.length < MIN_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n if (alphabet.length > MAX_ALPHABET_SIZE) {\n throw new RangeError(\n `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`,\n );\n }\n const seen = new Set<string>();\n for (const char of alphabet) {\n if (seen.has(char)) {\n throw new TypeError(`Alphabet contains duplicate character: \"${char}\"`);\n }\n seen.add(char);\n }\n}\n\n/**\n * Generates an ID of `length` characters drawn from `alphabet` using\n * rejection sampling to avoid modulo bias.\n *\n * The caller is responsible for providing valid `alphabet` and `length` values.\n * Use `validateAlphabetString` and `assertLength` before calling this.\n */\nexport function generateFromAlphabet(\n alphabet: string,\n length: number,\n getBytes: (count: number) => Uint8Array,\n): string {\n const alphabetSize = alphabet.length;\n\n // Find the smallest bitmask >= alphabetSize to reduce rejection rate.\n let mask = 1;\n while (mask < alphabetSize) mask = (mask << 1) | 1;\n\n // Overshoot buffer size to reduce round-trips. Most cases complete in one pass.\n const bufferMultiplier = Math.ceil((1.6 * mask * length) / alphabetSize);\n let result = \"\";\n\n while (result.length < length) {\n const bytes = getBytes(bufferMultiplier);\n for (let i = 0; i < bytes.length && result.length < length; i++) {\n const byte = (bytes[i] as number) & mask;\n if (byte < alphabetSize) {\n result += alphabet[byte];\n }\n }\n }\n\n return result;\n}\n","const MIN_LENGTH = 1;\nconst MAX_LENGTH = 255;\n\nexport function assertLength(length: number): void {\n if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {\n throw new RangeError(\n `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`,\n );\n }\n}\n\nexport function assertPrefix(prefix: string): void {\n if (typeof prefix !== \"string\" || prefix.length === 0) {\n throw new TypeError(\"Prefix must be a non-empty string\");\n }\n if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {\n throw new TypeError(\"Prefix must start with a letter and contain only letters and digits\");\n }\n}\n","// Returns a Uint8Array of `count` cryptographically secure random bytes.\n// Uses globalThis.crypto (Web Crypto API) — available in Node 20+,\n// all modern browsers, Cloudflare Workers, Deno, and Bun.\nexport function randomBytes(count: number): Uint8Array {\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\n/**\n * URL-safe alphabet used by default. 64 characters: A-Z, a-z, 0-9, _, -.\n * Chosen to be safe in URLs, filenames, and most log formats without encoding.\n */\nexport const DEFAULT_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a cryptographically secure random ID.\n *\n * @param length - Number of characters (1–255). Defaults to 21.\n * @returns A random URL-safe string of the requested length.\n *\n * @example\n * import { generateId } from \"sigilid\";\n * const id = generateId(); // \"K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function generateId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n}\n","import { DEFAULT_ALPHABET } from \"./index.js\";\nimport { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nconst DEFAULT_LENGTH = 21;\nconst SEPARATOR = \"_\";\n\n/**\n * A nominal type brand. Attach this to a string type to create a distinct\n * branded string that is not assignable from plain `string`.\n *\n * @example\n * type UserId = Brand<string, \"UserId\">;\n */\nexport type Brand<T, B> = T & { readonly __brand: B };\n\n/**\n * A branded string type for an entity named `T`.\n * Equivalent to `Brand<string, T>`, but more ergonomic for ID types.\n *\n * @example\n * type UserId = IdOf<\"User\">;\n * type PostId = IdOf<\"Post\">;\n */\nexport type IdOf<T extends string> = Brand<string, T>;\n\n/**\n * Casts a plain string to a branded `IdOf<T>`.\n *\n * This is a type-only operation at runtime — no validation is performed.\n * Use `assertValidId` from `\"sigilid/validate\"` first if you need runtime safety.\n *\n * @example\n * import { castId } from \"sigilid/typed\";\n * const id = castId<\"User\">(\"usr_abc123\");\n */\nexport function castId<T extends string>(value: string): IdOf<T> {\n return value as IdOf<T>;\n}\n\n/**\n * Returns a factory function that generates cryptographically secure branded IDs.\n *\n * The type parameter `T` sets the brand. If a prefix is provided, the format\n * is `{prefix}_{id}`, otherwise it is a plain random string.\n *\n * Validation happens at factory creation, not per-call.\n *\n * @param prefix - Optional alphanumeric prefix.\n * @param length - Length of the random portion (1–255). Defaults to 21.\n * @returns A zero-argument function returning `IdOf<T>`.\n *\n * @example\n * import { createTypedGenerator } from \"sigilid/typed\";\n * const userId = createTypedGenerator<\"User\">(\"usr\");\n * const id = userId(); // IdOf<\"User\"> = \"usr_K7gkJ_q3vR2nL8xH5eM0w\"\n */\nexport function createTypedGenerator<T extends string>(\n prefix?: string,\n length: number = DEFAULT_LENGTH,\n): () => IdOf<T> {\n if (prefix !== undefined) assertPrefix(prefix);\n assertLength(length);\n\n return (): IdOf<T> => {\n const raw = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);\n const value = prefix !== undefined ? `${prefix}${SEPARATOR}${raw}` : raw;\n return value as IdOf<T>;\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sigilid",
3
- "version": "1.0.0",
3
+ "version": "1.0.4",
4
4
  "description": "A tiny, tree-shakeable ID toolkit for TypeScript apps. Secure core with optional subpath exports for prefixed IDs, typed IDs, validation, and custom alphabets.",
5
5
  "license": "MIT",
6
6
  "author": "moritzmyrz",