lockly 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lockly contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # lockly
2
+
3
+ Cryptographically secure password generator CLI.
4
+
5
+ Generates unpredictable random passwords instantly using `node:crypto.getRandomValues()`.
6
+
7
+ ## Features
8
+
9
+ - **Cryptographically secure** — CSPRNG via `node:crypto`
10
+ - **Fast** — under 50ms response time
11
+ - **Customizable** — length, character sets, count
12
+ - **Pipe-friendly** — clean stdout output, no ANSI colors
13
+ - **Zero install** — run instantly with `npx`
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ # Run instantly with npx (recommended)
19
+ npx lockly
20
+
21
+ # Or install globally
22
+ npm install -g lockly
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic
28
+
29
+ ```bash
30
+ # Default: generate a 16-character password
31
+ lockly
32
+
33
+ # Set length (32 characters)
34
+ lockly -l 32
35
+ lockly --length 32
36
+
37
+ # Generate multiple passwords (5)
38
+ lockly -c 5
39
+ lockly --count 5
40
+ ```
41
+
42
+ ### Character set filtering
43
+
44
+ ```bash
45
+ # Exclude symbols
46
+ lockly --no-symbols
47
+
48
+ # Digits only (PIN)
49
+ lockly --no-uppercase --no-lowercase --no-symbols -l 6
50
+
51
+ # Uppercase and digits only
52
+ lockly --no-lowercase --no-symbols
53
+ ```
54
+
55
+ ### Piping
56
+
57
+ ```bash
58
+ # Copy to clipboard (macOS)
59
+ lockly | pbcopy
60
+
61
+ # Copy to clipboard (Linux)
62
+ lockly | xclip -selection clipboard
63
+
64
+ # Copy to clipboard (Windows PowerShell)
65
+ lockly | Set-Clipboard
66
+
67
+ # Save to file
68
+ lockly -c 10 -l 32 > passwords.txt
69
+
70
+ # Set as environment variable
71
+ export DB_PASSWORD=$(lockly -l 24 --no-symbols)
72
+ ```
73
+
74
+ ## Options
75
+
76
+ | Option | Description | Default | Range |
77
+ |--------|-------------|---------|-------|
78
+ | `-l, --length <number>` | Password length | 16 | 1–1024 |
79
+ | `-c, --count <number>` | Number of passwords | 1 | 1+ |
80
+ | `--no-uppercase` | Exclude uppercase (A-Z) | included | - |
81
+ | `--no-lowercase` | Exclude lowercase (a-z) | included | - |
82
+ | `--no-numbers` | Exclude digits (0-9) | included | - |
83
+ | `--no-symbols` | Exclude symbols | included | - |
84
+ | `-V, --version` | Show version | - | - |
85
+ | `-h, --help` | Show help | - | - |
86
+
87
+ ## Security
88
+
89
+ - **CSPRNG**: Uses `node:crypto.getRandomValues()` instead of `Math.random()`
90
+ - **Local execution**: No network requests — passwords are generated locally
91
+ - **Stateless**: Generated passwords are never stored
92
+ - **No modulo bias**: Rejection sampling ensures uniform distribution
93
+
94
+ ### Security tips
95
+
96
+ - Store generated passwords immediately in a secure location (e.g. password manager)
97
+ - Use piping to avoid passwords appearing in terminal history
98
+ - Use `-l 32` or longer for high-security purposes (root accounts, financial services, etc.)
99
+
100
+ ## Programmatic API
101
+
102
+ Use lockly as a library in your TypeScript/JavaScript project.
103
+
104
+ ```typescript
105
+ import { generatePassword } from 'lockly';
106
+
107
+ // Default usage
108
+ const passwords = generatePassword();
109
+ console.log(passwords[0]); // 16-character password
110
+
111
+ // With options
112
+ const customPasswords = generatePassword({
113
+ length: 32,
114
+ count: 5,
115
+ uppercase: true,
116
+ lowercase: true,
117
+ numbers: true,
118
+ symbols: false
119
+ });
120
+ ```
121
+
122
+ ## Requirements
123
+
124
+ - Node.js 20+
125
+
126
+ ## License
127
+
128
+ MIT
package/dist/cli.js ADDED
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { readFileSync } from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import { getRandomValues } from 'crypto';
7
+
8
+ var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
9
+ var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
10
+ var NUMBERS = "0123456789";
11
+ var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
12
+ function buildPool(options2) {
13
+ let pool = "";
14
+ if (options2.uppercase) pool += UPPERCASE;
15
+ if (options2.lowercase) pool += LOWERCASE;
16
+ if (options2.numbers) pool += NUMBERS;
17
+ if (options2.symbols) pool += SYMBOLS;
18
+ return pool;
19
+ }
20
+ function generateSingle(length2, pool) {
21
+ const poolLength = pool.length;
22
+ const limit = 256 - 256 % poolLength;
23
+ const chars = [];
24
+ while (chars.length < length2) {
25
+ const remaining = length2 - chars.length;
26
+ const bufferSize = remaining * 2;
27
+ const randomBytes = new Uint8Array(bufferSize);
28
+ getRandomValues(randomBytes);
29
+ for (let i = 0; i < randomBytes.length && chars.length < length2; i++) {
30
+ const byte = randomBytes[i];
31
+ if (byte < limit) {
32
+ chars.push(pool[byte % poolLength]);
33
+ }
34
+ }
35
+ }
36
+ return chars.join("");
37
+ }
38
+ function validateOptions(options2) {
39
+ if (!Number.isInteger(options2.length) || options2.length < 1) {
40
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
41
+ }
42
+ if (options2.length > 1024) {
43
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
44
+ }
45
+ if (!Number.isInteger(options2.count) || options2.count < 1) {
46
+ throw new Error("count\uB294 1 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4");
47
+ }
48
+ if (!options2.uppercase && !options2.lowercase && !options2.numbers && !options2.symbols) {
49
+ throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
50
+ }
51
+ }
52
+ function generatePassword(options2) {
53
+ const resolved = {
54
+ length: options2?.length ?? 16,
55
+ count: options2?.count ?? 1,
56
+ uppercase: options2?.uppercase ?? true,
57
+ lowercase: options2?.lowercase ?? true,
58
+ numbers: options2?.numbers ?? true,
59
+ symbols: options2?.symbols ?? true
60
+ };
61
+ validateOptions(resolved);
62
+ const pool = buildPool(resolved);
63
+ const passwords = [];
64
+ for (let i = 0; i < resolved.count; i++) {
65
+ passwords.push(generateSingle(resolved.length, pool));
66
+ }
67
+ return passwords;
68
+ }
69
+
70
+ // src/cli.ts
71
+ var __filename2 = fileURLToPath(import.meta.url);
72
+ var __dirname2 = dirname(__filename2);
73
+ var packageJsonPath = join(__dirname2, "../package.json");
74
+ var packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
75
+ var version = packageJson.version;
76
+ var program = new Command();
77
+ program.name("lockly").description("Cryptographically secure random password generator").version(version, "-V, --version", "\uBC84\uC804 \uD45C\uC2DC").option(
78
+ "-l, --length <number>",
79
+ "\uD328\uC2A4\uC6CC\uB4DC \uAE38\uC774 (\uAE30\uBCF8: 16, \uBC94\uC704: 1-1024)",
80
+ "16"
81
+ ).option("-c, --count <number>", "\uC0DD\uC131 \uAC1C\uC218 (\uAE30\uBCF8: 1)", "1").option("--no-uppercase", "\uB300\uBB38\uC790(A-Z) \uC81C\uC678").option("--no-lowercase", "\uC18C\uBB38\uC790(a-z) \uC81C\uC678").option("--no-numbers", "\uC22B\uC790(0-9) \uC81C\uC678").option("--no-symbols", "\uD2B9\uC218\uBB38\uC790 \uC81C\uC678").helpOption("-h, --help", "\uB3C4\uC6C0\uB9D0");
82
+ program.parse();
83
+ var options = program.opts();
84
+ var length = parseInt(options.length, 10);
85
+ var count = parseInt(options.count, 10);
86
+ if (isNaN(length)) {
87
+ console.error("\uC720\uD6A8\uD55C \uC22B\uC790\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694 (length)");
88
+ process.exit(1);
89
+ }
90
+ if (isNaN(count)) {
91
+ console.error("\uC720\uD6A8\uD55C \uC22B\uC790\uB97C \uC785\uB825\uD574\uC8FC\uC138\uC694 (count)");
92
+ process.exit(1);
93
+ }
94
+ try {
95
+ const passwords = generatePassword({
96
+ length,
97
+ count,
98
+ uppercase: options.uppercase,
99
+ lowercase: options.lowercase,
100
+ numbers: options.numbers,
101
+ symbols: options.symbols
102
+ });
103
+ for (const password of passwords) {
104
+ console.log(password);
105
+ }
106
+ process.exit(0);
107
+ } catch (error) {
108
+ if (error instanceof Error) {
109
+ console.error(error.message);
110
+ } else {
111
+ console.error("\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4");
112
+ }
113
+ process.exit(1);
114
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/generator.ts
6
+ var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
7
+ var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
8
+ var NUMBERS = "0123456789";
9
+ var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
10
+ function buildPool(options) {
11
+ let pool = "";
12
+ if (options.uppercase) pool += UPPERCASE;
13
+ if (options.lowercase) pool += LOWERCASE;
14
+ if (options.numbers) pool += NUMBERS;
15
+ if (options.symbols) pool += SYMBOLS;
16
+ return pool;
17
+ }
18
+ function generateSingle(length, pool) {
19
+ const poolLength = pool.length;
20
+ const limit = 256 - 256 % poolLength;
21
+ const chars = [];
22
+ while (chars.length < length) {
23
+ const remaining = length - chars.length;
24
+ const bufferSize = remaining * 2;
25
+ const randomBytes = new Uint8Array(bufferSize);
26
+ crypto.getRandomValues(randomBytes);
27
+ for (let i = 0; i < randomBytes.length && chars.length < length; i++) {
28
+ const byte = randomBytes[i];
29
+ if (byte < limit) {
30
+ chars.push(pool[byte % poolLength]);
31
+ }
32
+ }
33
+ }
34
+ return chars.join("");
35
+ }
36
+ function validateOptions(options) {
37
+ if (!Number.isInteger(options.length) || options.length < 1) {
38
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
39
+ }
40
+ if (options.length > 1024) {
41
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
42
+ }
43
+ if (!Number.isInteger(options.count) || options.count < 1) {
44
+ throw new Error("count\uB294 1 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4");
45
+ }
46
+ if (!options.uppercase && !options.lowercase && !options.numbers && !options.symbols) {
47
+ throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
48
+ }
49
+ }
50
+ function generatePassword(options) {
51
+ const resolved = {
52
+ length: options?.length ?? 16,
53
+ count: options?.count ?? 1,
54
+ uppercase: options?.uppercase ?? true,
55
+ lowercase: options?.lowercase ?? true,
56
+ numbers: options?.numbers ?? true,
57
+ symbols: options?.symbols ?? true
58
+ };
59
+ validateOptions(resolved);
60
+ const pool = buildPool(resolved);
61
+ const passwords = [];
62
+ for (let i = 0; i < resolved.count; i++) {
63
+ passwords.push(generateSingle(resolved.length, pool));
64
+ }
65
+ return passwords;
66
+ }
67
+
68
+ exports.generatePassword = generatePassword;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Password generation core logic
3
+ * Uses node:crypto for cryptographically secure random generation
4
+ */
5
+ /**
6
+ * Options for password generation
7
+ */
8
+ interface GenerateOptions {
9
+ /**
10
+ * Password length (1-1024)
11
+ * @default 16
12
+ */
13
+ length?: number;
14
+ /**
15
+ * Number of passwords to generate
16
+ * @default 1
17
+ */
18
+ count?: number;
19
+ /**
20
+ * Include uppercase letters (A-Z)
21
+ * @default true
22
+ */
23
+ uppercase?: boolean;
24
+ /**
25
+ * Include lowercase letters (a-z)
26
+ * @default true
27
+ */
28
+ lowercase?: boolean;
29
+ /**
30
+ * Include numbers (0-9)
31
+ * @default true
32
+ */
33
+ numbers?: boolean;
34
+ /**
35
+ * Include special characters
36
+ * @default true
37
+ */
38
+ symbols?: boolean;
39
+ }
40
+ /**
41
+ * Generate one or more cryptographically secure random passwords.
42
+ *
43
+ * Uses `node:crypto.getRandomValues()` as the entropy source (REQ-1, NFR-1).
44
+ * Applies rejection sampling to avoid modulo bias when the character pool
45
+ * length is not a power of two.
46
+ *
47
+ * @param options - Password generation options. All fields are optional and
48
+ * fall back to safe defaults (length=16, count=1, all charsets enabled).
49
+ * @returns An array of generated password strings. The array length equals
50
+ * `options.count` (default 1).
51
+ * @throws {Error} If options validation fails (invalid length, count, or charsets)
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * // Generate one 16-character password with all charsets
56
+ * const [pw] = generatePassword();
57
+ *
58
+ * // Generate three 32-character passwords without symbols
59
+ * const pws = generatePassword({ length: 32, count: 3, symbols: false });
60
+ * ```
61
+ */
62
+ declare function generatePassword(options?: GenerateOptions): string[];
63
+
64
+ export { type GenerateOptions, generatePassword };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Password generation core logic
3
+ * Uses node:crypto for cryptographically secure random generation
4
+ */
5
+ /**
6
+ * Options for password generation
7
+ */
8
+ interface GenerateOptions {
9
+ /**
10
+ * Password length (1-1024)
11
+ * @default 16
12
+ */
13
+ length?: number;
14
+ /**
15
+ * Number of passwords to generate
16
+ * @default 1
17
+ */
18
+ count?: number;
19
+ /**
20
+ * Include uppercase letters (A-Z)
21
+ * @default true
22
+ */
23
+ uppercase?: boolean;
24
+ /**
25
+ * Include lowercase letters (a-z)
26
+ * @default true
27
+ */
28
+ lowercase?: boolean;
29
+ /**
30
+ * Include numbers (0-9)
31
+ * @default true
32
+ */
33
+ numbers?: boolean;
34
+ /**
35
+ * Include special characters
36
+ * @default true
37
+ */
38
+ symbols?: boolean;
39
+ }
40
+ /**
41
+ * Generate one or more cryptographically secure random passwords.
42
+ *
43
+ * Uses `node:crypto.getRandomValues()` as the entropy source (REQ-1, NFR-1).
44
+ * Applies rejection sampling to avoid modulo bias when the character pool
45
+ * length is not a power of two.
46
+ *
47
+ * @param options - Password generation options. All fields are optional and
48
+ * fall back to safe defaults (length=16, count=1, all charsets enabled).
49
+ * @returns An array of generated password strings. The array length equals
50
+ * `options.count` (default 1).
51
+ * @throws {Error} If options validation fails (invalid length, count, or charsets)
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * // Generate one 16-character password with all charsets
56
+ * const [pw] = generatePassword();
57
+ *
58
+ * // Generate three 32-character passwords without symbols
59
+ * const pws = generatePassword({ length: 32, count: 3, symbols: false });
60
+ * ```
61
+ */
62
+ declare function generatePassword(options?: GenerateOptions): string[];
63
+
64
+ export { type GenerateOptions, generatePassword };
package/dist/index.js ADDED
@@ -0,0 +1,66 @@
1
+ import { getRandomValues } from 'crypto';
2
+
3
+ // src/generator.ts
4
+ var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
5
+ var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
6
+ var NUMBERS = "0123456789";
7
+ var SYMBOLS = "!@#$%^&*()_+-=[]{}|;:,.<>?";
8
+ function buildPool(options) {
9
+ let pool = "";
10
+ if (options.uppercase) pool += UPPERCASE;
11
+ if (options.lowercase) pool += LOWERCASE;
12
+ if (options.numbers) pool += NUMBERS;
13
+ if (options.symbols) pool += SYMBOLS;
14
+ return pool;
15
+ }
16
+ function generateSingle(length, pool) {
17
+ const poolLength = pool.length;
18
+ const limit = 256 - 256 % poolLength;
19
+ const chars = [];
20
+ while (chars.length < length) {
21
+ const remaining = length - chars.length;
22
+ const bufferSize = remaining * 2;
23
+ const randomBytes = new Uint8Array(bufferSize);
24
+ getRandomValues(randomBytes);
25
+ for (let i = 0; i < randomBytes.length && chars.length < length; i++) {
26
+ const byte = randomBytes[i];
27
+ if (byte < limit) {
28
+ chars.push(pool[byte % poolLength]);
29
+ }
30
+ }
31
+ }
32
+ return chars.join("");
33
+ }
34
+ function validateOptions(options) {
35
+ if (!Number.isInteger(options.length) || options.length < 1) {
36
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
37
+ }
38
+ if (options.length > 1024) {
39
+ throw new Error("\uAE38\uC774\uB294 1~1024 \uC0AC\uC774\uC5EC\uC57C \uD569\uB2C8\uB2E4");
40
+ }
41
+ if (!Number.isInteger(options.count) || options.count < 1) {
42
+ throw new Error("count\uB294 1 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4");
43
+ }
44
+ if (!options.uppercase && !options.lowercase && !options.numbers && !options.symbols) {
45
+ throw new Error("\uCD5C\uC18C 1\uAC1C \uC774\uC0C1\uC758 \uBB38\uC790\uC14B\uC744 \uD3EC\uD568\uD574\uC57C \uD569\uB2C8\uB2E4");
46
+ }
47
+ }
48
+ function generatePassword(options) {
49
+ const resolved = {
50
+ length: options?.length ?? 16,
51
+ count: options?.count ?? 1,
52
+ uppercase: options?.uppercase ?? true,
53
+ lowercase: options?.lowercase ?? true,
54
+ numbers: options?.numbers ?? true,
55
+ symbols: options?.symbols ?? true
56
+ };
57
+ validateOptions(resolved);
58
+ const pool = buildPool(resolved);
59
+ const passwords = [];
60
+ for (let i = 0; i < resolved.count; i++) {
61
+ passwords.push(generateSingle(resolved.length, pool));
62
+ }
63
+ return passwords;
64
+ }
65
+
66
+ export { generatePassword };
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "lockly",
3
+ "version": "0.1.0",
4
+ "description": "Cryptographically secure random password generator CLI",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "author": "Daniel Kyojun Ku <d@nielku.com>",
8
+ "keywords": [
9
+ "password",
10
+ "generator",
11
+ "cli",
12
+ "crypto",
13
+ "random",
14
+ "secure"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/kjunine/lockly.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/kjunine/lockly/issues"
22
+ },
23
+ "homepage": "https://github.com/kjunine/lockly#readme",
24
+ "bin": {
25
+ "lockly": "./dist/cli.js"
26
+ },
27
+ "main": "./dist/index.cjs",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "require": "./dist/index.cjs",
34
+ "import": "./dist/index.js"
35
+ }
36
+ },
37
+ "files": [
38
+ "dist"
39
+ ],
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "scripts": {
44
+ "build": "tsup",
45
+ "lint": "eslint src tests",
46
+ "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
47
+ "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
48
+ "test": "vitest run",
49
+ "test:coverage": "vitest run --coverage",
50
+ "check": "pnpm run lint && pnpm run format:check && pnpm run build && pnpm run test"
51
+ },
52
+ "dependencies": {
53
+ "commander": "^14.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@eslint/js": "^9.0.0",
57
+ "@types/node": "^22.0.0",
58
+ "@vitest/coverage-v8": "^4.0.0",
59
+ "eslint": "^9.0.0",
60
+ "eslint-config-prettier": "^10.1.8",
61
+ "prettier": "^3.2.0",
62
+ "tsup": "^8.0.0",
63
+ "typescript": "^5.3.0",
64
+ "typescript-eslint": "^8.0.0",
65
+ "vitest": "^4.0.0"
66
+ },
67
+ "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc"
68
+ }