@ppabari/strio 1.0.2

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.
@@ -0,0 +1,885 @@
1
+ import { R as RandomStringOptions, E as EntropyResult, V as ValidateOptions, c as ValidationResult } from './zod-CINbFpp9.cjs';
2
+ export { a as RandomStringResult, b as RandomStringSchemaOptions, S as StartWith, r as randomStringSchema } from './zod-CINbFpp9.cjs';
3
+
4
+ /**
5
+ * short-id.ts
6
+ * Collision-resistant short IDs with an optional checksum character.
7
+ *
8
+ * A checksum character (Luhn-style mod-N) is appended to the random
9
+ * portion so that single-character transcription errors are detectable
10
+ * without a database lookup. The checksum uses the same charset as the
11
+ * random portion — the output looks uniform.
12
+ *
13
+ * Format: [prefix_][random chars][checksum char]
14
+ *
15
+ * @example
16
+ * const id = generateId({ prefix: 'usr' });
17
+ * // → 'usr_K3xP9mQr2L4c' (10 random + 1 checksum = 11 chars after prefix)
18
+ *
19
+ * validateId('usr_K3xP9mQr2L4c', { prefix: 'usr' });
20
+ * // → { valid: true, ... }
21
+ */
22
+ interface ShortIdOptions {
23
+ /**
24
+ * Optional prefix string, e.g. `'usr'` → `'usr_K3xP9mQr'`.
25
+ * An underscore separator is added automatically between prefix and random part.
26
+ * Set `separator: ''` to disable.
27
+ */
28
+ prefix?: string;
29
+ /**
30
+ * Separator between prefix and random portion.
31
+ * @default '_'
32
+ */
33
+ separator?: string;
34
+ /**
35
+ * Length of the random portion (before checksum is added).
36
+ * Total random section = randomLength + 1 (checksum).
37
+ * @default 12
38
+ */
39
+ randomLength?: number;
40
+ /**
41
+ * Character pool for the random portion. Accepts a charset alias
42
+ * (e.g. `'base58'`) or a raw character string.
43
+ * @default base58 (no ambiguous chars)
44
+ */
45
+ charset?: string;
46
+ /**
47
+ * Whether to append a checksum character.
48
+ * Disable only if you need exact length control.
49
+ * @default true
50
+ */
51
+ checksum?: boolean;
52
+ }
53
+ interface ShortIdValidateOptions {
54
+ /** Expected prefix (without separator). */
55
+ prefix?: string;
56
+ /** Separator used when the ID was generated. @default '_' */
57
+ separator?: string;
58
+ /** Charset used when the ID was generated. @default base58 */
59
+ charset?: string;
60
+ /** Whether the ID was generated with a checksum. @default true */
61
+ checksum?: boolean;
62
+ }
63
+ interface ShortIdValidateResult {
64
+ valid: boolean;
65
+ errors: string[];
66
+ }
67
+ /**
68
+ * Generate a collision-resistant short ID.
69
+ *
70
+ * @example
71
+ * generateId() // → 'K3xP9mQr2L4Xc'
72
+ * generateId({ prefix: 'usr' }) // → 'usr_K3xP9mQr2L4Xc'
73
+ * generateId({ prefix: 'inv', randomLength: 8 }) // → 'inv_K3xP9mQrc'
74
+ * generateId({ charset: 'base62' }) // → '4jK9mQr2L4Xz3'
75
+ */
76
+ declare function generateId(options?: ShortIdOptions): string;
77
+ /**
78
+ * Validate a short ID against a known config.
79
+ * Checks prefix, separator, and checksum integrity.
80
+ *
81
+ * @example
82
+ * validateId('usr_K3xP9mQr2L4Xc', { prefix: 'usr' })
83
+ * // → { valid: true, errors: [] }
84
+ *
85
+ * validateId('usr_K3xP9mQr2L4Xa', { prefix: 'usr' })
86
+ * // → { valid: false, errors: ['Checksum mismatch — ID may be corrupted or mistyped.'] }
87
+ */
88
+ declare function validateId(id: string, options?: ShortIdValidateOptions): ShortIdValidateResult;
89
+
90
+ /**
91
+ * expiring-token.ts
92
+ * Time-bound tokens with tamper-evident expiry encoding.
93
+ *
94
+ * Token format (base62-encoded, no separators):
95
+ * [8 chars: expiry epoch seconds in base62] + [random payload]
96
+ *
97
+ * The expiry is prepended to the random portion, then the whole thing
98
+ * is encoded so it looks like a uniform random string. A simple XOR
99
+ * checksum guards against accidental corruption (not cryptographic —
100
+ * use a signed JWT if you need HMAC-level tamper protection).
101
+ *
102
+ * @example
103
+ * const { token, expiresAt } = generateExpiringToken({ ttlSeconds: 900 });
104
+ * // later…
105
+ * const result = verifyToken(token);
106
+ * result.valid // true / false
107
+ * result.expiresAt // Date
108
+ * result.expired // boolean
109
+ */
110
+ interface ExpiringTokenOptions {
111
+ /**
112
+ * How long (in seconds) before the token expires.
113
+ * @default 900 (15 minutes)
114
+ */
115
+ ttlSeconds?: number;
116
+ /**
117
+ * Length of the random payload (not counting the expiry prefix).
118
+ * Total token length = payloadLength + 8.
119
+ * @default 24
120
+ */
121
+ payloadLength?: number;
122
+ }
123
+ interface ExpiringTokenResult {
124
+ /** The opaque token string to store / transmit. */
125
+ token: string;
126
+ /** When this token stops being valid. */
127
+ expiresAt: Date;
128
+ /** Total token length in characters. */
129
+ length: number;
130
+ }
131
+ interface TokenVerifyResult {
132
+ /** Whether the token is structurally valid AND not yet expired. */
133
+ valid: boolean;
134
+ /** Whether the token format could be parsed (regardless of expiry). */
135
+ parsed: boolean;
136
+ /** Whether the token has passed its expiry time. */
137
+ expired: boolean;
138
+ /** Expiry time, if the token could be parsed. */
139
+ expiresAt: Date | null;
140
+ /** Seconds remaining (negative if expired). */
141
+ secondsRemaining: number | null;
142
+ }
143
+ /**
144
+ * Generate a self-expiring token.
145
+ *
146
+ * @example
147
+ * // Password reset link (15 min)
148
+ * const { token } = generateExpiringToken({ ttlSeconds: 900 });
149
+ *
150
+ * // Magic login link (10 min), longer payload
151
+ * const { token, expiresAt } = generateExpiringToken({
152
+ * ttlSeconds: 600,
153
+ * payloadLength: 32,
154
+ * });
155
+ */
156
+ declare function generateExpiringToken(options?: ExpiringTokenOptions): ExpiringTokenResult;
157
+ /**
158
+ * Verify an expiring token. Returns whether it's valid and unexpired.
159
+ *
160
+ * @example
161
+ * const result = verifyToken(token);
162
+ * if (!result.valid) {
163
+ * if (result.expired) throw new Error('Token expired');
164
+ * throw new Error('Invalid token');
165
+ * }
166
+ */
167
+ declare function verifyToken(token: string): TokenVerifyResult;
168
+
169
+ /**
170
+ * stream.ts
171
+ * Async generator (iterator) API for memory-efficient bulk generation.
172
+ *
173
+ * Unlike the `count` option (which allocates a Set of N strings upfront),
174
+ * the stream API yields strings one at a time — safe for generating
175
+ * hundreds of thousands of IDs without blowing the heap.
176
+ *
177
+ * @example Basic usage
178
+ * for await (const token of randomStringStream({ length: 24 })) {
179
+ * await db.insert({ token });
180
+ * }
181
+ * // Runs forever — break when done
182
+ *
183
+ * @example Generate exactly N
184
+ * import { take } from '@ppabari/strio';
185
+ * const ids = await take(randomStringStream({ length: 12 }), 10_000);
186
+ *
187
+ * @example Unique-only stream
188
+ * const stream = uniqueRandomStringStream({ length: 8, charset: 'base36' });
189
+ * for await (const id of stream) { ... }
190
+ */
191
+
192
+ /**
193
+ * Infinite async generator that yields random strings from the given options.
194
+ * Each value is independently generated (not deduplicated).
195
+ *
196
+ * Use `take()` or a `break` statement to stop the stream.
197
+ * For bulk operations, prefer `take(randomStringStream(opts), n)` over
198
+ * the `count` option — it avoids allocating a full Set upfront.
199
+ */
200
+ declare function randomStringStream(options?: RandomStringOptions): AsyncGenerator<string, never, unknown>;
201
+ /**
202
+ * Infinite async generator that yields globally unique strings.
203
+ * Deduplication is maintained via an in-memory Set.
204
+ *
205
+ * ⚠️ Memory grows with consumption. For very large sets (>1M), consider
206
+ * a probabilistic structure (Bloom filter) or external dedup store instead.
207
+ */
208
+ declare function uniqueRandomStringStream(options?: RandomStringOptions): AsyncGenerator<string, never, unknown>;
209
+ /**
210
+ * Collect the first `n` values from any async iterable into an array.
211
+ * Works with both `randomStringStream` and `uniqueRandomStringStream`.
212
+ *
213
+ * @example
214
+ * const tokens = await take(randomStringStream({ length: 32 }), 500);
215
+ * // → string[] of 500 tokens
216
+ */
217
+ declare function take<T>(iterable: AsyncIterable<T>, n: number): Promise<T[]>;
218
+ /**
219
+ * Like `take`, but filters values through a predicate.
220
+ * Pulls from the stream until `n` values satisfy `predicate`.
221
+ *
222
+ * @example
223
+ * // Get 10 tokens that start with a digit
224
+ * const tokens = await takeWhere(
225
+ * randomStringStream({ length: 12 }),
226
+ * 10,
227
+ * t => /^[0-9]/.test(t)
228
+ * );
229
+ */
230
+ declare function takeWhere<T>(iterable: AsyncIterable<T>, n: number, predicate: (value: T) => boolean): Promise<T[]>;
231
+
232
+ /**
233
+ * passphrase.ts
234
+ * Human-memorable passphrase generation using the EFF Large Wordlist.
235
+ *
236
+ * Each word is selected using cryptographically secure randomness.
237
+ * A 4-word passphrase gives ~51 bits of entropy; 6 words gives ~77 bits.
238
+ *
239
+ * The built-in wordlist is a curated 1296-word subset (6^4 = 1296) of the
240
+ * EFF Large Wordlist, suitable for 4-dice rolls. We use a 256-word subset
241
+ * here for bundle efficiency — for maximum entropy use the `customWords` option
242
+ * with the full EFF list.
243
+ *
244
+ * @example
245
+ * generatePassphrase()
246
+ * // → 'correct-horse-battery-staple'
247
+ *
248
+ * generatePassphrase({ words: 5, separator: ' ', capitalize: true })
249
+ * // → 'Correct Horse Battery Staple Fence'
250
+ */
251
+ /**
252
+ * 512-word curated subset — common, unambiguous English words.
253
+ * Large enough for good entropy while keeping bundle size small.
254
+ * Each word is 3–8 characters, avoiding technical jargon and offensive terms.
255
+ */
256
+ declare const WORD_LIST: readonly string[];
257
+ interface PassphraseOptions {
258
+ /**
259
+ * Number of words in the passphrase.
260
+ * More words = more entropy. 4 words ≈ 51 bits, 6 words ≈ 77 bits.
261
+ * @default 4
262
+ */
263
+ words?: number;
264
+ /**
265
+ * Separator between words.
266
+ * @default '-'
267
+ */
268
+ separator?: string;
269
+ /**
270
+ * Capitalize the first letter of each word.
271
+ * @default false
272
+ */
273
+ capitalize?: boolean;
274
+ /**
275
+ * Append a random digit to the end for services that require numbers.
276
+ * @default false
277
+ */
278
+ appendDigit?: boolean;
279
+ /**
280
+ * Use a custom word list instead of the built-in list.
281
+ * Must contain at least 2 words.
282
+ */
283
+ customWords?: readonly string[];
284
+ /**
285
+ * Prefix the passphrase with a fixed string.
286
+ */
287
+ prefix?: string;
288
+ }
289
+ interface PassphraseResult {
290
+ /** The generated passphrase string. */
291
+ passphrase: string;
292
+ /** Number of words used. */
293
+ wordCount: number;
294
+ /** Entropy estimate in bits. */
295
+ entropyBits: number;
296
+ }
297
+ /**
298
+ * Generate a human-memorable passphrase from random dictionary words.
299
+ *
300
+ * @example
301
+ * generatePassphrase()
302
+ * // → { passphrase: 'stone-river-proud-flame', wordCount: 4, entropyBits: 50.9 }
303
+ *
304
+ * generatePassphrase({ words: 5, separator: ' ', capitalize: true })
305
+ * // → { passphrase: 'Stone River Proud Flame Beach', wordCount: 5, entropyBits: 63.7 }
306
+ */
307
+ declare function generatePassphrase(options?: PassphraseOptions): PassphraseResult;
308
+
309
+ /**
310
+ * entropy.ts
311
+ * Estimate the Shannon entropy and strength of a random string configuration.
312
+ */
313
+
314
+ /**
315
+ * Estimates the entropy of strings generated by the given options.
316
+ *
317
+ * For pattern-based strings, each placeholder contributes independently.
318
+ * For length-based strings, entropy = log2(charsetSize) * effectiveLength.
319
+ *
320
+ * @example
321
+ * const e = estimateEntropy({ length: 32, uppercase: true, lowercase: true, numeric: true });
322
+ * console.log(e.bits); // ~190
323
+ * console.log(e.strength); // 'very-strong'
324
+ */
325
+ declare function estimateEntropy(options?: RandomStringOptions): EntropyResult;
326
+
327
+ /**
328
+ * validate.ts
329
+ * Validate that a string conforms to a given RandomStringOptions config.
330
+ */
331
+
332
+ /**
333
+ * Checks whether `str` satisfies the given validation rules.
334
+ *
335
+ * @example
336
+ * const result = validateRandomString('abc123', {
337
+ * minLength: 6,
338
+ * requireNumeric: true,
339
+ * requireLowercase: true,
340
+ * });
341
+ * result.valid; // true
342
+ *
343
+ * @example
344
+ * const result = validateRandomString('abc', { requireNumeric: true });
345
+ * result.valid; // false
346
+ * result.errors; // ['Must contain at least one numeric digit.']
347
+ */
348
+ declare function validateRandomString(str: string, options?: ValidateOptions): ValidationResult;
349
+
350
+ /**
351
+ * charset-aliases.ts
352
+ * Named character set aliases for common encoding schemes.
353
+ * Pass any alias name as the `charset` option in generateRandomString.
354
+ *
355
+ * @example
356
+ * import { generateRandomString, CHARSET_ALIASES } from '@ppabari/strio';
357
+ * generateRandomString({ charset: CHARSET_ALIASES.base58, length: 22 });
358
+ * // or via string shorthand:
359
+ * generateRandomString({ charset: 'base58', length: 22 });
360
+ */
361
+ declare const CHARSET_ALIASES: Record<string, string>;
362
+ type CharsetAlias = keyof typeof CHARSET_ALIASES;
363
+ /**
364
+ * Resolve a charset value: if it matches a known alias, return the
365
+ * corresponding character string; otherwise return the value as-is.
366
+ *
367
+ * This is called inside buildCharset so alias names work transparently
368
+ * as the `charset` option.
369
+ *
370
+ * @example
371
+ * resolveCharsetAlias('base58') // → '123456789ABCDEF...'
372
+ * resolveCharsetAlias('abc') // → 'abc'
373
+ */
374
+ declare function resolveCharsetAlias(value: string): string;
375
+
376
+ /**
377
+ * presets.ts
378
+ * Ready-to-use named presets for common use cases.
379
+ * Each preset is a plain `RandomStringOptions` object — pass it directly
380
+ * to `generateRandomString` or spread it to customise.
381
+ *
382
+ * @example
383
+ * import { generateRandomString, PRESETS } from 'secure-random-string';
384
+ * const token = generateRandomString(PRESETS.TOKEN);
385
+ * const longToken = generateRandomString({ ...PRESETS.TOKEN, length: 64 });
386
+ */
387
+ declare const PRESETS: {
388
+ /**
389
+ * API token / session key.
390
+ * 32-char alphanumeric (no symbols), readable=false for max entropy.
391
+ * ~190 bits of entropy.
392
+ */
393
+ readonly TOKEN: {
394
+ length: number;
395
+ numeric: true;
396
+ lowercase: true;
397
+ uppercase: true;
398
+ symbols: false;
399
+ };
400
+ /**
401
+ * Strong password.
402
+ * 20 chars, all character types including symbols.
403
+ * ~131 bits of entropy.
404
+ */
405
+ readonly PASSWORD: {
406
+ length: number;
407
+ numeric: true;
408
+ lowercase: true;
409
+ uppercase: true;
410
+ symbols: true;
411
+ };
412
+ /**
413
+ * Human-readable token — no ambiguous characters (0/O/l/I/1).
414
+ * Safe to read aloud or transcribe from a screen.
415
+ * 16 chars, ~93 bits of entropy.
416
+ */
417
+ readonly READABLE: {
418
+ length: number;
419
+ numeric: true;
420
+ lowercase: true;
421
+ uppercase: true;
422
+ symbols: false;
423
+ readable: true;
424
+ };
425
+ /**
426
+ * URL-safe slug identifier.
427
+ * Lowercase letters and digits only, starts with a letter.
428
+ * 12 chars, ~62 bits of entropy.
429
+ */
430
+ readonly SLUG: {
431
+ length: number;
432
+ numeric: true;
433
+ lowercase: true;
434
+ uppercase: false;
435
+ symbols: false;
436
+ startWith: "alphabet";
437
+ };
438
+ /**
439
+ * Lowercase hex string.
440
+ * Compatible with UUID hex components, color codes, hash representations.
441
+ * 32 chars (128-bit equivalent), ~128 bits of entropy.
442
+ */
443
+ readonly HEX: {
444
+ length: number;
445
+ charset: string;
446
+ };
447
+ /**
448
+ * Numeric PIN code. 6 digits, starts with a non-zero digit.
449
+ * ~17 bits of entropy — suitable for short-lived OTP codes.
450
+ */
451
+ readonly PIN: {
452
+ length: number;
453
+ numeric: true;
454
+ lowercase: false;
455
+ uppercase: false;
456
+ symbols: false;
457
+ exclude: string;
458
+ startWith: "numeric";
459
+ };
460
+ /**
461
+ * UUID-like string (not RFC 4122 compliant, but same visual format).
462
+ * Uses `pattern` to produce 8-4-4-4-12 hex grouping.
463
+ * ~122 bits of entropy (same as UUIDv4).
464
+ */
465
+ readonly UUID_LIKE: {
466
+ pattern: string;
467
+ charset: string;
468
+ };
469
+ /**
470
+ * Short alphanumeric ID — suitable for database record IDs.
471
+ * 8 chars, uppercase alphanumeric, starts with a letter.
472
+ * ~41 bits — good for IDs when combined with a namespace/prefix.
473
+ */
474
+ readonly SHORT_ID: {
475
+ length: number;
476
+ numeric: true;
477
+ lowercase: false;
478
+ uppercase: true;
479
+ symbols: false;
480
+ startWith: "alphabet";
481
+ };
482
+ /**
483
+ * Invitation / redemption code.
484
+ * 16 chars, readable (no ambiguous chars), pattern grouped for readability.
485
+ * @example 'KXPZ-9MR2-LQ4Y-8WVN'
486
+ */
487
+ readonly INVITE_CODE: {
488
+ pattern: string;
489
+ readable: true;
490
+ };
491
+ };
492
+ type PresetName = keyof typeof PRESETS;
493
+
494
+ /**
495
+ * secrets.ts
496
+ * Cryptographic secret and key generation utilities.
497
+ *
498
+ * Covers the full lifecycle of secrets used in real applications:
499
+ * - Raw bytes → generateBytes()
500
+ * - Hex keys (AES, ChaCha20) → generateHexKey()
501
+ * - Base64 keys (cloud imports) → generateBase64Key()
502
+ * - JWT secrets (HS256/384/512) → generateJwtSecret()
503
+ * - Structured API keys → generateApiKey()
504
+ * - Numeric OTP codes → generateOtp()
505
+ * - Secret masking for logs → maskSecret()
506
+ * - Constant-time comparison → timingSafeEqual()
507
+ * - Framework secret helpers → generateCookieSecret() etc.
508
+ *
509
+ * All functions use crypto.getRandomValues() — the same bias-free
510
+ * engine as the rest of strio.
511
+ */
512
+ /**
513
+ * Generate `n` cryptographically secure random bytes.
514
+ * Returns a `Uint8Array` — the native format accepted by Web Crypto,
515
+ * Node's `crypto`, and virtually every encryption library.
516
+ *
517
+ * @example
518
+ * const key = generateBytes(32); // 32-byte (256-bit) key material
519
+ * await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['encrypt']);
520
+ *
521
+ * const iv = generateBytes(12); // AES-GCM initialisation vector
522
+ * const nonce = generateBytes(24); // XSalsa20 nonce
523
+ */
524
+ declare function generateBytes(length: number): Uint8Array;
525
+ /**
526
+ * Generate a random key of `bits` length, returned as a lowercase hex string.
527
+ * The standard format for raw key material in most Node.js crypto libraries,
528
+ * Redis ACL passwords, database encryption keys, and many cloud services.
529
+ *
530
+ * @param bits - Key size in bits. Must be a positive multiple of 8.
531
+ * Common values: 128, 192, 256 (AES), 512 (HMAC-SHA512).
532
+ *
533
+ * @example
534
+ * generateHexKey(256) // → 64-char hex (AES-256 key)
535
+ * generateHexKey(128) // → 32-char hex (AES-128 key)
536
+ * generateHexKey(512) // → 128-char hex (HMAC-SHA512 key)
537
+ *
538
+ * // Use directly with Node crypto:
539
+ * const key = generateHexKey(256);
540
+ * const cipher = crypto.createCipheriv('aes-256-gcm', Buffer.from(key, 'hex'), iv);
541
+ */
542
+ declare function generateHexKey(bits?: number): string;
543
+ type Base64Variant = 'standard' | 'url-safe' | 'url-safe-no-pad';
544
+ /**
545
+ * Generate a random key of `bits` length, returned as a base64 string.
546
+ *
547
+ * Three variants:
548
+ * - `'standard'` — includes `+`, `/`, `=` padding (AWS, most CLIs)
549
+ * - `'url-safe'` — replaces `+`→`-`, `/`→`_`, keeps `=` (cookies, headers)
550
+ * - `'url-safe-no-pad'` — same but no `=` padding (JWT, URL query params)
551
+ *
552
+ * @param bits - Key size in bits. Must be a positive multiple of 8.
553
+ * @param variant - Output encoding variant. Default: `'url-safe-no-pad'`.
554
+ *
555
+ * @example
556
+ * generateBase64Key(256) // → url-safe, no padding (default)
557
+ * generateBase64Key(256, 'standard') // → standard base64 with padding
558
+ * generateBase64Key(256, 'url-safe') // → url-safe with padding
559
+ *
560
+ * // AWS KMS / GCP import format:
561
+ * const keyMaterial = generateBase64Key(256, 'standard');
562
+ *
563
+ * // Cookie signing secret (express-session, iron-session):
564
+ * const secret = generateBase64Key(256, 'url-safe-no-pad');
565
+ */
566
+ declare function generateBase64Key(bits?: number, variant?: Base64Variant): string;
567
+ type JwtAlgorithm = 'HS256' | 'HS384' | 'HS512';
568
+ type JwtSecretFormat = 'base64url' | 'hex' | 'utf8-like';
569
+ interface JwtSecretOptions {
570
+ /**
571
+ * HMAC algorithm this secret will be used with.
572
+ * Determines the minimum key length enforced automatically.
573
+ * @default 'HS256'
574
+ */
575
+ algorithm?: JwtAlgorithm;
576
+ /**
577
+ * Output format.
578
+ * - `'base64url'` — RFC 4648 §5, no padding. Default. Works with jsonwebtoken,
579
+ * jose, @auth/core, NextAuth, Fastify JWT, Lucia.
580
+ * - `'hex'` — lowercase hex. Works with jose when you import as `'oct'`.
581
+ * - `'utf8-like'` — base62 string. Usable directly as a string secret in older
582
+ * libraries that accept plain strings (not recommended for new code).
583
+ * @default 'base64url'
584
+ */
585
+ format?: JwtSecretFormat;
586
+ /**
587
+ * Override the key size in bits. Must be ≥ the algorithm minimum.
588
+ * Defaults to the algorithm's minimum (256 / 384 / 512).
589
+ */
590
+ bits?: number;
591
+ }
592
+ interface JwtSecretResult {
593
+ /** The secret value — pass this to your JWT library. */
594
+ secret: string;
595
+ /** Algorithm this secret was generated for. */
596
+ algorithm: JwtAlgorithm;
597
+ /** Actual key size in bits. */
598
+ bits: number;
599
+ /** Output format. */
600
+ format: JwtSecretFormat;
601
+ /**
602
+ * Usage example string for the most common library (jsonwebtoken).
603
+ * Handy for logging/debugging during setup.
604
+ */
605
+ example: string;
606
+ }
607
+ /**
608
+ * Generate a cryptographically secure JWT signing secret.
609
+ *
610
+ * Enforces RFC 7518 minimum key lengths:
611
+ * - HS256 → minimum 256 bits (32 bytes)
612
+ * - HS384 → minimum 384 bits (48 bytes)
613
+ * - HS512 → minimum 512 bits (64 bytes)
614
+ *
615
+ * @example
616
+ * // Default — HS256, base64url
617
+ * const { secret } = generateJwtSecret();
618
+ * jwt.sign(payload, secret, { algorithm: 'HS256' });
619
+ *
620
+ * // HS512 for maximum security
621
+ * const { secret } = generateJwtSecret({ algorithm: 'HS512' });
622
+ *
623
+ * // For jose / @auth/core
624
+ * const { secret } = generateJwtSecret({ algorithm: 'HS256', format: 'base64url' });
625
+ * // Set as AUTH_SECRET env var for NextAuth v5
626
+ *
627
+ * // For libraries expecting hex
628
+ * const { secret } = generateJwtSecret({ format: 'hex' });
629
+ */
630
+ declare function generateJwtSecret(options?: JwtSecretOptions): JwtSecretResult;
631
+ interface ApiKeyOptions {
632
+ /**
633
+ * Key type prefix, e.g. `'sk'` (secret key), `'pk'` (public key), `'tok'`.
634
+ * @default 'sk'
635
+ */
636
+ type?: string;
637
+ /**
638
+ * Environment prefix, e.g. `'live'`, `'test'`, `'dev'`.
639
+ * Omit to produce a key with no environment segment.
640
+ */
641
+ environment?: 'live' | 'test' | 'dev' | string;
642
+ /**
643
+ * Entropy of the random portion in bits.
644
+ * @default 256
645
+ */
646
+ bits?: number;
647
+ /**
648
+ * Charset for the random portion.
649
+ * @default 'base62'
650
+ */
651
+ charset?: 'base62' | 'base58' | 'hex' | string;
652
+ /**
653
+ * Separator between segments.
654
+ * @default '_'
655
+ */
656
+ separator?: string;
657
+ }
658
+ interface ApiKeyResult {
659
+ /** The full API key string. */
660
+ key: string;
661
+ /** The prefix segment, e.g. `'sk_live'`. */
662
+ prefix: string;
663
+ /** Character count of the random portion (not including prefix or separator). */
664
+ randomLength: number;
665
+ /** Entropy of the random portion in bits. */
666
+ bits: number;
667
+ }
668
+ /**
669
+ * Generate a structured API key in the style of Stripe, GitHub, or OpenAI.
670
+ *
671
+ * Format: `{type}_{environment}_{randomPortion}`
672
+ * e.g. `strio_live_K3xP9mQr2LzYw7NvTq8sHfGbJ5cD4eAK3xP9mQr2LzY`
673
+ *
674
+ * @example
675
+ * generateApiKey()
676
+ * // → { key: 'strio_live_K3xP9mQr2LzYw7Nv...', prefix: 'sk_live', ... }
677
+ *
678
+ * generateApiKey({ type: 'sk', environment: 'test', bits: 256 })
679
+ * // → 'strio_test_K3xP9mQr...'
680
+ *
681
+ * generateApiKey({ type: 'tok', bits: 128 })
682
+ * // → 'tok_K3xP9mQr2LzYw7Nv'
683
+ *
684
+ * generateApiKey({ type: 'pk', environment: 'live', charset: 'hex' })
685
+ * // → 'pub_live_a3f7c2b9...'
686
+ */
687
+ declare function generateApiKey(options?: ApiKeyOptions): ApiKeyResult;
688
+ interface OtpOptions {
689
+ /**
690
+ * Number of digits in the OTP.
691
+ * @default 6
692
+ */
693
+ digits?: number;
694
+ /**
695
+ * Whether to allow a leading zero.
696
+ * Set to `false` to ensure the first digit is 1–9 (e.g. for display in SMS).
697
+ * @default true
698
+ */
699
+ allowLeadingZero?: boolean;
700
+ }
701
+ interface OtpResult {
702
+ /** The OTP as a zero-padded string (e.g. `'048392'`). */
703
+ code: string;
704
+ /** The OTP as a number (e.g. `48392`). */
705
+ value: number;
706
+ /** Number of digits. */
707
+ digits: number;
708
+ }
709
+ /**
710
+ * Generate a cryptographically secure numeric OTP (one-time password).
711
+ * Uses modular arithmetic on a secure random integer — no modulo bias
712
+ * for digit counts up to 9 (10^9 < 2^30 < 2^32, safe in float64).
713
+ *
714
+ * @example
715
+ * generateOtp() // → { code: '483920', value: 483920, digits: 6 }
716
+ * generateOtp({ digits: 8 }) // → { code: '04839201', value: 4839201, digits: 8 }
717
+ *
718
+ * // SMS-friendly (no leading zero)
719
+ * generateOtp({ allowLeadingZero: false })
720
+ *
721
+ * // Use just the code string
722
+ * const { code } = generateOtp();
723
+ * await sms.send(phone, `Your code is ${code}`);
724
+ */
725
+ declare function generateOtp(options?: OtpOptions): OtpResult;
726
+ interface MaskSecretOptions {
727
+ /**
728
+ * Number of characters to reveal at the start of the secret.
729
+ * `0` reveals nothing (prefix only).
730
+ * @default 0 (show prefix only, mask everything else)
731
+ */
732
+ visibleStart?: number;
733
+ /**
734
+ * Number of characters to reveal at the end.
735
+ * @default 4
736
+ */
737
+ visibleEnd?: number;
738
+ /**
739
+ * Character used to mask the hidden portion.
740
+ * @default '*'
741
+ */
742
+ maskChar?: string;
743
+ /**
744
+ * Minimum number of mask characters shown, regardless of secret length.
745
+ * Prevents inferring length from short secrets.
746
+ * @default 4
747
+ */
748
+ minMaskLength?: number;
749
+ /**
750
+ * Maximum mask characters shown (caps a very long mask).
751
+ * @default 8
752
+ */
753
+ maxMaskLength?: number;
754
+ /**
755
+ * Known prefix segments to preserve literally before masking begins.
756
+ * e.g. `['strio_live_', 'strio_test_']` — the prefix is kept, the rest is masked.
757
+ * If the secret starts with one of these, the prefix is shown and
758
+ * `visibleStart` is counted from after the prefix.
759
+ */
760
+ knownPrefixes?: string[];
761
+ }
762
+ /**
763
+ * Mask a secret string for safe logging, display, or debugging.
764
+ * Preserves enough context to identify the key without exposing it.
765
+ *
766
+ * @example
767
+ * maskSecret('myapp_live_K3xP9mQr2LzYw7NvTq8sHfGb')
768
+ * // → 'strio_live_****...w7Nv' (auto-detects 'strio_live_' prefix)
769
+ *
770
+ * maskSecret('supersecretpassword')
771
+ * // → '****...word'
772
+ *
773
+ * maskSecret('eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIn0.abc123')
774
+ * // → '****...123'
775
+ *
776
+ * // Custom reveal
777
+ * maskSecret('myapp_live_K3xP9mQr2LzY', { visibleEnd: 6 })
778
+ * // → 'strio_live_****...LzY'
779
+ */
780
+ declare function maskSecret(secret: string, options?: MaskSecretOptions): string;
781
+ /**
782
+ * Compare two strings or byte arrays in constant time.
783
+ * Prevents timing attacks where an attacker infers secret content by
784
+ * measuring how long a comparison takes.
785
+ *
786
+ * **Always use this instead of `===` when comparing tokens, secrets,
787
+ * HMAC signatures, or any security-sensitive value.**
788
+ *
789
+ * The comparison time is proportional to `max(a.length, b.length)` —
790
+ * it does not short-circuit on mismatch.
791
+ *
792
+ * @returns `true` if the values are identical, `false` otherwise.
793
+ *
794
+ * @example
795
+ * // Comparing incoming token against stored token
796
+ * if (!timingSafeEqual(incomingToken, storedToken)) {
797
+ * throw new Error('Invalid token');
798
+ * }
799
+ *
800
+ * // Also works with Uint8Array (e.g. HMAC comparison)
801
+ * const valid = timingSafeEqual(computedHmac, expectedHmac);
802
+ */
803
+ declare function timingSafeEqual(a: string | Uint8Array, b: string | Uint8Array): boolean;
804
+ /**
805
+ * Generate a cookie signing secret for session libraries.
806
+ *
807
+ * Compatible with:
808
+ * - express-session (`secret` option)
809
+ * - iron-session (`password` option)
810
+ * - lucia (`sessionCookieOptions.password`)
811
+ * - cookie-signature
812
+ *
813
+ * Returns a 256-bit base64url string by default — long enough to resist
814
+ * brute force while remaining pasteable in .env files.
815
+ *
816
+ * @example
817
+ * // express-session
818
+ * app.use(session({ secret: generateCookieSecret() }));
819
+ *
820
+ * // iron-session
821
+ * const sessionOptions = { password: generateCookieSecret(), cookieName: 'session' };
822
+ *
823
+ * // .env usage
824
+ * console.log(`COOKIE_SECRET="${generateCookieSecret()}"`);
825
+ */
826
+ declare function generateCookieSecret(bits?: number): string;
827
+ /**
828
+ * Generate an encryption key suitable for AES-GCM or AES-CBC.
829
+ *
830
+ * @param keySize - `128`, `192`, or `256` bits. Default: 256.
831
+ * @param format - `'hex'` (default) or `'base64'` or `'bytes'`.
832
+ *
833
+ * @example
834
+ * generateAesKey() // → 64-char hex (AES-256)
835
+ * generateAesKey(128) // → 32-char hex (AES-128)
836
+ * generateAesKey(256, 'base64') // → base64url string
837
+ * generateAesKey(256, 'bytes') // → Uint8Array
838
+ */
839
+ declare function generateAesKey(keySize?: 128 | 192 | 256, format?: 'hex' | 'base64' | 'bytes'): string | Uint8Array;
840
+ /**
841
+ * Generate a secret for Next.js / NextAuth / Auth.js.
842
+ * Sets AUTH_SECRET in your .env file — used to sign JWTs and cookies.
843
+ * Output matches what `npx auth secret` generates.
844
+ *
845
+ * @example
846
+ * const secret = generateNextAuthSecret();
847
+ * console.log(`AUTH_SECRET="${secret}"`);
848
+ */
849
+ declare function generateNextAuthSecret(): string;
850
+ /**
851
+ * Generate a Django SECRET_KEY compatible string.
852
+ * Django expects a long random string of printable ASCII characters —
853
+ * at least 50 characters, no restrictions beyond that.
854
+ *
855
+ * @example
856
+ * const key = generateDjangoSecretKey();
857
+ * console.log(`SECRET_KEY="${key}"`);
858
+ */
859
+ declare function generateDjangoSecretKey(): string;
860
+ /**
861
+ * Generate a Rails / rack secret_key_base compatible string.
862
+ * Rails expects a 128+ character hex string for its key base.
863
+ *
864
+ * @example
865
+ * const key = generateRailsSecretKeyBase();
866
+ * console.log(`SECRET_KEY_BASE="${key}"`);
867
+ */
868
+ declare function generateRailsSecretKeyBase(): string;
869
+
870
+ /**
871
+ * @ppabari/strio
872
+ *
873
+ * Cryptographically secure string generation for Node.js and browsers.
874
+ * Zero dependencies. Fully typed. Bias-free.
875
+ */
876
+
877
+ declare function generateRandomString(options?: RandomStringOptions & {
878
+ count?: 1;
879
+ }): string;
880
+ declare function generateRandomString(options: RandomStringOptions & {
881
+ count: number;
882
+ }): string[];
883
+ declare function generateRandomStringAsync(options?: RandomStringOptions): Promise<string | string[]>;
884
+
885
+ export { type ApiKeyOptions, type ApiKeyResult, WORD_LIST as BUILT_IN_WORD_LIST, type Base64Variant, CHARSET_ALIASES, type CharsetAlias, EntropyResult, type ExpiringTokenOptions, type ExpiringTokenResult, type JwtAlgorithm, type JwtSecretFormat, type JwtSecretOptions, type JwtSecretResult, type MaskSecretOptions, type OtpOptions, type OtpResult, PRESETS, type PassphraseOptions, type PassphraseResult, type PresetName, RandomStringOptions, type ShortIdOptions, type ShortIdValidateOptions, type ShortIdValidateResult, type TokenVerifyResult, ValidateOptions, ValidationResult, estimateEntropy, generateAesKey, generateApiKey, generateBase64Key, generateBytes, generateCookieSecret, generateDjangoSecretKey, generateExpiringToken, generateHexKey, generateId, generateJwtSecret, generateNextAuthSecret, generateOtp, generatePassphrase, generateRailsSecretKeyBase, generateRandomString, generateRandomStringAsync, maskSecret, randomStringStream, resolveCharsetAlias, take, takeWhere, timingSafeEqual, uniqueRandomStringStream, validateId, validateRandomString, verifyToken };