sigilid 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,16 +4,50 @@
4
4
 
5
5
  [![CI](https://github.com/moritzmyrz/sigilid/actions/workflows/ci.yml/badge.svg)](https://github.com/moritzmyrz/sigilid/actions/workflows/ci.yml)
6
6
  [![npm version](https://img.shields.io/npm/v/sigilid)](https://www.npmjs.com/package/sigilid)
7
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/sigilid)](https://bundlephobia.com/package/sigilid)
8
+ [![install size](https://packagephobia.com/badge?p=sigilid)](https://packagephobia.com/result?p=sigilid)
7
9
  [![license](https://img.shields.io/npm/l/sigilid)](./LICENSE)
8
10
 
9
- Zero runtime dependencies. ESM-first with CJS compatibility. Strong TypeScript support.
11
+ `sigilid` gives you a secure, URL-safe ID generator as a zero-dependency ESM-first package. The root import is intentionally minimal — extra utilities like prefixed IDs, typed IDs, and validation live in subpath exports so your bundler only pulls in what you actually use.
10
12
 
11
13
  ```ts
12
14
  import { generateId } from "sigilid";
13
- generateId(); // "K7gkJ_q3vR2nL8xH5eM0w"
15
+
16
+ const id = generateId(); // "K7gkJ_q3vR2nL8xH5eM0w"
14
17
  ```
15
18
 
16
- For full documentation, see the [repository README](https://github.com/moritzmyrz/sigilid#readme).
19
+ ---
20
+
21
+ ## Features
22
+
23
+ - **Cryptographically secure** — uses `crypto.getRandomValues`, not `Math.random`
24
+ - **URL-safe by default** — 64-character alphabet: `A-Z a-z 0-9 _ -`
25
+ - **Tree-shakeable** — subpath exports mean your bundle only includes what you import
26
+ - **Zero runtime dependencies** — no third-party code in production output
27
+ - **ESM-first with CJS compatibility** — works in modern Node, edge runtimes, and all major bundlers
28
+ - **Strong TypeScript support** — strict types, branded ID types, precise inference
29
+ - **Predictable behavior** — explicit errors on invalid input, no silent failures
30
+ - **One package, six entrypoints** — `install sigilid`, then import only what you need
31
+
32
+ ---
33
+
34
+ ## Bundle size
35
+
36
+ All sizes are brotli-compressed. Each subpath is a standalone module — importing
37
+ one never pulls in the others.
38
+
39
+ | Import | Size |
40
+ | -------------------- | ------ |
41
+ | `sigilid` | ~348 B |
42
+ | `sigilid/non-secure` | ~222 B |
43
+ | `sigilid/prefix` | ~496 B |
44
+ | `sigilid/typed` | ~486 B |
45
+ | `sigilid/validate` | ~366 B |
46
+ | `sigilid/alphabet` | ~381 B |
47
+
48
+ Zero runtime dependencies. [Verified by size-limit on every PR.](link to workflow)
49
+
50
+ ---
17
51
 
18
52
  ## Install
19
53
 
@@ -21,17 +55,319 @@ For full documentation, see the [repository README](https://github.com/moritzmyr
21
55
  npm install sigilid
22
56
  ```
23
57
 
24
- ## Subpath exports
58
+ ```bash
59
+ pnpm add sigilid
60
+ ```
61
+
62
+ ```bash
63
+ yarn add sigilid
64
+ ```
65
+
66
+ Node 20+ required. Works in all modern runtimes that expose the Web Crypto API (`globalThis.crypto`).
67
+
68
+ ---
69
+
70
+ ## Quick start
71
+
72
+ ```ts
73
+ import { generateId } from "sigilid";
74
+
75
+ // Default: 21 URL-safe characters using crypto.getRandomValues
76
+ generateId(); // "K7gkJ_q3vR2nL8xH5eM0w"
77
+ generateId(12); // "aX4_p9Qr2mNs"
78
+ ```
79
+
80
+ ---
81
+
82
+ ## Why sigilid?
83
+
84
+ Most apps eventually need more than a plain random string. They need prefixed IDs to distinguish entity types in logs, branded TypeScript types to prevent mixing `userId` and `postId`, and validation helpers at API boundaries.
85
+
86
+ `sigilid` is a focused toolkit for exactly that. The root package is as lean as it gets. Everything optional is a subpath import.
87
+
88
+ ---
89
+
90
+ ## When to use the root import vs subpath exports
91
+
92
+ | If you need... | Import from... |
93
+ | --------------------------------- | -------------------- |
94
+ | A secure random URL-safe ID | `sigilid` |
95
+ | A non-crypto ID (tests, fixtures) | `sigilid/non-secure` |
96
+ | Prefixed IDs like `usr_abc123` | `sigilid/prefix` |
97
+ | Branded TypeScript ID types | `sigilid/typed` |
98
+ | Validation at API boundaries | `sigilid/validate` |
99
+ | IDs from a custom character set | `sigilid/alphabet` |
100
+
101
+ The root import has no dependency on any of the subpath modules. Importing only `sigilid` will not pull in prefix, validation, or alphabet code.
102
+
103
+ ---
104
+
105
+ ## API reference
106
+
107
+ ### `sigilid` — secure root
108
+
109
+ ```ts
110
+ import { generateId, DEFAULT_ALPHABET } from "sigilid";
111
+
112
+ generateId(); // 21-character secure ID
113
+ generateId(12); // 12-character secure ID
114
+
115
+ console.log(DEFAULT_ALPHABET);
116
+ // "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-"
117
+ ```
118
+
119
+ `generateId` throws a `RangeError` if `length` is outside the range 1–255 or is not an integer.
120
+
121
+ ---
122
+
123
+ ### `sigilid/non-secure` — Math.random-based
124
+
125
+ ```ts
126
+ import { generateNonSecureId } from "sigilid/non-secure";
127
+
128
+ generateNonSecureId(); // 21-character ID using Math.random
129
+ generateNonSecureId(8); // 8-character ID
130
+ ```
131
+
132
+ **Not suitable for tokens, secrets, or session identifiers.** Use this only when you explicitly do not need cryptographic quality — for example, in test fixtures or non-sensitive local keys.
133
+
134
+ ---
135
+
136
+ ### `sigilid/prefix` — prefixed IDs
137
+
138
+ ```ts
139
+ import { generatePrefixedId, createPrefixedGenerator } from "sigilid/prefix";
140
+
141
+ // One-off prefixed ID
142
+ generatePrefixedId("usr"); // "usr_K7gkJ_q3vR2nL8xH5eM0w"
143
+ generatePrefixedId("doc", 10); // "doc_aX4p9Qr2mN"
144
+
145
+ // Factory for repeated use
146
+ const userId = createPrefixedGenerator("usr");
147
+ userId(); // "usr_K7gkJ_q3vR2nL8xH5eM0w"
148
+ userId(); // "usr_Xp9mN2qL5vR8nK3eJ7cHw"
149
+ ```
150
+
151
+ Prefix rules:
152
+
153
+ - Must start with a letter
154
+ - Must contain only letters and digits
155
+ - Separator is always `_`
156
+
157
+ Throws `TypeError` for invalid prefixes. Throws `RangeError` for invalid lengths.
158
+
159
+ ---
160
+
161
+ ### `sigilid/typed` — branded TypeScript ID types
162
+
163
+ ```ts
164
+ import { createTypedGenerator, castId } from "sigilid/typed";
165
+ import type { IdOf, Brand } from "sigilid/typed";
166
+
167
+ // Define typed generators for your entities
168
+ const userId = createTypedGenerator<"User">("usr");
169
+ const postId = createTypedGenerator<"Post">("post");
170
+
171
+ const uid = userId(); // IdOf<"User"> = "usr_K7gkJ_q3vR2nL8xH5eM0w"
172
+ const pid = postId(); // IdOf<"Post">
173
+
174
+ // TypeScript prevents mixing them up
175
+ function getUser(id: IdOf<"User">) {
176
+ /* ... */
177
+ }
178
+ getUser(uid); // ✓
179
+ getUser(pid); // ✗ type error
180
+
181
+ // Cast an untyped string at a trust boundary
182
+ const fromDb = castId<"User">(row.user_id);
183
+
184
+ // Unprefixed typed ID
185
+ const tokenGen = createTypedGenerator<"Token">();
186
+ const token = tokenGen(); // IdOf<"Token">, no prefix
187
+ ```
188
+
189
+ `Brand<T, B>` and `IdOf<T>` are pure type-level utilities — no runtime cost.
190
+
191
+ ---
192
+
193
+ ### `sigilid/validate` — validation helpers
194
+
195
+ ```ts
196
+ import { isValidId, assertValidId, parseId } from "sigilid/validate";
197
+ import type { ValidationOptions } from "sigilid/validate";
198
+
199
+ // Boolean check
200
+ isValidId("K7gkJ_q3vR2nL8xH5eM0w"); // true
201
+ isValidId("bad id!"); // false
202
+ isValidId("usr_K7gkJ_q3vR2nL8xH5eM0w", { prefix: "usr" }); // true
203
+ isValidId("abc123", { length: 6, alphabet: "abc123def456" }); // true
204
+
205
+ // Throws TypeError if invalid — good for API boundaries
206
+ assertValidId(req.params.id);
207
+ assertValidId(req.params.id, { prefix: "usr" });
208
+
209
+ // Returns the value if valid, throws if not — useful in pipelines
210
+ const id = parseId(rawInput);
211
+ const id = parseId(rawInput, { prefix: "usr", length: 21 });
212
+ ```
213
+
214
+ `ValidationOptions`:
215
+
216
+ | Option | Type | Description |
217
+ | ---------- | -------- | --------------------------------------------------------------------- |
218
+ | `length` | `number` | Expected length of the ID (or ID portion after prefix) |
219
+ | `prefix` | `string` | Expected prefix; separator `_` is assumed |
220
+ | `alphabet` | `string` | Characters the ID must be drawn from (defaults to `DEFAULT_ALPHABET`) |
221
+
222
+ ---
223
+
224
+ ### `sigilid/alphabet` — custom alphabets
225
+
226
+ ```ts
227
+ import { createAlphabet, validateAlphabet } from "sigilid/alphabet";
228
+
229
+ // Validate first (optional — createAlphabet validates internally)
230
+ validateAlphabet("0123456789abcdef");
231
+
232
+ // Create a bound generator
233
+ const hex = createAlphabet("0123456789abcdef");
234
+ hex.generate(); // 21-character hex string
235
+ hex.generate(32); // 32-character hex string
236
+
237
+ // Binary IDs (contrived, but works)
238
+ const binary = createAlphabet("01");
239
+ binary.generate(16); // "1010011001110101"
240
+ ```
241
+
242
+ `createAlphabet` throws immediately if:
243
+
244
+ - the alphabet has fewer than 2 characters
245
+ - the alphabet has more than 256 characters
246
+ - the alphabet contains duplicate characters
247
+
248
+ `generate(length?)` uses rejection sampling to avoid modulo bias.
249
+
250
+ ---
251
+
252
+ ## Built for real TypeScript apps
253
+
254
+ ### Branded types in practice
255
+
256
+ If you have multiple entity ID types in your codebase, the TypeScript compiler
257
+ can silently allow you to pass a `userId` where a `postId` is expected — both
258
+ are just `string`. Branded types close that gap.
259
+
260
+ ```ts
261
+ import { createTypedGenerator } from "sigilid/typed";
262
+ import type { IdOf } from "sigilid/typed";
263
+
264
+ const newUserId = createTypedGenerator<"User">("usr");
265
+ const newPostId = createTypedGenerator<"Post">("post");
266
+
267
+ type UserId = IdOf<"User">;
268
+ type PostId = IdOf<"Post">;
269
+
270
+ // Your service functions now accept precise types
271
+ async function deletePost(postId: PostId) {
272
+ /* ... */
273
+ }
274
+ async function getUser(userId: UserId) {
275
+ /* ... */
276
+ }
277
+
278
+ const uid = newUserId();
279
+ const pid = newPostId();
280
+
281
+ deletePost(pid); // ✓
282
+ deletePost(uid); // ✗ Argument of type 'IdOf<"User">' is not assignable to 'IdOf<"Post">'
283
+ ```
284
+
285
+ ### Validation at the edge
286
+
287
+ ```ts
288
+ import { parseId } from "sigilid/validate";
289
+
290
+ // In an Express/Hono/Fastify handler
291
+ app.get("/users/:id", (req, res) => {
292
+ const id = parseId(req.params.id, { prefix: "usr" });
293
+ // id is a plain string, validated — throws before reaching your service
294
+ });
295
+ ```
296
+
297
+ ### Tree-shaking
298
+
299
+ Because each subpath is a separate bundle with no cross-imports, bundlers like
300
+ Vite, esbuild, and webpack can eliminate unused entrypoints entirely. An app
301
+ that imports only `generateId` will not include any prefix, validation, or
302
+ alphabet code.
303
+
304
+ ---
305
+
306
+ ## Why not just use Nano ID?
307
+
308
+ Nano ID is excellent. If all you need is the smallest possible secure random
309
+ string generator, it may still be the right call — it has a longer track record
310
+ and an even smaller core.
311
+
312
+ `sigilid` is worth considering if:
313
+
314
+ - You want prefixed IDs and typed IDs in the same package
315
+ - You want validation helpers that know about your ID format
316
+ - You want stricter TypeScript ergonomics out of the box
317
+ - You want a single library that handles the full ID lifecycle
318
+
319
+ If you are already using Nano ID and are happy with it, there is no compelling
320
+ reason to switch just for the root `generateId` function — the behavior is
321
+ similar. The subpath ecosystem is where `sigilid` earns its place.
322
+
323
+ ---
324
+
325
+ ## Runtime and environment notes
326
+
327
+ `sigilid` uses `globalThis.crypto.getRandomValues`, which is available in:
328
+
329
+ - Node.js 20+ (stable, no flags required)
330
+ - All modern browsers
331
+ - Edge runtimes: Cloudflare Workers, Vercel Edge, Deno, Bun
332
+
333
+ If you are targeting an environment without Web Crypto, use `sigilid/non-secure`
334
+ with the understanding that `Math.random` is not cryptographically safe.
335
+
336
+ ---
337
+
338
+ ## Package exports
339
+
340
+ | Import | Entry file | Description |
341
+ | -------------------- | -------------------- | ---------------------------------- |
342
+ | `sigilid` | `dist/index.js` | Secure root generator |
343
+ | `sigilid/non-secure` | `dist/non-secure.js` | Math.random-based generator |
344
+ | `sigilid/prefix` | `dist/prefix.js` | Prefixed ID helpers |
345
+ | `sigilid/typed` | `dist/typed.js` | Branded types and typed generators |
346
+ | `sigilid/validate` | `dist/validate.js` | Validation helpers |
347
+ | `sigilid/alphabet` | `dist/alphabet.js` | Custom alphabet factory |
348
+
349
+ All exports are available as ESM (`.js`) and CommonJS (`.cjs`) with TypeScript declarations (`.d.ts`).
350
+
351
+ ---
352
+
353
+ ## Contributing
354
+
355
+ Contributions are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup
356
+ instructions, coding standards, and PR expectations.
357
+
358
+ See [ARCHITECTURE.md](./ARCHITECTURE.md) for an explanation of the design
359
+ decisions and constraints contributors should keep in mind.
360
+
361
+ ---
362
+
363
+ ## Release and versioning
364
+
365
+ `sigilid` uses [Semantic Versioning](https://semver.org/). Breaking API changes
366
+ will bump the major version. Releases are managed with
367
+ [Changesets](https://github.com/changesets/changesets).
25
368
 
26
- | Import | Description |
27
- | -------------------- | --------------------------- |
28
- | `sigilid` | Secure random ID generator |
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 |
369
+ ---
34
370
 
35
371
  ## License
36
372
 
37
- MIT
373
+ MIT — see [LICENSE](./LICENSE).
package/dist/alphabet.js CHANGED
@@ -1,84 +1 @@
1
- // src/internal/alphabet.ts
2
- var MIN_ALPHABET_SIZE = 2;
3
- var MAX_ALPHABET_SIZE = 256;
4
- function validateAlphabetString(alphabet) {
5
- if (typeof alphabet !== "string") {
6
- throw new TypeError("Alphabet must be a string");
7
- }
8
- if (alphabet.length < MIN_ALPHABET_SIZE) {
9
- throw new RangeError(
10
- `Alphabet must have at least ${MIN_ALPHABET_SIZE} characters, got ${alphabet.length}`
11
- );
12
- }
13
- if (alphabet.length > MAX_ALPHABET_SIZE) {
14
- throw new RangeError(
15
- `Alphabet must have at most ${MAX_ALPHABET_SIZE} characters, got ${alphabet.length}`
16
- );
17
- }
18
- const seen = /* @__PURE__ */ new Set();
19
- for (const char of alphabet) {
20
- if (seen.has(char)) {
21
- throw new TypeError(`Alphabet contains duplicate character: "${char}"`);
22
- }
23
- seen.add(char);
24
- }
25
- }
26
- function generateFromAlphabet(alphabet, length, getBytes) {
27
- const alphabetSize = alphabet.length;
28
- let mask = 1;
29
- while (mask < alphabetSize) mask = mask << 1 | 1;
30
- const bufferMultiplier = Math.ceil(1.6 * mask * length / alphabetSize);
31
- let result = "";
32
- while (result.length < length) {
33
- const bytes = getBytes(bufferMultiplier);
34
- for (let i = 0; i < bytes.length && result.length < length; i++) {
35
- const byte = bytes[i] & mask;
36
- if (byte < alphabetSize) {
37
- result += alphabet[byte];
38
- }
39
- }
40
- }
41
- return result;
42
- }
43
-
44
- // src/internal/assert.ts
45
- var MIN_LENGTH = 1;
46
- var MAX_LENGTH = 255;
47
- function assertLength(length) {
48
- if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {
49
- throw new RangeError(
50
- `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`
51
- );
52
- }
53
- }
54
-
55
- // src/internal/random.ts
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
- }
62
- const bytes = new Uint8Array(count);
63
- globalThis.crypto.getRandomValues(bytes);
64
- return bytes;
65
- }
66
-
67
- // src/alphabet.ts
68
- var DEFAULT_LENGTH = 21;
69
- function validateAlphabet(alphabet) {
70
- validateAlphabetString(alphabet);
71
- }
72
- function createAlphabet(alphabet) {
73
- validateAlphabetString(alphabet);
74
- return {
75
- generate(length = DEFAULT_LENGTH) {
76
- assertLength(length);
77
- return generateFromAlphabet(alphabet, length, randomBytes);
78
- }
79
- };
80
- }
81
-
82
- export { createAlphabet, validateAlphabet };
83
- //# sourceMappingURL=alphabet.js.map
84
- //# sourceMappingURL=alphabet.js.map
1
+ function i(t){if(typeof t!="string")throw new TypeError("alphabet must be a string");if(t.length<2)throw new RangeError("alphabet must have at least 2 characters");if(t.length>256)throw new RangeError("alphabet must have at most 256 characters");if(new Set(t).size!==t.length)throw new TypeError("alphabet has duplicate characters")}function g(t,r,m){let o=t.length,e=1;for(;e<o;)e=e<<1|1;let c=Math.ceil(1.6*e*r/o),n="";for(;n.length<r;){let s=m(c);for(let a=0;a<s.length&&n.length<r;a++){let u=s[a]&e;u<o&&(n+=t[u]);}}return n}function h(t){if(!Number.isInteger(t)||t<1||t>255)throw new RangeError(`length must be 1\u2013255, got ${t}`)}function l(t){let r=new Uint8Array(t);return globalThis.crypto.getRandomValues(r),r}var p=21;function v(t){i(t);}function E(t){return i(t),{generate(r=p){return h(r),g(t,r,l)}}}export{E as createAlphabet,v as validateAlphabet};
package/dist/index.js CHANGED
@@ -1,55 +1 @@
1
- // src/internal/alphabet.ts
2
- function generateFromAlphabet(alphabet, length, getBytes) {
3
- const alphabetSize = alphabet.length;
4
- let mask = 1;
5
- while (mask < alphabetSize) mask = mask << 1 | 1;
6
- const bufferMultiplier = Math.ceil(1.6 * mask * length / alphabetSize);
7
- let result = "";
8
- while (result.length < length) {
9
- const bytes = getBytes(bufferMultiplier);
10
- for (let i = 0; i < bytes.length && result.length < length; i++) {
11
- const byte = bytes[i] & mask;
12
- if (byte < alphabetSize) {
13
- result += alphabet[byte];
14
- }
15
- }
16
- }
17
- return result;
18
- }
19
-
20
- // src/internal/assert.ts
21
- var MIN_LENGTH = 1;
22
- var MAX_LENGTH = 255;
23
- function assertLength(length) {
24
- if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {
25
- throw new RangeError(
26
- `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`
27
- );
28
- }
29
- }
30
-
31
- // src/internal/constants.ts
32
- var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
33
-
34
- // src/internal/random.ts
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
- }
41
- const bytes = new Uint8Array(count);
42
- globalThis.crypto.getRandomValues(bytes);
43
- return bytes;
44
- }
45
-
46
- // src/index.ts
47
- var DEFAULT_LENGTH = 21;
48
- function generateId(length = DEFAULT_LENGTH) {
49
- assertLength(length);
50
- return generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);
51
- }
52
-
53
- export { DEFAULT_ALPHABET, generateId };
54
- //# sourceMappingURL=index.js.map
55
- //# sourceMappingURL=index.js.map
1
+ function m(t,r,p){let o=t.length,e=1;for(;e<o;)e=e<<1|1;let g=Math.ceil(1.6*e*r/o),n="";for(;n.length<r;){let i=p(g);for(let a=0;a<i.length&&n.length<r;a++){let s=i[a]&e;s<o&&(n+=t[s]);}}return n}function u(t){if(!Number.isInteger(t)||t<1||t>255)throw new RangeError(`length must be 1\u2013255, got ${t}`)}var h="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";function c(t){let r=new Uint8Array(t);return globalThis.crypto.getRandomValues(r),r}var f=21;function x(t=f){return u(t),m(h,t,c)}export{h as DEFAULT_ALPHABET,x as generateId};
@@ -1,29 +1 @@
1
- // src/internal/assert.ts
2
- var MIN_LENGTH = 1;
3
- var MAX_LENGTH = 255;
4
- function assertLength(length) {
5
- if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {
6
- throw new RangeError(
7
- `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`
8
- );
9
- }
10
- }
11
-
12
- // src/internal/constants.ts
13
- var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
14
-
15
- // src/non-secure.ts
16
- var DEFAULT_LENGTH = 21;
17
- function generateNonSecureId(length = DEFAULT_LENGTH) {
18
- assertLength(length);
19
- let result = "";
20
- const size = DEFAULT_ALPHABET.length;
21
- for (let i = 0; i < length; i++) {
22
- result += DEFAULT_ALPHABET[Math.floor(Math.random() * size)];
23
- }
24
- return result;
25
- }
26
-
27
- export { generateNonSecureId };
28
- //# sourceMappingURL=non-secure.js.map
29
- //# sourceMappingURL=non-secure.js.map
1
+ function o(t){if(!Number.isInteger(t)||t<1||t>255)throw new RangeError(`length must be 1\u2013255, got ${t}`)}var r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";var i=21;function c(t=i){o(t);let e="",s=r.length;for(let n=0;n<t;n++)e+=r[Math.floor(Math.random()*s)];return e}export{c as generateNonSecureId};
package/dist/prefix.js CHANGED
@@ -1,74 +1 @@
1
- // src/internal/alphabet.ts
2
- function generateFromAlphabet(alphabet, length, getBytes) {
3
- const alphabetSize = alphabet.length;
4
- let mask = 1;
5
- while (mask < alphabetSize) mask = mask << 1 | 1;
6
- const bufferMultiplier = Math.ceil(1.6 * mask * length / alphabetSize);
7
- let result = "";
8
- while (result.length < length) {
9
- const bytes = getBytes(bufferMultiplier);
10
- for (let i = 0; i < bytes.length && result.length < length; i++) {
11
- const byte = bytes[i] & mask;
12
- if (byte < alphabetSize) {
13
- result += alphabet[byte];
14
- }
15
- }
16
- }
17
- return result;
18
- }
19
-
20
- // src/internal/assert.ts
21
- var MIN_LENGTH = 1;
22
- var MAX_LENGTH = 255;
23
- function assertLength(length) {
24
- if (!Number.isInteger(length) || length < MIN_LENGTH || length > MAX_LENGTH) {
25
- throw new RangeError(
26
- `ID length must be an integer between ${MIN_LENGTH} and ${MAX_LENGTH}, got ${length}`
27
- );
28
- }
29
- }
30
- function assertPrefix(prefix) {
31
- if (typeof prefix !== "string" || prefix.length === 0) {
32
- throw new TypeError("Prefix must be a non-empty string");
33
- }
34
- if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(prefix)) {
35
- throw new TypeError("Prefix must start with a letter and contain only letters and digits");
36
- }
37
- }
38
-
39
- // src/internal/constants.ts
40
- var DEFAULT_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
41
-
42
- // src/internal/random.ts
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
- }
49
- const bytes = new Uint8Array(count);
50
- globalThis.crypto.getRandomValues(bytes);
51
- return bytes;
52
- }
53
-
54
- // src/prefix.ts
55
- var DEFAULT_LENGTH = 21;
56
- var SEPARATOR = "_";
57
- function generatePrefixedId(prefix, length = DEFAULT_LENGTH) {
58
- assertPrefix(prefix);
59
- assertLength(length);
60
- const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);
61
- return `${prefix}${SEPARATOR}${id}`;
62
- }
63
- function createPrefixedGenerator(prefix, length = DEFAULT_LENGTH) {
64
- assertPrefix(prefix);
65
- assertLength(length);
66
- return () => {
67
- const id = generateFromAlphabet(DEFAULT_ALPHABET, length, randomBytes);
68
- return `${prefix}${SEPARATOR}${id}`;
69
- };
70
- }
71
-
72
- export { createPrefixedGenerator, generatePrefixedId };
73
- //# sourceMappingURL=prefix.js.map
74
- //# sourceMappingURL=prefix.js.map
1
+ function a(t,r,e){let i=t.length,n=1;for(;n<i;)n=n<<1|1;let b=Math.ceil(1.6*n*r/i),o="";for(;o.length<r;){let h=e(b);for(let s=0;s<h.length&&o.length<r;s++){let f=h[s]&n;f<i&&(o+=t[f]);}}return o}function u(t){if(!Number.isInteger(t)||t<1||t>255)throw new RangeError(`length must be 1\u2013255, got ${t}`)}function c(t){if(typeof t!="string"||t.length===0)throw new TypeError("prefix must be a non-empty string");if(!/^[a-zA-Z][a-zA-Z0-9]*$/.test(t))throw new TypeError("prefix must start with a letter and contain only alphanumeric characters")}var m="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";function g(t){let r=new Uint8Array(t);return globalThis.crypto.getRandomValues(r),r}var p=21,l="_";function P(t,r=p){c(t),u(r);let e=a(m,r,g);return `${t}${l}${e}`}function $(t,r=p){return c(t),u(r),()=>{let e=a(m,r,g);return `${t}${l}${e}`}}export{$ as createPrefixedGenerator,P as generatePrefixedId};