pnpm-catalog-updates 0.7.19 → 1.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.
@@ -5,96 +5,96 @@
5
5
  * Provides detailed error messages and suggestions.
6
6
  */
7
7
 
8
- import { getConfig, validateCliOptions, ValidationResult } from '@pcu/utils';
8
+ import { getConfig, type ValidationResult, validateCliOptions } from '@pcu/utils'
9
9
 
10
10
  export interface ValidatedOptions {
11
- workspace?: string;
12
- catalog?: string;
13
- format?: string;
14
- target?: string;
15
- interactive?: boolean;
16
- dryRun?: boolean;
17
- force?: boolean;
18
- prerelease?: boolean;
19
- include?: string[];
20
- exclude?: string[];
21
- createBackup?: boolean;
22
- verbose?: boolean;
23
- color?: boolean;
24
- registry?: string;
25
- timeout?: number;
11
+ workspace?: string
12
+ catalog?: string
13
+ format?: string
14
+ target?: string
15
+ interactive?: boolean
16
+ dryRun?: boolean
17
+ force?: boolean
18
+ prerelease?: boolean
19
+ include?: string[]
20
+ exclude?: string[]
21
+ createBackup?: boolean
22
+ verbose?: boolean
23
+ color?: boolean
24
+ registry?: string
25
+ timeout?: number
26
26
  }
27
27
 
28
28
  export class CommandValidator {
29
- private config = getConfig().getConfig();
29
+ private config = getConfig().getConfig()
30
30
 
31
31
  /**
32
32
  * Validate check command options
33
33
  */
34
34
  validateCheckOptions(options: any): ValidationResult {
35
- const errors: string[] = [];
36
- const warnings: string[] = [];
35
+ const errors: string[] = []
36
+ const warnings: string[] = []
37
37
 
38
38
  // Basic validation using utility
39
- const basicValidation = validateCliOptions(options);
40
- errors.push(...basicValidation.errors);
41
- warnings.push(...basicValidation.warnings);
39
+ const basicValidation = validateCliOptions(options)
40
+ errors.push(...basicValidation.errors)
41
+ warnings.push(...basicValidation.warnings)
42
42
 
43
43
  // Check-specific validations
44
44
  if (options.catalog && typeof options.catalog !== 'string') {
45
- errors.push('Catalog name must be a string');
45
+ errors.push('Catalog name must be a string')
46
46
  }
47
47
 
48
48
  // Validate mutually exclusive options
49
49
  if (options.interactive && options.format === 'json') {
50
- warnings.push('Interactive mode is not useful with JSON output format');
50
+ warnings.push('Interactive mode is not useful with JSON output format')
51
51
  }
52
52
 
53
53
  if (options.verbose && options.silent) {
54
- errors.push('Cannot use both --verbose and --silent options');
54
+ errors.push('Cannot use both --verbose and --silent options')
55
55
  }
56
56
 
57
57
  return {
58
58
  isValid: errors.length === 0,
59
59
  errors,
60
60
  warnings,
61
- };
61
+ }
62
62
  }
63
63
 
64
64
  /**
65
65
  * Validate update command options
66
66
  */
67
67
  validateUpdateOptions(options: any): ValidationResult {
68
- const errors: string[] = [];
69
- const warnings: string[] = [];
68
+ const errors: string[] = []
69
+ const warnings: string[] = []
70
70
 
71
71
  // Basic validation
72
- const basicValidation = validateCliOptions(options);
73
- errors.push(...basicValidation.errors);
74
- warnings.push(...basicValidation.warnings);
72
+ const basicValidation = validateCliOptions(options)
73
+ errors.push(...basicValidation.errors)
74
+ warnings.push(...basicValidation.warnings)
75
75
 
76
76
  // Update-specific validations
77
77
  if (options.interactive && options.dryRun) {
78
- errors.push('Cannot use --interactive with --dry-run');
78
+ errors.push('Cannot use --interactive with --dry-run')
79
79
  }
80
80
 
81
81
  if (options.force && !options.dryRun && !options.createBackup) {
82
- warnings.push('Using --force without backup. Consider using --create-backup for safety');
82
+ warnings.push('Using --force without backup. Consider using --create-backup for safety')
83
83
  }
84
84
 
85
85
  if (options.target === 'major' && !options.force && !options.interactive) {
86
86
  warnings.push(
87
87
  'Major updates may contain breaking changes. Consider using --interactive or --force'
88
- );
88
+ )
89
89
  }
