@bfra.me/workspace-analyzer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +402 -0
- package/lib/chunk-4LSFAAZW.js +1 -0
- package/lib/chunk-JDF7DQ4V.js +27 -0
- package/lib/chunk-WOJ4C7N7.js +7122 -0
- package/lib/cli.d.ts +1 -0
- package/lib/cli.js +318 -0
- package/lib/index.d.ts +3701 -0
- package/lib/index.js +1262 -0
- package/lib/types/index.d.ts +146 -0
- package/lib/types/index.js +28 -0
- package/package.json +89 -0
- package/src/analyzers/analyzer.ts +201 -0
- package/src/analyzers/architectural-analyzer.ts +304 -0
- package/src/analyzers/build-config-analyzer.ts +334 -0
- package/src/analyzers/circular-import-analyzer.ts +463 -0
- package/src/analyzers/config-consistency-analyzer.ts +335 -0
- package/src/analyzers/dead-code-analyzer.ts +565 -0
- package/src/analyzers/duplicate-code-analyzer.ts +626 -0
- package/src/analyzers/duplicate-dependency-analyzer.ts +381 -0
- package/src/analyzers/eslint-config-analyzer.ts +281 -0
- package/src/analyzers/exports-field-analyzer.ts +324 -0
- package/src/analyzers/index.ts +388 -0
- package/src/analyzers/large-dependency-analyzer.ts +535 -0
- package/src/analyzers/package-json-analyzer.ts +349 -0
- package/src/analyzers/peer-dependency-analyzer.ts +275 -0
- package/src/analyzers/tree-shaking-analyzer.ts +623 -0
- package/src/analyzers/tsconfig-analyzer.ts +382 -0
- package/src/analyzers/unused-dependency-analyzer.ts +356 -0
- package/src/analyzers/version-alignment-analyzer.ts +308 -0
- package/src/api/analyze-workspace.ts +245 -0
- package/src/api/index.ts +11 -0
- package/src/cache/cache-manager.ts +495 -0
- package/src/cache/cache-schema.ts +247 -0
- package/src/cache/change-detector.ts +169 -0
- package/src/cache/file-hasher.ts +65 -0
- package/src/cache/index.ts +47 -0
- package/src/cli/commands/analyze.ts +240 -0
- package/src/cli/commands/index.ts +5 -0
- package/src/cli/index.ts +61 -0
- package/src/cli/types.ts +65 -0
- package/src/cli/ui.ts +213 -0
- package/src/cli.ts +9 -0
- package/src/config/defaults.ts +183 -0
- package/src/config/index.ts +81 -0
- package/src/config/loader.ts +270 -0
- package/src/config/merger.ts +229 -0
- package/src/config/schema.ts +263 -0
- package/src/core/incremental-analyzer.ts +462 -0
- package/src/core/index.ts +34 -0
- package/src/core/orchestrator.ts +416 -0
- package/src/graph/dependency-graph.ts +408 -0
- package/src/graph/index.ts +19 -0
- package/src/index.ts +417 -0
- package/src/parser/config-parser.ts +491 -0
- package/src/parser/import-extractor.ts +340 -0
- package/src/parser/index.ts +54 -0
- package/src/parser/typescript-parser.ts +95 -0
- package/src/performance/bundle-estimator.ts +444 -0
- package/src/performance/index.ts +27 -0
- package/src/reporters/console-reporter.ts +355 -0
- package/src/reporters/index.ts +49 -0
- package/src/reporters/json-reporter.ts +273 -0
- package/src/reporters/markdown-reporter.ts +349 -0
- package/src/reporters/reporter.ts +399 -0
- package/src/rules/builtin-rules.ts +709 -0
- package/src/rules/index.ts +52 -0
- package/src/rules/rule-engine.ts +409 -0
- package/src/scanner/index.ts +18 -0
- package/src/scanner/workspace-scanner.ts +403 -0
- package/src/types/index.ts +176 -0
- package/src/types/result.ts +19 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/pattern-matcher.ts +48 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration merging utilities for workspace analyzer.
|
|
3
|
+
*
|
|
4
|
+
* Provides deep merging of configuration sources with proper precedence:
|
|
5
|
+
* programmatic options > config file > defaults
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {WorkspaceAnalyzerConfig} from './loader'
|
|
9
|
+
import type {WorkspaceAnalyzerConfigOutput} from './schema'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
DEFAULT_ANALYZER_CONFIG,
|
|
13
|
+
DEFAULT_CACHE_DIR,
|
|
14
|
+
DEFAULT_CONCURRENCY,
|
|
15
|
+
DEFAULT_HASH_ALGORITHM,
|
|
16
|
+
DEFAULT_MAX_CACHE_AGE,
|
|
17
|
+
DEFAULT_PACKAGE_PATTERNS,
|
|
18
|
+
} from './defaults'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Complete configuration after merging all sources.
|
|
22
|
+
*/
|
|
23
|
+
export interface MergedConfig {
|
|
24
|
+
/** Glob patterns for files to include */
|
|
25
|
+
readonly include: readonly string[]
|
|
26
|
+
/** Glob patterns for files to exclude */
|
|
27
|
+
readonly exclude: readonly string[]
|
|
28
|
+
/** Minimum severity level to report */
|
|
29
|
+
readonly minSeverity: 'info' | 'warning' | 'error' | 'critical'
|
|
30
|
+
/** Categories of issues to check (empty means all) */
|
|
31
|
+
readonly categories: readonly (
|
|
32
|
+
| 'configuration'
|
|
33
|
+
| 'dependency'
|
|
34
|
+
| 'architecture'
|
|
35
|
+
| 'performance'
|
|
36
|
+
| 'circular-import'
|
|
37
|
+
| 'unused-export'
|
|
38
|
+
| 'type-safety'
|
|
39
|
+
)[]
|
|
40
|
+
/** Enable incremental analysis caching */
|
|
41
|
+
readonly cache: boolean
|
|
42
|
+
/** Custom rules configuration */
|
|
43
|
+
readonly rules: Readonly<Record<string, unknown>>
|
|
44
|
+
/** Glob patterns for package locations */
|
|
45
|
+
readonly packagePatterns: readonly string[]
|
|
46
|
+
/** Maximum parallel analysis operations */
|
|
47
|
+
readonly concurrency: number
|
|
48
|
+
/** Directory for analysis cache files */
|
|
49
|
+
readonly cacheDir: string
|
|
50
|
+
/** Maximum cache age in milliseconds */
|
|
51
|
+
readonly maxCacheAge: number
|
|
52
|
+
/** Hash algorithm for file content */
|
|
53
|
+
readonly hashAlgorithm: 'sha256' | 'md5'
|
|
54
|
+
/** Per-analyzer configuration */
|
|
55
|
+
readonly analyzers: Readonly<
|
|
56
|
+
Record<
|
|
57
|
+
string,
|
|
58
|
+
{
|
|
59
|
+
readonly enabled?: boolean
|
|
60
|
+
readonly severity?: 'info' | 'warning' | 'error' | 'critical'
|
|
61
|
+
readonly options?: Readonly<Record<string, unknown>>
|
|
62
|
+
}
|
|
63
|
+
>
|
|
64
|
+
>
|
|
65
|
+
/** Architectural analysis rules */
|
|
66
|
+
readonly architecture?: {
|
|
67
|
+
readonly layers?: readonly {
|
|
68
|
+
readonly name: string
|
|
69
|
+
readonly patterns: readonly string[]
|
|
70
|
+
readonly allowedImports: readonly string[]
|
|
71
|
+
}[]
|
|
72
|
+
readonly allowBarrelExports?: boolean | readonly string[]
|
|
73
|
+
readonly enforcePublicApi?: boolean
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Gets the default merged configuration.
|
|
79
|
+
*/
|
|
80
|
+
export function getDefaultMergedConfig(): MergedConfig {
|
|
81
|
+
return {
|
|
82
|
+
include: DEFAULT_ANALYZER_CONFIG.include,
|
|
83
|
+
exclude: DEFAULT_ANALYZER_CONFIG.exclude,
|
|
84
|
+
minSeverity: DEFAULT_ANALYZER_CONFIG.minSeverity,
|
|
85
|
+
categories: DEFAULT_ANALYZER_CONFIG.categories,
|
|
86
|
+
cache: DEFAULT_ANALYZER_CONFIG.cache,
|
|
87
|
+
rules: DEFAULT_ANALYZER_CONFIG.rules,
|
|
88
|
+
packagePatterns: DEFAULT_PACKAGE_PATTERNS,
|
|
89
|
+
concurrency: DEFAULT_CONCURRENCY,
|
|
90
|
+
cacheDir: DEFAULT_CACHE_DIR,
|
|
91
|
+
maxCacheAge: DEFAULT_MAX_CACHE_AGE,
|
|
92
|
+
hashAlgorithm: DEFAULT_HASH_ALGORITHM,
|
|
93
|
+
analyzers: {},
|
|
94
|
+
architecture: undefined,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Merges analyzer-specific configurations.
|
|
100
|
+
*
|
|
101
|
+
* @param baseAnalyzers - Base analyzer config from file
|
|
102
|
+
* @param overrideAnalyzers - Override config from programmatic options
|
|
103
|
+
* @returns Merged analyzer configurations
|
|
104
|
+
*/
|
|
105
|
+
export function mergeAnalyzerConfigs(
|
|
106
|
+
baseAnalyzers: MergedConfig['analyzers'],
|
|
107
|
+
overrideAnalyzers?: WorkspaceAnalyzerConfig['analyzers'],
|
|
108
|
+
): MergedConfig['analyzers'] {
|
|
109
|
+
if (overrideAnalyzers == null) {
|
|
110
|
+
return baseAnalyzers
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const merged = {...baseAnalyzers}
|
|
114
|
+
|
|
115
|
+
for (const [id, config] of Object.entries(overrideAnalyzers)) {
|
|
116
|
+
const base = merged[id]
|
|
117
|
+
if (base == null) {
|
|
118
|
+
merged[id] = config
|
|
119
|
+
} else {
|
|
120
|
+
merged[id] = {
|
|
121
|
+
...base,
|
|
122
|
+
...config,
|
|
123
|
+
options: config.options == null ? base.options : {...base.options, ...config.options},
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return merged
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Converts a validated config to a merged config structure.
|
|
133
|
+
*/
|
|
134
|
+
function validatedToMerged(config: WorkspaceAnalyzerConfigOutput): MergedConfig {
|
|
135
|
+
return {
|
|
136
|
+
include: config.include,
|
|
137
|
+
exclude: config.exclude,
|
|
138
|
+
minSeverity: config.minSeverity,
|
|
139
|
+
categories: config.categories,
|
|
140
|
+
cache: config.cache,
|
|
141
|
+
rules: config.rules,
|
|
142
|
+
packagePatterns: config.packagePatterns,
|
|
143
|
+
concurrency: config.concurrency,
|
|
144
|
+
cacheDir: config.cacheDir,
|
|
145
|
+
maxCacheAge: config.maxCacheAge,
|
|
146
|
+
hashAlgorithm: config.hashAlgorithm,
|
|
147
|
+
analyzers: config.analyzers,
|
|
148
|
+
architecture: config.architecture,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Applies partial options to a merged configuration.
|
|
154
|
+
*/
|
|
155
|
+
function applyOptions(base: MergedConfig, options: WorkspaceAnalyzerConfig): MergedConfig {
|
|
156
|
+
return {
|
|
157
|
+
include: options.include ?? base.include,
|
|
158
|
+
exclude: options.exclude ?? base.exclude,
|
|
159
|
+
minSeverity: options.minSeverity ?? base.minSeverity,
|
|
160
|
+
categories: options.categories ?? base.categories,
|
|
161
|
+
cache: options.cache ?? base.cache,
|
|
162
|
+
rules: options.rules == null ? base.rules : {...base.rules, ...options.rules},
|
|
163
|
+
packagePatterns: options.packagePatterns ?? base.packagePatterns,
|
|
164
|
+
concurrency: options.concurrency ?? base.concurrency,
|
|
165
|
+
cacheDir: options.cacheDir ?? base.cacheDir,
|
|
166
|
+
maxCacheAge: options.maxCacheAge ?? base.maxCacheAge,
|
|
167
|
+
hashAlgorithm: options.hashAlgorithm ?? base.hashAlgorithm,
|
|
168
|
+
analyzers: mergeAnalyzerConfigs(base.analyzers, options.analyzers),
|
|
169
|
+
architecture: options.architecture ?? base.architecture,
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Merges configuration from multiple sources.
|
|
175
|
+
*
|
|
176
|
+
* Precedence (highest to lowest):
|
|
177
|
+
* 1. Programmatic options passed to analyzeWorkspace()
|
|
178
|
+
* 2. Configuration file (workspace-analyzer.config.ts)
|
|
179
|
+
* 3. Default values
|
|
180
|
+
*
|
|
181
|
+
* @param fileConfig - Configuration loaded from file (optional)
|
|
182
|
+
* @param programmaticOptions - Options passed programmatically (optional)
|
|
183
|
+
* @returns Fully merged configuration with all defaults applied
|
|
184
|
+
*/
|
|
185
|
+
export function mergeConfig(
|
|
186
|
+
fileConfig?: WorkspaceAnalyzerConfigOutput,
|
|
187
|
+
programmaticOptions?: WorkspaceAnalyzerConfig,
|
|
188
|
+
): MergedConfig {
|
|
189
|
+
// Start with defaults
|
|
190
|
+
let merged = getDefaultMergedConfig()
|
|
191
|
+
|
|
192
|
+
// Apply file config if present
|
|
193
|
+
if (fileConfig != null) {
|
|
194
|
+
merged = validatedToMerged(fileConfig)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Apply programmatic options with highest precedence
|
|
198
|
+
if (programmaticOptions != null) {
|
|
199
|
+
merged = applyOptions(merged, programmaticOptions)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return merged
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates analyzer options from merged configuration for a specific analyzer.
|
|
207
|
+
*
|
|
208
|
+
* @param mergedConfig - The fully merged configuration
|
|
209
|
+
* @param analyzerId - The ID of the analyzer to get options for
|
|
210
|
+
* @returns Options for the specified analyzer
|
|
211
|
+
*/
|
|
212
|
+
export function getAnalyzerOptions(
|
|
213
|
+
mergedConfig: MergedConfig,
|
|
214
|
+
analyzerId: string,
|
|
215
|
+
): {
|
|
216
|
+
enabled: boolean
|
|
217
|
+
minSeverity: MergedConfig['minSeverity']
|
|
218
|
+
categories: MergedConfig['categories']
|
|
219
|
+
options: Record<string, unknown>
|
|
220
|
+
} {
|
|
221
|
+
const analyzerConfig = mergedConfig.analyzers[analyzerId]
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
enabled: analyzerConfig?.enabled ?? true,
|
|
225
|
+
minSeverity: analyzerConfig?.severity ?? mergedConfig.minSeverity,
|
|
226
|
+
categories: mergedConfig.categories,
|
|
227
|
+
options: (analyzerConfig?.options ?? {}) as Record<string, unknown>,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zod schemas for configuration validation.
|
|
3
|
+
*
|
|
4
|
+
* Provides runtime validation for workspace analyzer configuration,
|
|
5
|
+
* ensuring type safety and helpful error messages for invalid configs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {z} from 'zod'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_ANALYZER_CONFIG,
|
|
12
|
+
DEFAULT_CACHE_DIR,
|
|
13
|
+
DEFAULT_CONCURRENCY,
|
|
14
|
+
DEFAULT_MAX_CACHE_AGE,
|
|
15
|
+
DEFAULT_PACKAGE_PATTERNS,
|
|
16
|
+
} from './defaults'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Valid severity levels for issue filtering.
|
|
20
|
+
*/
|
|
21
|
+
export const severitySchema = z.enum(['info', 'warning', 'error', 'critical'])
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Valid issue categories for filtering.
|
|
25
|
+
*/
|
|
26
|
+
export const categorySchema = z.enum([
|
|
27
|
+
'configuration',
|
|
28
|
+
'dependency',
|
|
29
|
+
'architecture',
|
|
30
|
+
'performance',
|
|
31
|
+
'circular-import',
|
|
32
|
+
'unused-export',
|
|
33
|
+
'type-safety',
|
|
34
|
+
])
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Schema for custom rule configuration.
|
|
38
|
+
*/
|
|
39
|
+
export const ruleConfigSchema = z
|
|
40
|
+
.record(z.string(), z.unknown())
|
|
41
|
+
.describe('Custom rule configuration keyed by rule ID')
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Schema for analyzer-specific options.
|
|
45
|
+
*/
|
|
46
|
+
export const analyzerOptionsSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
enabled: z.boolean().optional().describe('Whether this analyzer is enabled'),
|
|
49
|
+
severity: severitySchema.optional().describe('Override default severity'),
|
|
50
|
+
options: z.record(z.string(), z.unknown()).optional().describe('Analyzer-specific options'),
|
|
51
|
+
})
|
|
52
|
+
.strict()
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Schema for layer configuration in architectural analysis.
|
|
56
|
+
*/
|
|
57
|
+
export const layerConfigSchema = z.object({
|
|
58
|
+
name: z.string().describe('Layer name'),
|
|
59
|
+
patterns: z.array(z.string()).describe('File path patterns matching this layer'),
|
|
60
|
+
allowedImports: z.array(z.string()).describe('Layers this layer can import from'),
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Schema for architectural rules configuration.
|
|
65
|
+
*/
|
|
66
|
+
export const architecturalRulesSchema = z
|
|
67
|
+
.object({
|
|
68
|
+
layers: z
|
|
69
|
+
.array(layerConfigSchema)
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('Custom layer definitions for layer violation detection'),
|
|
72
|
+
allowBarrelExports: z
|
|
73
|
+
.union([z.boolean(), z.array(z.string())])
|
|
74
|
+
.optional()
|
|
75
|
+
.describe('Allow barrel exports (true/false or array of allowed paths)'),
|
|
76
|
+
enforcePublicApi: z.boolean().optional().describe('Enforce explicit public API exports'),
|
|
77
|
+
})
|
|
78
|
+
.strict()
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Core analyzer configuration schema.
|
|
82
|
+
*/
|
|
83
|
+
export const analyzerConfigSchema = z
|
|
84
|
+
.object({
|
|
85
|
+
include: z
|
|
86
|
+
.array(z.string())
|
|
87
|
+
.optional()
|
|
88
|
+
.default(DEFAULT_ANALYZER_CONFIG.include as unknown as string[])
|
|
89
|
+
.describe('Glob patterns for files to include in analysis'),
|
|
90
|
+
exclude: z
|
|
91
|
+
.array(z.string())
|
|
92
|
+
.optional()
|
|
93
|
+
.default(DEFAULT_ANALYZER_CONFIG.exclude as unknown as string[])
|
|
94
|
+
.describe('Glob patterns for files to exclude from analysis'),
|
|
95
|
+
minSeverity: severitySchema
|
|
96
|
+
.optional()
|
|
97
|
+
.default(DEFAULT_ANALYZER_CONFIG.minSeverity)
|
|
98
|
+
.describe('Minimum severity level to report'),
|
|
99
|
+
categories: z
|
|
100
|
+
.array(categorySchema)
|
|
101
|
+
.optional()
|
|
102
|
+
.default([])
|
|
103
|
+
.describe('Categories of issues to check (empty means all)'),
|
|
104
|
+
cache: z.boolean().optional().default(true).describe('Enable incremental analysis caching'),
|
|
105
|
+
rules: ruleConfigSchema.optional().default({}).describe('Custom rules configuration'),
|
|
106
|
+
})
|
|
107
|
+
.strict()
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Workspace-specific options schema.
|
|
111
|
+
*/
|
|
112
|
+
export const workspaceOptionsSchema = z
|
|
113
|
+
.object({
|
|
114
|
+
packagePatterns: z
|
|
115
|
+
.array(z.string())
|
|
116
|
+
.optional()
|
|
117
|
+
.default(DEFAULT_PACKAGE_PATTERNS as unknown as string[])
|
|
118
|
+
.describe('Glob patterns for package locations'),
|
|
119
|
+
concurrency: z
|
|
120
|
+
.number()
|
|
121
|
+
.int()
|
|
122
|
+
.min(1)
|
|
123
|
+
.max(16)
|
|
124
|
+
.optional()
|
|
125
|
+
.default(DEFAULT_CONCURRENCY)
|
|
126
|
+
.describe('Maximum parallel analysis operations'),
|
|
127
|
+
cacheDir: z
|
|
128
|
+
.string()
|
|
129
|
+
.optional()
|
|
130
|
+
.default(DEFAULT_CACHE_DIR)
|
|
131
|
+
.describe('Directory for analysis cache files'),
|
|
132
|
+
maxCacheAge: z
|
|
133
|
+
.number()
|
|
134
|
+
.int()
|
|
135
|
+
.min(0)
|
|
136
|
+
.optional()
|
|
137
|
+
.default(DEFAULT_MAX_CACHE_AGE)
|
|
138
|
+
.describe('Maximum cache age in milliseconds'),
|
|
139
|
+
hashAlgorithm: z
|
|
140
|
+
.enum(['sha256', 'md5'])
|
|
141
|
+
.optional()
|
|
142
|
+
.default('sha256')
|
|
143
|
+
.describe('Hash algorithm for file content'),
|
|
144
|
+
})
|
|
145
|
+
.strict()
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Per-analyzer configuration in the config file.
|
|
149
|
+
*/
|
|
150
|
+
export const analyzersConfigSchema = z
|
|
151
|
+
.record(z.string(), analyzerOptionsSchema)
|
|
152
|
+
.optional()
|
|
153
|
+
.default({})
|
|
154
|
+
.describe('Per-analyzer configuration overrides')
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Complete workspace analyzer configuration schema.
|
|
158
|
+
*/
|
|
159
|
+
export const workspaceAnalyzerConfigSchema = z
|
|
160
|
+
.object({
|
|
161
|
+
// Extend from core analyzer config
|
|
162
|
+
...analyzerConfigSchema.shape,
|
|
163
|
+
// Add workspace-specific options
|
|
164
|
+
...workspaceOptionsSchema.shape,
|
|
165
|
+
// Per-analyzer configuration
|
|
166
|
+
analyzers: analyzersConfigSchema,
|
|
167
|
+
// Architectural rules
|
|
168
|
+
architecture: architecturalRulesSchema.optional().describe('Architectural analysis rules'),
|
|
169
|
+
})
|
|
170
|
+
.strict()
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Schema for the analyzeWorkspace() options parameter.
|
|
174
|
+
*/
|
|
175
|
+
export const analyzeWorkspaceOptionsSchema = z
|
|
176
|
+
.object({
|
|
177
|
+
// Include base config options
|
|
178
|
+
...analyzerConfigSchema.shape,
|
|
179
|
+
// Include workspace options
|
|
180
|
+
...workspaceOptionsSchema.shape,
|
|
181
|
+
// API-specific options
|
|
182
|
+
configPath: z.string().optional().describe('Path to workspace-analyzer.config.ts file'),
|
|
183
|
+
// onProgress is validated at runtime, not in schema (function types not well-supported in zod v4)
|
|
184
|
+
analyzers: analyzersConfigSchema,
|
|
185
|
+
architecture: architecturalRulesSchema.optional(),
|
|
186
|
+
})
|
|
187
|
+
.strict()
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Inferred types from schemas.
|
|
191
|
+
*/
|
|
192
|
+
export type SeverityInput = z.input<typeof severitySchema>
|
|
193
|
+
export type CategoryInput = z.input<typeof categorySchema>
|
|
194
|
+
export type AnalyzerConfigInput = z.input<typeof analyzerConfigSchema>
|
|
195
|
+
export type WorkspaceOptionsInput = z.input<typeof workspaceOptionsSchema>
|
|
196
|
+
export type WorkspaceAnalyzerConfigInput = z.input<typeof workspaceAnalyzerConfigSchema>
|
|
197
|
+
export type AnalyzeWorkspaceOptionsInput = z.input<typeof analyzeWorkspaceOptionsSchema>
|
|
198
|
+
|
|
199
|
+
export type SeverityOutput = z.output<typeof severitySchema>
|
|
200
|
+
export type CategoryOutput = z.output<typeof categorySchema>
|
|
201
|
+
export type AnalyzerConfigOutput = z.output<typeof analyzerConfigSchema>
|
|
202
|
+
export type WorkspaceOptionsOutput = z.output<typeof workspaceOptionsSchema>
|
|
203
|
+
export type WorkspaceAnalyzerConfigOutput = z.output<typeof workspaceAnalyzerConfigSchema>
|
|
204
|
+
export type AnalyzeWorkspaceOptionsOutput = z.output<typeof analyzeWorkspaceOptionsSchema>
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Validates and parses analyzer configuration.
|
|
208
|
+
*
|
|
209
|
+
* @param config - Raw configuration input
|
|
210
|
+
* @returns Validated and defaulted configuration
|
|
211
|
+
* @throws {z.ZodError} If validation fails
|
|
212
|
+
*/
|
|
213
|
+
export function parseAnalyzerConfig(config: unknown): AnalyzerConfigOutput {
|
|
214
|
+
return analyzerConfigSchema.parse(config)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Validates and parses workspace analyzer configuration.
|
|
219
|
+
*
|
|
220
|
+
* @param config - Raw configuration input
|
|
221
|
+
* @returns Validated and defaulted configuration
|
|
222
|
+
* @throws {z.ZodError} If validation fails
|
|
223
|
+
*/
|
|
224
|
+
export function parseWorkspaceAnalyzerConfig(config: unknown): WorkspaceAnalyzerConfigOutput {
|
|
225
|
+
return workspaceAnalyzerConfigSchema.parse(config)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Safe parse result type for configuration validation.
|
|
230
|
+
*/
|
|
231
|
+
export type SafeParseResult<T> = {success: true; data: T} | {success: false; error: z.ZodError}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Safely validates configuration without throwing.
|
|
235
|
+
*
|
|
236
|
+
* @param config - Raw configuration input
|
|
237
|
+
* @returns Validation result with success status and data/error
|
|
238
|
+
*/
|
|
239
|
+
export function safeParseWorkspaceAnalyzerConfig(
|
|
240
|
+
config: unknown,
|
|
241
|
+
): SafeParseResult<WorkspaceAnalyzerConfigOutput> {
|
|
242
|
+
const result = workspaceAnalyzerConfigSchema.safeParse(config)
|
|
243
|
+
if (result.success) {
|
|
244
|
+
return {success: true, data: result.data}
|
|
245
|
+
}
|
|
246
|
+
return {success: false, error: result.error}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Validates analyzeWorkspace() options.
|
|
251
|
+
*
|
|
252
|
+
* @param options - Raw options input
|
|
253
|
+
* @returns Validation result with success status and data/error
|
|
254
|
+
*/
|
|
255
|
+
export function safeParseAnalyzeOptions(
|
|
256
|
+
options: unknown,
|
|
257
|
+
): SafeParseResult<AnalyzeWorkspaceOptionsOutput> {
|
|
258
|
+
const result = analyzeWorkspaceOptionsSchema.safeParse(options)
|
|
259
|
+
if (result.success) {
|
|
260
|
+
return {success: true, data: result.data}
|
|
261
|
+
}
|
|
262
|
+
return {success: false, error: result.error}
|
|
263
|
+
}
|