pnpm-catalog-updates 1.0.3 → 1.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.
Files changed (51) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +22031 -10684
  3. package/dist/index.js.map +1 -1
  4. package/package.json +7 -2
  5. package/src/cli/__tests__/commandRegistrar.test.ts +248 -0
  6. package/src/cli/commandRegistrar.ts +785 -0
  7. package/src/cli/commands/__tests__/aiCommand.test.ts +161 -0
  8. package/src/cli/commands/__tests__/analyzeCommand.test.ts +283 -0
  9. package/src/cli/commands/__tests__/checkCommand.test.ts +435 -0
  10. package/src/cli/commands/__tests__/graphCommand.test.ts +312 -0
  11. package/src/cli/commands/__tests__/initCommand.test.ts +317 -0
  12. package/src/cli/commands/__tests__/rollbackCommand.test.ts +400 -0
  13. package/src/cli/commands/__tests__/securityCommand.test.ts +467 -0
  14. package/src/cli/commands/__tests__/themeCommand.test.ts +166 -0
  15. package/src/cli/commands/__tests__/updateCommand.test.ts +720 -0
  16. package/src/cli/commands/__tests__/workspaceCommand.test.ts +286 -0
  17. package/src/cli/commands/aiCommand.ts +163 -0
  18. package/src/cli/commands/analyzeCommand.ts +219 -0
  19. package/src/cli/commands/checkCommand.ts +91 -98
  20. package/src/cli/commands/graphCommand.ts +475 -0
  21. package/src/cli/commands/initCommand.ts +64 -54
  22. package/src/cli/commands/rollbackCommand.ts +334 -0
  23. package/src/cli/commands/securityCommand.ts +165 -100
  24. package/src/cli/commands/themeCommand.ts +148 -0
  25. package/src/cli/commands/updateCommand.ts +215 -263
  26. package/src/cli/commands/workspaceCommand.ts +73 -0
  27. package/src/cli/constants/cliChoices.ts +93 -0
  28. package/src/cli/formatters/__tests__/__snapshots__/outputFormatter.test.ts.snap +557 -0
  29. package/src/cli/formatters/__tests__/ciFormatter.test.ts +526 -0
  30. package/src/cli/formatters/__tests__/outputFormatter.test.ts +448 -0
  31. package/src/cli/formatters/__tests__/progressBar.test.ts +709 -0
  32. package/src/cli/formatters/ciFormatter.ts +964 -0
  33. package/src/cli/formatters/colorUtils.ts +145 -0
  34. package/src/cli/formatters/outputFormatter.ts +615 -332
  35. package/src/cli/formatters/progressBar.ts +43 -52
  36. package/src/cli/formatters/versionFormatter.ts +132 -0
  37. package/src/cli/handlers/aiAnalysisHandler.ts +205 -0
  38. package/src/cli/handlers/changelogHandler.ts +113 -0
  39. package/src/cli/handlers/index.ts +9 -0
  40. package/src/cli/handlers/installHandler.ts +130 -0
  41. package/src/cli/index.ts +175 -726
  42. package/src/cli/interactive/InteractiveOptionsCollector.ts +387 -0
  43. package/src/cli/interactive/interactivePrompts.ts +189 -83
  44. package/src/cli/interactive/optionUtils.ts +89 -0
  45. package/src/cli/themes/colorTheme.ts +43 -16
  46. package/src/cli/utils/cliOutput.ts +118 -0
  47. package/src/cli/utils/commandHelpers.ts +249 -0
  48. package/src/cli/validators/commandValidator.ts +321 -336
  49. package/src/cli/validators/index.ts +37 -2
  50. package/src/cli/options/globalOptions.ts +0 -437
  51. package/src/cli/options/index.ts +0 -5
@@ -1,5 +1,40 @@
1
1
  /**
2
- * CLI Validators Entry Point
2
+ * CLI Validators
3
+ *
4
+ * QUAL-002: Centralized validation exports
3
5
  */
4
6
 