90
90
 
91
91
  // Validate include/exclude patterns
92
92
  if (options.include && options.exclude) {
93
93
  const overlapping = options.include.some((inc: string) =>
94
94
  options.exclude.some((exc: string) => inc === exc)
95
- );
95
+ )
96
96
  if (overlapping) {
97
- warnings.push('Some patterns appear in both include and exclude lists');
97
+ warnings.push('Some patterns appear in both include and exclude lists')
98
98
  }
99
99
  }
100
100
 
@@ -102,40 +102,40 @@ export class CommandValidator {
102
102
  isValid: errors.length === 0,
103
103
  errors,
104
104
  warnings,
105
- };
105
+ }
106
106
  }
107
107
 
108
108
  /**
109
109
  * Validate analyze command arguments
110
110
  */
111
111
  validateAnalyzeArgs(catalog: string, packageName: string, version?: string): ValidationResult {
112
- const errors: string[] = [];
113
- const warnings: string[] = [];
112
+ const errors: string[] = []
113
+ const warnings: string[] = []
114
114
 
115
115
  // Validate catalog name
116
116
  if (!catalog || catalog.trim() === '') {
117
- errors.push('Catalog name is required');
117
+ errors.push('Catalog name is required')
118
118
  } else if (catalog.includes('/') || catalog.includes('\\')) {
119
- errors.push('Catalog name cannot contain path separators');
119
+ errors.push('Catalog name cannot contain path separators')
120
120
  }
121
121
 
122
122
  // Validate package name
123
123
  if (!packageName || packageName.trim() === '') {
124
- errors.push('Package name is required');
124
+ errors.push('Package name is required')
125
125
  } else {
126
126
  // Basic package name validation
127
- const packageNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
127
+ const packageNameRegex = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/
128
128
  if (!packageNameRegex.test(packageName)) {
129
- errors.push('Invalid package name format');
129
+ errors.push('Invalid package name format')
130
130
  }
131
131
  }
132
132
 
133
133
  // Validate version if provided
134
134
  if (version) {
135
135
  const semverRegex =
136
- /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
136
+ /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
137
137
  if (!semverRegex.test(version)) {
138
- errors.push('Invalid version format. Use semantic versioning (e.g., 1.2.3)');
138
+ errors.push('Invalid version format. Use semantic versioning (e.g., 1.2.3)')
139
139
  }
140
140
  }
141
141
 
@@ -143,40 +143,40 @@ export class CommandValidator {
143
143
  isValid: errors.length === 0,
144
144
  errors,
145
145
  warnings,
146
- };
146
+ }
147
147
  }
148
148
 
149
149
  /**
150
150
  * Validate workspace command options
151
151
  */
152
152
  validateWorkspaceOptions(options: any): ValidationResult {
153
- const errors: string[] = [];
154
- const warnings: string[] = [];
153
+ const errors: string[] = []
154
+ const warnings: string[] = []
155
155
 
156
156
  // Basic validation
157
- const basicValidation = validateCliOptions(options);
158
- errors.push(...basicValidation.errors);
159
- warnings.push(...basicValidation.warnings);
157
+ const basicValidation = validateCliOptions(options)
158
+ errors.push(...basicValidation.errors)
159
+ warnings.push(...basicValidation.warnings)
160
160
 
161
161
  // Workspace-specific validations
162
- const actionCount = [options.validate, options.stats, options.info].filter(Boolean).length;
162
+ const actionCount = [options.validate, options.stats, options.info].filter(Boolean).length
163
163
  if (actionCount > 1) {
164
- errors.push('Cannot use multiple workspace actions simultaneously');
164
+ errors.push('Cannot use multiple workspace actions simultaneously')
165
165
  }
166
166
 
167
167
  return {
168
168
  isValid: errors.length === 0,
169
169
  errors,
170
170
  warnings,
171
- };
171
+ }
172
172
  }
