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.
Files changed (67) hide show
  1. package/.editorconfig +12 -0
  2. package/.github/CLAUDE_MD_AUTOMATION.md +248 -0
  3. package/.github/PROGRESSIVE_QUALITY_IMPLEMENTATION.md +408 -0
  4. package/.github/PROGRESSIVE_QUALITY_PROPOSAL.md +443 -0
  5. package/.github/RELEASE_CHECKLIST.md +100 -0
  6. package/.github/dependabot.yml +50 -0
  7. package/.github/git-sync.sh +48 -0
  8. package/.github/workflows/claude-md-validation.yml +82 -0
  9. package/.github/workflows/nightly-gitleaks-verification.yml +176 -0
  10. package/.github/workflows/pnpm-ci.yml.example +53 -0
  11. package/.github/workflows/python-ci.yml.example +69 -0
  12. package/.github/workflows/quality-legacy.yml.backup +165 -0
  13. package/.github/workflows/quality-progressive.yml.example +291 -0
  14. package/.github/workflows/quality.yml +436 -0
  15. package/.github/workflows/release.yml +53 -0
  16. package/.nvmrc +1 -0
  17. package/.prettierignore +14 -0
  18. package/.prettierrc +9 -0
  19. package/.stylelintrc.json +5 -0
  20. package/README.md +212 -0
  21. package/config/.lighthouserc.js +45 -0
  22. package/config/.pre-commit-config.yaml +66 -0
  23. package/config/constants.js +128 -0
  24. package/config/defaults.js +124 -0
  25. package/config/pyproject.toml +124 -0
  26. package/config/quality-config.schema.json +97 -0
  27. package/config/quality-python.yml +89 -0
  28. package/config/requirements-dev.txt +15 -0
  29. package/create-saas-monetization.js +1465 -0
  30. package/eslint.config.cjs +117 -0
  31. package/eslint.config.ts.cjs +99 -0
  32. package/legal/README.md +106 -0
  33. package/legal/copyright.md +76 -0
  34. package/legal/disclaimer.md +146 -0
  35. package/legal/privacy-policy.html +324 -0
  36. package/legal/privacy-policy.md +196 -0
  37. package/legal/terms-of-service.md +224 -0
  38. package/lib/billing-dashboard.html +645 -0
  39. package/lib/config-validator.js +163 -0
  40. package/lib/dependency-monitoring-basic.js +185 -0
  41. package/lib/dependency-monitoring-premium.js +1490 -0
  42. package/lib/error-reporter.js +444 -0
  43. package/lib/interactive/prompt.js +128 -0
  44. package/lib/interactive/questions.js +146 -0
  45. package/lib/license-validator.js +403 -0
  46. package/lib/licensing.js +989 -0
  47. package/lib/package-utils.js +187 -0
  48. package/lib/project-maturity.js +516 -0
  49. package/lib/security-enhancements.js +340 -0
  50. package/lib/setup-enhancements.js +317 -0
  51. package/lib/smart-strategy-generator.js +344 -0
  52. package/lib/telemetry.js +323 -0
  53. package/lib/template-loader.js +252 -0
  54. package/lib/typescript-config-generator.js +210 -0
  55. package/lib/ui-helpers.js +74 -0
  56. package/lib/validation/base-validator.js +174 -0
  57. package/lib/validation/cache-manager.js +158 -0
  58. package/lib/validation/config-security.js +741 -0
  59. package/lib/validation/documentation.js +326 -0
  60. package/lib/validation/index.js +186 -0
  61. package/lib/validation/validation-factory.js +153 -0
  62. package/lib/validation/workflow-validation.js +172 -0
  63. package/lib/yaml-utils.js +120 -0
  64. package/marketing/beta-user-email-campaign.md +372 -0
  65. package/marketing/landing-page.html +721 -0
  66. package/package.json +165 -0
  67. 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