5
- export * from './commandValidator.js'
7
+ export {
8
+ type AIOptions,
9
+ // Types
10
+ type BaseCommandOptions,
11
+ // Composable validation utilities
12
+ composeValidators,
13
+ errorsOnly,
14
+ // Help text utilities
15
+ formatChoicesHelp,
16
+ type GraphOptions,
17
+ type InteractiveOptions,
18
+ type PatternOptions,
19
+ type SecurityOptions,
20
+ severityChoicesHelp,
21
+ type TargetOptions,
22
+ targetChoicesHelp,
23
+ validateAIOptions,
24
+ // Pre-composed validators for common commands
25
+ validateAnalyzeOptions,
26
+ validateCheckOptions,
27
+ // Individual validation rules (can be composed)
28
+ validateFormat,
29
+ validateGraphFormat,
30
+ validateGraphOptions,
31
+ validateGraphType,
32
+ validateInitOptions,
33
+ validateInteractiveConflicts,
34
+ validatePatterns,
35
+ validateSecurityOptions,
36
+ validateSeverity,
37
+ validateTarget,
38
+ validateUpdateOptions,
39
+ validateWorkspacePath,
40
+ } from './commandValidator.js'
@@ -1,437 +0,0 @@
1
- /**
2
- * Global CLI Options
3
- *
4
- * Defines standardized option definitions for all CLI commands.
5
- * Provides consistent option parsing and validation.
6
- */
7
-
8
- import { Option } from 'commander'
9
-
10
- export interface GlobalCliOptions {
11
- workspace?: string
12
- verbose?: boolean
13
- color?: boolean
14
- registry?: string
15
- timeout?: number
16
- config?: string
17
- }
18
-
19
- export interface CheckCliOptions extends GlobalCliOptions {
20
- catalog?: string
21
- format?: 'table' | 'json' | 'yaml' | 'minimal'
22
- target?: 'latest' | 'greatest' | 'minor' | 'patch' | 'newest'
23
- prerelease?: boolean
24
- include?: string[]
25
- exclude?: string[]
26
- }
27
-
28
- export interface UpdateCliOptions extends CheckCliOptions {
29
- interactive?: boolean
30
- dryRun?: boolean
31
- force?: boolean
32
- createBackup?: boolean
33
- }
34
-
35
- export interface AnalyzeCliOptions extends GlobalCliOptions {
36
- format?: 'table' | 'json' | 'yaml' | 'minimal'
37
- ai?: boolean
38
- provider?: 'auto' | 'claude' | 'gemini' | 'codex'
39
- analysisType?: 'impact' | 'security' | 'compatibility' | 'recommend'
40
- skipCache?: boolean
41
- }
42
-
43
- export interface WorkspaceCliOptions extends GlobalCliOptions {
44
- validate?: boolean
45
- stats?: boolean
46
- info?: boolean
47
- format?: 'table' | 'json' | 'yaml' | 'minimal'
48
- }
49
-
50
- /**
51
- * Global options available to all commands
52
- */
53
- export const globalOptions = [
54
- new Option('-w, --workspace <path>', 'workspace directory path').env('PCU_WORKSPACE'),
55
-
56
- new Option('-v, --verbose', 'enable verbose logging').env('PCU_VERBOSE'),
57
-
58
- new Option('--no-color', 'disable colored output').env('PCU_NO_COLOR'),
59
-
60
- new Option('--registry <url>', 'NPM registry URL').env('PCU_REGISTRY'),
61
-
62
- new Option('--timeout <ms>', 'request timeout in milliseconds')
63
- .argParser(parseInt)
64
- .env('PCU_TIMEOUT'),
65
-
66
- new Option('--config <path>', 'path to configuration file').env('PCU_CONFIG'),
67
- ]
68
-
69
- /**
70
- * Check command specific options
71
- */
72
- export const checkOptions = [
73
- ...globalOptions,
74
-
75
- new Option('--catalog <name>', 'check specific catalog only').env('PCU_CATALOG'),
76
-
77
- new Option('-f, --format <type>', 'output format')
78
- .choices(['table', 'json', 'yaml', 'minimal'])
79
- .default('table')
80
- .env('PCU_OUTPUT_FORMAT'),
81
-
82
- new Option('-t, --target <type>', 'update target')
83
- .choices(['latest', 'greatest', 'minor', 'patch', 'newest'])
84
- .default('latest')
85
- .env('PCU_UPDATE_TARGET'),
86
-
87
- new Option('--prerelease', 'include prerelease versions').env('PCU_PRERELEASE'),
88
-
89
- new Option('--include <pattern...>', 'include packages matching pattern').env('PCU_INCLUDE'),
90
-
91
- new Option('--exclude <pattern...>', 'exclude packages matching pattern').env('PCU_EXCLUDE'),
92
- ]
93
-
94
- /**
95
- * Update command specific options
96
- */
97
- export const updateOptions = [
98
- ...checkOptions,
99
-
100
- new Option('-i, --interactive', 'interactive mode to choose updates').env('PCU_INTERACTIVE'),
101
-
102
- new Option('-d, --dry-run', 'preview changes without writing files').env('PCU_DRY_RUN'),
103
-
104
- new Option('--force', 'force updates even if risky').env('PCU_FORCE'),
105
-
106
- new Option('--create-backup', 'create backup files before updating').env('PCU_CREATE_BACKUP'),
107
- ]
108
-
109
- /**
110
- * Analyze command specific options
111
- */
112
- export const analyzeOptions = [
113
- ...globalOptions,
114
-
115
- new Option('-f, --format <type>', 'output format')
116
- .choices(['table', 'json', 'yaml', 'minimal'])
117
- .default('table')
118
- .env('PCU_OUTPUT_FORMAT'),
119
-
120
- new Option('--ai', 'enable AI-powered analysis').env('PCU_AI_ENABLED'),
121
-
122
- new Option('--provider <name>', 'AI provider to use')
123
- .choices(['auto', 'claude', 'gemini', 'codex'])
124
- .default('auto')
125
- .env('PCU_AI_PROVIDER'),
126
-
127
- new Option('--analysis-type <type>', 'type of AI analysis')
128
- .choices(['impact', 'security', 'compatibility', 'recommend'])
129
- .default('impact')
130
- .env('PCU_AI_ANALYSIS_TYPE'),
131
-
132
- new Option('--skip-cache', 'skip AI analysis cache').env('PCU_AI_SKIP_CACHE'),
133
- ]
134
-
135
- /**
136
- * Workspace command specific options
137
- */
138
- export const workspaceOptions = [
139
- ...globalOptions,
140
-
141
- new Option('--validate', 'validate workspace configuration'),
142
-
143
- new Option('--stats', 'show workspace statistics'),
144
-
145
- new Option('--info', 'show workspace information'),
146
-
147
- new Option('-f, --format <type>', 'output format')
148
- .choices(['table', 'json', 'yaml', 'minimal'])
149
- .default('table')
150
- .env('PCU_OUTPUT_FORMAT'),
151
- ]
152
-
153
- /**
154
- * Option groups for better help organization
155
- */
156
- export const optionGroups = {
157
- global: {
158
- title: 'Global Options',
159
- options: globalOptions,
160
- },
161
- output: {
162
- title: 'Output Options',
163
- options: [
164
- new Option('-f, --format <type>', 'output format')
165
- .choices(['table', 'json', 'yaml', 'minimal'])
166
- .default('table'),
167
- new Option('--no-color', 'disable colored output'),
168
- new Option('-v, --verbose', 'enable verbose logging'),
169
- ],
170
- },
171
- filtering: {
172
- title: 'Filtering Options',
173
- options: [
174
- new Option('--catalog <name>', 'check specific catalog only'),
175
- new Option('--include <pattern...>', 'include packages matching pattern'),
176
- new Option('--exclude <pattern...>', 'exclude packages matching pattern'),
177
- ],
178
- },
179
- update: {
180
- title: 'Update Options',
181
- options: [
182
- new Option('-t, --target <type>', 'update target')
183
- .choices(['latest', 'greatest', 'minor', 'patch', 'newest'])
184
- .default('latest'),
185
- new Option('--prerelease', 'include prerelease versions'),
186
- new Option('-i, --interactive', 'interactive mode'),
187
- new Option('-d, --dry-run', 'preview changes only'),
188
- new Option('--force', 'force updates'),
189
- new Option('--create-backup', 'create backup files'),
190
- ],
191
- },
192
- registry: {
193
- title: 'Registry Options',
194
- options: [
195
- new Option('--registry <url>', 'NPM registry URL'),
196
- new Option('--timeout <ms>', 'request timeout').argParser(parseInt),
197
- ],
198
- },
199
- }
200
-
201
- /**
202
- * Utility functions for option handling
203
- */
204
- export class OptionUtils {
205
- private static parseBoolean(value: unknown): boolean {
206
- if (value === undefined || value === null) return false
207
- if (typeof value === 'boolean') return value
208
- if (typeof value === 'number') return value !== 0
209
- if (typeof value === 'string') {
210
- const normalized = value.trim().toLowerCase()
211
- if (normalized === '') return false
212
- if (['false', '0', 'no', 'off', 'n'].includes(normalized)) return false
213
- if (['true', '1', 'yes', 'on', 'y'].includes(normalized)) return true
214
- return true
215
- }
216
- return Boolean(value)
217
- }
218
-
219
- /**
220
- * Parse and validate global options
221
- */
222
- static parseGlobalOptions(options: any): GlobalCliOptions {
223
- const parsed: GlobalCliOptions = {}
224
-
225
- if (options.workspace) {
226
- parsed.workspace = String(options.workspace).trim()
227
- }
228
-
229
- if (options.verbose !== undefined) {
230
- parsed.verbose = OptionUtils.parseBoolean(options.verbose)
231
- }
232
-
233
- if (options.color !== undefined) {
234
- parsed.color = OptionUtils.parseBoolean(options.color)
235
- }
236
-
237
- if (options.registry) {
238
- parsed.registry = String(options.registry).trim()
239
- }
240
-
241
- if (options.timeout) {
242
- const timeout = parseInt(String(options.timeout), 10)
243
- if (!Number.isNaN(timeout) && timeout > 0) {
244
- parsed.timeout = timeout
245
- }
246
- }
247
-
248
- if (options.config) {
249
- parsed.config = String(options.config).trim()
250
- }
251
-
252
- return parsed
253
- }
254
-
255
- /**
256
- * Parse check command options
257
- */
258
- static parseCheckOptions(options: any): CheckCliOptions {
259
- const global = OptionUtils.parseGlobalOptions(options)
260
- const check: CheckCliOptions = { ...global }
261
-
262
- if (options.catalog) {
263
- check.catalog = String(options.catalog).trim()
264
- }
265
-
266
- if (options.format && typeof options.format === 'string') {
267
- check.format = options.format as Exclude<CheckCliOptions['format'], undefined>
268
- }
269
-
270
- if (options.target && typeof options.target === 'string') {
271
- check.target = options.target as Exclude<CheckCliOptions['target'], undefined>
272
- }
273
-
274
- if (options.prerelease !== undefined) {
275
- check.prerelease = OptionUtils.parseBoolean(options.prerelease)
276
- }
277
-
278
- if (options.include) {
279
- check.include = Array.isArray(options.include)
280
- ? options.include.map((p: any) => String(p).trim()).filter(Boolean)
281
- : [String(options.include).trim()].filter(Boolean)
282
- }
283
-
284
- if (options.exclude) {
285
- check.exclude = Array.isArray(options.exclude)
286
- ? options.exclude.map((p: any) => String(p).trim()).filter(Boolean)
287
- : [String(options.exclude).trim()].filter(Boolean)
288
- }
289
-
290
- return check
291
- }
292
-
293
- /**
294
- * Parse update command options
295
- */
296
- static parseUpdateOptions(options: any): UpdateCliOptions {
297
- const check = OptionUtils.parseCheckOptions(options)
298
- const update: UpdateCliOptions = { ...check }
299
-
300
- if (options.interactive !== undefined) {
301
- update.interactive = OptionUtils.parseBoolean(options.interactive)
302
- }
303
-
304
- if (options.dryRun !== undefined) {
305
- update.dryRun = OptionUtils.parseBoolean(options.dryRun)
306
- }
307
-
308
- if (options.force !== undefined) {
309
- update.force = OptionUtils.parseBoolean(options.force)
310
- }
311
-
312
- if (options.createBackup !== undefined) {
313
- update.createBackup = OptionUtils.parseBoolean(options.createBackup)
314
- }
315
-
316
- return update
317
- }
318
-
319
- /**
320
- * Parse analyze command options
321
- */
322
- static parseAnalyzeOptions(options: any): AnalyzeCliOptions {
323
- const global = OptionUtils.parseGlobalOptions(options)
324
- const analyze: AnalyzeCliOptions = { ...global }
325
-
326
- if (options.format && typeof options.format === 'string') {
327
- analyze.format = options.format as Exclude<AnalyzeCliOptions['format'], undefined>
328
- }
329
-
330
- if (options.ai !== undefined) {
331
- analyze.ai = OptionUtils.parseBoolean(options.ai)
332
- }
333
-
334
- if (options.provider && typeof options.provider === 'string') {
335
- analyze.provider = options.provider as Exclude<AnalyzeCliOptions['provider'], undefined>
336
- }
337
-
338
- if (options.analysisType && typeof options.analysisType === 'string') {
339
- analyze.analysisType = options.analysisType as Exclude<
340
- AnalyzeCliOptions['analysisType'],
341
- undefined
342
- >
343
- }
344
-
345
- if (options.skipCache !== undefined) {
346
- analyze.skipCache = OptionUtils.parseBoolean(options.skipCache)
347
- }
348
-
349
- return analyze
350
- }
351
-
352
- /**
353
- * Parse workspace command options
354
- */
355
- static parseWorkspaceOptions(options: any): WorkspaceCliOptions {
356
- const global = OptionUtils.parseGlobalOptions(options)
357
- const workspace: WorkspaceCliOptions = { ...global }
358
-
359
- if (options.validate !== undefined) {
360
- workspace.validate = OptionUtils.parseBoolean(options.validate)
361
- }
362
-
363
- if (options.stats !== undefined) {
364
- workspace.stats = OptionUtils.parseBoolean(options.stats)
365
- }
366
-
367
- if (options.info !== undefined) {
368
- workspace.info = OptionUtils.parseBoolean(options.info)
369
- }
370
-
371
- if (options.format && typeof options.format === 'string') {
372
- workspace.format = options.format as Exclude<WorkspaceCliOptions['format'], undefined>
373
- }
374
-
375
- return workspace
376
- }
377
-
378
- /**
379
- * Generate help text for option group
380
- */
381
- static generateHelpText(groupName: keyof typeof optionGroups): string {
382
- const group = optionGroups[groupName]
383
- if (!group) return ''
384
-
385
- const lines = [`${group.title}:`]
386
-
387
- for (const option of group.options) {
388
- const flags = option.flags
389
- const description = option.description || ''
390
- const choices = option.argChoices ? ` (choices: ${option.argChoices.join(', ')})` : ''
391
- const defaultValue = option.defaultValue ? ` (default: ${option.defaultValue})` : ''
392
-
393
- lines.push(` ${flags.padEnd(30)} ${description}${choices}${defaultValue}`)
394
- }
395
-
396
- return lines.join('\n')
397
- }
398
-
399
- /**
400
- * Validate option combinations
401
- */
402
- static validateOptionCombinations(command: string, options: any): string[] {
403
- const errors: string[] = []
404
-
405
- switch (command) {
406
- case 'update':
407
- if (
408
- OptionUtils.parseBoolean(options.interactive) &&
409
- OptionUtils.parseBoolean(options.dryRun)
410
- ) {
411
- errors.push('Cannot use --interactive with --dry-run')
412
- }
413
- break
414
-
415
- case 'workspace': {
416
- const actionCount = [options.validate, options.stats, options.info].filter((v) =>
417
- OptionUtils.parseBoolean(v)
418
- ).length
419
- if (actionCount > 1) {
420
- errors.push('Cannot use multiple workspace actions simultaneously')
421
- }
422
- if (actionCount === 0) {
423
- // Default to info
424
- options.info = true
425
- }
426
- break
427
- }
428
- }
429
-
430
- // Global validations
431
- if (OptionUtils.parseBoolean(options.verbose) && OptionUtils.parseBoolean(options.silent)) {
432
- errors.push('Cannot use both --verbose and --silent')
433
- }
434
-
435
- return errors
436
- }
437
- }
@@ -1,5 +0,0 @@
1
- /**
2
- * CLI Options Entry Point
3
- */
4
-
5
- export * from './globalOptions.js'