173
173
 
174
174
  /**
175
175
  * Validate global options
176
176
  */
177
177
  validateGlobalOptions(options: any): ValidationResult {
178
- const errors: string[] = [];
179
- const warnings: string[] = [];
178
+ const errors: string[] = []
179
+ const warnings: string[] = []
180
180
 
181
181
  // Validate workspace path
182
182
  if (options.workspace) {
@@ -186,16 +186,14 @@ export class CommandValidator {
186
186
 
187
187
  // Validate color options
188
188
  if (options.noColor && options.color) {
189
- errors.push('Cannot use both --color and --no-color');
189
+ errors.push('Cannot use both --color and --no-color')
190
190
  }
191
191
 
192
192
  // Check for deprecated options (future-proofing)
193
- const deprecatedOptions = ['silent']; // Example
193
+ const deprecatedOptions = ['silent'] // Example
194
194
  for (const deprecated of deprecatedOptions) {
195
195
  if (options[deprecated]) {
196
- warnings.push(
197
- `Option --${deprecated} is deprecated and will be removed in future versions`
198
- );
196
+ warnings.push(`Option --${deprecated} is deprecated and will be removed in future versions`)
199
197
  }
200
198
  }
201
199
 
@@ -203,127 +201,127 @@ export class CommandValidator {
203
201
  isValid: errors.length === 0,
204
202
  errors,
205
203
  warnings,
206
- };
204
+ }
207
205
  }
208
206
 
209
207
  /**
210
208
  * Validate configuration object
211
209
  */
212
210
  validateConfigFile(configPath: string): ValidationResult {
213
- const errors: string[] = [];
214
- const warnings: string[] = [];
211
+ const errors: string[] = []
212
+ const warnings: string[] = []
215
213
 
216
214
  try {
217
- const fs = require('fs');
215
+ const fs = require('node:fs')
218
216
  // const path = require('path'); // Reserved for future use
219
217
 
220
218
  if (!fs.existsSync(configPath)) {
221
- errors.push(`Configuration file not found: ${configPath}`);
222
- return { isValid: false, errors, warnings };
219
+ errors.push(`Configuration file not found: ${configPath}`)
220
+ return { isValid: false, errors, warnings }
223
221
  }
224
222
 
225
- let config: any;
223
+ let config: any
226
224
 
227
225
  if (configPath.endsWith('.js')) {
228
226
  // JavaScript config file
229
227
  try {
230
- delete require.cache[require.resolve(configPath)];
231
- config = require(configPath);
228
+ delete require.cache[require.resolve(configPath)]
229
+ config = require(configPath)
232
230
  } catch (error) {
233
- errors.push(`Failed to load JavaScript config: ${error}`);
234
- return { isValid: false, errors, warnings };
231
+ errors.push(`Failed to load JavaScript config: ${error}`)
232
+ return { isValid: false, errors, warnings }
235
233
  }
236
234
  } else {
237
235
  // JSON config file
238
236
  try {
239
- const content = fs.readFileSync(configPath, 'utf-8');
240
- config = JSON.parse(content);
237
+ const content = fs.readFileSync(configPath, 'utf-8')
238
+ config = JSON.parse(content)
241
239
  } catch (error) {
242
- errors.push(`Failed to parse JSON config: ${error}`);
243
- return { isValid: false, errors, warnings };
240
+ errors.push(`Failed to parse JSON config: ${error}`)
241
+ return { isValid: false, errors, warnings }
244
242
  }
245
243
  }
246
244
 
247
245
  // Validate config structure
248
246
  if (typeof config !== 'object' || config === null) {
249
- errors.push('Configuration must be an object');
250
- return { isValid: false, errors, warnings };
247
+ errors.push('Configuration must be an object')
248
+ return { isValid: false, errors, warnings }
251
249
  }
252
250
 
253
251
  // Validate known configuration sections
254
252
  if (config.registry && typeof config.registry !== 'object') {
255
- errors.push('registry configuration must be an object');
253
+ errors.push('registry configuration must be an object')
256
254
  }
257
255
 
258
256
  if (config.update && typeof config.update !== 'object') {
259
- errors.push('update configuration must be an object');
257
+ errors.push('update configuration must be an object')
260
258
  }
261
259
 
262
260
  if (config.output && typeof config.output !== 'object') {
263
- errors.push('output configuration must be an object');
261
+ errors.push('output configuration must be an object')
264
262
  }
265
263
 
266
264
  // Check for unknown top-level keys
267
- const knownKeys = ['registry', 'update', 'output', 'workspace', 'notification', 'logging'];
268
- const unknownKeys = Object.keys(config).filter((key) => !knownKeys.includes(key));
265
+ const knownKeys = ['registry', 'update', 'output', 'workspace', 'notification', 'logging']
266
+ const unknownKeys = Object.keys(config).filter((key) => !knownKeys.includes(key))
269
267
 
270
268
  if (unknownKeys.length > 0) {
271
- warnings.push(`Unknown configuration keys: ${unknownKeys.join(', ')}`);
269
+ warnings.push(`Unknown configuration keys: ${unknownKeys.join(', ')}`)
272
270
  }
273
271
  } catch (error) {
274
- errors.push(`Failed to validate configuration file: ${error}`);
272
+ errors.push(`Failed to validate configuration file: ${error}`)
275
273
  }
276
274
 
277
275
  return {
278
276
  isValid: errors.length === 0,
279
277
  errors,
280
278
  warnings,
281
- };
279
+ }
282
280
  }
