@poorvithmp/safegen 2.0.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 ADDED
@@ -0,0 +1,12 @@
1
+ # @poorvith-mp/safegen
2
+
3
+ SafeGen's zero-dependency TypeScript core for generating and auditing passwords, passphrases, PINs, and custom patterns with the runtime's native cryptographic random source.
4
+
5
+ ```ts
6
+ import { calculateAudit, generatePassword } from '@poorvith-mp/safegen';
7
+
8
+ const credential = generatePassword({ length: 20 });
9
+ console.log(credential, calculateAudit(credential));
10
+ ```
11
+
12
+ [Documentation](https://github.com/poorvith-mp/safegen#package-and-cli) · [SafeGen](https://safegen.poorvithmp.com)
@@ -0,0 +1,5 @@
1
+ import type { PasswordOptions, SecurityAudit } from './types.js';
2
+ export declare function formatCrackTime(seconds: number): string;
3
+ export declare function calculateAudit(password: string, options?: PasswordOptions): SecurityAudit;
4
+ export declare const calculateDetailedAudit: typeof calculateAudit;
5
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAkB,MAAM,YAAY,CAAC;AAEjF,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAUvD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,aAAa,CAsC7F;AAED,eAAO,MAAM,sBAAsB,uBAAiB,CAAC"}
package/dist/audit.js ADDED
@@ -0,0 +1,73 @@
1
+ import { CHARACTER_SETS, PASSPHRASE_WORDS } from './generator.js';
2
+ export function formatCrackTime(seconds) {
3
+ if (seconds < 0.001)
4
+ return 'Instantaneous (< 1 millisecond)';
5
+ if (seconds < 1)
6
+ return `${Math.round(seconds * 1000)} milliseconds`;
7
+ if (seconds < 60)
8
+ return `${Math.round(seconds)} seconds`;
9
+ if (seconds < 3_600)
10
+ return `${Math.round(seconds / 60)} minutes`;
11
+ if (seconds < 86_400)
12
+ return `${Math.round(seconds / 3_600)} hours`;
13
+ if (seconds < 31_536_000)
14
+ return `${Math.round(seconds / 86_400)} days`;
15
+ if (seconds < 3_153_600_000)
16
+ return `${Math.round(seconds / 31_536_000)} years`;
17
+ if (seconds < 315_360_000_000)
18
+ return `${Math.round(seconds / 3_153_600_000)} centuries`;
19
+ return 'Over 1,000 centuries at the stated guess rate';
20
+ }
21
+ export function calculateAudit(password, options = {}) {
22
+ if (!password)
23
+ return { entropy: 0, rating: 'Weak', timeToCrackSeconds: 0, crackTime: 'N/A', crackTimeFormatted: 'N/A', poolSize: 0, score: 0, warnings: ['No password generated'], tips: ['Generate a credential before auditing it'] };
24
+ const hasUpper = [...password].some((char) => CHARACTER_SETS.uppercase.includes(char));
25
+ const hasLower = [...password].some((char) => CHARACTER_SETS.lowercase.includes(char));
26
+ const hasNumber = [...password].some((char) => CHARACTER_SETS.numbers.includes(char));
27
+ const hasSymbol = [...password].some((char) => CHARACTER_SETS.symbols.includes(char));
28
+ let poolSize = (hasUpper ? 26 : 0) + (hasLower ? 26 : 0) + (hasNumber ? 10 : 0) + (hasSymbol ? CHARACTER_SETS.symbols.length : 0);
29
+ let entropy;
30
+ if (options.mode === 'passphrase') {
31
+ const words = options.words ?? options.wordCount ?? password.split(options.separator ?? '-').length;
32
+ poolSize = PASSPHRASE_WORDS.length;
33
+ entropy = words * Math.log2(poolSize) + (options.includeNumber ? Math.log2(words * 100) : 0);
34
+ }
35
+ else if (options.mode === 'pin') {
36
+ poolSize = 10;
37
+ entropy = password.length * Math.log2(10);
38
+ }
39
+ else if (options.mode === 'pattern') {
40
+ const template = options.template ?? options.pattern ?? '';
41
+ entropy = [...template].reduce((total, char) => total + (char === 'L' || char === 'l' ? Math.log2(26) : char === 'n' ? Math.log2(10) : char === 'S' || char === 's' ? Math.log2(CHARACTER_SETS.symbols.length) : 0), 0);
42
+ }
43
+ else {
44
+ entropy = password.length * Math.log2(poolSize || 1);
45
+ }
46
+ entropy = Math.round(entropy);
47
+ const timeToCrackSeconds = 2 ** entropy / 200_000_000_000;
48
+ let rating = entropy < 40 ? 'Weak' : entropy < 65 ? 'Medium' : entropy < 90 ? 'Strong' : 'Very strong';
49
+ let score = Math.min(100, Math.round(entropy / 90 * 100));
50
+ if (rating === 'Weak')
51
+ score = Math.max(10, Math.min(35, score));
52
+ if (rating === 'Medium')
53
+ score = Math.max(40, Math.min(68, score));
54
+ if (rating === 'Strong')
55
+ score = Math.max(70, Math.min(88, score));
56
+ const warnings = [];
57
+ const tips = [];
58
+ if (password.length < 12)
59
+ warnings.push('Length is under 12 characters');
60
+ if (!hasSymbol && (options.mode ?? 'random') === 'random')
61
+ warnings.push('No special symbols included');
62
+ if (!hasNumber && (options.mode ?? 'random') === 'random')
63
+ warnings.push('No numeric digits included');
64
+ if (entropy >= 80)
65
+ tips.push('Excellent entropy for high-value accounts');
66
+ if (options.mode === 'passphrase')
67
+ tips.push('Passphrases offer high security while remaining easy to type and remember');
68
+ if ((options.mode ?? 'random') === 'random' && password.length >= 16)
69
+ tips.push('Resistant to offline dictionary and rainbow table attacks');
70
+ const crackTime = formatCrackTime(timeToCrackSeconds);
71
+ return { entropy, rating, timeToCrackSeconds, crackTime, crackTimeFormatted: crackTime, poolSize, score, warnings, tips };
72
+ }
73
+ export const calculateDetailedAudit = calculateAudit;
@@ -0,0 +1,2 @@
1
+ export declare function secureRandomInt(max: number): number;
2
+ //# sourceMappingURL=crypto-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto-provider.d.ts","sourceRoot":"","sources":["../src/crypto-provider.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWnD"}
@@ -0,0 +1,14 @@
1
+ export function secureRandomInt(max) {
2
+ if (!Number.isSafeInteger(max) || max <= 0 || max > 0x1_0000_0000) {
3
+ throw new RangeError('Random range is invalid');
4
+ }
5
+ if (!globalThis.crypto?.getRandomValues) {
6
+ throw new Error('A native cryptographic random source is required');
7
+ }
8
+ const sample = new Uint32Array(1);
9
+ const limit = Math.floor(0x1_0000_0000 / max) * max;
10
+ do
11
+ globalThis.crypto.getRandomValues(sample);
12
+ while (sample[0] >= limit);
13
+ return sample[0] % max;
14
+ }
@@ -0,0 +1,17 @@
1
+ import { secureRandomInt } from './crypto-provider.js';
2
+ import type { PassphraseOptions, PasswordOptions, PatternOptions, PinOptions, RandomPasswordOptions } from './types.js';
3
+ export declare const CHARACTER_SETS: {
4
+ readonly uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
5
+ readonly lowercase: "abcdefghijklmnopqrstuvwxyz";
6
+ readonly numbers: "0123456789";
7
+ readonly symbols: "!@#$%^&*()_+{}[]<>?/|~=-";
8
+ };
9
+ export declare const PASSPHRASE_WORDS: readonly ["anchor", "beacon", "canvas", "cipher", "cobalt", "crane", "delta", "drift", "echo", "amber", "falcon", "flint", "fossil", "glacier", "granite", "harbor", "horizon", "indigo", "island", "jasper", "lagoon", "lunar", "magnet", "marble", "matrix", "meridian", "meteor", "nexus", "noble", "oasis", "obsidian", "orbit", "opal", "origin", "phantom", "phoenix", "prism", "pulse", "pyramid", "quartz", "radar", "radius", "raven", "ripple", "ruby", "safari", "sapphire", "saturn", "shadow", "shield", "signal", "silicon", "solar", "sonic", "spectrum", "sphere", "summit", "tactic", "timber", "titan", "topaz", "torpedo", "trace", "tropic", "tundra", "vector", "velocity", "velvet", "vessel", "vortex", "whisper", "zenith", "astral", "breeze", "canyon", "cascade", "celestial", "citadel", "comet", "crest", "crystal", "eclipse", "ember", "equinox", "frontier", "galaxy", "haven", "infinity", "kinetic", "lantern", "legacy", "monolith", "nebula", "odyssey", "pioneer", "quantum", "sanctuary", "solace", "stellar", "symphony", "vanguard", "vista", "voyage"];
10
+ export declare const randomInt: typeof secureRandomInt;
11
+ export declare function generatePassword(options?: RandomPasswordOptions): string;
12
+ export declare const generateRandomPassword: typeof generatePassword;
13
+ export declare function generatePassphrase(options?: PassphraseOptions): string;
14
+ export declare function generatePIN(options?: PinOptions): string;
15
+ export declare function generatePattern(options?: PatternOptions): string;
16
+ export declare function generateCredential(options: PasswordOptions): string;
17
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,cAAc,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExH,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,eAAO,MAAM,gBAAgB,iiCAYnB,CAAC;AAEX,eAAO,MAAM,SAAS,wBAAkB,CAAC;AAazC,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAa5E;AAED,eAAO,MAAM,sBAAsB,yBAAmB,CAAC;AAEvD,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAS1E;AAED,wBAAgB,WAAW,CAAC,OAAO,GAAE,UAAe,GAAG,MAAM,CAI5D;AAED,wBAAgB,eAAe,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,CASpE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAKnE"}
@@ -0,0 +1,90 @@
1
+ import { secureRandomInt } from './crypto-provider.js';
2
+ export const CHARACTER_SETS = {
3
+ uppercase: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
4
+ lowercase: 'abcdefghijklmnopqrstuvwxyz',
5
+ numbers: '0123456789',
6
+ symbols: '!@#$%^&*()_+{}[]<>?/|~=-',
7
+ };
8
+ export const PASSPHRASE_WORDS = [
9
+ 'anchor', 'beacon', 'canvas', 'cipher', 'cobalt', 'crane', 'delta', 'drift', 'echo', 'amber',
10
+ 'falcon', 'flint', 'fossil', 'glacier', 'granite', 'harbor', 'horizon', 'indigo', 'island', 'jasper',
11
+ 'lagoon', 'lunar', 'magnet', 'marble', 'matrix', 'meridian', 'meteor', 'nexus', 'noble', 'oasis',
12
+ 'obsidian', 'orbit', 'opal', 'origin', 'phantom', 'phoenix', 'prism', 'pulse', 'pyramid', 'quartz',
13
+ 'radar', 'radius', 'raven', 'ripple', 'ruby', 'safari', 'sapphire', 'saturn', 'shadow', 'shield',
14
+ 'signal', 'silicon', 'solar', 'sonic', 'spectrum', 'sphere', 'summit', 'tactic', 'timber', 'titan',
15
+ 'topaz', 'torpedo', 'trace', 'tropic', 'tundra', 'vector', 'velocity', 'velvet', 'vessel', 'vortex',
16
+ 'whisper', 'zenith', 'astral', 'breeze', 'canyon', 'cascade', 'celestial', 'citadel', 'comet', 'crest',
17
+ 'crystal', 'eclipse', 'ember', 'equinox', 'frontier', 'galaxy', 'haven', 'infinity', 'kinetic', 'lantern',
18
+ 'legacy', 'monolith', 'nebula', 'odyssey', 'pioneer', 'quantum', 'sanctuary', 'solace', 'stellar',
19
+ 'symphony', 'vanguard', 'vista', 'voyage',
20
+ ];
21
+ export const randomInt = secureRandomInt;
22
+ function randomChar(characters) { return characters[randomInt(characters.length)]; }
23
+ function shuffle(values) {
24
+ const result = [...values];
25
+ for (let index = result.length - 1; index > 0; index -= 1) {
26
+ const target = randomInt(index + 1);
27
+ [result[index], result[target]] = [result[target], result[index]];
28
+ }
29
+ return result;
30
+ }
31
+ export function generatePassword(options = {}) {
32
+ const settings = { length: 16, uppercase: true, lowercase: true, numbers: true, symbols: true, ...options };
33
+ if (!Number.isInteger(settings.length) || settings.length < 1)
34
+ throw new RangeError('Password length must be a positive integer');
35
+ const sets = [
36
+ settings.uppercase ? CHARACTER_SETS.uppercase : '', settings.lowercase ? CHARACTER_SETS.lowercase : '',
37
+ settings.numbers ? CHARACTER_SETS.numbers : '', settings.symbols ? CHARACTER_SETS.symbols : '',
38
+ ].filter(Boolean);
39
+ if (sets.length === 0)
40
+ throw new Error('Select at least one character set');
41
+ if (settings.length < sets.length)
42
+ throw new RangeError('Password length is shorter than the selected character-set count');
43
+ const result = sets.map(randomChar);
44
+ const pool = sets.join('');
45
+ while (result.length < settings.length)
46
+ result.push(randomChar(pool));
47
+ return shuffle(result).join('');
48
+ }
49
+ export const generateRandomPassword = generatePassword;
50
+ export function generatePassphrase(options = {}) {
51
+ const count = options.words ?? options.wordCount ?? 4;
52
+ if (!Number.isInteger(count) || count < 1)
53
+ throw new RangeError('Word count must be a positive integer');
54
+ const words = Array.from({ length: count }, () => {
55
+ const word = PASSPHRASE_WORDS[randomInt(PASSPHRASE_WORDS.length)];
56
+ return options.capitalize ? word[0].toUpperCase() + word.slice(1) : word;
57
+ });
58
+ if (options.includeNumber)
59
+ words[randomInt(words.length)] += randomInt(100);
60
+ return words.join(options.separator ?? '-');
61
+ }
62
+ export function generatePIN(options = {}) {
63
+ const length = options.length ?? options.pinLength ?? 6;
64
+ if (!Number.isInteger(length) || length < 1)
65
+ throw new RangeError('PIN length must be a positive integer');
66
+ return Array.from({ length }, () => randomChar(CHARACTER_SETS.numbers)).join('');
67
+ }
68
+ export function generatePattern(options = {}) {
69
+ const template = options.template ?? options.pattern ?? 'Lnnn-Lnnn-S';
70
+ return [...template].map((character) => {
71
+ if (character === 'L')
72
+ return randomChar(CHARACTER_SETS.uppercase);
73
+ if (character === 'l')
74
+ return randomChar(CHARACTER_SETS.lowercase);
75
+ if (character === 'n')
76
+ return randomChar(CHARACTER_SETS.numbers);
77
+ if (character === 'S' || character === 's')
78
+ return randomChar(CHARACTER_SETS.symbols);
79
+ return character;
80
+ }).join('');
81
+ }
82
+ export function generateCredential(options) {
83
+ if (options.mode === 'passphrase')
84
+ return generatePassphrase(options);
85
+ if (options.mode === 'pin')
86
+ return generatePIN(options);
87
+ if (options.mode === 'pattern')
88
+ return generatePattern(options);
89
+ return generatePassword(options);
90
+ }
@@ -0,0 +1,5 @@
1
+ export * from './audit.js';
2
+ export * from './crypto-provider.js';
3
+ export * from './generator.js';
4
+ export type * from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,mBAAmB,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './audit.js';
2
+ export * from './crypto-provider.js';
3
+ export * from './generator.js';
@@ -0,0 +1,39 @@
1
+ export type GeneratorMode = 'random' | 'passphrase' | 'pin' | 'pattern';
2
+ export type StrengthRating = 'Weak' | 'Medium' | 'Strong' | 'Very strong';
3
+ export interface RandomPasswordOptions {
4
+ length?: number;
5
+ uppercase?: boolean;
6
+ lowercase?: boolean;
7
+ numbers?: boolean;
8
+ symbols?: boolean;
9
+ }
10
+ export interface PassphraseOptions {
11
+ words?: number;
12
+ wordCount?: number;
13
+ separator?: '-' | '_' | '.' | ' ' | string;
14
+ capitalize?: boolean;
15
+ includeNumber?: boolean;
16
+ }
17
+ export interface PinOptions {
18
+ length?: number;
19
+ pinLength?: number;
20
+ }
21
+ export interface PatternOptions {
22
+ template?: string;
23
+ pattern?: string;
24
+ }
25
+ export interface PasswordOptions extends RandomPasswordOptions, PassphraseOptions, PinOptions, PatternOptions {
26
+ mode?: GeneratorMode;
27
+ }
28
+ export interface SecurityAudit {
29
+ entropy: number;
30
+ rating: StrengthRating;
31
+ timeToCrackSeconds: number;
32
+ crackTime: string;
33
+ crackTimeFormatted: string;
34
+ poolSize: number;
35
+ score: number;
36
+ warnings: string[];
37
+ tips: string[];
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,YAAY,GAAG,KAAK,GAAG,SAAS,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,aAAa,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,MAAM,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACnE,MAAM,WAAW,cAAc;IAAG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;AAEvE,MAAM,WAAW,eAAgB,SAAQ,qBAAqB,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc;IAC3G,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,cAAc,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@poorvithmp/safegen",
3
+ "version": "2.0.0",
4
+ "description": "Typed, zero-dependency credential generation and auditing for browsers and Node.js.",
5
+ "author": "Poorvith M P",
6
+ "homepage": "https://safegen.poorvithmp.com",
7
+ "repository": { "type": "git", "url": "git+https://github.com/poorvith-mp/safegen.git", "directory": "packages/core" },
8
+ "bugs": { "url": "https://github.com/poorvith-mp/safegen/issues" },
9
+ "keywords": ["password", "passphrase", "credentials", "cryptography", "typescript"],
10
+ "type": "module",
11
+ "sideEffects": false,
12
+ "files": ["dist", "README.md"],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "types": "./dist/index.d.ts",
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "license": "MIT",
27
+ "engines": {
28
+ "node": ">=20"
29
+ }
30
+ }