@magentrix-corp/magentrix-cli 1.3.16 → 1.3.17
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 +25 -25
- package/README.md +1166 -1166
- package/actions/autopublish.old.js +293 -293
- package/actions/config.js +182 -182
- package/actions/create.js +466 -466
- package/actions/help.js +164 -164
- package/actions/iris/buildStage.js +874 -874
- package/actions/iris/delete.js +256 -256
- package/actions/iris/dev.js +391 -391
- package/actions/iris/index.js +6 -6
- package/actions/iris/link.js +375 -375
- package/actions/iris/recover.js +268 -268
- package/actions/main.js +80 -80
- package/actions/publish.js +1420 -1420
- package/actions/pull.js +684 -684
- package/actions/setup.js +148 -148
- package/actions/status.js +17 -17
- package/actions/update.js +248 -248
- package/bin/magentrix.js +393 -393
- package/package.json +55 -55
- package/utils/assetPaths.js +158 -158
- package/utils/autopublishLock.js +77 -77
- package/utils/cacher.js +206 -206
- package/utils/cli/checkInstanceUrl.js +76 -74
- package/utils/cli/helpers/compare.js +282 -282
- package/utils/cli/helpers/ensureApiKey.js +63 -63
- package/utils/cli/helpers/ensureCredentials.js +68 -68
- package/utils/cli/helpers/ensureInstanceUrl.js +75 -75
- package/utils/cli/writeRecords.js +262 -262
- package/utils/compare.js +135 -135
- package/utils/compress.js +17 -17
- package/utils/config.js +527 -527
- package/utils/debug.js +144 -144
- package/utils/diagnostics/testPublishLogic.js +96 -96
- package/utils/diff.js +49 -49
- package/utils/downloadAssets.js +291 -291
- package/utils/filetag.js +115 -115
- package/utils/hash.js +14 -14
- package/utils/iris/backup.js +411 -411
- package/utils/iris/builder.js +541 -541
- package/utils/iris/config-reader.js +664 -664
- package/utils/iris/deleteHelper.js +150 -150
- package/utils/iris/errors.js +537 -537
- package/utils/iris/linker.js +601 -601
- package/utils/iris/lock.js +360 -360
- package/utils/iris/validation.js +360 -360
- package/utils/iris/validator.js +281 -281
- package/utils/iris/zipper.js +248 -248
- package/utils/logger.js +291 -291
- package/utils/magentrix/api/assets.js +220 -220
- package/utils/magentrix/api/auth.js +107 -107
- package/utils/magentrix/api/createEntity.js +61 -61
- package/utils/magentrix/api/deleteEntity.js +55 -55
- package/utils/magentrix/api/iris.js +251 -251
- package/utils/magentrix/api/meqlQuery.js +36 -36
- package/utils/magentrix/api/retrieveEntity.js +86 -86
- package/utils/magentrix/api/updateEntity.js +66 -66
- package/utils/magentrix/fetch.js +168 -168
- package/utils/merge.js +22 -22
- package/utils/permissionError.js +70 -70
- package/utils/preferences.js +40 -40
- package/utils/progress.js +469 -469
- package/utils/spinner.js +43 -43
- package/utils/template.js +52 -52
- package/utils/updateFileBase.js +121 -121
- package/utils/workspaces.js +108 -108
- package/vars/config.js +11 -11
- package/vars/global.js +50 -50
package/utils/iris/errors.js
CHANGED
|
@@ -1,537 +1,537 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error handling utilities for Iris Vue integration.
|
|
3
|
-
* Provides user-friendly error messages with actionable solutions.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Error types for categorization and handling.
|
|
10
|
-
*/
|
|
11
|
-
export const ErrorTypes = {
|
|
12
|
-
PERMISSION: 'PERMISSION',
|
|
13
|
-
DISK_FULL: 'DISK_FULL',
|
|
14
|
-
FILE_LOCKED: 'FILE_LOCKED',
|
|
15
|
-
NOT_FOUND: 'NOT_FOUND',
|
|
16
|
-
INVALID_CONFIG: 'INVALID_CONFIG',
|
|
17
|
-
NETWORK: 'NETWORK',
|
|
18
|
-
VALIDATION: 'VALIDATION',
|
|
19
|
-
CONCURRENT_OPERATION: 'CONCURRENT_OPERATION',
|
|
20
|
-
TIMEOUT: 'TIMEOUT',
|
|
21
|
-
CORRUPTED: 'CORRUPTED',
|
|
22
|
-
UNKNOWN: 'UNKNOWN'
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Detect the type of error from an Error object or error code.
|
|
27
|
-
*
|
|
28
|
-
* @param {Error | string} error - The error to analyze
|
|
29
|
-
* @returns {string} - One of ErrorTypes
|
|
30
|
-
*/
|
|
31
|
-
export function detectErrorType(error) {
|
|
32
|
-
const code = error?.code || error;
|
|
33
|
-
const message = (error?.message || String(error)).toLowerCase();
|
|
34
|
-
|
|
35
|
-
// Permission errors
|
|
36
|
-
if (code === 'EACCES' || code === 'EPERM' || message.includes('permission denied')) {
|
|
37
|
-
return ErrorTypes.PERMISSION;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Disk full errors
|
|
41
|
-
if (code === 'ENOSPC' || message.includes('no space left') || message.includes('disk full')) {
|
|
42
|
-
return ErrorTypes.DISK_FULL;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// File locked errors (Windows mainly)
|
|
46
|
-
if (code === 'EBUSY' || code === 'ENOTEMPTY' || message.includes('resource busy') ||
|
|
47
|
-
message.includes('being used by another process')) {
|
|
48
|
-
return ErrorTypes.FILE_LOCKED;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Not found errors
|
|
52
|
-
if (code === 'ENOENT' || message.includes('no such file') || message.includes('not found')) {
|
|
53
|
-
return ErrorTypes.NOT_FOUND;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Network errors
|
|
57
|
-
if (code === 'ENOTFOUND' || code === 'ECONNREFUSED' || code === 'ECONNRESET' ||
|
|
58
|
-
code === 'ETIMEDOUT' || message.includes('network') || message.includes('fetch')) {
|
|
59
|
-
return ErrorTypes.NETWORK;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// JSON/config parsing errors
|
|
63
|
-
if (message.includes('json') || message.includes('parse') || message.includes('syntax')) {
|
|
64
|
-
return ErrorTypes.CORRUPTED;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Timeout errors
|
|
68
|
-
if (code === 'ETIMEDOUT' || message.includes('timeout')) {
|
|
69
|
-
return ErrorTypes.TIMEOUT;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return ErrorTypes.UNKNOWN;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Format a permission error with helpful guidance.
|
|
77
|
-
*
|
|
78
|
-
* @param {Object} options - Error context
|
|
79
|
-
* @param {string} options.operation - What operation failed (e.g., 'write', 'delete', 'read')
|
|
80
|
-
* @param {string} options.path - The path that caused the error
|
|
81
|
-
* @param {string} options.platform - The platform ('win32', 'darwin', 'linux')
|
|
82
|
-
* @returns {string} - Formatted error message
|
|
83
|
-
*/
|
|
84
|
-
export function formatPermissionError(options) {
|
|
85
|
-
const { operation = 'access', path = '', platform = process.platform } = options;
|
|
86
|
-
|
|
87
|
-
const lines = [
|
|
88
|
-
'',
|
|
89
|
-
chalk.bgRed.white.bold(' Permission Error '),
|
|
90
|
-
chalk.red('─'.repeat(50)),
|
|
91
|
-
'',
|
|
92
|
-
chalk.white(`Unable to ${operation} files at:`),
|
|
93
|
-
chalk.gray(` ${path}`),
|
|
94
|
-
'',
|
|
95
|
-
chalk.cyan('Possible causes:'),
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
if (platform === 'win32') {
|
|
99
|
-
lines.push(
|
|
100
|
-
chalk.gray(' 1. File is open in another program (VS Code, Explorer, antivirus)'),
|
|
101
|
-
chalk.gray(' 2. You need to run the command as Administrator'),
|
|
102
|
-
chalk.gray(' 3. The file is marked as read-only'),
|
|
103
|
-
'',
|
|
104
|
-
chalk.cyan('Solutions:'),
|
|
105
|
-
chalk.white(' 1. Close any programs that might have the file open'),
|
|
106
|
-
chalk.white(' 2. Run your terminal as Administrator:'),
|
|
107
|
-
chalk.gray(' Right-click terminal > "Run as administrator"'),
|
|
108
|
-
chalk.white(' 3. Check file properties and uncheck "Read-only"'),
|
|
109
|
-
chalk.white(' 4. Temporarily disable antivirus real-time scanning')
|
|
110
|
-
);
|
|
111
|
-
} else {
|
|
112
|
-
lines.push(
|
|
113
|
-
chalk.gray(' 1. Files are owned by a different user'),
|
|
114
|
-
chalk.gray(' 2. Insufficient permissions on directory'),
|
|
115
|
-
chalk.gray(' 3. File system is mounted read-only'),
|
|
116
|
-
'',
|
|
117
|
-
chalk.cyan('Solutions:'),
|
|
118
|
-
chalk.white(' 1. Change ownership of the files:'),
|
|
119
|
-
chalk.yellow(` sudo chown -R $(whoami) "${path}"`),
|
|
120
|
-
chalk.white(' 2. Fix directory permissions:'),
|
|
121
|
-
chalk.yellow(` chmod -R u+rwX "${path}"`),
|
|
122
|
-
chalk.white(' 3. Use sudo for this operation (if appropriate):'),
|
|
123
|
-
chalk.yellow(' sudo magentrix <command>')
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
128
|
-
|
|
129
|
-
return lines.join('\n');
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Format a disk full error with helpful guidance.
|
|
134
|
-
*
|
|
135
|
-
* @param {Object} options - Error context
|
|
136
|
-
* @param {string} options.operation - What operation failed
|
|
137
|
-
* @param {string} options.path - The path that caused the error
|
|
138
|
-
* @returns {string} - Formatted error message
|
|
139
|
-
*/
|
|
140
|
-
export function formatDiskFullError(options) {
|
|
141
|
-
const { operation = 'write', path = '' } = options;
|
|
142
|
-
const platform = process.platform;
|
|
143
|
-
|
|
144
|
-
const lines = [
|
|
145
|
-
'',
|
|
146
|
-
chalk.bgRed.white.bold(' Disk Full '),
|
|
147
|
-
chalk.red('─'.repeat(50)),
|
|
148
|
-
'',
|
|
149
|
-
chalk.white(`Cannot ${operation} - no disk space available.`),
|
|
150
|
-
chalk.gray(` Path: ${path}`),
|
|
151
|
-
'',
|
|
152
|
-
chalk.cyan('Solutions:'),
|
|
153
|
-
];
|
|
154
|
-
|
|
155
|
-
if (platform === 'win32') {
|
|
156
|
-
lines.push(
|
|
157
|
-
chalk.white(' 1. Free up disk space:'),
|
|
158
|
-
chalk.gray(' - Empty the Recycle Bin'),
|
|
159
|
-
chalk.gray(' - Run Disk Cleanup (cleanmgr.exe)'),
|
|
160
|
-
chalk.gray(' - Uninstall unused programs'),
|
|
161
|
-
chalk.white(' 2. Clear npm cache:'),
|
|
162
|
-
chalk.yellow(' npm cache clean --force'),
|
|
163
|
-
chalk.white(' 3. Check available space:'),
|
|
164
|
-
chalk.yellow(' wmic logicaldisk get size,freespace,caption')
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
lines.push(
|
|
168
|
-
chalk.white(' 1. Check disk usage:'),
|
|
169
|
-
chalk.yellow(' df -h'),
|
|
170
|
-
chalk.white(' 2. Find large files:'),
|
|
171
|
-
chalk.yellow(' du -sh * | sort -rh | head -20'),
|
|
172
|
-
chalk.white(' 3. Clear npm cache:'),
|
|
173
|
-
chalk.yellow(' npm cache clean --force'),
|
|
174
|
-
chalk.white(' 4. Clear system logs (macOS):'),
|
|
175
|
-
chalk.yellow(' sudo rm -rf /private/var/log/*'),
|
|
176
|
-
chalk.white(' 5. Clear trash:'),
|
|
177
|
-
chalk.yellow(' rm -rf ~/.Trash/*')
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
182
|
-
|
|
183
|
-
return lines.join('\n');
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Format a file locked error with helpful guidance.
|
|
188
|
-
*
|
|
189
|
-
* @param {Object} options - Error context
|
|
190
|
-
* @param {string} options.path - The path that caused the error
|
|
191
|
-
* @returns {string} - Formatted error message
|
|
192
|
-
*/
|
|
193
|
-
export function formatFileLockError(options) {
|
|
194
|
-
const { path = '' } = options;
|
|
195
|
-
const platform = process.platform;
|
|
196
|
-
|
|
197
|
-
const lines = [
|
|
198
|
-
'',
|
|
199
|
-
chalk.bgYellow.black.bold(' File In Use '),
|
|
200
|
-
chalk.yellow('─'.repeat(50)),
|
|
201
|
-
'',
|
|
202
|
-
chalk.white('Cannot modify file - it is being used by another program.'),
|
|
203
|
-
chalk.gray(` Path: ${path}`),
|
|
204
|
-
'',
|
|
205
|
-
chalk.cyan('This commonly happens when:'),
|
|
206
|
-
chalk.gray(' - Your IDE (VS Code, WebStorm) has the file open'),
|
|
207
|
-
chalk.gray(' - A dev server is running and watching files'),
|
|
208
|
-
chalk.gray(' - Antivirus is scanning the file'),
|
|
209
|
-
chalk.gray(' - Another terminal has the directory open'),
|
|
210
|
-
'',
|
|
211
|
-
chalk.cyan('Solutions:'),
|
|
212
|
-
];
|
|
213
|
-
|
|
214
|
-
if (platform === 'win32') {
|
|
215
|
-
lines.push(
|
|
216
|
-
chalk.white(' 1. Close applications that might be using the file'),
|
|
217
|
-
chalk.white(' 2. Close any Vue/React dev servers running from this project'),
|
|
218
|
-
chalk.white(' 3. Close and reopen VS Code'),
|
|
219
|
-
chalk.white(' 4. Find what is using the file:'),
|
|
220
|
-
chalk.gray(' - Open Resource Monitor (resmon.exe)'),
|
|
221
|
-
chalk.gray(' - Go to CPU > Associated Handles'),
|
|
222
|
-
chalk.gray(' - Search for the filename')
|
|
223
|
-
);
|
|
224
|
-
} else {
|
|
225
|
-
lines.push(
|
|
226
|
-
chalk.white(' 1. Close applications that might be using the file'),
|
|
227
|
-
chalk.white(' 2. Stop any running dev servers'),
|
|
228
|
-
chalk.white(' 3. Find what is using the file:'),
|
|
229
|
-
chalk.yellow(` lsof "${path}"`),
|
|
230
|
-
chalk.white(' 4. Wait a few seconds and try again')
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
lines.push('', chalk.yellow('─'.repeat(50)), '');
|
|
235
|
-
|
|
236
|
-
return lines.join('\n');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Format a network error with helpful guidance.
|
|
241
|
-
*
|
|
242
|
-
* @param {Object} options - Error context
|
|
243
|
-
* @param {string} options.url - The URL that failed
|
|
244
|
-
* @param {Error} options.error - The original error
|
|
245
|
-
* @returns {string} - Formatted error message
|
|
246
|
-
*/
|
|
247
|
-
export function formatNetworkError(options) {
|
|
248
|
-
const { url = '', error } = options;
|
|
249
|
-
const code = error?.code || '';
|
|
250
|
-
const message = error?.message || '';
|
|
251
|
-
|
|
252
|
-
const lines = [
|
|
253
|
-
'',
|
|
254
|
-
chalk.bgRed.white.bold(' Network Error '),
|
|
255
|
-
chalk.red('─'.repeat(50)),
|
|
256
|
-
'',
|
|
257
|
-
chalk.white('Failed to connect to the server.'),
|
|
258
|
-
chalk.gray(` URL: ${url}`),
|
|
259
|
-
chalk.gray(` Error: ${message}`),
|
|
260
|
-
'',
|
|
261
|
-
chalk.cyan('Possible causes:'),
|
|
262
|
-
];
|
|
263
|
-
|
|
264
|
-
if (code === 'ENOTFOUND' || message.includes('getaddrinfo')) {
|
|
265
|
-
lines.push(
|
|
266
|
-
chalk.gray(' - The server hostname could not be resolved'),
|
|
267
|
-
chalk.gray(' - DNS server issues'),
|
|
268
|
-
chalk.gray(' - Typo in the URL'),
|
|
269
|
-
'',
|
|
270
|
-
chalk.cyan('Solutions:'),
|
|
271
|
-
chalk.white(' 1. Check that the URL is correct'),
|
|
272
|
-
chalk.white(' 2. Verify your internet connection'),
|
|
273
|
-
chalk.white(' 3. Try accessing the URL in a browser')
|
|
274
|
-
);
|
|
275
|
-
} else if (code === 'ECONNREFUSED') {
|
|
276
|
-
lines.push(
|
|
277
|
-
chalk.gray(' - The server is not running'),
|
|
278
|
-
chalk.gray(' - Wrong port number'),
|
|
279
|
-
chalk.gray(' - Firewall blocking the connection'),
|
|
280
|
-
'',
|
|
281
|
-
chalk.cyan('Solutions:'),
|
|
282
|
-
chalk.white(' 1. Verify the server is running'),
|
|
283
|
-
chalk.white(' 2. Check if the URL and port are correct'),
|
|
284
|
-
chalk.white(' 3. Check firewall settings')
|
|
285
|
-
);
|
|
286
|
-
} else if (code === 'ETIMEDOUT' || message.includes('timeout')) {
|
|
287
|
-
lines.push(
|
|
288
|
-
chalk.gray(' - Server took too long to respond'),
|
|
289
|
-
chalk.gray(' - Network congestion'),
|
|
290
|
-
chalk.gray(' - Server might be overloaded'),
|
|
291
|
-
'',
|
|
292
|
-
chalk.cyan('Solutions:'),
|
|
293
|
-
chalk.white(' 1. Wait a moment and try again'),
|
|
294
|
-
chalk.white(' 2. Check if you can access the server in a browser'),
|
|
295
|
-
chalk.white(' 3. Try from a different network')
|
|
296
|
-
);
|
|
297
|
-
} else {
|
|
298
|
-
lines.push(
|
|
299
|
-
chalk.gray(' - Network connectivity issues'),
|
|
300
|
-
chalk.gray(' - Server is unreachable'),
|
|
301
|
-
'',
|
|
302
|
-
chalk.cyan('Solutions:'),
|
|
303
|
-
chalk.white(' 1. Check your internet connection'),
|
|
304
|
-
chalk.white(' 2. Try again in a few minutes'),
|
|
305
|
-
chalk.white(' 3. Verify the URL is correct')
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
310
|
-
|
|
311
|
-
return lines.join('\n');
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Format a corrupted config error with helpful guidance.
|
|
316
|
-
*
|
|
317
|
-
* @param {Object} options - Error context
|
|
318
|
-
* @param {string} options.configPath - Path to the corrupted config
|
|
319
|
-
* @param {Error} options.error - The original error
|
|
320
|
-
* @returns {string} - Formatted error message
|
|
321
|
-
*/
|
|
322
|
-
export function formatCorruptedConfigError(options) {
|
|
323
|
-
const { configPath = '', error } = options;
|
|
324
|
-
const message = error?.message || 'Invalid JSON format';
|
|
325
|
-
|
|
326
|
-
const lines = [
|
|
327
|
-
'',
|
|
328
|
-
chalk.bgRed.white.bold(' Config File Corrupted '),
|
|
329
|
-
chalk.red('─'.repeat(50)),
|
|
330
|
-
'',
|
|
331
|
-
chalk.white('The configuration file could not be read due to invalid format.'),
|
|
332
|
-
chalk.gray(` Path: ${configPath}`),
|
|
333
|
-
chalk.gray(` Error: ${message}`),
|
|
334
|
-
'',
|
|
335
|
-
chalk.cyan('This can happen when:'),
|
|
336
|
-
chalk.gray(' - A previous write was interrupted'),
|
|
337
|
-
chalk.gray(' - The file was manually edited with invalid JSON'),
|
|
338
|
-
chalk.gray(' - Disk errors corrupted the file'),
|
|
339
|
-
'',
|
|
340
|
-
chalk.cyan('Solutions:'),
|
|
341
|
-
chalk.white(' 1. Check if a backup exists:'),
|
|
342
|
-
chalk.yellow(` ls -la "${configPath}.bak"`),
|
|
343
|
-
chalk.white(' 2. If backup exists, restore it:'),
|
|
344
|
-
chalk.yellow(` cp "${configPath}.bak" "${configPath}"`),
|
|
345
|
-
chalk.white(' 3. If no backup, you may need to delete and recreate:'),
|
|
346
|
-
chalk.yellow(` rm "${configPath}"`),
|
|
347
|
-
chalk.gray(' Then re-run your command to regenerate the config.'),
|
|
348
|
-
'',
|
|
349
|
-
chalk.bgYellow.black(' Warning '),
|
|
350
|
-
chalk.yellow(' Deleting the config will lose your linked project history.'),
|
|
351
|
-
chalk.yellow(' You will need to re-link your Vue projects.'),
|
|
352
|
-
'',
|
|
353
|
-
chalk.red('─'.repeat(50)),
|
|
354
|
-
''
|
|
355
|
-
];
|
|
356
|
-
|
|
357
|
-
return lines.join('\n');
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* Format a concurrent operation error with helpful guidance.
|
|
362
|
-
*
|
|
363
|
-
* @param {Object} options - Error context
|
|
364
|
-
* @param {string} options.operation - The operation that was blocked
|
|
365
|
-
* @param {string} options.lockHolder - Description of what holds the lock
|
|
366
|
-
* @param {string} options.lockFile - Path to the lock file
|
|
367
|
-
* @returns {string} - Formatted error message
|
|
368
|
-
*/
|
|
369
|
-
export function formatConcurrentOperationError(options) {
|
|
370
|
-
const { operation = 'this operation', lockHolder = 'another process', lockFile = '' } = options;
|
|
371
|
-
|
|
372
|
-
const lines = [
|
|
373
|
-
'',
|
|
374
|
-
chalk.bgYellow.black.bold(' Operation Blocked '),
|
|
375
|
-
chalk.yellow('─'.repeat(50)),
|
|
376
|
-
'',
|
|
377
|
-
chalk.white(`Cannot ${operation} - ${lockHolder} is currently running.`),
|
|
378
|
-
'',
|
|
379
|
-
chalk.cyan('Why this happens:'),
|
|
380
|
-
chalk.gray(' Running multiple operations simultaneously can cause:'),
|
|
381
|
-
chalk.gray(' - File corruption'),
|
|
382
|
-
chalk.gray(' - Data loss'),
|
|
383
|
-
chalk.gray(' - Inconsistent state'),
|
|
384
|
-
'',
|
|
385
|
-
chalk.cyan('Solutions:'),
|
|
386
|
-
chalk.white(' 1. Wait for the other operation to complete'),
|
|
387
|
-
chalk.white(' 2. If the other operation crashed, remove the lock file:'),
|
|
388
|
-
lockFile ? chalk.yellow(` rm "${lockFile}"`) : chalk.gray(' (lock file path not available)'),
|
|
389
|
-
chalk.white(' 3. Check for running magentrix processes:'),
|
|
390
|
-
chalk.yellow(' ps aux | grep magentrix'),
|
|
391
|
-
'',
|
|
392
|
-
chalk.yellow('─'.repeat(50)),
|
|
393
|
-
''
|
|
394
|
-
];
|
|
395
|
-
|
|
396
|
-
return lines.join('\n');
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Format a timeout error with helpful guidance.
|
|
401
|
-
*
|
|
402
|
-
* @param {Object} options - Error context
|
|
403
|
-
* @param {string} options.operation - What operation timed out
|
|
404
|
-
* @param {number} options.timeoutMs - The timeout value in milliseconds
|
|
405
|
-
* @returns {string} - Formatted error message
|
|
406
|
-
*/
|
|
407
|
-
export function formatTimeoutError(options) {
|
|
408
|
-
const { operation = 'Operation', timeoutMs = 0 } = options;
|
|
409
|
-
const seconds = Math.round(timeoutMs / 1000);
|
|
410
|
-
|
|
411
|
-
const lines = [
|
|
412
|
-
'',
|
|
413
|
-
chalk.bgYellow.black.bold(' Operation Timed Out '),
|
|
414
|
-
chalk.yellow('─'.repeat(50)),
|
|
415
|
-
'',
|
|
416
|
-
chalk.white(`${operation} did not complete within ${seconds} seconds.`),
|
|
417
|
-
'',
|
|
418
|
-
chalk.cyan('Possible causes:'),
|
|
419
|
-
chalk.gray(' - The server is slow or unresponsive'),
|
|
420
|
-
chalk.gray(' - Network latency is high'),
|
|
421
|
-
chalk.gray(' - Large amount of data being processed'),
|
|
422
|
-
'',
|
|
423
|
-
chalk.cyan('Solutions:'),
|
|
424
|
-
chalk.white(' 1. Try running the command again'),
|
|
425
|
-
chalk.white(' 2. Check your network connection'),
|
|
426
|
-
chalk.white(' 3. If working with large files, allow more time'),
|
|
427
|
-
'',
|
|
428
|
-
chalk.yellow('─'.repeat(50)),
|
|
429
|
-
''
|
|
430
|
-
];
|
|
431
|
-
|
|
432
|
-
return lines.join('\n');
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
/**
|
|
436
|
-
* Format any error with automatic type detection.
|
|
437
|
-
*
|
|
438
|
-
* @param {Error} error - The error to format
|
|
439
|
-
* @param {Object} context - Additional context for the error
|
|
440
|
-
* @returns {string} - Formatted error message
|
|
441
|
-
*/
|
|
442
|
-
export function formatError(error, context = {}) {
|
|
443
|
-
const errorType = detectErrorType(error);
|
|
444
|
-
|
|
445
|
-
switch (errorType) {
|
|
446
|
-
case ErrorTypes.PERMISSION:
|
|
447
|
-
return formatPermissionError({
|
|
448
|
-
operation: context.operation || 'access',
|
|
449
|
-
path: context.path || error.path || ''
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
case ErrorTypes.DISK_FULL:
|
|
453
|
-
return formatDiskFullError({
|
|
454
|
-
operation: context.operation || 'write',
|
|
455
|
-
path: context.path || error.path || ''
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
case ErrorTypes.FILE_LOCKED:
|
|
459
|
-
return formatFileLockError({
|
|
460
|
-
path: context.path || error.path || ''
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
case ErrorTypes.NETWORK:
|
|
464
|
-
return formatNetworkError({
|
|
465
|
-
url: context.url || '',
|
|
466
|
-
error
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
case ErrorTypes.CORRUPTED:
|
|
470
|
-
return formatCorruptedConfigError({
|
|
471
|
-
configPath: context.configPath || context.path || '',
|
|
472
|
-
error
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
case ErrorTypes.TIMEOUT:
|
|
476
|
-
return formatTimeoutError({
|
|
477
|
-
operation: context.operation || 'Operation',
|
|
478
|
-
timeoutMs: context.timeoutMs || 30000
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
case ErrorTypes.CONCURRENT_OPERATION:
|
|
482
|
-
return formatConcurrentOperationError({
|
|
483
|
-
operation: context.operation || 'this operation',
|
|
484
|
-
lockHolder: context.lockHolder || 'another process',
|
|
485
|
-
lockFile: context.lockFile || ''
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
default:
|
|
489
|
-
// Generic error formatting
|
|
490
|
-
return [
|
|
491
|
-
'',
|
|
492
|
-
chalk.bgRed.white.bold(' Error '),
|
|
493
|
-
chalk.red('─'.repeat(50)),
|
|
494
|
-
'',
|
|
495
|
-
chalk.white(error.message || String(error)),
|
|
496
|
-
context.path ? chalk.gray(` Path: ${context.path}`) : '',
|
|
497
|
-
'',
|
|
498
|
-
chalk.red('─'.repeat(50)),
|
|
499
|
-
''
|
|
500
|
-
].filter(Boolean).join('\n');
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Wrap an async function with error handling.
|
|
506
|
-
*
|
|
507
|
-
* @param {Function} fn - The async function to wrap
|
|
508
|
-
* @param {Object} context - Context for error messages
|
|
509
|
-
* @returns {Function} - Wrapped function
|
|
510
|
-
*/
|
|
511
|
-
export function withErrorHandling(fn, context = {}) {
|
|
512
|
-
return async (...args) => {
|
|
513
|
-
try {
|
|
514
|
-
return await fn(...args);
|
|
515
|
-
} catch (error) {
|
|
516
|
-
console.log(formatError(error, context));
|
|
517
|
-
return { success: false, error: error.message };
|
|
518
|
-
}
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Check if an error is recoverable (can be retried).
|
|
524
|
-
*
|
|
525
|
-
* @param {Error} error - The error to check
|
|
526
|
-
* @returns {boolean} - True if the error might be recoverable with retry
|
|
527
|
-
*/
|
|
528
|
-
export function isRecoverableError(error) {
|
|
529
|
-
const errorType = detectErrorType(error);
|
|
530
|
-
|
|
531
|
-
// These error types might succeed if retried
|
|
532
|
-
return [
|
|
533
|
-
ErrorTypes.NETWORK,
|
|
534
|
-
ErrorTypes.TIMEOUT,
|
|
535
|
-
ErrorTypes.FILE_LOCKED
|
|
536
|
-
].includes(errorType);
|
|
537
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for Iris Vue integration.
|
|
3
|
+
* Provides user-friendly error messages with actionable solutions.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error types for categorization and handling.
|
|
10
|
+
*/
|
|
11
|
+
export const ErrorTypes = {
|
|
12
|
+
PERMISSION: 'PERMISSION',
|
|
13
|
+
DISK_FULL: 'DISK_FULL',
|
|
14
|
+
FILE_LOCKED: 'FILE_LOCKED',
|
|
15
|
+
NOT_FOUND: 'NOT_FOUND',
|
|
16
|
+
INVALID_CONFIG: 'INVALID_CONFIG',
|
|
17
|
+
NETWORK: 'NETWORK',
|
|
18
|
+
VALIDATION: 'VALIDATION',
|
|
19
|
+
CONCURRENT_OPERATION: 'CONCURRENT_OPERATION',
|
|
20
|
+
TIMEOUT: 'TIMEOUT',
|
|
21
|
+
CORRUPTED: 'CORRUPTED',
|
|
22
|
+
UNKNOWN: 'UNKNOWN'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Detect the type of error from an Error object or error code.
|
|
27
|
+
*
|
|
28
|
+
* @param {Error | string} error - The error to analyze
|
|
29
|
+
* @returns {string} - One of ErrorTypes
|
|
30
|
+
*/
|
|
31
|
+
export function detectErrorType(error) {
|
|
32
|
+
const code = error?.code || error;
|
|
33
|
+
const message = (error?.message || String(error)).toLowerCase();
|
|
34
|
+
|
|
35
|
+
// Permission errors
|
|
36
|
+
if (code === 'EACCES' || code === 'EPERM' || message.includes('permission denied')) {
|
|
37
|
+
return ErrorTypes.PERMISSION;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Disk full errors
|
|
41
|
+
if (code === 'ENOSPC' || message.includes('no space left') || message.includes('disk full')) {
|
|
42
|
+
return ErrorTypes.DISK_FULL;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// File locked errors (Windows mainly)
|
|
46
|
+
if (code === 'EBUSY' || code === 'ENOTEMPTY' || message.includes('resource busy') ||
|
|
47
|
+
message.includes('being used by another process')) {
|
|
48
|
+
return ErrorTypes.FILE_LOCKED;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Not found errors
|
|
52
|
+
if (code === 'ENOENT' || message.includes('no such file') || message.includes('not found')) {
|
|
53
|
+
return ErrorTypes.NOT_FOUND;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Network errors
|
|
57
|
+
if (code === 'ENOTFOUND' || code === 'ECONNREFUSED' || code === 'ECONNRESET' ||
|
|
58
|
+
code === 'ETIMEDOUT' || message.includes('network') || message.includes('fetch')) {
|
|
59
|
+
return ErrorTypes.NETWORK;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// JSON/config parsing errors
|
|
63
|
+
if (message.includes('json') || message.includes('parse') || message.includes('syntax')) {
|
|
64
|
+
return ErrorTypes.CORRUPTED;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Timeout errors
|
|
68
|
+
if (code === 'ETIMEDOUT' || message.includes('timeout')) {
|
|
69
|
+
return ErrorTypes.TIMEOUT;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return ErrorTypes.UNKNOWN;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Format a permission error with helpful guidance.
|
|
77
|
+
*
|
|
78
|
+
* @param {Object} options - Error context
|
|
79
|
+
* @param {string} options.operation - What operation failed (e.g., 'write', 'delete', 'read')
|
|
80
|
+
* @param {string} options.path - The path that caused the error
|
|
81
|
+
* @param {string} options.platform - The platform ('win32', 'darwin', 'linux')
|
|
82
|
+
* @returns {string} - Formatted error message
|
|
83
|
+
*/
|
|
84
|
+
export function formatPermissionError(options) {
|
|
85
|
+
const { operation = 'access', path = '', platform = process.platform } = options;
|
|
86
|
+
|
|
87
|
+
const lines = [
|
|
88
|
+
'',
|
|
89
|
+
chalk.bgRed.white.bold(' Permission Error '),
|
|
90
|
+
chalk.red('─'.repeat(50)),
|
|
91
|
+
'',
|
|
92
|
+
chalk.white(`Unable to ${operation} files at:`),
|
|
93
|
+
chalk.gray(` ${path}`),
|
|
94
|
+
'',
|
|
95
|
+
chalk.cyan('Possible causes:'),
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
if (platform === 'win32') {
|
|
99
|
+
lines.push(
|
|
100
|
+
chalk.gray(' 1. File is open in another program (VS Code, Explorer, antivirus)'),
|
|
101
|
+
chalk.gray(' 2. You need to run the command as Administrator'),
|
|
102
|
+
chalk.gray(' 3. The file is marked as read-only'),
|
|
103
|
+
'',
|
|
104
|
+
chalk.cyan('Solutions:'),
|
|
105
|
+
chalk.white(' 1. Close any programs that might have the file open'),
|
|
106
|
+
chalk.white(' 2. Run your terminal as Administrator:'),
|
|
107
|
+
chalk.gray(' Right-click terminal > "Run as administrator"'),
|
|
108
|
+
chalk.white(' 3. Check file properties and uncheck "Read-only"'),
|
|
109
|
+
chalk.white(' 4. Temporarily disable antivirus real-time scanning')
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
lines.push(
|
|
113
|
+
chalk.gray(' 1. Files are owned by a different user'),
|
|
114
|
+
chalk.gray(' 2. Insufficient permissions on directory'),
|
|
115
|
+
chalk.gray(' 3. File system is mounted read-only'),
|
|
116
|
+
'',
|
|
117
|
+
chalk.cyan('Solutions:'),
|
|
118
|
+
chalk.white(' 1. Change ownership of the files:'),
|
|
119
|
+
chalk.yellow(` sudo chown -R $(whoami) "${path}"`),
|
|
120
|
+
chalk.white(' 2. Fix directory permissions:'),
|
|
121
|
+
chalk.yellow(` chmod -R u+rwX "${path}"`),
|
|
122
|
+
chalk.white(' 3. Use sudo for this operation (if appropriate):'),
|
|
123
|
+
chalk.yellow(' sudo magentrix <command>')
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
128
|
+
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Format a disk full error with helpful guidance.
|
|
134
|
+
*
|
|
135
|
+
* @param {Object} options - Error context
|
|
136
|
+
* @param {string} options.operation - What operation failed
|
|
137
|
+
* @param {string} options.path - The path that caused the error
|
|
138
|
+
* @returns {string} - Formatted error message
|
|
139
|
+
*/
|
|
140
|
+
export function formatDiskFullError(options) {
|
|
141
|
+
const { operation = 'write', path = '' } = options;
|
|
142
|
+
const platform = process.platform;
|
|
143
|
+
|
|
144
|
+
const lines = [
|
|
145
|
+
'',
|
|
146
|
+
chalk.bgRed.white.bold(' Disk Full '),
|
|
147
|
+
chalk.red('─'.repeat(50)),
|
|
148
|
+
'',
|
|
149
|
+
chalk.white(`Cannot ${operation} - no disk space available.`),
|
|
150
|
+
chalk.gray(` Path: ${path}`),
|
|
151
|
+
'',
|
|
152
|
+
chalk.cyan('Solutions:'),
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
if (platform === 'win32') {
|
|
156
|
+
lines.push(
|
|
157
|
+
chalk.white(' 1. Free up disk space:'),
|
|
158
|
+
chalk.gray(' - Empty the Recycle Bin'),
|
|
159
|
+
chalk.gray(' - Run Disk Cleanup (cleanmgr.exe)'),
|
|
160
|
+
chalk.gray(' - Uninstall unused programs'),
|
|
161
|
+
chalk.white(' 2. Clear npm cache:'),
|
|
162
|
+
chalk.yellow(' npm cache clean --force'),
|
|
163
|
+
chalk.white(' 3. Check available space:'),
|
|
164
|
+
chalk.yellow(' wmic logicaldisk get size,freespace,caption')
|
|
165
|
+
);
|
|
166
|
+
} else {
|
|
167
|
+
lines.push(
|
|
168
|
+
chalk.white(' 1. Check disk usage:'),
|
|
169
|
+
chalk.yellow(' df -h'),
|
|
170
|
+
chalk.white(' 2. Find large files:'),
|
|
171
|
+
chalk.yellow(' du -sh * | sort -rh | head -20'),
|
|
172
|
+
chalk.white(' 3. Clear npm cache:'),
|
|
173
|
+
chalk.yellow(' npm cache clean --force'),
|
|
174
|
+
chalk.white(' 4. Clear system logs (macOS):'),
|
|
175
|
+
chalk.yellow(' sudo rm -rf /private/var/log/*'),
|
|
176
|
+
chalk.white(' 5. Clear trash:'),
|
|
177
|
+
chalk.yellow(' rm -rf ~/.Trash/*')
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
182
|
+
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Format a file locked error with helpful guidance.
|
|
188
|
+
*
|
|
189
|
+
* @param {Object} options - Error context
|
|
190
|
+
* @param {string} options.path - The path that caused the error
|
|
191
|
+
* @returns {string} - Formatted error message
|
|
192
|
+
*/
|
|
193
|
+
export function formatFileLockError(options) {
|
|
194
|
+
const { path = '' } = options;
|
|
195
|
+
const platform = process.platform;
|
|
196
|
+
|
|
197
|
+
const lines = [
|
|
198
|
+
'',
|
|
199
|
+
chalk.bgYellow.black.bold(' File In Use '),
|
|
200
|
+
chalk.yellow('─'.repeat(50)),
|
|
201
|
+
'',
|
|
202
|
+
chalk.white('Cannot modify file - it is being used by another program.'),
|
|
203
|
+
chalk.gray(` Path: ${path}`),
|
|
204
|
+
'',
|
|
205
|
+
chalk.cyan('This commonly happens when:'),
|
|
206
|
+
chalk.gray(' - Your IDE (VS Code, WebStorm) has the file open'),
|
|
207
|
+
chalk.gray(' - A dev server is running and watching files'),
|
|
208
|
+
chalk.gray(' - Antivirus is scanning the file'),
|
|
209
|
+
chalk.gray(' - Another terminal has the directory open'),
|
|
210
|
+
'',
|
|
211
|
+
chalk.cyan('Solutions:'),
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
if (platform === 'win32') {
|
|
215
|
+
lines.push(
|
|
216
|
+
chalk.white(' 1. Close applications that might be using the file'),
|
|
217
|
+
chalk.white(' 2. Close any Vue/React dev servers running from this project'),
|
|
218
|
+
chalk.white(' 3. Close and reopen VS Code'),
|
|
219
|
+
chalk.white(' 4. Find what is using the file:'),
|
|
220
|
+
chalk.gray(' - Open Resource Monitor (resmon.exe)'),
|
|
221
|
+
chalk.gray(' - Go to CPU > Associated Handles'),
|
|
222
|
+
chalk.gray(' - Search for the filename')
|
|
223
|
+
);
|
|
224
|
+
} else {
|
|
225
|
+
lines.push(
|
|
226
|
+
chalk.white(' 1. Close applications that might be using the file'),
|
|
227
|
+
chalk.white(' 2. Stop any running dev servers'),
|
|
228
|
+
chalk.white(' 3. Find what is using the file:'),
|
|
229
|
+
chalk.yellow(` lsof "${path}"`),
|
|
230
|
+
chalk.white(' 4. Wait a few seconds and try again')
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
lines.push('', chalk.yellow('─'.repeat(50)), '');
|
|
235
|
+
|
|
236
|
+
return lines.join('\n');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Format a network error with helpful guidance.
|
|
241
|
+
*
|
|
242
|
+
* @param {Object} options - Error context
|
|
243
|
+
* @param {string} options.url - The URL that failed
|
|
244
|
+
* @param {Error} options.error - The original error
|
|
245
|
+
* @returns {string} - Formatted error message
|
|
246
|
+
*/
|
|
247
|
+
export function formatNetworkError(options) {
|
|
248
|
+
const { url = '', error } = options;
|
|
249
|
+
const code = error?.code || '';
|
|
250
|
+
const message = error?.message || '';
|
|
251
|
+
|
|
252
|
+
const lines = [
|
|
253
|
+
'',
|
|
254
|
+
chalk.bgRed.white.bold(' Network Error '),
|
|
255
|
+
chalk.red('─'.repeat(50)),
|
|
256
|
+
'',
|
|
257
|
+
chalk.white('Failed to connect to the server.'),
|
|
258
|
+
chalk.gray(` URL: ${url}`),
|
|
259
|
+
chalk.gray(` Error: ${message}`),
|
|
260
|
+
'',
|
|
261
|
+
chalk.cyan('Possible causes:'),
|
|
262
|
+
];
|
|
263
|
+
|
|
264
|
+
if (code === 'ENOTFOUND' || message.includes('getaddrinfo')) {
|
|
265
|
+
lines.push(
|
|
266
|
+
chalk.gray(' - The server hostname could not be resolved'),
|
|
267
|
+
chalk.gray(' - DNS server issues'),
|
|
268
|
+
chalk.gray(' - Typo in the URL'),
|
|
269
|
+
'',
|
|
270
|
+
chalk.cyan('Solutions:'),
|
|
271
|
+
chalk.white(' 1. Check that the URL is correct'),
|
|
272
|
+
chalk.white(' 2. Verify your internet connection'),
|
|
273
|
+
chalk.white(' 3. Try accessing the URL in a browser')
|
|
274
|
+
);
|
|
275
|
+
} else if (code === 'ECONNREFUSED') {
|
|
276
|
+
lines.push(
|
|
277
|
+
chalk.gray(' - The server is not running'),
|
|
278
|
+
chalk.gray(' - Wrong port number'),
|
|
279
|
+
chalk.gray(' - Firewall blocking the connection'),
|
|
280
|
+
'',
|
|
281
|
+
chalk.cyan('Solutions:'),
|
|
282
|
+
chalk.white(' 1. Verify the server is running'),
|
|
283
|
+
chalk.white(' 2. Check if the URL and port are correct'),
|
|
284
|
+
chalk.white(' 3. Check firewall settings')
|
|
285
|
+
);
|
|
286
|
+
} else if (code === 'ETIMEDOUT' || message.includes('timeout')) {
|
|
287
|
+
lines.push(
|
|
288
|
+
chalk.gray(' - Server took too long to respond'),
|
|
289
|
+
chalk.gray(' - Network congestion'),
|
|
290
|
+
chalk.gray(' - Server might be overloaded'),
|
|
291
|
+
'',
|
|
292
|
+
chalk.cyan('Solutions:'),
|
|
293
|
+
chalk.white(' 1. Wait a moment and try again'),
|
|
294
|
+
chalk.white(' 2. Check if you can access the server in a browser'),
|
|
295
|
+
chalk.white(' 3. Try from a different network')
|
|
296
|
+
);
|
|
297
|
+
} else {
|
|
298
|
+
lines.push(
|
|
299
|
+
chalk.gray(' - Network connectivity issues'),
|
|
300
|
+
chalk.gray(' - Server is unreachable'),
|
|
301
|
+
'',
|
|
302
|
+
chalk.cyan('Solutions:'),
|
|
303
|
+
chalk.white(' 1. Check your internet connection'),
|
|
304
|
+
chalk.white(' 2. Try again in a few minutes'),
|
|
305
|
+
chalk.white(' 3. Verify the URL is correct')
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
lines.push('', chalk.red('─'.repeat(50)), '');
|
|
310
|
+
|
|
311
|
+
return lines.join('\n');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Format a corrupted config error with helpful guidance.
|
|
316
|
+
*
|
|
317
|
+
* @param {Object} options - Error context
|
|
318
|
+
* @param {string} options.configPath - Path to the corrupted config
|
|
319
|
+
* @param {Error} options.error - The original error
|
|
320
|
+
* @returns {string} - Formatted error message
|
|
321
|
+
*/
|
|
322
|
+
export function formatCorruptedConfigError(options) {
|
|
323
|
+
const { configPath = '', error } = options;
|
|
324
|
+
const message = error?.message || 'Invalid JSON format';
|
|
325
|
+
|
|
326
|
+
const lines = [
|
|
327
|
+
'',
|
|
328
|
+
chalk.bgRed.white.bold(' Config File Corrupted '),
|
|
329
|
+
chalk.red('─'.repeat(50)),
|
|
330
|
+
'',
|
|
331
|
+
chalk.white('The configuration file could not be read due to invalid format.'),
|
|
332
|
+
chalk.gray(` Path: ${configPath}`),
|
|
333
|
+
chalk.gray(` Error: ${message}`),
|
|
334
|
+
'',
|
|
335
|
+
chalk.cyan('This can happen when:'),
|
|
336
|
+
chalk.gray(' - A previous write was interrupted'),
|
|
337
|
+
chalk.gray(' - The file was manually edited with invalid JSON'),
|
|
338
|
+
chalk.gray(' - Disk errors corrupted the file'),
|
|
339
|
+
'',
|
|
340
|
+
chalk.cyan('Solutions:'),
|
|
341
|
+
chalk.white(' 1. Check if a backup exists:'),
|
|
342
|
+
chalk.yellow(` ls -la "${configPath}.bak"`),
|
|
343
|
+
chalk.white(' 2. If backup exists, restore it:'),
|
|
344
|
+
chalk.yellow(` cp "${configPath}.bak" "${configPath}"`),
|
|
345
|
+
chalk.white(' 3. If no backup, you may need to delete and recreate:'),
|
|
346
|
+
chalk.yellow(` rm "${configPath}"`),
|
|
347
|
+
chalk.gray(' Then re-run your command to regenerate the config.'),
|
|
348
|
+
'',
|
|
349
|
+
chalk.bgYellow.black(' Warning '),
|
|
350
|
+
chalk.yellow(' Deleting the config will lose your linked project history.'),
|
|
351
|
+
chalk.yellow(' You will need to re-link your Vue projects.'),
|
|
352
|
+
'',
|
|
353
|
+
chalk.red('─'.repeat(50)),
|
|
354
|
+
''
|
|
355
|
+
];
|
|
356
|
+
|
|
357
|
+
return lines.join('\n');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Format a concurrent operation error with helpful guidance.
|
|
362
|
+
*
|
|
363
|
+
* @param {Object} options - Error context
|
|
364
|
+
* @param {string} options.operation - The operation that was blocked
|
|
365
|
+
* @param {string} options.lockHolder - Description of what holds the lock
|
|
366
|
+
* @param {string} options.lockFile - Path to the lock file
|
|
367
|
+
* @returns {string} - Formatted error message
|
|
368
|
+
*/
|
|
369
|
+
export function formatConcurrentOperationError(options) {
|
|
370
|
+
const { operation = 'this operation', lockHolder = 'another process', lockFile = '' } = options;
|
|
371
|
+
|
|
372
|
+
const lines = [
|
|
373
|
+
'',
|
|
374
|
+
chalk.bgYellow.black.bold(' Operation Blocked '),
|
|
375
|
+
chalk.yellow('─'.repeat(50)),
|
|
376
|
+
'',
|
|
377
|
+
chalk.white(`Cannot ${operation} - ${lockHolder} is currently running.`),
|
|
378
|
+
'',
|
|
379
|
+
chalk.cyan('Why this happens:'),
|
|
380
|
+
chalk.gray(' Running multiple operations simultaneously can cause:'),
|
|
381
|
+
chalk.gray(' - File corruption'),
|
|
382
|
+
chalk.gray(' - Data loss'),
|
|
383
|
+
chalk.gray(' - Inconsistent state'),
|
|
384
|
+
'',
|
|
385
|
+
chalk.cyan('Solutions:'),
|
|
386
|
+
chalk.white(' 1. Wait for the other operation to complete'),
|
|
387
|
+
chalk.white(' 2. If the other operation crashed, remove the lock file:'),
|
|
388
|
+
lockFile ? chalk.yellow(` rm "${lockFile}"`) : chalk.gray(' (lock file path not available)'),
|
|
389
|
+
chalk.white(' 3. Check for running magentrix processes:'),
|
|
390
|
+
chalk.yellow(' ps aux | grep magentrix'),
|
|
391
|
+
'',
|
|
392
|
+
chalk.yellow('─'.repeat(50)),
|
|
393
|
+
''
|
|
394
|
+
];
|
|
395
|
+
|
|
396
|
+
return lines.join('\n');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Format a timeout error with helpful guidance.
|
|
401
|
+
*
|
|
402
|
+
* @param {Object} options - Error context
|
|
403
|
+
* @param {string} options.operation - What operation timed out
|
|
404
|
+
* @param {number} options.timeoutMs - The timeout value in milliseconds
|
|
405
|
+
* @returns {string} - Formatted error message
|
|
406
|
+
*/
|
|
407
|
+
export function formatTimeoutError(options) {
|
|
408
|
+
const { operation = 'Operation', timeoutMs = 0 } = options;
|
|
409
|
+
const seconds = Math.round(timeoutMs / 1000);
|
|
410
|
+
|
|
411
|
+
const lines = [
|
|
412
|
+
'',
|
|
413
|
+
chalk.bgYellow.black.bold(' Operation Timed Out '),
|
|
414
|
+
chalk.yellow('─'.repeat(50)),
|
|
415
|
+
'',
|
|
416
|
+
chalk.white(`${operation} did not complete within ${seconds} seconds.`),
|
|
417
|
+
'',
|
|
418
|
+
chalk.cyan('Possible causes:'),
|
|
419
|
+
chalk.gray(' - The server is slow or unresponsive'),
|
|
420
|
+
chalk.gray(' - Network latency is high'),
|
|
421
|
+
chalk.gray(' - Large amount of data being processed'),
|
|
422
|
+
'',
|
|
423
|
+
chalk.cyan('Solutions:'),
|
|
424
|
+
chalk.white(' 1. Try running the command again'),
|
|
425
|
+
chalk.white(' 2. Check your network connection'),
|
|
426
|
+
chalk.white(' 3. If working with large files, allow more time'),
|
|
427
|
+
'',
|
|
428
|
+
chalk.yellow('─'.repeat(50)),
|
|
429
|
+
''
|
|
430
|
+
];
|
|
431
|
+
|
|
432
|
+
return lines.join('\n');
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Format any error with automatic type detection.
|
|
437
|
+
*
|
|
438
|
+
* @param {Error} error - The error to format
|
|
439
|
+
* @param {Object} context - Additional context for the error
|
|
440
|
+
* @returns {string} - Formatted error message
|
|
441
|
+
*/
|
|
442
|
+
export function formatError(error, context = {}) {
|
|
443
|
+
const errorType = detectErrorType(error);
|
|
444
|
+
|
|
445
|
+
switch (errorType) {
|
|
446
|
+
case ErrorTypes.PERMISSION:
|
|
447
|
+
return formatPermissionError({
|
|
448
|
+
operation: context.operation || 'access',
|
|
449
|
+
path: context.path || error.path || ''
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
case ErrorTypes.DISK_FULL:
|
|
453
|
+
return formatDiskFullError({
|
|
454
|
+
operation: context.operation || 'write',
|
|
455
|
+
path: context.path || error.path || ''
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
case ErrorTypes.FILE_LOCKED:
|
|
459
|
+
return formatFileLockError({
|
|
460
|
+
path: context.path || error.path || ''
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
case ErrorTypes.NETWORK:
|
|
464
|
+
return formatNetworkError({
|
|
465
|
+
url: context.url || '',
|
|
466
|
+
error
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
case ErrorTypes.CORRUPTED:
|
|
470
|
+
return formatCorruptedConfigError({
|
|
471
|
+
configPath: context.configPath || context.path || '',
|
|
472
|
+
error
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
case ErrorTypes.TIMEOUT:
|
|
476
|
+
return formatTimeoutError({
|
|
477
|
+
operation: context.operation || 'Operation',
|
|
478
|
+
timeoutMs: context.timeoutMs || 30000
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
case ErrorTypes.CONCURRENT_OPERATION:
|
|
482
|
+
return formatConcurrentOperationError({
|
|
483
|
+
operation: context.operation || 'this operation',
|
|
484
|
+
lockHolder: context.lockHolder || 'another process',
|
|
485
|
+
lockFile: context.lockFile || ''
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
default:
|
|
489
|
+
// Generic error formatting
|
|
490
|
+
return [
|
|
491
|
+
'',
|
|
492
|
+
chalk.bgRed.white.bold(' Error '),
|
|
493
|
+
chalk.red('─'.repeat(50)),
|
|
494
|
+
'',
|
|
495
|
+
chalk.white(error.message || String(error)),
|
|
496
|
+
context.path ? chalk.gray(` Path: ${context.path}`) : '',
|
|
497
|
+
'',
|
|
498
|
+
chalk.red('─'.repeat(50)),
|
|
499
|
+
''
|
|
500
|
+
].filter(Boolean).join('\n');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Wrap an async function with error handling.
|
|
506
|
+
*
|
|
507
|
+
* @param {Function} fn - The async function to wrap
|
|
508
|
+
* @param {Object} context - Context for error messages
|
|
509
|
+
* @returns {Function} - Wrapped function
|
|
510
|
+
*/
|
|
511
|
+
export function withErrorHandling(fn, context = {}) {
|
|
512
|
+
return async (...args) => {
|
|
513
|
+
try {
|
|
514
|
+
return await fn(...args);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
console.log(formatError(error, context));
|
|
517
|
+
return { success: false, error: error.message };
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Check if an error is recoverable (can be retried).
|
|
524
|
+
*
|
|
525
|
+
* @param {Error} error - The error to check
|
|
526
|
+
* @returns {boolean} - True if the error might be recoverable with retry
|
|
527
|
+
*/
|
|
528
|
+
export function isRecoverableError(error) {
|
|
529
|
+
const errorType = detectErrorType(error);
|
|
530
|
+
|
|
531
|
+
// These error types might succeed if retried
|
|
532
|
+
return [
|
|
533
|
+
ErrorTypes.NETWORK,
|
|
534
|
+
ErrorTypes.TIMEOUT,
|
|
535
|
+
ErrorTypes.FILE_LOCKED
|
|
536
|
+
].includes(errorType);
|
|
537
|
+
}
|