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 +349 -13
- package/dist/alphabet.js +1 -84
- package/dist/index.js +1 -55
- package/dist/non-secure.js +1 -29
- package/dist/prefix.js +1 -74
- package/dist/typed.js +1 -72
- package/dist/validate.js +1 -45
- package/package.json +51 -20
- package/dist/alphabet.cjs +0 -87
- package/dist/alphabet.cjs.map +0 -1
- package/dist/alphabet.d.cts +0 -34
- package/dist/alphabet.js.map +0 -1
- package/dist/index.cjs +0 -58
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -19
- package/dist/index.js.map +0 -1
- package/dist/non-secure.cjs +0 -31
- package/dist/non-secure.cjs.map +0 -1
- package/dist/non-secure.d.cts +0 -19
- package/dist/non-secure.js.map +0 -1
- package/dist/prefix.cjs +0 -77
- package/dist/prefix.cjs.map +0 -1
- package/dist/prefix.d.cts +0 -33
- package/dist/prefix.js.map +0 -1
- package/dist/typed.cjs +0 -75
- package/dist/typed.cjs.map +0 -1
- package/dist/typed.d.cts +0 -50
- package/dist/typed.js.map +0 -1
- package/dist/validate.cjs +0 -49
- package/dist/validate.cjs.map +0 -1
- package/dist/validate.d.cts +0 -46
- package/dist/validate.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,16 +4,50 @@
|
|
|
4
4
|
|
|
5
5
|
[](https://github.com/moritzmyrz/sigilid/actions/workflows/ci.yml)
|
|
6
6
|
[](https://www.npmjs.com/package/sigilid)
|
|
7
|
+
[](https://bundlephobia.com/package/sigilid)
|
|
8
|
+
[](https://packagephobia.com/result?p=sigilid)
|
|
7
9
|
[](./LICENSE)
|
|
8
10
|
|
|
9
|
-
|
|
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
|
-
|
|
15
|
+
|
|
16
|
+
const id = generateId(); // "K7gkJ_q3vR2nL8xH5eM0w"
|
|
14
17
|
```
|
|
15
18
|
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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};
|
package/dist/non-secure.js
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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};
|