stegdoc 4.0.0 → 5.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.
@@ -1,190 +1,190 @@
1
- /**
2
- * Interactive prompts for encode/decode commands
3
- */
4
-
5
- /**
6
- * Check if running in interactive mode (no relevant flags provided)
7
- * @param {object} options - Command options
8
- * @param {string} command - Command name ('encode' or 'decode')
9
- * @returns {boolean}
10
- */
11
- function shouldRunInteractive(options, command) {
12
- // If --yes flag is set, never run interactive
13
- if (options.yes) return false;
14
-
15
- // If --quiet flag is set, never run interactive
16
- if (options.quiet) return false;
17
-
18
- if (command === 'encode') {
19
- // Run interactive if no format or password specified
20
- const hasFormat = options.format && options.format !== 'xlsx'; // xlsx is default
21
- const hasPassword = !!options.password;
22
- return !hasFormat && !hasPassword;
23
- }
24
-
25
- if (command === 'decode') {
26
- // For decode, only prompt if encrypted file needs password
27
- // This is handled separately in decode command
28
- return false;
29
- }
30
-
31
- return false;
32
- }
33
-
34
- /**
35
- * Prompt for encode options interactively
36
- * @param {string} filename - Input filename for context
37
- * @returns {Promise<object>} Selected options
38
- */
39
- async function promptEncodeOptions(filename) {
40
- // Dynamic import for ESM inquirer
41
- const { default: inquirer } = await import('inquirer');
42
-
43
- console.log();
44
-
45
- // First get format and encryption choice
46
- const basicAnswers = await inquirer.prompt([
47
- {
48
- type: 'list',
49
- name: 'format',
50
- message: 'Select output format:',
51
- choices: [
52
- { name: 'XLSX (Excel) - recommended, better hiding spots', value: 'xlsx' },
53
- { name: 'DOCX (Word) - fallback option', value: 'docx' },
54
- ],
55
- default: 'xlsx',
56
- },
57
- {
58
- type: 'confirm',
59
- name: 'useEncryption',
60
- message: 'Encrypt the file with a password?',
61
- default: true,
62
- },
63
- ]);
64
-
65
- let password;
66
-
67
- // If encryption is enabled, prompt for password with confirmation
68
- if (basicAnswers.useEncryption) {
69
- let passwordsMatch = false;
70
-
71
- while (!passwordsMatch) {
72
- const { password: pwd } = await inquirer.prompt([
73
- {
74
- type: 'password',
75
- name: 'password',
76
- message: 'Enter encryption password:',
77
- mask: '*',
78
- validate: (input) => {
79
- if (input.length < 8) {
80
- return 'Password must be at least 8 characters';
81
- }
82
- return true;
83
- },
84
- },
85
- ]);
86
-
87
- const { passwordConfirm } = await inquirer.prompt([
88
- {
89
- type: 'password',
90
- name: 'passwordConfirm',
91
- message: 'Confirm password:',
92
- mask: '*',
93
- },
94
- ]);
95
-
96
- if (pwd === passwordConfirm) {
97
- password = pwd;
98
- passwordsMatch = true;
99
- } else {
100
- console.log('Passwords do not match. Please try again.\n');
101
- }
102
- }
103
- }
104
-
105
- // Get chunk size
106
- const { chunkSize } = await inquirer.prompt([
107
- {
108
- type: 'input',
109
- name: 'chunkSize',
110
- message: 'Chunk size or number of parts (e.g., 5MB, "3 parts", or "max"):',
111
- default: '5MB',
112
- validate: (input) => {
113
- const trimmed = input.trim().toLowerCase();
114
- // Allow special values for no splitting
115
- if (['0', 'max', 'single', 'none'].includes(trimmed)) {
116
- return true;
117
- }
118
- // Allow "X parts" format
119
- if (/^\d+\s*parts?$/i.test(trimmed)) {
120
- const num = parseInt(trimmed, 10);
121
- if (num < 1) return 'Number of parts must be at least 1';
122
- return true;
123
- }
124
- // Allow size format
125
- if (!/^\d+(\.\d+)?\s*(B|KB|MB|GB)?$/i.test(trimmed)) {
126
- return 'Use "5MB", "3 parts", or "max" for no splitting';
127
- }
128
- return true;
129
- },
130
- },
131
- ]);
132
-
133
- return {
134
- format: basicAnswers.format,
135
- password,
136
- chunkSize,
137
- };
138
- }
139
-
140
- /**
141
- * Prompt for password when decoding encrypted file
142
- * @returns {Promise<string>} Password
143
- */
144
- async function promptPassword() {
145
- const { default: inquirer } = await import('inquirer');
146
-
147
- const { password } = await inquirer.prompt([
148
- {
149
- type: 'password',
150
- name: 'password',
151
- message: 'Enter decryption password:',
152
- mask: '*',
153
- validate: (input) => {
154
- if (!input) {
155
- return 'Password is required for encrypted files';
156
- }
157
- return true;
158
- },
159
- },
160
- ]);
161
-
162
- return password;
163
- }
164
-
165
- /**
166
- * Prompt for confirmation before overwriting
167
- * @param {string} filePath - Path to file that will be overwritten
168
- * @returns {Promise<boolean>} True if user confirms
169
- */
170
- async function promptOverwrite(filePath) {
171
- const { default: inquirer } = await import('inquirer');
172
-
173
- const { confirm } = await inquirer.prompt([
174
- {
175
- type: 'confirm',
176
- name: 'confirm',
177
- message: `File "${filePath}" already exists. Overwrite?`,
178
- default: false,
179
- },
180
- ]);
181
-
182
- return confirm;
183
- }
184
-
185
- module.exports = {
186
- shouldRunInteractive,
187
- promptEncodeOptions,
188
- promptPassword,
189
- promptOverwrite,
190
- };
1
+ /**
2
+ * Interactive prompts for encode/decode commands
3
+ */
4
+
5
+ /**
6
+ * Check if running in interactive mode (no relevant flags provided)
7
+ * @param {object} options - Command options
8
+ * @param {string} command - Command name ('encode' or 'decode')
9
+ * @returns {boolean}
10
+ */
11
+ function shouldRunInteractive(options, command) {
12
+ // If --yes flag is set, never run interactive
13
+ if (options.yes) return false;
14
+
15
+ // If --quiet flag is set, never run interactive
16
+ if (options.quiet) return false;
17
+
18
+ if (command === 'encode') {
19
+ // Run interactive if no format or password specified
20
+ const hasFormat = options.format && options.format !== 'xlsx'; // xlsx is default
21
+ const hasPassword = !!options.password;
22
+ return !hasFormat && !hasPassword;
23
+ }
24
+
25
+ if (command === 'decode') {
26
+ // For decode, only prompt if encrypted file needs password
27
+ // This is handled separately in decode command
28
+ return false;
29
+ }
30
+
31
+ return false;
32
+ }
33
+
34
+ /**
35
+ * Prompt for encode options interactively
36
+ * @param {string} filename - Input filename for context
37
+ * @returns {Promise<object>} Selected options
38
+ */
39
+ async function promptEncodeOptions(filename) {
40
+ // Dynamic import for ESM inquirer
41
+ const { default: inquirer } = await import('inquirer');
42
+
43
+ console.log();
44
+
45
+ // First get format and encryption choice
46
+ const basicAnswers = await inquirer.prompt([
47
+ {
48
+ type: 'list',
49
+ name: 'format',
50
+ message: 'Select output format:',
51
+ choices: [
52
+ { name: 'XLSX (Excel) - recommended, any file size', value: 'xlsx' },
53
+ { name: 'DOCX (Word) - files under 1 MB only', value: 'docx' },
54
+ ],
55
+ default: 'xlsx',
56
+ },
57
+ {
58
+ type: 'confirm',
59
+ name: 'useEncryption',
60
+ message: 'Encrypt the file with a password?',
61
+ default: true,
62
+ },
63
+ ]);
64
+
65
+ let password;
66
+
67
+ // If encryption is enabled, prompt for password with confirmation
68
+ if (basicAnswers.useEncryption) {
69
+ let passwordsMatch = false;
70
+
71
+ while (!passwordsMatch) {
72
+ const { password: pwd } = await inquirer.prompt([
73
+ {
74
+ type: 'password',
75
+ name: 'password',
76
+ message: 'Enter encryption password:',
77
+ mask: '*',
78
+ validate: (input) => {
79
+ if (input.length < 8) {
80
+ return 'Password must be at least 8 characters';
81
+ }
82
+ return true;
83
+ },
84
+ },
85
+ ]);
86
+
87
+ const { passwordConfirm } = await inquirer.prompt([
88
+ {
89
+ type: 'password',
90
+ name: 'passwordConfirm',
91
+ message: 'Confirm password:',
92
+ mask: '*',
93
+ },
94
+ ]);
95
+
96
+ if (pwd === passwordConfirm) {
97
+ password = pwd;
98
+ passwordsMatch = true;
99
+ } else {
100
+ console.log('Passwords do not match. Please try again.\n');
101
+ }
102
+ }
103
+ }
104
+
105
+ // Get chunk size
106
+ const { chunkSize } = await inquirer.prompt([
107
+ {
108
+ type: 'input',
109
+ name: 'chunkSize',
110
+ message: 'Chunk size or number of parts (e.g., 5MB, "3 parts", or "max"):',
111
+ default: '5MB',
112
+ validate: (input) => {
113
+ const trimmed = input.trim().toLowerCase();
114
+ // Allow special values for no splitting
115
+ if (['0', 'max', 'single', 'none'].includes(trimmed)) {
116
+ return true;
117
+ }
118
+ // Allow "X parts" format
119
+ if (/^\d+\s*parts?$/i.test(trimmed)) {
120
+ const num = parseInt(trimmed, 10);
121
+ if (num < 1) return 'Number of parts must be at least 1';
122
+ return true;
123
+ }
124
+ // Allow size format
125
+ if (!/^\d+(\.\d+)?\s*(B|KB|MB|GB)?$/i.test(trimmed)) {
126
+ return 'Use "5MB", "3 parts", or "max" for no splitting';
127
+ }
128
+ return true;
129
+ },
130
+ },
131
+ ]);
132
+
133
+ return {
134
+ format: basicAnswers.format,
135
+ password,
136
+ chunkSize,
137
+ };
138
+ }
139
+
140
+ /**
141
+ * Prompt for password when decoding encrypted file
142
+ * @returns {Promise<string>} Password
143
+ */
144
+ async function promptPassword() {
145
+ const { default: inquirer } = await import('inquirer');
146
+
147
+ const { password } = await inquirer.prompt([
148
+ {
149
+ type: 'password',
150
+ name: 'password',
151
+ message: 'Enter decryption password:',
152
+ mask: '*',
153
+ validate: (input) => {
154
+ if (!input) {
155
+ return 'Password is required for encrypted files';
156
+ }
157
+ return true;
158
+ },
159
+ },
160
+ ]);
161
+
162
+ return password;
163
+ }
164
+
165
+ /**
166
+ * Prompt for confirmation before overwriting
167
+ * @param {string} filePath - Path to file that will be overwritten
168
+ * @returns {Promise<boolean>} True if user confirms
169
+ */
170
+ async function promptOverwrite(filePath) {
171
+ const { default: inquirer } = await import('inquirer');
172
+
173
+ const { confirm } = await inquirer.prompt([
174
+ {
175
+ type: 'confirm',
176
+ name: 'confirm',
177
+ message: `File "${filePath}" already exists. Overwrite?`,
178
+ default: false,
179
+ },
180
+ ]);
181
+
182
+ return confirm;
183
+ }
184
+
185
+ module.exports = {
186
+ shouldRunInteractive,
187
+ promptEncodeOptions,
188
+ promptPassword,
189
+ promptOverwrite,
190
+ };