create-qa-architect 5.0.0 → 5.0.6
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/.github/RELEASE_CHECKLIST.md +2 -4
- package/.github/workflows/daily-deploy-check.yml +136 -0
- package/.github/workflows/nightly-gitleaks-verification.yml +1 -1
- package/.github/workflows/quality.yml +3 -0
- package/.github/workflows/release.yml +12 -10
- package/.github/workflows/weekly-audit.yml +173 -0
- package/LICENSE +66 -0
- package/README.md +44 -30
- package/config/defaults.js +22 -1
- package/config/quality-config.schema.json +1 -1
- package/create-saas-monetization.js +75 -27
- package/docs/ARCHITECTURE.md +53 -0
- package/docs/DEPLOYMENT.md +62 -0
- package/docs/PREFLIGHT_REPORT.md +108 -0
- package/docs/SLA_GATES.md +28 -0
- package/docs/TESTING.md +61 -0
- package/docs/security/SOC2_STARTER.md +29 -0
- package/lib/config-validator.js +8 -2
- package/lib/dependency-monitoring-basic.js +73 -26
- package/lib/dependency-monitoring-premium.js +21 -19
- package/lib/github-api.js +249 -0
- package/lib/interactive/questions.js +4 -0
- package/lib/license-validator.js +1 -1
- package/lib/licensing.js +11 -10
- package/lib/package-utils.js +224 -8
- package/lib/project-maturity.js +1 -1
- package/lib/setup-enhancements.js +33 -0
- package/lib/template-loader.js +2 -0
- package/lib/ui-helpers.js +2 -1
- package/lib/validation/base-validator.js +5 -1
- package/lib/validation/cache-manager.js +1 -0
- package/lib/validation/config-security.js +5 -4
- package/lib/validation/validation-factory.js +1 -1
- package/lib/yaml-utils.js +15 -10
- package/package.json +18 -13
- package/scripts/check-docs.sh +63 -0
- package/scripts/smart-test-strategy.sh +98 -0
- package/scripts/test-e2e-package.sh +283 -0
- package/scripts/validate-command-patterns.js +112 -0
- package/setup.js +161 -44
- package/templates/QUALITY_TROUBLESHOOTING.md +403 -0
- package/templates/ci/circleci-config.yml +35 -0
- package/templates/ci/gitlab-ci.yml +47 -0
- package/templates/integration-tests/api-service.test.js +244 -0
- package/templates/integration-tests/frontend-app.test.js +267 -0
- package/templates/scripts/smart-test-strategy.sh +109 -0
- package/templates/test-stubs/e2e.smoke.test.js +12 -0
- package/templates/test-stubs/unit.test.js +7 -0
- package/legal/README.md +0 -106
- package/legal/copyright.md +0 -76
- package/legal/disclaimer.md +0 -146
- package/legal/privacy-policy.html +0 -324
- package/legal/privacy-policy.md +0 -196
- package/legal/terms-of-service.md +0 -224
- package/marketing/beta-user-email-campaign.md +0 -372
- package/marketing/landing-page.html +0 -721
package/lib/package-utils.js
CHANGED
|
@@ -50,17 +50,17 @@ function mergeDevDependencies(initialDevDeps = {}, defaultDevDeps) {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Merge lint-staged configuration, preserving existing patterns
|
|
53
|
-
* @param {
|
|
54
|
-
* @param {
|
|
55
|
-
* @param {
|
|
56
|
-
* @param {
|
|
57
|
-
* @returns {
|
|
53
|
+
* @param {Record<string, string|string[]>} [existing] - Existing lint-staged config
|
|
54
|
+
* @param {Record<string, string|string[]>} defaults - Default lint-staged config
|
|
55
|
+
* @param {{stylelintTargets?: string[]}} [options] - Merge options
|
|
56
|
+
* @param {(pattern: string) => boolean} [patternChecker] - Function to check if a pattern matches certain criteria
|
|
57
|
+
* @returns {Record<string, string|string[]>} Merged lint-staged config
|
|
58
58
|
*/
|
|
59
59
|
function mergeLintStaged(
|
|
60
|
+
defaults = {},
|
|
60
61
|
existing = {},
|
|
61
|
-
defaults,
|
|
62
62
|
options = {},
|
|
63
|
-
patternChecker =
|
|
63
|
+
patternChecker = _pattern => false
|
|
64
64
|
) {
|
|
65
65
|
const merged = { ...existing }
|
|
66
66
|
const stylelintTargets = options.stylelintTargets || []
|
|
@@ -88,7 +88,8 @@ function mergeLintStaged(
|
|
|
88
88
|
: [merged[pattern]]
|
|
89
89
|
|
|
90
90
|
const newCommands = [...existingCommands]
|
|
91
|
-
|
|
91
|
+
const commandList = Array.isArray(commands) ? commands : [commands]
|
|
92
|
+
commandList.forEach(command => {
|
|
92
93
|
if (!newCommands.includes(command)) {
|
|
93
94
|
newCommands.push(command)
|
|
94
95
|
}
|
|
@@ -177,6 +178,219 @@ function getAuditCommand(packageManager) {
|
|
|
177
178
|
return commands[packageManager] || 'npm audit'
|
|
178
179
|
}
|
|
179
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Detect monorepo type and configuration
|
|
183
|
+
* @param {string} projectPath - Path to the project directory
|
|
184
|
+
* @returns {Object} Monorepo info: { type, isMonorepo, packages, tool }
|
|
185
|
+
*/
|
|
186
|
+
function detectMonorepoType(projectPath = process.cwd()) {
|
|
187
|
+
const fs = require('fs')
|
|
188
|
+
const path = require('path')
|
|
189
|
+
|
|
190
|
+
const result = {
|
|
191
|
+
isMonorepo: false,
|
|
192
|
+
type: null, // 'workspaces' | 'lerna' | 'nx' | 'turborepo' | 'rush'
|
|
193
|
+
tool: null, // Specific tool name
|
|
194
|
+
packages: [], // List of workspace package paths
|
|
195
|
+
packageManager: detectPackageManager(projectPath),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check for Nx (nx.json)
|
|
199
|
+
const nxJsonPath = path.join(projectPath, 'nx.json')
|
|
200
|
+
if (fs.existsSync(nxJsonPath)) {
|
|
201
|
+
result.isMonorepo = true
|
|
202
|
+
result.type = 'nx'
|
|
203
|
+
result.tool = 'nx'
|
|
204
|
+
try {
|
|
205
|
+
const nxJson = JSON.parse(fs.readFileSync(nxJsonPath, 'utf8'))
|
|
206
|
+
result.config = nxJson
|
|
207
|
+
} catch {
|
|
208
|
+
// Ignore parse errors
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Check for Turborepo (turbo.json)
|
|
213
|
+
const turboJsonPath = path.join(projectPath, 'turbo.json')
|
|
214
|
+
if (fs.existsSync(turboJsonPath)) {
|
|
215
|
+
result.isMonorepo = true
|
|
216
|
+
result.type = 'turborepo'
|
|
217
|
+
result.tool = 'turborepo'
|
|
218
|
+
try {
|
|
219
|
+
const turboJson = JSON.parse(fs.readFileSync(turboJsonPath, 'utf8'))
|
|
220
|
+
result.config = turboJson
|
|
221
|
+
} catch {
|
|
222
|
+
// Ignore parse errors
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Check for Rush (rush.json)
|
|
227
|
+
const rushJsonPath = path.join(projectPath, 'rush.json')
|
|
228
|
+
if (fs.existsSync(rushJsonPath)) {
|
|
229
|
+
result.isMonorepo = true
|
|
230
|
+
result.type = 'rush'
|
|
231
|
+
result.tool = 'rush'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check for Lerna (lerna.json)
|
|
235
|
+
const lernaJsonPath = path.join(projectPath, 'lerna.json')
|
|
236
|
+
if (fs.existsSync(lernaJsonPath)) {
|
|
237
|
+
result.isMonorepo = true
|
|
238
|
+
result.type = 'lerna'
|
|
239
|
+
result.tool = 'lerna'
|
|
240
|
+
try {
|
|
241
|
+
const lernaJson = JSON.parse(fs.readFileSync(lernaJsonPath, 'utf8'))
|
|
242
|
+
result.packages = lernaJson.packages || ['packages/*']
|
|
243
|
+
} catch {
|
|
244
|
+
result.packages = ['packages/*']
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Check for pnpm workspaces (pnpm-workspace.yaml)
|
|
249
|
+
const pnpmWorkspacePath = path.join(projectPath, 'pnpm-workspace.yaml')
|
|
250
|
+
if (fs.existsSync(pnpmWorkspacePath)) {
|
|
251
|
+
result.isMonorepo = true
|
|
252
|
+
result.type = result.type || 'workspaces'
|
|
253
|
+
result.tool = result.tool || 'pnpm'
|
|
254
|
+
try {
|
|
255
|
+
const yaml = fs.readFileSync(pnpmWorkspacePath, 'utf8')
|
|
256
|
+
// Simple line-by-line YAML parsing (safer than regex)
|
|
257
|
+
const lines = yaml.split('\n')
|
|
258
|
+
let inPackages = false
|
|
259
|
+
const packages = []
|
|
260
|
+
for (const line of lines) {
|
|
261
|
+
if (line.trim() === 'packages:') {
|
|
262
|
+
inPackages = true
|
|
263
|
+
continue
|
|
264
|
+
}
|
|
265
|
+
if (inPackages) {
|
|
266
|
+
// Check if line is a list item (starts with -)
|
|
267
|
+
const match = line.match(/^\s*-\s*['"]?([^'"]+)['"]?\s*$/)
|
|
268
|
+
if (match) {
|
|
269
|
+
packages.push(match[1])
|
|
270
|
+
} else if (
|
|
271
|
+
line.trim() &&
|
|
272
|
+
!line.startsWith(' ') &&
|
|
273
|
+
!line.startsWith('\t')
|
|
274
|
+
) {
|
|
275
|
+
// New top-level key, stop parsing packages
|
|
276
|
+
break
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (packages.length > 0) {
|
|
281
|
+
result.packages = packages
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
// Ignore parse errors
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Check for npm/yarn workspaces in package.json
|
|
289
|
+
const packageJsonPath = path.join(projectPath, 'package.json')
|
|
290
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
291
|
+
try {
|
|
292
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
293
|
+
if (packageJson.workspaces) {
|
|
294
|
+
result.isMonorepo = true
|
|
295
|
+
result.type = result.type || 'workspaces'
|
|
296
|
+
// workspaces can be array or object with packages key
|
|
297
|
+
const workspaces = Array.isArray(packageJson.workspaces)
|
|
298
|
+
? packageJson.workspaces
|
|
299
|
+
: packageJson.workspaces.packages || []
|
|
300
|
+
result.packages = result.packages.length ? result.packages : workspaces
|
|
301
|
+
if (!result.tool) {
|
|
302
|
+
result.tool =
|
|
303
|
+
result.packageManager === 'yarn' ? 'yarn' : result.packageManager
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
// Ignore parse errors
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Resolve workspace package paths to actual directories
|
|
312
|
+
if (result.isMonorepo && result.packages.length > 0) {
|
|
313
|
+
result.resolvedPackages = resolveWorkspacePackages(
|
|
314
|
+
projectPath,
|
|
315
|
+
result.packages
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return result
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Resolve workspace glob patterns to actual package directories
|
|
324
|
+
* @param {string} projectPath - Root project path
|
|
325
|
+
* @param {Array<string>} patterns - Workspace patterns (e.g., ['packages/*', 'apps/*'])
|
|
326
|
+
* @returns {Array<Object>} Resolved packages with name and path
|
|
327
|
+
*/
|
|
328
|
+
function resolveWorkspacePackages(projectPath, patterns) {
|
|
329
|
+
const fs = require('fs')
|
|
330
|
+
const path = require('path')
|
|
331
|
+
const packages = []
|
|
332
|
+
|
|
333
|
+
for (const pattern of patterns) {
|
|
334
|
+
// Handle simple glob patterns like 'packages/*'
|
|
335
|
+
if (pattern.endsWith('/*')) {
|
|
336
|
+
const baseDir = path.join(projectPath, pattern.slice(0, -2))
|
|
337
|
+
if (fs.existsSync(baseDir)) {
|
|
338
|
+
try {
|
|
339
|
+
const entries = fs.readdirSync(baseDir, { withFileTypes: true })
|
|
340
|
+
for (const entry of entries) {
|
|
341
|
+
if (entry.isDirectory()) {
|
|
342
|
+
const pkgPath = path.join(baseDir, entry.name)
|
|
343
|
+
const pkgJsonPath = path.join(pkgPath, 'package.json')
|
|
344
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
345
|
+
try {
|
|
346
|
+
const pkgJson = JSON.parse(
|
|
347
|
+
fs.readFileSync(pkgJsonPath, 'utf8')
|
|
348
|
+
)
|
|
349
|
+
packages.push({
|
|
350
|
+
name: pkgJson.name || entry.name,
|
|
351
|
+
path: pkgPath,
|
|
352
|
+
relativePath: path.relative(projectPath, pkgPath),
|
|
353
|
+
})
|
|
354
|
+
} catch {
|
|
355
|
+
packages.push({
|
|
356
|
+
name: entry.name,
|
|
357
|
+
path: pkgPath,
|
|
358
|
+
relativePath: path.relative(projectPath, pkgPath),
|
|
359
|
+
})
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} catch {
|
|
365
|
+
// Ignore read errors
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
} else if (!pattern.includes('*')) {
|
|
369
|
+
// Direct path without glob
|
|
370
|
+
const pkgPath = path.join(projectPath, pattern)
|
|
371
|
+
const pkgJsonPath = path.join(pkgPath, 'package.json')
|
|
372
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
373
|
+
try {
|
|
374
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'))
|
|
375
|
+
packages.push({
|
|
376
|
+
name: pkgJson.name || path.basename(pkgPath),
|
|
377
|
+
path: pkgPath,
|
|
378
|
+
relativePath: pattern,
|
|
379
|
+
})
|
|
380
|
+
} catch {
|
|
381
|
+
packages.push({
|
|
382
|
+
name: path.basename(pkgPath),
|
|
383
|
+
path: pkgPath,
|
|
384
|
+
relativePath: pattern,
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return packages
|
|
392
|
+
}
|
|
393
|
+
|
|
180
394
|
module.exports = {
|
|
181
395
|
mergeScripts,
|
|
182
396
|
mergeDevDependencies,
|
|
@@ -184,4 +398,6 @@ module.exports = {
|
|
|
184
398
|
detectPackageManager,
|
|
185
399
|
getInstallCommand,
|
|
186
400
|
getAuditCommand,
|
|
401
|
+
detectMonorepoType,
|
|
402
|
+
resolveWorkspacePackages,
|
|
187
403
|
}
|
package/lib/project-maturity.js
CHANGED
|
@@ -435,7 +435,7 @@ class ProjectMaturityDetector {
|
|
|
435
435
|
|
|
436
436
|
/**
|
|
437
437
|
* Generate GitHub Actions outputs for maturity detection
|
|
438
|
-
* @returns {string} GitHub Actions output format
|
|
438
|
+
* @returns {{maturity: string, sourceCount: number, testCount: number, hasDeps: boolean, hasDocs: boolean, hasCss: boolean, requiredChecks: string, optionalChecks: string, disabledChecks: string}} GitHub Actions output format
|
|
439
439
|
*/
|
|
440
440
|
generateGitHubActionsOutput() {
|
|
441
441
|
const maturity = this.detect()
|
|
@@ -80,6 +80,10 @@ function applyProductionQualityFixes(projectPath = '.', options = {}) {
|
|
|
80
80
|
copyIntegrationTestTemplates(projectPath, projectType)
|
|
81
81
|
fixes.push(`✅ Added ${projectType} integration test templates`)
|
|
82
82
|
|
|
83
|
+
// Fix 6b: Add starter unit and e2e smoke test stubs
|
|
84
|
+
copyTestStubs(projectPath)
|
|
85
|
+
fixes.push('✅ Added unit and e2e smoke test stubs')
|
|
86
|
+
|
|
83
87
|
// Fix 7: Apply security-first configuration
|
|
84
88
|
const securityFixes = applySecurityFirstConfiguration(projectPath)
|
|
85
89
|
fixes.push('✅ Applied security-first configuration:')
|
|
@@ -201,6 +205,35 @@ function getTestTypesDocumentation(projectType) {
|
|
|
201
205
|
)
|
|
202
206
|
}
|
|
203
207
|
|
|
208
|
+
function copyTestStubs(projectPath) {
|
|
209
|
+
const stubDir = path.join(__dirname, '../templates/test-stubs')
|
|
210
|
+
if (!fs.existsSync(stubDir)) return
|
|
211
|
+
|
|
212
|
+
const targets = [
|
|
213
|
+
{
|
|
214
|
+
source: path.join(stubDir, 'unit.test.js'),
|
|
215
|
+
dest: path.join(projectPath, 'tests', 'unit', 'sample.test.js'),
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
source: path.join(stubDir, 'e2e.smoke.test.js'),
|
|
219
|
+
dest: path.join(projectPath, 'tests', 'e2e', 'smoke.test.js'),
|
|
220
|
+
},
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
targets.forEach(({ source, dest }) => {
|
|
224
|
+
if (!fs.existsSync(source)) return
|
|
225
|
+
|
|
226
|
+
const destDir = path.dirname(dest)
|
|
227
|
+
if (!fs.existsSync(destDir)) {
|
|
228
|
+
fs.mkdirSync(destDir, { recursive: true })
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!fs.existsSync(dest)) {
|
|
232
|
+
fs.copyFileSync(source, dest)
|
|
233
|
+
}
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
204
237
|
/**
|
|
205
238
|
* Generate comprehensive pre-commit hook
|
|
206
239
|
* This replaces the narrow CLAUDE.md-only validation
|
package/lib/template-loader.js
CHANGED
|
@@ -126,6 +126,7 @@ class TemplateLoader {
|
|
|
126
126
|
* // Only loads from whitelisted dirs: config/, .github/, lib/
|
|
127
127
|
*/
|
|
128
128
|
async loadTemplates(dir, baseDir = dir, isPackageDir = false) {
|
|
129
|
+
/** @type {Record<string, string>} */
|
|
129
130
|
const templates = {}
|
|
130
131
|
|
|
131
132
|
try {
|
|
@@ -177,6 +178,7 @@ class TemplateLoader {
|
|
|
177
178
|
* @returns {Promise<Object>} Merged template map
|
|
178
179
|
*/
|
|
179
180
|
async mergeTemplates(customDir, defaultsDir) {
|
|
181
|
+
/** @type {Record<string, string>} */
|
|
180
182
|
const merged = {}
|
|
181
183
|
|
|
182
184
|
// Load defaults first (from package directory - restrict to known template dirs)
|
package/lib/ui-helpers.js
CHANGED
|
@@ -55,7 +55,8 @@ function showProgress(message) {
|
|
|
55
55
|
|
|
56
56
|
// Try to use ora for interactive terminals
|
|
57
57
|
try {
|
|
58
|
-
const
|
|
58
|
+
const oraImport = require('ora')
|
|
59
|
+
const ora = /** @type {any} */ (oraImport.default || oraImport)
|
|
59
60
|
return ora(message).start()
|
|
60
61
|
} catch {
|
|
61
62
|
// Fallback if ora not installed (graceful degradation)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Error & {code?: string}} ErrWithCode
|
|
5
|
+
*/
|
|
6
|
+
|
|
3
7
|
/**
|
|
4
8
|
* Base Validator Class
|
|
5
9
|
* Provides common error handling, state management, and validation patterns
|
|
@@ -81,7 +85,7 @@ class BaseValidator {
|
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
87
|
* Format error message for user
|
|
84
|
-
* @param {
|
|
88
|
+
* @param {ErrWithCode} error - The error object
|
|
85
89
|
* @param {string} context - The context
|
|
86
90
|
* @returns {string} Formatted error message
|
|
87
91
|
*/
|
|
@@ -14,6 +14,7 @@ class CacheManager {
|
|
|
14
14
|
options.cacheDir || path.join(process.cwd(), '.create-qa-architect-cache')
|
|
15
15
|
this.ttl = options.ttl || 24 * 60 * 60 * 1000 // Default: 24 hours in milliseconds
|
|
16
16
|
this.enabled = options.enabled !== false // Enable by default
|
|
17
|
+
this.verbose = Boolean(options.verbose)
|
|
17
18
|
|
|
18
19
|
// Ensure cache directory exists
|
|
19
20
|
if (this.enabled) {
|
|
@@ -10,16 +10,17 @@ const { showProgress } = require('../ui-helpers')
|
|
|
10
10
|
|
|
11
11
|
// Pinned gitleaks version for reproducible security scanning
|
|
12
12
|
const GITLEAKS_VERSION = '8.28.0'
|
|
13
|
-
//
|
|
13
|
+
// SHA256 checksums of EXTRACTED BINARIES from gitleaks v8.28.0 release
|
|
14
|
+
// Note: These are checksums of the binary itself, not the tarball/zip archives
|
|
14
15
|
const GITLEAKS_CHECKSUMS = {
|
|
15
16
|
'linux-x64':
|
|
16
|
-
'
|
|
17
|
+
'5fd1b3b0073269484d40078662e921d07427340ab9e6ed526ccd215a565b3298',
|
|
17
18
|
'darwin-x64':
|
|
18
|
-
'
|
|
19
|
+
'cf09ad7a85683d90221db8324f036f23c8c29107145e1fc4a0dffbfa9e89c09a',
|
|
19
20
|
'darwin-arm64':
|
|
20
21
|
'5588b5d942dffa048720f7e6e1d274283219fb5722a2c7564d22e83ba39087d7',
|
|
21
22
|
'win32-x64':
|
|
22
|
-
'
|
|
23
|
+
'54230c22688d19939f316cd3e2e040cd067ece40a3a8c5b684e5110c62ecbf52',
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
package/lib/yaml-utils.js
CHANGED
|
@@ -82,17 +82,22 @@ function convertToYaml(obj, indent = 0) {
|
|
|
82
82
|
if (Array.isArray(obj)) {
|
|
83
83
|
obj.forEach(item => {
|
|
84
84
|
if (typeof item === 'object' && item !== null) {
|
|
85
|
-
// For objects in arrays,
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
yaml +=
|
|
85
|
+
// For objects in arrays, prefix first line with "- " and indent rest
|
|
86
|
+
const entries = Object.entries(item)
|
|
87
|
+
entries.forEach(([key, value], idx) => {
|
|
88
|
+
const safeKey = formatYamlValue(key)
|
|
89
|
+
const prefix = idx === 0 ? `${spaces}- ` : `${spaces} `
|
|
90
|
+
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
yaml += `${prefix}${safeKey}:\n`
|
|
93
|
+
yaml += convertToYaml(value, indent + 4)
|
|
94
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
95
|
+
yaml += `${prefix}${safeKey}:\n`
|
|
96
|
+
yaml += convertToYaml(value, indent + 4)
|
|
97
|
+
} else {
|
|
98
|
+
yaml += `${prefix}${safeKey}: ${formatYamlValue(value)}\n`
|
|
94
99
|
}
|
|
95
|
-
}
|
|
100
|
+
})
|
|
96
101
|
} else {
|
|
97
102
|
yaml += `${spaces}- ${formatYamlValue(item)}\n`
|
|
98
103
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-qa-architect",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.6",
|
|
4
4
|
"description": "QA Architect - Bootstrap quality automation for JavaScript/TypeScript and Python projects with GitHub Actions, pre-commit hooks, linting, formatting, and smart test strategy",
|
|
5
5
|
"main": "setup.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,26 +12,26 @@
|
|
|
12
12
|
"format": "prettier --write .",
|
|
13
13
|
"format:check": "prettier --check .",
|
|
14
14
|
"lint": "eslint . && stylelint \"**/*.{css,scss,sass,less,pcss}\" --allow-empty-input",
|
|
15
|
+
"type-check": "tsc --noEmit",
|
|
15
16
|
"lint:fix": "eslint . --fix && stylelint \"**/*.{css,scss,sass,less,pcss}\" --fix --allow-empty-input",
|
|
16
17
|
"security:audit": "npm audit --audit-level high",
|
|
17
18
|
"security:secrets": "node -e \"const fs=require('fs');const content=fs.readFileSync('package.json','utf8');if(/[\\\"\\'][a-zA-Z0-9+/]{20,}[\\\"\\']/.test(content)){console.error('❌ Potential hardcoded secrets in package.json');process.exit(1)}else{console.log('✅ No secrets detected in package.json')}\"",
|
|
18
19
|
"security:config": "node setup.js --security-config",
|
|
19
20
|
"validate:docs": "node setup.js --validate-docs",
|
|
20
|
-
"validate:claude": "node scripts/validate-claude-md.js",
|
|
21
21
|
"validate:comprehensive": "node setup.js --comprehensive --no-markdownlint",
|
|
22
|
-
"validate:all": "npm run validate:comprehensive && npm run security:audit
|
|
22
|
+
"validate:all": "npm run validate:comprehensive && npm run security:audit",
|
|
23
23
|
"validate:pre-push": "npm run test:patterns --if-present && npm run lint && npm run format:check && npm run test:commands --if-present && npm test --if-present",
|
|
24
|
-
"test": "node tests/setup.test.js && node tests/integration.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/parallel-validation.test.js && node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/template-loader.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/real-world-packages.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/real-purchase-flow.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/project-maturity-cli.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-
|
|
25
|
-
"test:unit": "node tests/setup.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/template-loader.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-
|
|
24
|
+
"test": "export QAA_DEVELOPER=true && node tests/setup.test.js && node tests/integration.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/parallel-validation.test.js && node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/template-loader.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/real-world-packages.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/real-purchase-flow.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/project-maturity-cli.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-command-patterns.test.js && node tests/gitleaks-binary-resolution.test.js && node tests/gitleaks-production-checksums.test.js && node tests/gitleaks-checksum-verification.test.js && node tests/gitleaks-real-binary-test.js && node tests/tier-enforcement.test.js",
|
|
25
|
+
"test:unit": "export QAA_DEVELOPER=true && node tests/setup.test.js && node tests/error-paths.test.js && node tests/error-messages.test.js && node tests/cache-manager.test.js && node tests/template-loader.test.js && node tests/telemetry.test.js && node tests/error-reporter.test.js && node tests/validation-factory.test.js && node tests/setup-error-coverage.test.js && node tests/licensing.test.js && node tests/security-licensing.test.js && node tests/base-validator.test.js && node tests/dependency-monitoring-basic.test.js && node tests/workflow-validation.test.js && node tests/setup-critical-paths.test.js && node tests/project-maturity.test.js && node tests/package-manager-detection.test.js && node tests/check-docs.test.js && node tests/validate-command-patterns.test.js && node tests/gitleaks-binary-resolution.test.js && node tests/gitleaks-production-checksums.test.js && node tests/gitleaks-checksum-verification.test.js",
|
|
26
26
|
"test:fast": "npm run test:unit",
|
|
27
27
|
"test:medium": "npm run test:fast && npm run test:patterns && npm run test:commands",
|
|
28
|
-
"test:slow": "node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/real-world-packages.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/real-purchase-flow.test.js && node tests/project-maturity-cli.test.js && node tests/gitleaks-real-binary-test.
|
|
28
|
+
"test:slow": "export QAA_DEVELOPER=true && node tests/python-integration.test.js && node tests/interactive.test.js && node tests/monorepo.test.js && node tests/critical-fixes.test.js && node tests/interactive-routing-fix.test.js && node tests/premium-dependency-monitoring.test.js && node tests/multi-language-dependency-monitoring.test.js && node tests/cli-deps-integration.test.js && node tests/real-world-packages.test.js && node tests/python-detection-sensitivity.test.js && node tests/python-parser-fixes.test.js && node tests/real-purchase-flow.test.js && node tests/project-maturity-cli.test.js && node tests/gitleaks-real-binary-test.js && npm run test:e2e",
|
|
29
29
|
"test:comprehensive": "npm run test:patterns && npm test && npm run test:commands && npm run test:e2e && npm run security:audit",
|
|
30
30
|
"test:real-binary": "RUN_REAL_BINARY_TEST=1 node tests/gitleaks-real-binary-test.js",
|
|
31
|
-
"test:commands": "node tests/command-execution.test.js",
|
|
31
|
+
"test:commands": "export QAA_DEVELOPER=true && node tests/command-execution.test.js",
|
|
32
32
|
"test:patterns": "node scripts/validate-command-patterns.js",
|
|
33
33
|
"test:coverage": "c8 --reporter=html --reporter=text --reporter=lcov npm test",
|
|
34
|
-
"test:e2e": "bash scripts/test-e2e-package.sh",
|
|
34
|
+
"test:e2e": "export QAA_DEVELOPER=true && bash scripts/test-e2e-package.sh",
|
|
35
35
|
"test:all": "npm run test:patterns && npm test && npm run test:commands && npm run test:e2e",
|
|
36
36
|
"coverage": "npm run test:coverage && echo '\nCoverage report generated in coverage/index.html'",
|
|
37
37
|
"docs:check": "bash scripts/check-docs.sh",
|
|
@@ -63,14 +63,18 @@
|
|
|
63
63
|
"security-audit"
|
|
64
64
|
],
|
|
65
65
|
"author": "Brett Stark",
|
|
66
|
-
"license": "
|
|
66
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
67
67
|
"files": [
|
|
68
68
|
"setup.js",
|
|
69
69
|
"create-saas-monetization.js",
|
|
70
70
|
"config/",
|
|
71
|
+
"docs/",
|
|
71
72
|
"lib/",
|
|
72
73
|
"legal/",
|
|
73
74
|
"marketing/",
|
|
75
|
+
"templates/",
|
|
76
|
+
"scripts/",
|
|
77
|
+
"LICENSE",
|
|
74
78
|
".github/",
|
|
75
79
|
".prettierrc",
|
|
76
80
|
".prettierignore",
|
|
@@ -82,12 +86,12 @@
|
|
|
82
86
|
],
|
|
83
87
|
"repository": {
|
|
84
88
|
"type": "git",
|
|
85
|
-
"url": "git+https://github.com/
|
|
89
|
+
"url": "git+https://github.com/vibebuildlab/qa-architect.git"
|
|
86
90
|
},
|
|
87
91
|
"bugs": {
|
|
88
|
-
"url": "https://github.com/
|
|
92
|
+
"url": "https://github.com/vibebuildlab/qa-architect/issues"
|
|
89
93
|
},
|
|
90
|
-
"homepage": "https://
|
|
94
|
+
"homepage": "https://vibebuildlab.com/tools/qa-architect",
|
|
91
95
|
"engines": {
|
|
92
96
|
"node": ">=20"
|
|
93
97
|
},
|
|
@@ -102,7 +106,9 @@
|
|
|
102
106
|
}
|
|
103
107
|
},
|
|
104
108
|
"devDependencies": {
|
|
109
|
+
"@types/node": "^20",
|
|
105
110
|
"actionlint": "^2.0.6",
|
|
111
|
+
"typescript": "^5",
|
|
106
112
|
"c8": "^10.1.2",
|
|
107
113
|
"eslint": "^9.12.0",
|
|
108
114
|
"eslint-plugin-security": "^3.0.1",
|
|
@@ -119,7 +125,6 @@
|
|
|
119
125
|
},
|
|
120
126
|
"lint-staged": {
|
|
121
127
|
"CLAUDE.md": [
|
|
122
|
-
"node scripts/validate-claude-md.js",
|
|
123
128
|
"prettier --write"
|
|
124
129
|
],
|
|
125
130
|
"config/defaults.js": [
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Documentation consistency checker
|
|
3
|
+
# Run before any release to catch documentation gaps
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
echo "🔍 Checking documentation consistency..."
|
|
8
|
+
|
|
9
|
+
# Check version consistency
|
|
10
|
+
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
11
|
+
if ! grep -q "## \[$PACKAGE_VERSION\]" CHANGELOG.md; then
|
|
12
|
+
echo "❌ CHANGELOG.md missing entry for version $PACKAGE_VERSION"
|
|
13
|
+
exit 1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# Check that setup.js file creation matches README documentation
|
|
17
|
+
echo "📁 Verifying file inventory..."
|
|
18
|
+
|
|
19
|
+
# Extract files created by setup.js
|
|
20
|
+
SETUP_FILES=$(grep -E "writeFileSync.*Path" setup.js | sed -E 's/.*writeFileSync\([^,]+, [^)]+\)//' | wc -l)
|
|
21
|
+
echo "Setup script creates approximately $SETUP_FILES files"
|
|
22
|
+
|
|
23
|
+
# Check for common missing files in README
|
|
24
|
+
MISSING_FILES=()
|
|
25
|
+
|
|
26
|
+
if grep -q "\.nvmrc" setup.js && ! grep -q "\.nvmrc" README.md; then
|
|
27
|
+
MISSING_FILES+=(".nvmrc")
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
if grep -q "\.npmrc" setup.js && ! grep -q "\.npmrc" README.md; then
|
|
31
|
+
MISSING_FILES+=(".npmrc")
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if grep -q "stylelintrc" setup.js && ! grep -q "stylelintrc" README.md; then
|
|
35
|
+
MISSING_FILES+=(".stylelintrc.json")
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
if grep -q "lighthouserc" setup.js && ! grep -q "lighthouserc" README.md; then
|
|
39
|
+
MISSING_FILES+=(".lighthouserc.js")
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if [ ${#MISSING_FILES[@]} -gt 0 ]; then
|
|
43
|
+
echo "❌ README.md missing documentation for files:"
|
|
44
|
+
printf ' - %s\n' "${MISSING_FILES[@]}"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Check for Python features if implemented
|
|
49
|
+
if grep -q "Python" setup.js; then
|
|
50
|
+
if ! grep -q "Python" README.md; then
|
|
51
|
+
echo "❌ Python features implemented but not documented in README.md"
|
|
52
|
+
exit 1
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Security validation is now handled by:
|
|
57
|
+
# - /bs:audit --security command
|
|
58
|
+
# - security-auditor agent
|
|
59
|
+
# - npm audit in CI workflows
|
|
60
|
+
# - gitleaks in pre-push hooks
|
|
61
|
+
echo "🔐 Security validation handled by automated tooling (npm audit, gitleaks, CI workflows)"
|
|
62
|
+
|
|
63
|
+
echo "✅ Documentation consistency checks passed!"
|