sigilid 1.0.4 → 1.0.5
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/dist/alphabet.cjs +5 -0
- package/dist/alphabet.cjs.map +1 -1
- package/dist/alphabet.js +5 -0
- package/dist/alphabet.js.map +1 -1
- package/dist/index.cjs +8 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/non-secure.cjs +1 -1
- package/dist/non-secure.cjs.map +1 -1
- package/dist/non-secure.js +1 -1
- package/dist/non-secure.js.map +1 -1
- package/dist/prefix.cjs +8 -3
- package/dist/prefix.cjs.map +1 -1
- package/dist/prefix.js +8 -3
- package/dist/prefix.js.map +1 -1
- package/dist/typed.cjs +8 -3
- package/dist/typed.cjs.map +1 -1
- package/dist/typed.js +8 -3
- package/dist/typed.js.map +1 -1
- package/dist/validate.cjs +6 -2
- package/dist/validate.cjs.map +1 -1
- package/dist/validate.js +6 -2
- package/dist/validate.js.map +1 -1
- package/package.json +6 -1
package/dist/alphabet.cjs
CHANGED
|
@@ -56,6 +56,11 @@ function assertLength(length) {
|
|
|
56
56
|
|
|
57
57
|
// src/internal/random.ts
|
|
58
58
|
function randomBytes(count) {
|
|
59
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
60
|
+
throw new Error(
|
|
61
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
62
|
+
);
|
|
63
|
+
}
|
|
59
64
|
const bytes = new Uint8Array(count);
|
|
60
65
|
globalThis.crypto.getRandomValues(bytes);
|
|
61
66
|
return bytes;
|
package/dist/alphabet.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":";;;AAAA,IAAM,iBAAA,GAAoB,CAAA;AAC1B,IAAM,iBAAA,GAAoB,GAAA;AAEnB,SAAS,uBAAuB,QAAA,EAAwB;AAC7D,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,MAAM,IAAI,UAAU,2BAA2B,CAAA;AAAA,EACjD;AACA,EAAA,IAAI,QAAA,CAAS,SAAS,iBAAA,EAAmB;AACvC,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,4BAAA,EAA+B,iBAAiB,CAAA,iBAAA,EAAoB,QAAA,CAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACA,EAAA,IAAI,QAAA,CAAS,SAAS,iBAAA,EAAmB;AACvC,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,2BAAA,EAA8B,iBAAiB,CAAA,iBAAA,EAAoB,QAAA,CAAS,MAAM,CAAA;AAAA,KACpF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,QAAQ,QAAA,EAAU;AAC3B,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG;AAClB,MAAA,MAAM,IAAI,SAAA,CAAU,CAAA,wCAAA,EAA2C,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACxE;AACA,IAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA,EACf;AACF;AASO,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;;;
|
|
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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\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"]}
|
package/dist/alphabet.js
CHANGED
|
@@ -54,6 +54,11 @@ function assertLength(length) {
|
|
|
54
54
|
|
|
55
55
|
// src/internal/random.ts
|
|
56
56
|
function randomBytes(count) {
|
|
57
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
60
|
+
);
|
|
61
|
+
}
|
|
57
62
|
const bytes = new Uint8Array(count);
|
|
58
63
|
globalThis.crypto.getRandomValues(bytes);
|
|
59
64
|
return bytes;
|
package/dist/alphabet.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/random.ts","../src/alphabet.ts"],"names":[],"mappings":";AAAA,IAAM,iBAAA,GAAoB,CAAA;AAC1B,IAAM,iBAAA,GAAoB,GAAA;AAEnB,SAAS,uBAAuB,QAAA,EAAwB;AAC7D,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,MAAM,IAAI,UAAU,2BAA2B,CAAA;AAAA,EACjD;AACA,EAAA,IAAI,QAAA,CAAS,SAAS,iBAAA,EAAmB;AACvC,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,4BAAA,EAA+B,iBAAiB,CAAA,iBAAA,EAAoB,QAAA,CAAS,MAAM,CAAA;AAAA,KACrF;AAAA,EACF;AACA,EAAA,IAAI,QAAA,CAAS,SAAS,iBAAA,EAAmB;AACvC,IAAA,MAAM,IAAI,UAAA;AAAA,MACR,CAAA,2BAAA,EAA8B,iBAAiB,CAAA,iBAAA,EAAoB,QAAA,CAAS,MAAM,CAAA;AAAA,KACpF;AAAA,EACF;AACA,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,KAAA,MAAW,QAAQ,QAAA,EAAU;AAC3B,IAAA,IAAI,IAAA,CAAK,GAAA,CAAI,IAAI,CAAA,EAAG;AAClB,MAAA,MAAM,IAAI,SAAA,CAAU,CAAA,wCAAA,EAA2C,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,IACxE;AACA,IAAA,IAAA,CAAK,IAAI,IAAI,CAAA;AAAA,EACf;AACF;AASO,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;;;
|
|
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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -30,15 +30,22 @@ function assertLength(length) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// src/internal/constants.ts
|
|
34
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
35
|
+
|
|
33
36
|
// src/internal/random.ts
|
|
34
37
|
function randomBytes(count) {
|
|
38
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
39
|
+
throw new Error(
|
|
40
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
41
|
+
);
|
|
42
|
+
}
|
|
35
43
|
const bytes = new Uint8Array(count);
|
|
36
44
|
globalThis.crypto.getRandomValues(bytes);
|
|
37
45
|
return bytes;
|
|
38
46
|
}
|
|
39
47
|
|
|
40
48
|
// src/index.ts
|
|
41
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
42
49
|
var DEFAULT_LENGTH = 21;
|
|
43
50
|
function generateId(length = DEFAULT_LENGTH) {
|
|
44
51
|
assertLength(length);
|
package/dist/index.cjs.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;;;
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.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;;;ACLO,IAAM,gBAAA,GAAmB;;;ACDzB,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,IAAI,OAAO,UAAA,CAAW,MAAA,EAAQ,eAAA,KAAoB,UAAA,EAAY;AAC5D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACNA,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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\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 { DEFAULT_ALPHABET } from \"./internal/constants.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nexport { DEFAULT_ALPHABET };\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.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -28,15 +28,22 @@ function assertLength(length) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// src/internal/constants.ts
|
|
32
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
33
|
+
|
|
31
34
|
// src/internal/random.ts
|
|
32
35
|
function randomBytes(count) {
|
|
36
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
37
|
+
throw new Error(
|
|
38
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
39
|
+
);
|
|
40
|
+
}
|
|
33
41
|
const bytes = new Uint8Array(count);
|
|
34
42
|
globalThis.crypto.getRandomValues(bytes);
|
|
35
43
|
return bytes;
|
|
36
44
|
}
|
|
37
45
|
|
|
38
46
|
// src/index.ts
|
|
39
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
40
47
|
var DEFAULT_LENGTH = 21;
|
|
41
48
|
function generateId(length = DEFAULT_LENGTH) {
|
|
42
49
|
assertLength(length);
|
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;;;
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.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;;;ACLO,IAAM,gBAAA,GAAmB;;;ACDzB,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,IAAI,OAAO,UAAA,CAAW,MAAA,EAAQ,eAAA,KAAoB,UAAA,EAAY;AAC5D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACNA,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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\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 { DEFAULT_ALPHABET } from \"./internal/constants.js\";\nimport { randomBytes } from \"./internal/random.js\";\n\nexport { DEFAULT_ALPHABET };\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/non-secure.cjs
CHANGED
package/dist/non-secure.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/assert.ts","../src/
|
|
1
|
+
{"version":3,"sources":["../src/internal/assert.ts","../src/internal/constants.ts","../src/non-secure.ts"],"names":[],"mappings":";;;AAAA,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;;;ACLO,IAAM,gBAAA,GAAmB,kEAAA;;;ACDhC,IAAM,cAAA,GAAiB,EAAA;AAkBhB,SAAS,mBAAA,CAAoB,SAAiB,cAAA,EAAwB;AAC3E,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,MAAM,OAAO,gBAAA,CAAiB,MAAA;AAC9B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,MAAA,IAAU,iBAAiB,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,IAAI,CAAC,CAAA;AAAA,EAC7D;AACA,EAAA,OAAO,MAAA;AACT","file":"non-secure.cjs","sourcesContent":["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","/**\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","import { assertLength } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a non-cryptographic random ID using `Math.random`.\n *\n * **Not suitable for security-sensitive contexts** such as tokens, session\n * identifiers, or secrets. Use `generateId` from `\"sigilid\"` when in doubt.\n *\n * Useful for cases where performance matters more than entropy quality, such\n * as temporary keys in test fixtures or non-sensitive local identifiers.\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 { generateNonSecureId } from \"sigilid/non-secure\";\n * const id = generateNonSecureId(); // \"a5Fq2J8mXkR9vL3nP0eHw\"\n */\nexport function generateNonSecureId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n let result = \"\";\n const size = DEFAULT_ALPHABET.length;\n for (let i = 0; i < length; i++) {\n result += DEFAULT_ALPHABET[Math.floor(Math.random() * size)];\n }\n return result;\n}\n"]}
|
package/dist/non-secure.js
CHANGED
package/dist/non-secure.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/assert.ts","../src/
|
|
1
|
+
{"version":3,"sources":["../src/internal/assert.ts","../src/internal/constants.ts","../src/non-secure.ts"],"names":[],"mappings":";AAAA,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;;;ACLO,IAAM,gBAAA,GAAmB,kEAAA;;;ACDhC,IAAM,cAAA,GAAiB,EAAA;AAkBhB,SAAS,mBAAA,CAAoB,SAAiB,cAAA,EAAwB;AAC3E,EAAA,YAAA,CAAa,MAAM,CAAA;AACnB,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,MAAM,OAAO,gBAAA,CAAiB,MAAA;AAC9B,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,EAAQ,CAAA,EAAA,EAAK;AAC/B,IAAA,MAAA,IAAU,iBAAiB,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,GAAI,IAAI,CAAC,CAAA;AAAA,EAC7D;AACA,EAAA,OAAO,MAAA;AACT","file":"non-secure.js","sourcesContent":["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","/**\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","import { assertLength } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.js\";\n\nconst DEFAULT_LENGTH = 21;\n\n/**\n * Generates a non-cryptographic random ID using `Math.random`.\n *\n * **Not suitable for security-sensitive contexts** such as tokens, session\n * identifiers, or secrets. Use `generateId` from `\"sigilid\"` when in doubt.\n *\n * Useful for cases where performance matters more than entropy quality, such\n * as temporary keys in test fixtures or non-sensitive local identifiers.\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 { generateNonSecureId } from \"sigilid/non-secure\";\n * const id = generateNonSecureId(); // \"a5Fq2J8mXkR9vL3nP0eHw\"\n */\nexport function generateNonSecureId(length: number = DEFAULT_LENGTH): string {\n assertLength(length);\n let result = \"\";\n const size = DEFAULT_ALPHABET.length;\n for (let i = 0; i < length; i++) {\n result += DEFAULT_ALPHABET[Math.floor(Math.random() * size)];\n }\n return result;\n}\n"]}
|
package/dist/prefix.cjs
CHANGED
|
@@ -38,16 +38,21 @@ function assertPrefix(prefix) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/internal/constants.ts
|
|
42
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
43
|
+
|
|
41
44
|
// src/internal/random.ts
|
|
42
45
|
function randomBytes(count) {
|
|
46
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
43
51
|
const bytes = new Uint8Array(count);
|
|
44
52
|
globalThis.crypto.getRandomValues(bytes);
|
|
45
53
|
return bytes;
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
// src/index.ts
|
|
49
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
50
|
-
|
|
51
56
|
// src/prefix.ts
|
|
52
57
|
var DEFAULT_LENGTH = 21;
|
|
53
58
|
var SEPARATOR = "_";
|
package/dist/prefix.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.ts","../src/internal/random.ts","../src/prefix.ts"],"names":[],"mappings":"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","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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.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"]}
|
package/dist/prefix.js
CHANGED
|
@@ -36,16 +36,21 @@ function assertPrefix(prefix) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// src/internal/constants.ts
|
|
40
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
41
|
+
|
|
39
42
|
// src/internal/random.ts
|
|
40
43
|
function randomBytes(count) {
|
|
44
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
41
49
|
const bytes = new Uint8Array(count);
|
|
42
50
|
globalThis.crypto.getRandomValues(bytes);
|
|
43
51
|
return bytes;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
// src/index.ts
|
|
47
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
48
|
-
|
|
49
54
|
// src/prefix.ts
|
|
50
55
|
var DEFAULT_LENGTH = 21;
|
|
51
56
|
var SEPARATOR = "_";
|
package/dist/prefix.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.ts","../src/internal/random.ts","../src/prefix.ts"],"names":[],"mappings":"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","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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.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"]}
|
package/dist/typed.cjs
CHANGED
|
@@ -38,16 +38,21 @@ function assertPrefix(prefix) {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// src/internal/constants.ts
|
|
42
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
43
|
+
|
|
41
44
|
// src/internal/random.ts
|
|
42
45
|
function randomBytes(count) {
|
|
46
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
43
51
|
const bytes = new Uint8Array(count);
|
|
44
52
|
globalThis.crypto.getRandomValues(bytes);
|
|
45
53
|
return bytes;
|
|
46
54
|
}
|
|
47
55
|
|
|
48
|
-
// src/index.ts
|
|
49
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
50
|
-
|
|
51
56
|
// src/typed.ts
|
|
52
57
|
var DEFAULT_LENGTH = 21;
|
|
53
58
|
var SEPARATOR = "_";
|
package/dist/typed.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.ts","../src/internal/random.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;;;ACdO,IAAM,gBAAA,GAAmB,kEAAA;;;ACDzB,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,IAAI,OAAO,UAAA,CAAW,MAAA,EAAQ,eAAA,KAAoB,UAAA,EAAY;AAC5D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACRA,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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.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
CHANGED
|
@@ -36,16 +36,21 @@ function assertPrefix(prefix) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// src/internal/constants.ts
|
|
40
|
+
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
41
|
+
|
|
39
42
|
// src/internal/random.ts
|
|
40
43
|
function randomBytes(count) {
|
|
44
|
+
if (typeof globalThis.crypto?.getRandomValues !== "function") {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. Use sigilid/non-secure if cryptographic quality is not required."
|
|
47
|
+
);
|
|
48
|
+
}
|
|
41
49
|
const bytes = new Uint8Array(count);
|
|
42
50
|
globalThis.crypto.getRandomValues(bytes);
|
|
43
51
|
return bytes;
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
// src/index.ts
|
|
47
|
-
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
48
|
-
|
|
49
54
|
// src/typed.ts
|
|
50
55
|
var DEFAULT_LENGTH = 21;
|
|
51
56
|
var SEPARATOR = "_";
|
package/dist/typed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/
|
|
1
|
+
{"version":3,"sources":["../src/internal/alphabet.ts","../src/internal/assert.ts","../src/internal/constants.ts","../src/internal/random.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;;;ACdO,IAAM,gBAAA,GAAmB,kEAAA;;;ACDzB,SAAS,YAAY,KAAA,EAA2B;AACrD,EAAA,IAAI,OAAO,UAAA,CAAW,MAAA,EAAQ,eAAA,KAAoB,UAAA,EAAY;AAC5D,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,MAAM,KAAA,GAAQ,IAAI,UAAA,CAAW,KAAK,CAAA;AAClC,EAAA,UAAA,CAAW,MAAA,CAAO,gBAAgB,KAAK,CAAA;AACvC,EAAA,OAAO,KAAA;AACT;;;ACRA,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","/**\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","// 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 if (typeof globalThis.crypto?.getRandomValues !== \"function\") {\n throw new Error(\n \"Web Crypto API (globalThis.crypto.getRandomValues) is not available in this runtime. \" +\n \"Use sigilid/non-secure if cryptographic quality is not required.\",\n );\n }\n const bytes = new Uint8Array(count);\n globalThis.crypto.getRandomValues(bytes);\n return bytes;\n}\n","import { generateFromAlphabet } from \"./internal/alphabet.js\";\nimport { assertLength, assertPrefix } from \"./internal/assert.js\";\nimport { DEFAULT_ALPHABET } from \"./internal/constants.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/validate.cjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
// src/
|
|
3
|
+
// src/internal/constants.ts
|
|
4
4
|
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
5
5
|
|
|
6
6
|
// src/internal/validation.ts
|
|
7
7
|
function isValidIdValue(value, options = {}) {
|
|
8
8
|
if (typeof value !== "string" || value.length === 0) return false;
|
|
9
|
+
if (options.alphabet !== void 0 && options.alphabet.length === 0) {
|
|
10
|
+
throw new TypeError("ValidationOptions.alphabet must not be empty");
|
|
11
|
+
}
|
|
9
12
|
let idPart = value;
|
|
10
13
|
if (options.prefix !== void 0) {
|
|
11
14
|
const expected = `${options.prefix}_`;
|
|
@@ -30,7 +33,8 @@ function isValidId(value, options = {}) {
|
|
|
30
33
|
}
|
|
31
34
|
function assertValidId(value, options = {}) {
|
|
32
35
|
if (!isValidId(value, options)) {
|
|
33
|
-
|
|
36
|
+
const preview = value.length > 8 ? `${value.slice(0, 8)}\u2026` : value;
|
|
37
|
+
throw new TypeError(`Invalid ID: "${preview}"`);
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
function parseId(value, options = {}) {
|
package/dist/validate.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/internal/constants.ts","../src/internal/validation.ts","../src/validate.ts"],"names":[],"mappings":";;;AAIO,IAAM,gBAAA,GAAmB,kEAAA;;;ACKzB,SAAS,cAAA,CAAe,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAY;AACtF,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,KAAW,GAAG,OAAO,KAAA;AAE5D,EAAA,IAAI,QAAQ,QAAA,KAAa,MAAA,IAAa,OAAA,CAAQ,QAAA,CAAS,WAAW,CAAA,EAAG;AACnE,IAAA,MAAM,IAAI,UAAU,8CAA8C,CAAA;AAAA,EACpE;AAEA,EAAA,IAAI,MAAA,GAAS,KAAA;AAEb,EAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAW;AAChC,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,CAAA;AAClC,IAAA,IAAI,CAAC,KAAA,CAAM,UAAA,CAAW,QAAQ,GAAG,OAAO,KAAA;AACxC,IAAA,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,QAAQ,MAAA,KAAW,MAAA,IAAa,OAAO,MAAA,KAAW,OAAA,CAAQ,QAAQ,OAAO,KAAA;AAC7E,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAEhC,EAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,IAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,OAAA,CAAQ,QAAQ,CAAA;AACxC,IAAA,KAAA,MAAW,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,GAAG,OAAO,KAAA;AAAA,IACjC;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;;;AChBO,SAAS,SAAA,CAAU,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAY;AACjF,EAAA,MAAM,IAAA,GAAO,EAAE,QAAA,EAAU,gBAAA,EAAkB,GAAG,OAAA,EAAQ;AACtD,EAAA,OAAO,cAAA,CAAe,OAAO,IAAI,CAAA;AACnC;AAWO,SAAS,aAAA,CAAc,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAS;AAClF,EAAA,IAAI,CAAC,SAAA,CAAU,KAAA,EAAO,OAAO,CAAA,EAAG;AAE9B,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAA,GAAS,CAAA,GAAI,CAAA,EAAG,MAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,KAAA;AAC7D,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,aAAA,EAAgB,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EAChD;AACF;AAYO,SAAS,OAAA,CAAQ,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAW;AAC9E,EAAA,aAAA,CAAc,OAAO,OAAO,CAAA;AAC5B,EAAA,OAAO,KAAA;AACT","file":"validate.cjs","sourcesContent":["/**\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","export interface ValidationOptions {\n /** Expected exact length of the ID portion (excluding prefix and separator). */\n length?: number;\n /** Expected prefix. The separator `_` is assumed. */\n prefix?: string;\n /** Alphabet the ID characters must belong to. Defaults to DEFAULT_ALPHABET. */\n alphabet?: string;\n}\n\nexport function isValidIdValue(value: string, options: ValidationOptions = {}): boolean {\n if (typeof value !== \"string\" || value.length === 0) return false;\n\n if (options.alphabet !== undefined && options.alphabet.length === 0) {\n throw new TypeError(\"ValidationOptions.alphabet must not be empty\");\n }\n\n let idPart = value;\n\n if (options.prefix !== undefined) {\n const expected = `${options.prefix}_`;\n if (!value.startsWith(expected)) return false;\n idPart = value.slice(expected.length);\n }\n\n if (options.length !== undefined && idPart.length !== options.length) return false;\n if (idPart.length === 0) return false;\n\n if (options.alphabet !== undefined) {\n const charSet = new Set(options.alphabet);\n for (const char of idPart) {\n if (!charSet.has(char)) return false;\n }\n }\n\n return true;\n}\n","import { DEFAULT_ALPHABET } from \"./internal/constants.js\";\nimport type { ValidationOptions } from \"./internal/validation.js\";\nimport { isValidIdValue } from \"./internal/validation.js\";\n\nexport type { ValidationOptions };\n\n/**\n * Returns `true` if `value` is a valid ID, `false` otherwise.\n *\n * By default, validates that the value is a non-empty string of characters\n * from the default URL-safe alphabet. Options can constrain length, prefix,\n * and alphabet.\n *\n * @example\n * import { isValidId } from \"sigilid/validate\";\n * isValidId(\"K7gkJ_q3vR2nL8xH5eM0w\"); // true\n * isValidId(\"usr_K7gkJ_q3vR2nL8xH5eM0w\", { prefix: \"usr\" }); // true\n * isValidId(\"\", {}); // false\n */\nexport function isValidId(value: string, options: ValidationOptions = {}): boolean {\n const opts = { alphabet: DEFAULT_ALPHABET, ...options };\n return isValidIdValue(value, opts);\n}\n\n/**\n * Throws a `TypeError` if `value` is not a valid ID.\n *\n * Useful for asserting IDs at API boundaries.\n *\n * @example\n * import { assertValidId } from \"sigilid/validate\";\n * assertValidId(req.params.id); // throws if invalid\n */\nexport function assertValidId(value: string, options: ValidationOptions = {}): void {\n if (!isValidId(value, options)) {\n // Truncate to avoid leaking potentially sensitive values into logs.\n const preview = value.length > 8 ? `${value.slice(0, 8)}…` : value;\n throw new TypeError(`Invalid ID: \"${preview}\"`);\n }\n}\n\n/**\n * Parses and returns the ID if valid, otherwise throws.\n *\n * A convenience wrapper over `assertValidId` for use in pipelines where\n * you want the validated value back from the same call.\n *\n * @example\n * import { parseId } from \"sigilid/validate\";\n * const id = parseId(rawInput); // throws if invalid, returns the same string if valid\n */\nexport function parseId(value: string, options: ValidationOptions = {}): string {\n assertValidId(value, options);\n return value;\n}\n"]}
|
package/dist/validate.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/internal/constants.ts
|
|
2
2
|
var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
|
|
3
3
|
|
|
4
4
|
// src/internal/validation.ts
|
|
5
5
|
function isValidIdValue(value, options = {}) {
|
|
6
6
|
if (typeof value !== "string" || value.length === 0) return false;
|
|
7
|
+
if (options.alphabet !== void 0 && options.alphabet.length === 0) {
|
|
8
|
+
throw new TypeError("ValidationOptions.alphabet must not be empty");
|
|
9
|
+
}
|
|
7
10
|
let idPart = value;
|
|
8
11
|
if (options.prefix !== void 0) {
|
|
9
12
|
const expected = `${options.prefix}_`;
|
|
@@ -28,7 +31,8 @@ function isValidId(value, options = {}) {
|
|
|
28
31
|
}
|
|
29
32
|
function assertValidId(value, options = {}) {
|
|
30
33
|
if (!isValidId(value, options)) {
|
|
31
|
-
|
|
34
|
+
const preview = value.length > 8 ? `${value.slice(0, 8)}\u2026` : value;
|
|
35
|
+
throw new TypeError(`Invalid ID: "${preview}"`);
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
function parseId(value, options = {}) {
|
package/dist/validate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
1
|
+
{"version":3,"sources":["../src/internal/constants.ts","../src/internal/validation.ts","../src/validate.ts"],"names":[],"mappings":";AAIO,IAAM,gBAAA,GAAmB,kEAAA;;;ACKzB,SAAS,cAAA,CAAe,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAY;AACtF,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,MAAA,KAAW,GAAG,OAAO,KAAA;AAE5D,EAAA,IAAI,QAAQ,QAAA,KAAa,MAAA,IAAa,OAAA,CAAQ,QAAA,CAAS,WAAW,CAAA,EAAG;AACnE,IAAA,MAAM,IAAI,UAAU,8CAA8C,CAAA;AAAA,EACpE;AAEA,EAAA,IAAI,MAAA,GAAS,KAAA;AAEb,EAAA,IAAI,OAAA,CAAQ,WAAW,MAAA,EAAW;AAChC,IAAA,MAAM,QAAA,GAAW,CAAA,EAAG,OAAA,CAAQ,MAAM,CAAA,CAAA,CAAA;AAClC,IAAA,IAAI,CAAC,KAAA,CAAM,UAAA,CAAW,QAAQ,GAAG,OAAO,KAAA;AACxC,IAAA,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,QAAA,CAAS,MAAM,CAAA;AAAA,EACtC;AAEA,EAAA,IAAI,QAAQ,MAAA,KAAW,MAAA,IAAa,OAAO,MAAA,KAAW,OAAA,CAAQ,QAAQ,OAAO,KAAA;AAC7E,EAAA,IAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,OAAO,KAAA;AAEhC,EAAA,IAAI,OAAA,CAAQ,aAAa,MAAA,EAAW;AAClC,IAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,OAAA,CAAQ,QAAQ,CAAA;AACxC,IAAA,KAAA,MAAW,QAAQ,MAAA,EAAQ;AACzB,MAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,IAAI,GAAG,OAAO,KAAA;AAAA,IACjC;AAAA,EACF;AAEA,EAAA,OAAO,IAAA;AACT;;;AChBO,SAAS,SAAA,CAAU,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAY;AACjF,EAAA,MAAM,IAAA,GAAO,EAAE,QAAA,EAAU,gBAAA,EAAkB,GAAG,OAAA,EAAQ;AACtD,EAAA,OAAO,cAAA,CAAe,OAAO,IAAI,CAAA;AACnC;AAWO,SAAS,aAAA,CAAc,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAS;AAClF,EAAA,IAAI,CAAC,SAAA,CAAU,KAAA,EAAO,OAAO,CAAA,EAAG;AAE9B,IAAA,MAAM,OAAA,GAAU,KAAA,CAAM,MAAA,GAAS,CAAA,GAAI,CAAA,EAAG,MAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,MAAA,CAAA,GAAM,KAAA;AAC7D,IAAA,MAAM,IAAI,SAAA,CAAU,CAAA,aAAA,EAAgB,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EAChD;AACF;AAYO,SAAS,OAAA,CAAQ,KAAA,EAAe,OAAA,GAA6B,EAAC,EAAW;AAC9E,EAAA,aAAA,CAAc,OAAO,OAAO,CAAA;AAC5B,EAAA,OAAO,KAAA;AACT","file":"validate.js","sourcesContent":["/**\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","export interface ValidationOptions {\n /** Expected exact length of the ID portion (excluding prefix and separator). */\n length?: number;\n /** Expected prefix. The separator `_` is assumed. */\n prefix?: string;\n /** Alphabet the ID characters must belong to. Defaults to DEFAULT_ALPHABET. */\n alphabet?: string;\n}\n\nexport function isValidIdValue(value: string, options: ValidationOptions = {}): boolean {\n if (typeof value !== \"string\" || value.length === 0) return false;\n\n if (options.alphabet !== undefined && options.alphabet.length === 0) {\n throw new TypeError(\"ValidationOptions.alphabet must not be empty\");\n }\n\n let idPart = value;\n\n if (options.prefix !== undefined) {\n const expected = `${options.prefix}_`;\n if (!value.startsWith(expected)) return false;\n idPart = value.slice(expected.length);\n }\n\n if (options.length !== undefined && idPart.length !== options.length) return false;\n if (idPart.length === 0) return false;\n\n if (options.alphabet !== undefined) {\n const charSet = new Set(options.alphabet);\n for (const char of idPart) {\n if (!charSet.has(char)) return false;\n }\n }\n\n return true;\n}\n","import { DEFAULT_ALPHABET } from \"./internal/constants.js\";\nimport type { ValidationOptions } from \"./internal/validation.js\";\nimport { isValidIdValue } from \"./internal/validation.js\";\n\nexport type { ValidationOptions };\n\n/**\n * Returns `true` if `value` is a valid ID, `false` otherwise.\n *\n * By default, validates that the value is a non-empty string of characters\n * from the default URL-safe alphabet. Options can constrain length, prefix,\n * and alphabet.\n *\n * @example\n * import { isValidId } from \"sigilid/validate\";\n * isValidId(\"K7gkJ_q3vR2nL8xH5eM0w\"); // true\n * isValidId(\"usr_K7gkJ_q3vR2nL8xH5eM0w\", { prefix: \"usr\" }); // true\n * isValidId(\"\", {}); // false\n */\nexport function isValidId(value: string, options: ValidationOptions = {}): boolean {\n const opts = { alphabet: DEFAULT_ALPHABET, ...options };\n return isValidIdValue(value, opts);\n}\n\n/**\n * Throws a `TypeError` if `value` is not a valid ID.\n *\n * Useful for asserting IDs at API boundaries.\n *\n * @example\n * import { assertValidId } from \"sigilid/validate\";\n * assertValidId(req.params.id); // throws if invalid\n */\nexport function assertValidId(value: string, options: ValidationOptions = {}): void {\n if (!isValidId(value, options)) {\n // Truncate to avoid leaking potentially sensitive values into logs.\n const preview = value.length > 8 ? `${value.slice(0, 8)}…` : value;\n throw new TypeError(`Invalid ID: \"${preview}\"`);\n }\n}\n\n/**\n * Parses and returns the ID if valid, otherwise throws.\n *\n * A convenience wrapper over `assertValidId` for use in pipelines where\n * you want the validated value back from the same call.\n *\n * @example\n * import { parseId } from \"sigilid/validate\";\n * const id = parseId(rawInput); // throws if invalid, returns the same string if valid\n */\nexport function parseId(value: string, options: ValidationOptions = {}): string {\n assertValidId(value, options);\n return value;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sigilid",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
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",
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"bugs": {
|
|
14
14
|
"url": "https://github.com/moritzmyrz/sigilid/issues"
|
|
15
15
|
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20"
|
|
18
|
+
},
|
|
16
19
|
"keywords": [
|
|
17
20
|
"id",
|
|
18
21
|
"uid",
|
|
@@ -71,6 +74,7 @@
|
|
|
71
74
|
"build": "tsup",
|
|
72
75
|
"test": "vitest run",
|
|
73
76
|
"test:watch": "vitest",
|
|
77
|
+
"test:coverage": "vitest run --coverage",
|
|
74
78
|
"lint": "biome check .",
|
|
75
79
|
"lint:fix": "biome check --write .",
|
|
76
80
|
"typecheck": "tsc --noEmit",
|
|
@@ -78,6 +82,7 @@
|
|
|
78
82
|
},
|
|
79
83
|
"devDependencies": {
|
|
80
84
|
"@biomejs/biome": "*",
|
|
85
|
+
"@vitest/coverage-v8": "^3.0.7",
|
|
81
86
|
"tsup": "^8.3.5",
|
|
82
87
|
"typescript": "*",
|
|
83
88
|
"vitest": "^3.0.7"
|