create-qa-architect 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +12 -0
- package/.github/CLAUDE_MD_AUTOMATION.md +248 -0
- package/.github/PROGRESSIVE_QUALITY_IMPLEMENTATION.md +408 -0
- package/.github/PROGRESSIVE_QUALITY_PROPOSAL.md +443 -0
- package/.github/RELEASE_CHECKLIST.md +100 -0
- package/.github/dependabot.yml +50 -0
- package/.github/git-sync.sh +48 -0
- package/.github/workflows/claude-md-validation.yml +82 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +176 -0
- package/.github/workflows/pnpm-ci.yml.example +53 -0
- package/.github/workflows/python-ci.yml.example +69 -0
- package/.github/workflows/quality-legacy.yml.backup +165 -0
- package/.github/workflows/quality-progressive.yml.example +291 -0
- package/.github/workflows/quality.yml +436 -0
- package/.github/workflows/release.yml +53 -0
- package/.nvmrc +1 -0
- package/.prettierignore +14 -0
- package/.prettierrc +9 -0
- package/.stylelintrc.json +5 -0
- package/README.md +212 -0
- package/config/.lighthouserc.js +45 -0
- package/config/.pre-commit-config.yaml +66 -0
- package/config/constants.js +128 -0
- package/config/defaults.js +124 -0
- package/config/pyproject.toml +124 -0
- package/config/quality-config.schema.json +97 -0
- package/config/quality-python.yml +89 -0
- package/config/requirements-dev.txt +15 -0
- package/create-saas-monetization.js +1465 -0
- package/eslint.config.cjs +117 -0
- package/eslint.config.ts.cjs +99 -0
- package/legal/README.md +106 -0
- package/legal/copyright.md +76 -0
- package/legal/disclaimer.md +146 -0
- package/legal/privacy-policy.html +324 -0
- package/legal/privacy-policy.md +196 -0
- package/legal/terms-of-service.md +224 -0
- package/lib/billing-dashboard.html +645 -0
- package/lib/config-validator.js +163 -0
- package/lib/dependency-monitoring-basic.js +185 -0
- package/lib/dependency-monitoring-premium.js +1490 -0
- package/lib/error-reporter.js +444 -0
- package/lib/interactive/prompt.js +128 -0
- package/lib/interactive/questions.js +146 -0
- package/lib/license-validator.js +403 -0
- package/lib/licensing.js +989 -0
- package/lib/package-utils.js +187 -0
- package/lib/project-maturity.js +516 -0
- package/lib/security-enhancements.js +340 -0
- package/lib/setup-enhancements.js +317 -0
- package/lib/smart-strategy-generator.js +344 -0
- package/lib/telemetry.js +323 -0
- package/lib/template-loader.js +252 -0
- package/lib/typescript-config-generator.js +210 -0
- package/lib/ui-helpers.js +74 -0
- package/lib/validation/base-validator.js +174 -0
- package/lib/validation/cache-manager.js +158 -0
- package/lib/validation/config-security.js +741 -0
- package/lib/validation/documentation.js +326 -0
- package/lib/validation/index.js +186 -0
- package/lib/validation/validation-factory.js +153 -0
- package/lib/validation/workflow-validation.js +172 -0
- package/lib/yaml-utils.js +120 -0
- package/marketing/beta-user-email-campaign.md +372 -0
- package/marketing/landing-page.html +721 -0
- package/package.json +165 -0
- package/setup.js +2076 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const { execSync } = require('child_process')
|
|
5
|
+
const { showProgress } = require('../ui-helpers')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Documentation Validator
|
|
9
|
+
* Uses mature tools for comprehensive documentation validation
|
|
10
|
+
*/
|
|
11
|
+
class DocumentationValidator {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.issues = []
|
|
14
|
+
this.warnings = []
|
|
15
|
+
this.options = options
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Validate all documentation
|
|
20
|
+
*/
|
|
21
|
+
async validateAll() {
|
|
22
|
+
console.log('📖 Running documentation validation with mature tools...')
|
|
23
|
+
|
|
24
|
+
this.issues = []
|
|
25
|
+
this.warnings = []
|
|
26
|
+
|
|
27
|
+
if (!this.options.disableMarkdownlint) {
|
|
28
|
+
await this.runMarkdownlint()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
await this.validateBasicStructure()
|
|
32
|
+
await this.validatePackageJsonAlignment()
|
|
33
|
+
await this.validateFileReferences()
|
|
34
|
+
await this.validateScriptReferences()
|
|
35
|
+
|
|
36
|
+
// Show warnings but don't fail on them
|
|
37
|
+
if (this.warnings.length > 0) {
|
|
38
|
+
console.warn(`⚠️ Found ${this.warnings.length} documentation warning(s):`)
|
|
39
|
+
this.warnings.forEach(warning => console.warn(` ${warning}`))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (this.issues.length > 0) {
|
|
43
|
+
console.error(`❌ Found ${this.issues.length} documentation issue(s):`)
|
|
44
|
+
this.issues.forEach(issue => console.error(` ${issue}`))
|
|
45
|
+
throw new Error('Documentation validation failed')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
console.log('✅ Documentation validation passed')
|
|
49
|
+
return {
|
|
50
|
+
issues: this.issues,
|
|
51
|
+
warnings: this.warnings,
|
|
52
|
+
passed: this.issues.length === 0,
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Run markdownlint for comprehensive markdown validation
|
|
58
|
+
*/
|
|
59
|
+
async runMarkdownlint() {
|
|
60
|
+
if (!fs.existsSync('README.md')) return
|
|
61
|
+
|
|
62
|
+
const spinner = showProgress(
|
|
63
|
+
'Validating markdown files with markdownlint...'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
// Check if markdownlint-cli2 is available
|
|
68
|
+
try {
|
|
69
|
+
execSync('npx markdownlint-cli2 --version', { stdio: 'pipe' })
|
|
70
|
+
} catch {
|
|
71
|
+
// markdownlint-cli2 not available, skip with info message
|
|
72
|
+
spinner.info(
|
|
73
|
+
'markdownlint-cli2 not found - skipping markdown validation'
|
|
74
|
+
)
|
|
75
|
+
console.log(
|
|
76
|
+
'ℹ️ markdownlint-cli2 not found - install for enhanced markdown validation'
|
|
77
|
+
)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Run markdownlint on markdown files with default config (exclude node_modules)
|
|
82
|
+
execSync('npx markdownlint-cli2 "**/*.md" "!node_modules/**"', {
|
|
83
|
+
stdio: 'pipe',
|
|
84
|
+
encoding: 'utf8',
|
|
85
|
+
})
|
|
86
|
+
// If we get here, markdownlint passed with no errors
|
|
87
|
+
spinner.succeed('Markdown validation passed')
|
|
88
|
+
} catch (error) {
|
|
89
|
+
// Check both stdout and stderr for errors (markdownlint uses stderr for errors)
|
|
90
|
+
if (error.status !== 0) {
|
|
91
|
+
const stdout = error.stdout ? error.stdout.toString().trim() : ''
|
|
92
|
+
const stderr = error.stderr ? error.stderr.toString().trim() : ''
|
|
93
|
+
const output = stderr || stdout // Prefer stderr as markdownlint writes errors there
|
|
94
|
+
|
|
95
|
+
if (output) {
|
|
96
|
+
const lines = output.split('\n')
|
|
97
|
+
|
|
98
|
+
// Filter out the header/summary lines and only include actual errors
|
|
99
|
+
const errorLines = lines.filter(line => {
|
|
100
|
+
const trimmed = line.trim()
|
|
101
|
+
return (
|
|
102
|
+
trimmed &&
|
|
103
|
+
!trimmed.startsWith('markdownlint-cli2') &&
|
|
104
|
+
!trimmed.startsWith('Finding:') &&
|
|
105
|
+
!trimmed.startsWith('Linting:') &&
|
|
106
|
+
!trimmed.startsWith('Summary:') &&
|
|
107
|
+
!trimmed.startsWith('Unable to use configuration file') && // Skip config errors
|
|
108
|
+
(trimmed.includes(':') || trimmed.includes('MD'))
|
|
109
|
+
) // Actual errors have line:column or MD rule format
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Convert markdown lint issues to warnings (non-blocking)
|
|
113
|
+
// Markdown style is subjective and shouldn't fail CI
|
|
114
|
+
errorLines.forEach(issue => {
|
|
115
|
+
if (issue.trim()) {
|
|
116
|
+
this.warnings.push(`Markdown lint: ${issue.trim()}`)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// Add summary warning
|
|
121
|
+
if (errorLines.length > 0) {
|
|
122
|
+
spinner.warn(
|
|
123
|
+
`markdownlint found ${errorLines.length} style issue(s)`
|
|
124
|
+
)
|
|
125
|
+
this.warnings.push(
|
|
126
|
+
`💡 markdownlint found ${errorLines.length} style issue(s). These are warnings only.`
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
spinner.succeed('Markdown validation passed')
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Basic structure validation for common documentation requirements
|
|
138
|
+
*/
|
|
139
|
+
async validateBasicStructure() {
|
|
140
|
+
if (!fs.existsSync('README.md')) {
|
|
141
|
+
this.issues.push('No README.md found')
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const readme = fs.readFileSync('README.md', 'utf8')
|
|
146
|
+
|
|
147
|
+
// Check for basic sections that good documentation should have
|
|
148
|
+
// Convert to warnings - structure is suggestive, not mandatory
|
|
149
|
+
const suggestedSections = ['install', 'usage', 'description']
|
|
150
|
+
const readmeLower = readme.toLowerCase()
|
|
151
|
+
|
|
152
|
+
for (const section of suggestedSections) {
|
|
153
|
+
if (!readmeLower.includes(section)) {
|
|
154
|
+
this.warnings.push(
|
|
155
|
+
`README.md could include a "${section}" section for better clarity`
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if package.json exists but README doesn't mention installation (advisory)
|
|
161
|
+
if (fs.existsSync('package.json')) {
|
|
162
|
+
if (
|
|
163
|
+
!readmeLower.includes('npm install') &&
|
|
164
|
+
!readmeLower.includes('pnpm install') &&
|
|
165
|
+
!readmeLower.includes('yarn install')
|
|
166
|
+
) {
|
|
167
|
+
this.warnings.push(
|
|
168
|
+
'README.md should include package manager installation instructions'
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validate package.json alignment with documentation
|
|
176
|
+
*/
|
|
177
|
+
async validatePackageJsonAlignment() {
|
|
178
|
+
if (!fs.existsSync('package.json')) {
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
|
183
|
+
|
|
184
|
+
// Check README exists if this is a published package
|
|
185
|
+
if (packageJson.name && !fs.existsSync('README.md')) {
|
|
186
|
+
this.issues.push(
|
|
187
|
+
'package.json defines a package name but no README.md exists'
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Advisory checks for package.json metadata (warnings, not failures)
|
|
192
|
+
if (
|
|
193
|
+
!packageJson.description ||
|
|
194
|
+
packageJson.description.trim().length === 0
|
|
195
|
+
) {
|
|
196
|
+
this.warnings.push('package.json should have a meaningful description')
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Only require keywords for packages that are likely to be published
|
|
200
|
+
if (
|
|
201
|
+
packageJson.name &&
|
|
202
|
+
packageJson.name !== 'my-project' &&
|
|
203
|
+
packageJson.version &&
|
|
204
|
+
(!packageJson.keywords || packageJson.keywords.length === 0)
|
|
205
|
+
) {
|
|
206
|
+
this.warnings.push(
|
|
207
|
+
'Published packages should have keywords in package.json for discoverability'
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Advisory license check
|
|
212
|
+
if (!packageJson.license) {
|
|
213
|
+
this.warnings.push('package.json should specify a license')
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Validate version is semver compliant if present
|
|
217
|
+
if (packageJson.version && !/^\d+\.\d+\.\d+/.test(packageJson.version)) {
|
|
218
|
+
this.issues.push(
|
|
219
|
+
`package.json version "${packageJson.version}" is not semver compliant`
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Validate README file references
|
|
226
|
+
*/
|
|
227
|
+
async validateFileReferences() {
|
|
228
|
+
if (!fs.existsSync('README.md')) {
|
|
229
|
+
return
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const readme = fs.readFileSync('README.md', 'utf8')
|
|
233
|
+
|
|
234
|
+
// Match code-fenced file references like `filename.ext`
|
|
235
|
+
// Looking for patterns like `package.json`, `setup.js`, etc.
|
|
236
|
+
const fileRefRegex = /`([a-zA-Z0-9_./-]+\.[a-zA-Z0-9]+)`/g
|
|
237
|
+
const matches = [...readme.matchAll(fileRefRegex)]
|
|
238
|
+
|
|
239
|
+
for (const match of matches) {
|
|
240
|
+
const filePath = match[1]
|
|
241
|
+
|
|
242
|
+
// Skip common non-file references
|
|
243
|
+
if (
|
|
244
|
+
filePath.includes('example') ||
|
|
245
|
+
filePath.includes('placeholder') ||
|
|
246
|
+
filePath.includes('your-') ||
|
|
247
|
+
filePath.includes('my-') ||
|
|
248
|
+
filePath.startsWith('http') ||
|
|
249
|
+
filePath.startsWith('www.') ||
|
|
250
|
+
// Skip common placeholders and examples
|
|
251
|
+
filePath.includes('*.') ||
|
|
252
|
+
filePath.includes('{') ||
|
|
253
|
+
filePath.includes('}')
|
|
254
|
+
) {
|
|
255
|
+
continue
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Skip files that this tool CREATES in target projects (not in package repo)
|
|
259
|
+
const createdByTool = [
|
|
260
|
+
// Python project files
|
|
261
|
+
'pyproject.toml',
|
|
262
|
+
'requirements.txt',
|
|
263
|
+
'requirements-dev.txt',
|
|
264
|
+
'.pre-commit-config.yaml',
|
|
265
|
+
// Workflow files (created in .github/workflows/)
|
|
266
|
+
'quality.yml',
|
|
267
|
+
'quality-python.yml',
|
|
268
|
+
// Optional tooling configs
|
|
269
|
+
'.lighthouserc.js',
|
|
270
|
+
'vercel.json',
|
|
271
|
+
]
|
|
272
|
+
|
|
273
|
+
if (createdByTool.includes(filePath)) {
|
|
274
|
+
continue
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Check if file exists
|
|
278
|
+
|
|
279
|
+
if (!fs.existsSync(filePath)) {
|
|
280
|
+
this.issues.push(`README.md references non-existent file: ${filePath}`)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Validate README script references
|
|
287
|
+
*/
|
|
288
|
+
async validateScriptReferences() {
|
|
289
|
+
if (!fs.existsSync('README.md') || !fs.existsSync('package.json')) {
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const readme = fs.readFileSync('README.md', 'utf8')
|
|
294
|
+
const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'))
|
|
295
|
+
const scripts = packageJson.scripts || {}
|
|
296
|
+
|
|
297
|
+
// Match npm script references like `npm run script-name`
|
|
298
|
+
const scriptRefRegex = /npm run ([a-z0-9:_-]+)/gi
|
|
299
|
+
const matches = [...readme.matchAll(scriptRefRegex)]
|
|
300
|
+
|
|
301
|
+
for (const match of matches) {
|
|
302
|
+
const scriptName = match[1]
|
|
303
|
+
|
|
304
|
+
// Skip scripts that this tool CREATES in target projects (not in package repo)
|
|
305
|
+
const createdByTool = [
|
|
306
|
+
'lighthouse:ci',
|
|
307
|
+
'python:format',
|
|
308
|
+
'python:lint',
|
|
309
|
+
'python:type-check',
|
|
310
|
+
'python:test',
|
|
311
|
+
]
|
|
312
|
+
|
|
313
|
+
if (createdByTool.includes(scriptName)) {
|
|
314
|
+
continue
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (!scripts[scriptName]) {
|
|
318
|
+
this.issues.push(
|
|
319
|
+
`README.md references non-existent script: npm run ${scriptName}`
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
module.exports = { DocumentationValidator }
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { ConfigSecurityScanner } = require('./config-security')
|
|
4
|
+
const { DocumentationValidator } = require('./documentation')
|
|
5
|
+
const { WorkflowValidator } = require('./workflow-validation')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enhanced Validation Runner
|
|
9
|
+
* Coordinates all validation checks
|
|
10
|
+
*/
|
|
11
|
+
class ValidationRunner {
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.options = options
|
|
14
|
+
this.configScanner = new ConfigSecurityScanner(options)
|
|
15
|
+
this.docValidator = new DocumentationValidator(options)
|
|
16
|
+
this.workflowValidator = new WorkflowValidator(options)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Run configuration security checks
|
|
21
|
+
*/
|
|
22
|
+
async runConfigSecurity() {
|
|
23
|
+
return await this.configScanner.scanAll()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run documentation accuracy checks
|
|
28
|
+
*/
|
|
29
|
+
async runDocumentationValidation() {
|
|
30
|
+
return await this.docValidator.validateAll()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Run workflow validation checks
|
|
35
|
+
*/
|
|
36
|
+
async runWorkflowValidation() {
|
|
37
|
+
return await this.workflowValidator.validateAll()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Run comprehensive validation
|
|
42
|
+
*/
|
|
43
|
+
async runComprehensiveCheck() {
|
|
44
|
+
console.log('🔍 Running comprehensive validation...\n')
|
|
45
|
+
|
|
46
|
+
const results = {
|
|
47
|
+
configSecurity: null,
|
|
48
|
+
documentation: null,
|
|
49
|
+
workflows: null,
|
|
50
|
+
overall: { passed: true, issues: [] },
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
console.log('⏳ [1/3] Running configuration security scan...')
|
|
55
|
+
results.configSecurity = await this.runConfigSecurity()
|
|
56
|
+
console.log('✅ [1/3] Configuration security complete')
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.log('❌ [1/3] Configuration security failed')
|
|
59
|
+
results.configSecurity = { passed: false, error: error.message }
|
|
60
|
+
results.overall.passed = false
|
|
61
|
+
results.overall.issues.push(`Configuration Security: ${error.message}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log('') // Add spacing between checks
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
console.log('⏳ [2/3] Running documentation validation...')
|
|
68
|
+
results.documentation = await this.runDocumentationValidation()
|
|
69
|
+
console.log('✅ [2/3] Documentation validation complete')
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.log('❌ [2/3] Documentation validation failed')
|
|
72
|
+
results.documentation = { passed: false, error: error.message }
|
|
73
|
+
results.overall.passed = false
|
|
74
|
+
results.overall.issues.push(`Documentation: ${error.message}`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('') // Add spacing between checks
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
console.log('⏳ [3/3] Running workflow validation...')
|
|
81
|
+
results.workflows = await this.runWorkflowValidation()
|
|
82
|
+
console.log('✅ [3/3] Workflow validation complete')
|
|
83
|
+
} catch (error) {
|
|
84
|
+
console.log('❌ [3/3] Workflow validation failed')
|
|
85
|
+
results.workflows = { passed: false, error: error.message }
|
|
86
|
+
results.overall.passed = false
|
|
87
|
+
results.overall.issues.push(`Workflows: ${error.message}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('') // Add spacing
|
|
91
|
+
|
|
92
|
+
if (results.overall.passed) {
|
|
93
|
+
console.log('✅ All validation checks passed!')
|
|
94
|
+
} else {
|
|
95
|
+
// Detect test scenario and use appropriate prefix
|
|
96
|
+
const isTest = process.argv.join(' ').includes('test')
|
|
97
|
+
const prefix = isTest ? '📋 TEST SCENARIO:' : '❌'
|
|
98
|
+
console.error(`${prefix} Validation failed with issues:`)
|
|
99
|
+
results.overall.issues.forEach(issue => console.error(` - ${issue}`))
|
|
100
|
+
throw new Error('Comprehensive validation failed')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return results
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run comprehensive validation with parallel execution
|
|
108
|
+
* Runs all validations concurrently for better performance
|
|
109
|
+
*/
|
|
110
|
+
async runComprehensiveCheckParallel() {
|
|
111
|
+
console.log('🔍 Running comprehensive validation (parallel)...\n')
|
|
112
|
+
|
|
113
|
+
const results = {
|
|
114
|
+
configSecurity: null,
|
|
115
|
+
documentation: null,
|
|
116
|
+
workflows: null,
|
|
117
|
+
overall: { passed: true, issues: [] },
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Run all validations in parallel
|
|
121
|
+
const validationPromises = [
|
|
122
|
+
this.runConfigSecurity()
|
|
123
|
+
.then(result => {
|
|
124
|
+
results.configSecurity = result
|
|
125
|
+
console.log('✅ Configuration security complete')
|
|
126
|
+
})
|
|
127
|
+
.catch(error => {
|
|
128
|
+
console.log('❌ Configuration security failed')
|
|
129
|
+
results.configSecurity = { passed: false, error: error.message }
|
|
130
|
+
results.overall.passed = false
|
|
131
|
+
results.overall.issues.push(
|
|
132
|
+
`Configuration Security: ${error.message}`
|
|
133
|
+
)
|
|
134
|
+
}),
|
|
135
|
+
|
|
136
|
+
this.runDocumentationValidation()
|
|
137
|
+
.then(result => {
|
|
138
|
+
results.documentation = result
|
|
139
|
+
console.log('✅ Documentation validation complete')
|
|
140
|
+
})
|
|
141
|
+
.catch(error => {
|
|
142
|
+
console.log('❌ Documentation validation failed')
|
|
143
|
+
results.documentation = { passed: false, error: error.message }
|
|
144
|
+
results.overall.passed = false
|
|
145
|
+
results.overall.issues.push(`Documentation: ${error.message}`)
|
|
146
|
+
}),
|
|
147
|
+
|
|
148
|
+
this.runWorkflowValidation()
|
|
149
|
+
.then(result => {
|
|
150
|
+
results.workflows = result
|
|
151
|
+
console.log('✅ Workflow validation complete')
|
|
152
|
+
})
|
|
153
|
+
.catch(error => {
|
|
154
|
+
console.log('❌ Workflow validation failed')
|
|
155
|
+
results.workflows = { passed: false, error: error.message }
|
|
156
|
+
results.overall.passed = false
|
|
157
|
+
results.overall.issues.push(`Workflows: ${error.message}`)
|
|
158
|
+
}),
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
// Wait for all validations to complete
|
|
162
|
+
await Promise.all(validationPromises)
|
|
163
|
+
|
|
164
|
+
console.log('') // Add spacing
|
|
165
|
+
|
|
166
|
+
if (results.overall.passed) {
|
|
167
|
+
console.log('✅ All validation checks passed!')
|
|
168
|
+
} else {
|
|
169
|
+
// Detect test scenario and use appropriate prefix
|
|
170
|
+
const isTest = process.argv.join(' ').includes('test')
|
|
171
|
+
const prefix = isTest ? '📋 TEST SCENARIO:' : '❌'
|
|
172
|
+
console.error(`${prefix} Validation failed with issues:`)
|
|
173
|
+
results.overall.issues.forEach(issue => console.error(` - ${issue}`))
|
|
174
|
+
throw new Error('Comprehensive validation failed')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return results
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
ValidationRunner,
|
|
183
|
+
ConfigSecurityScanner,
|
|
184
|
+
DocumentationValidator,
|
|
185
|
+
WorkflowValidator,
|
|
186
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { ConfigSecurityScanner } = require('./config-security')
|
|
4
|
+
const { DocumentationValidator } = require('./documentation')
|
|
5
|
+
const { WorkflowValidator } = require('./workflow-validation')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validation Factory
|
|
9
|
+
* Implements dependency injection pattern for validators
|
|
10
|
+
* Centralizes validator instantiation and configuration
|
|
11
|
+
*/
|
|
12
|
+
class ValidationFactory {
|
|
13
|
+
constructor(globalOptions = {}) {
|
|
14
|
+
this.globalOptions = globalOptions
|
|
15
|
+
this.validators = new Map()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create and register a validator instance
|
|
20
|
+
* @param {string} type - Validator type ('security', 'documentation', 'workflow')
|
|
21
|
+
* @param {object} options - Validator-specific options
|
|
22
|
+
* @returns {object} Validator instance
|
|
23
|
+
*/
|
|
24
|
+
createValidator(type, options = {}) {
|
|
25
|
+
// Merge global options with validator-specific options
|
|
26
|
+
const mergedOptions = { ...this.globalOptions, ...options }
|
|
27
|
+
|
|
28
|
+
let validator
|
|
29
|
+
switch (type) {
|
|
30
|
+
case 'security':
|
|
31
|
+
validator = new ConfigSecurityScanner(mergedOptions)
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
case 'documentation':
|
|
35
|
+
validator = new DocumentationValidator(mergedOptions)
|
|
36
|
+
break
|
|
37
|
+
|
|
38
|
+
case 'workflow':
|
|
39
|
+
validator = new WorkflowValidator(mergedOptions)
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
default:
|
|
43
|
+
throw new Error(`Unknown validator type: ${type}`)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Register validator for later retrieval
|
|
47
|
+
this.validators.set(type, validator)
|
|
48
|
+
|
|
49
|
+
return validator
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get a registered validator
|
|
54
|
+
* @param {string} type - Validator type
|
|
55
|
+
* @returns {object|null} Validator instance or null if not found
|
|
56
|
+
*/
|
|
57
|
+
getValidator(type) {
|
|
58
|
+
return this.validators.get(type) || null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create all validators
|
|
63
|
+
* @returns {object} Map of all validator instances
|
|
64
|
+
*/
|
|
65
|
+
createAllValidators(options = {}) {
|
|
66
|
+
const types = ['security', 'documentation', 'workflow']
|
|
67
|
+
const validators = {}
|
|
68
|
+
|
|
69
|
+
types.forEach(type => {
|
|
70
|
+
validators[type] = this.createValidator(type, options)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
return validators
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Run all validators
|
|
78
|
+
* @returns {object} Validation results
|
|
79
|
+
*/
|
|
80
|
+
async validateAll() {
|
|
81
|
+
const results = {
|
|
82
|
+
passed: true,
|
|
83
|
+
issues: [],
|
|
84
|
+
warnings: [],
|
|
85
|
+
validators: {},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const [type, validator] of this.validators) {
|
|
89
|
+
try {
|
|
90
|
+
await validator.validate()
|
|
91
|
+
|
|
92
|
+
results.validators[type] = {
|
|
93
|
+
passed: validator.passed(),
|
|
94
|
+
issues: validator.getIssues(),
|
|
95
|
+
warnings: validator.getWarnings(),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Aggregate issues and warnings
|
|
99
|
+
results.issues.push(...validator.getIssues())
|
|
100
|
+
results.warnings.push(...validator.getWarnings())
|
|
101
|
+
|
|
102
|
+
if (!validator.passed()) {
|
|
103
|
+
results.passed = false
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
results.passed = false
|
|
107
|
+
results.issues.push(`${type} validator failed: ${error.message}`)
|
|
108
|
+
|
|
109
|
+
results.validators[type] = {
|
|
110
|
+
passed: false,
|
|
111
|
+
error: error.message,
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return results
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Clear all registered validators
|
|
121
|
+
*/
|
|
122
|
+
clear() {
|
|
123
|
+
this.validators.clear()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get validation summary
|
|
128
|
+
* @returns {object} Summary of all validators
|
|
129
|
+
*/
|
|
130
|
+
getSummary() {
|
|
131
|
+
const summary = {
|
|
132
|
+
total: this.validators.size,
|
|
133
|
+
passed: 0,
|
|
134
|
+
failed: 0,
|
|
135
|
+
totalIssues: 0,
|
|
136
|
+
totalWarnings: 0,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
for (const validator of this.validators.values()) {
|
|
140
|
+
if (validator.passed()) {
|
|
141
|
+
summary.passed++
|
|
142
|
+
} else {
|
|
143
|
+
summary.failed++
|
|
144
|
+
}
|
|
145
|
+
summary.totalIssues += validator.getIssues().length
|
|
146
|
+
summary.totalWarnings += validator.getWarnings().length
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return summary
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = ValidationFactory
|