283
281
 
284
282
  /**
285
283
  * Sanitize and normalize options
286
284
  */
287
285
  sanitizeOptions(options: any): ValidatedOptions {
288
- const sanitized: ValidatedOptions = {};
286
+ const sanitized: ValidatedOptions = {}
289
287
 
290
288
  // String options
291
289
  if (options.workspace) {
292
- sanitized.workspace = String(options.workspace).trim();
290
+ sanitized.workspace = String(options.workspace).trim()
293
291
  }
294
292
  if (options.catalog) {
295
- sanitized.catalog = String(options.catalog).trim();
293
+ sanitized.catalog = String(options.catalog).trim()
296
294
  }
297
295
  if (options.format) {
298
- sanitized.format = String(options.format).toLowerCase().trim();
296
+ sanitized.format = String(options.format).toLowerCase().trim()
299
297
  }
300
298
  if (options.target) {
301
- sanitized.target = String(options.target).toLowerCase().trim();
299
+ sanitized.target = String(options.target).toLowerCase().trim()
302
300
  }
303
301
  if (options.registry) {
304
- sanitized.registry = String(options.registry).trim();
302
+ sanitized.registry = String(options.registry).trim()
305
303
  }
306
304
 
307
305
  // Boolean options
308
- sanitized.interactive = Boolean(options.interactive);
309
- sanitized.dryRun = Boolean(options.dryRun);
310
- sanitized.force = Boolean(options.force);
311
- sanitized.prerelease = Boolean(options.prerelease);
312
- sanitized.createBackup = Boolean(options.createBackup);
313
- sanitized.verbose = Boolean(options.verbose);
306
+ sanitized.interactive = Boolean(options.interactive)
307
+ sanitized.dryRun = Boolean(options.dryRun)
308
+ sanitized.force = Boolean(options.force)
309
+ sanitized.prerelease = Boolean(options.prerelease)
310
+ sanitized.createBackup = Boolean(options.createBackup)
311
+ sanitized.verbose = Boolean(options.verbose)
314
312
 
315
313
  // Handle color option (tri-state: true, false, or undefined)
316
314
  if (options.color !== undefined) {
317
- sanitized.color = Boolean(options.color);
315
+ sanitized.color = Boolean(options.color)
318
316
  } else if (options.noColor) {
319
- sanitized.color = false;
317
+ sanitized.color = false
320
318
  }
321
319
 
322
320
  // Number options
323
321
  if (options.timeout) {
324
- const timeout = parseInt(String(options.timeout), 10);
325
- if (!isNaN(timeout) && timeout > 0) {
326
- sanitized.timeout = timeout;
322
+ const timeout = parseInt(String(options.timeout), 10)
323
+ if (!Number.isNaN(timeout) && timeout > 0) {
324
+ sanitized.timeout = timeout
327
325
  }
328
326
  }
329
327
 
@@ -331,64 +329,62 @@ export class CommandValidator {
331
329
  if (options.include) {
332
330
  sanitized.include = Array.isArray(options.include)
333
331
  ? options.include.map((p: any) => String(p).trim()).filter(Boolean)
334
- : [String(options.include).trim()].filter(Boolean);
332
+ : [String(options.include).trim()].filter(Boolean)
335
333
  }
336
334
  if (options.exclude) {
337
335
  sanitized.exclude = Array.isArray(options.exclude)
338
336
  ? options.exclude.map((p: any) => String(p).trim()).filter(Boolean)
339
- : [String(options.exclude).trim()].filter(Boolean);
337
+ : [String(options.exclude).trim()].filter(Boolean)
340
338
  }
341
339
 
342
- return sanitized;
340
+ return sanitized
343
341
  }
344
342
 
345
343
  /**
346
344
  * Get validation suggestions based on common mistakes
347
345
  */
348
346
  getSuggestions(command: string, options: any): string[] {
349
- const suggestions: string[] = [];
347
+ const suggestions: string[] = []
350
348
 
351
349
  switch (command) {
352
350
  case 'check':
353
351
  if (!options.workspace) {
354
- suggestions.push('Consider specifying --workspace for non-standard directory structures');
352
+ suggestions.push('Consider specifying --workspace for non-standard directory structures')
355
353
  }
356
354
  if (options.format === 'json' && options.verbose) {
357
- suggestions.push(
358
- 'JSON output already includes detailed info, --verbose may be redundant'
359
- );
355
+ suggestions.push('JSON output already includes detailed info, --verbose may be redundant')
360
356
  }
361
- break;
357
+ break
362
358
 
363
359
  case 'update':
364
360
  if (!options.dryRun && !options.createBackup && !options.force) {
365
- suggestions.push('Consider using --dry-run first to preview changes');
361
+ suggestions.push('Consider using --dry-run first to preview changes')
366
362
  }
367
363
  if (options.target === 'greatest' && !options.prerelease) {
368
- suggestions.push('Add --prerelease to include pre-release versions with greatest target');
364
+ suggestions.push('Add --prerelease to include pre-release versions with greatest target')
369
365
  }
370
- break;
366
+ break
371
367
 
372
368
  case 'analyze':
373
369
  if (!options.format) {
374
- suggestions.push('Use --format json for programmatic consumption of analysis data');
370
+ suggestions.push('Use --format json for programmatic consumption of analysis data')
375
371
  }
376
- break;
372
+ break
377
373
 
378
374
  case 'workspace':
379
375
  if (!options.validate && !options.stats) {
380
376
  suggestions.push(
381
377
  'Use --validate to check workspace integrity or --stats for detailed information'
382
- );
378
+ )
383
379
  }
384
- break;
380
+ break
385
381
  }
386
382
 
387
383
  // General suggestions
388
384
  if (this.config.output.verbose && !options.verbose) {
389
- suggestions.push('Global verbose mode is enabled in config');
385
+ suggestions.push('Global verbose mode is enabled in config')
390
386
  }
391
387
 
392
- return suggestions;
388
+ return suggestions
393
389
  }
394
390
  }
@@ -2,4 +2,4 @@
2
2
  * CLI Validators Entry Point
3
3
  */
4
4
 
5
- export * from './commandValidator.js';
5
+ export * from './commandValidator.js'
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // CLI entry point for pnpm-catalog-updates
4
- export * from './cli/index';
4
+ export * from './cli/index'