create-backlist 9.0.0 → 10.0.1

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/bin/qa.js CHANGED
@@ -1,19 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // ═══════════════════════════════════════════════════════════════════════════
4
- // Backlist QA CLI — qa.js v9.0
4
+ // Backlist QA CLI — qa.js v10.0
5
5
  // Standalone QA entry point: `node bin/qa.js [mode] [--flags]`
6
6
  // Copyright (c) W.A.H.ISHAN — MIT License
7
7
  //
8
- // NEW in v9.0:
9
- // ✦ Live terminal dashboard with real-time metrics
10
- // ✦ Full end-to-end test suite (API, auth, security, UI, performance)
11
- // ✦ --scope flag: all | backend | frontend | security | performance
12
- // ✦ --watch flag: file-change triggered re-run
13
- // ✦ Post-generation auto-run mode
14
- // ✦ SIGINT / SIGTERM graceful shutdown with report flush
15
- // ✦ JSON output for CI pipeline integration
16
- // ✦ Compact status bar summary on exit
8
+ // NEW in v10.0:
9
+ // ✦ --url=<localhost> --prod=<production> flags for HTTP-based QA
10
+ // ✦ URL QA mode probe real running URLs (no browser required)
11
+ // ✦ HTTP security header scanner, SEO checker, a11y basics
12
+ // ✦ Route crawler & benchmark per endpoint
13
+ // ✦ Dual-URL diff report (localhost vs production)
14
+ // ✦ All v9.0 features retained (manual, auto, history, post-gen)
17
15
  // ═══════════════════════════════════════════════════════════════════════════
18
16
 
19
17
  import * as p from '@clack/prompts';
@@ -23,16 +21,19 @@ import path from 'node:path';
23
21
  import {
24
22
  runManualQA,
25
23
  runAutomatedQA,
24
+ runUrlQA,
26
25
  viewQAHistory,
27
26
  initQASystem,
28
27
  autoRunPostGeneration,
29
28
  } from '../src/qa/qa-engine.js';
30
29
 
31
- // ── CLI flags ─────────────────────────────────────────────────────────────
30
+ // ── Version ───────────────────────────────────────────────────────────────
31
+ const VERSION = '10.0.0';
32
32
 
33
- const argv = process.argv.slice(2);
34
- const flags = new Set(argv.filter(a => a.startsWith('--')));
35
- const posArgs = argv.filter(a => !a.startsWith('--'));
33
+ // ── CLI flags ─────────────────────────────────────────────────────────────
34
+ const argv = process.argv.slice(2);
35
+ const flags = new Set(argv.filter(a => a.startsWith('--')));
36
+ const posArgs = argv.filter(a => !a.startsWith('--'));
36
37
 
37
38
  const isContinuous = flags.has('--continuous') || flags.has('--watch') || flags.has('-w');
38
39
  const isCI = process.env.QA_CI === '1' || flags.has('--ci');
@@ -40,17 +41,20 @@ const noColor = flags.has('--no-color') || process.env.NO_COLOR;
40
41
  const isPostGen = flags.has('--post-gen');
41
42
  const scope = argv.find(a => a.startsWith('--scope='))?.split('=')[1] ?? 'all';
42
43
 
43
- if (noColor) chalk.level = 0;
44
+ // v10.0: URL flags
45
+ const localUrlFlag = argv.find(a => a.startsWith('--url='))?.split('=').slice(1).join('=') ?? '';
46
+ const prodUrlFlag = argv.find(a => a.startsWith('--prod='))?.split('=').slice(1).join('=') ?? '';
44
47
 
45
- // ── Graceful shutdown ────────────────────────────────────────────────────
48
+ if (noColor) chalk.level = 0;
46
49
 
50
+ // ── Graceful shutdown ─────────────────────────────────────────────────────
47
51
  let shuttingDown = false;
48
52
 
49
53
  function registerShutdownHandlers() {
50
54
  const shutdown = (signal) => {
51
55
  if (shuttingDown) return;
52
56
  shuttingDown = true;
53
- process.stdout.write('\x1b[?25h'); // ensure cursor restored
57
+ process.stdout.write('\x1b[?25h');
54
58
  console.log('\n' + chalk.gray(` ${signal} received — shutting down gracefully…`));
55
59
  p.cancel('QA session ended.');
56
60
  process.exit(0);
@@ -60,7 +64,6 @@ function registerShutdownHandlers() {
60
64
  }
61
65
 
62
66
  // ── Banner ────────────────────────────────────────────────────────────────
63
-
64
67
  function printQABanner() {
65
68
  if (isCI) return;
66
69
 
@@ -72,14 +75,14 @@ function printQABanner() {
72
75
 
73
76
  console.log('');
74
77
  console.log(c1(' ╔══════════════════════════════════════════════════════════════╗'));
75
- console.log(c1(' ║') + c2.bold(' ____ ___ ________ __ ____ ___________ ') + c1('║'));
76
- console.log(c1(' ║') + c2.bold(' / __ )/ | / ____/ //_/ / / / _/ ___/_ ') + c1('║'));
77
- console.log(c1(' ║') + c2.bold(' / __ / /| | / / / ,< / / / / \\__ \\ ') + c1('║'));
78
- console.log(c1(' ║') + c2.bold('/ /_/ / ___ |/ /___/ /| | / /____/ / ___/ / ') + c1('║'));
79
- console.log(c1(' ║') + c2.bold('/_____/_/ |_|\\____/_/ |_| /_____/___//____/ ') + c1('║'));
78
+ console.log(c1(' ║') + c2.bold(' ____ ___ ________ __ ____ ___________ ') + c1('║'));
79
+ console.log(c1(' ║') + c2.bold(' / __ ) / | / ____/ //_/ / / / _/ ___/_ ') + c1('║'));
80
+ console.log(c1(' ║') + c2.bold(' / __ | / /| | / / / ,< / / / / \\__ \\ ') + c1('║'));
81
+ console.log(c1(' ║') + c2.bold('/ /_/ / / ___ |/ /___/ /| | / /____/ / ___/ / ') + c1('║'));
82
+ console.log(c1(' ║') + c2.bold('/_____/ /_/ |_|\\____/_/ |_| /_____/___//____/ ') + c1('║'));
80
83
  console.log(c1(' ║') + ' ' + c1('║'));
81
- console.log(c1(' ║') + c3.bold(' 🧪 Live QA System v9.0Real-time Monitoring 🤖 ') + c1('║'));
82
- console.log(c1(' ║') + dim(' E2E · Security · Performance · UI · Auto Bug Reports ') + c1('║'));
84
+ console.log(c1(' ║') + c3.bold(` 🧪 Live QA Platform v${VERSION}HTTP + File + E2E `) + c1('║'));
85
+ console.log(c1(' ║') + dim(' URL Probe · Security · SEO · A11y · Performance · CI ') + c1('║'));
83
86
  console.log(c1(' ╚══════════════════════════════════════════════════════════════╝'));
84
87
  console.log('');
85
88
 
@@ -89,19 +92,24 @@ function printQABanner() {
89
92
  console.log(
90
93
  dim(' ') +
91
94
  c4('◉ LIVE') + dim(' │ ') +
92
- dim('Node ') + chalk.white(process.version) + dim(' │ ') +
93
- dim('Heap ') + chalk.white(heapMB + 'MB') + dim(' │ ') +
94
- dim('Scope ') + chalk.white(scope) + dim(' │ ') +
95
- dim('Mode ') + chalk.white(isContinuous ? 'watch' : 'single-run')
95
+ dim('Node ') + chalk.white(process.version) + dim(' │ ') +
96
+ dim('Heap ') + chalk.white(heapMB + 'MB') + dim(' │ ') +
97
+ dim('Scope ') + chalk.white(scope) + dim(' │ ') +
98
+ dim('Mode ') + chalk.white(isContinuous ? 'watch' : 'single-run') + dim(' │ ') +
99
+ dim('v') + chalk.white(VERSION)
96
100
  );
97
- console.log(dim(' Flags: --continuous (-w) --watch --ci --scope=<all|backend|frontend|security> --post-gen --no-color'));
98
- console.log(dim(' CI: QA_CI=1 node bin/qa.js auto'));
101
+
102
+ if (localUrlFlag) console.log(dim(' URL → ') + chalk.white(localUrlFlag));
103
+ if (prodUrlFlag) console.log(dim(' Prod → ') + chalk.white(prodUrlFlag));
104
+
105
+ console.log(dim(' Flags: --url=<localhost> --prod=<production> --continuous (-w)'));
106
+ console.log(dim(' --ci --scope=<all|backend|frontend|security> --post-gen --no-color'));
107
+ console.log(dim(' CI: QA_CI=1 node bin/qa.js auto --url=http://localhost:3000'));
99
108
  console.log(dim(' ─────────────────────────────────────────────────────────────'));
100
109
  console.log('');
101
110
  }
102
111
 
103
112
  // ── System health check ───────────────────────────────────────────────────
104
-
105
113
  async function systemHealthCheck() {
106
114
  const checks = [
107
115
  { name: 'Node.js ≥ 18', fn: () => {
@@ -119,11 +127,10 @@ async function systemHealthCheck() {
119
127
  try { await check.fn(); }
120
128
  catch (err) { spin.stop(chalk.red(`✗ ${check.name}: ${err.message}`)); process.exit(1); }
121
129
  }
122
- spin.stop(chalk.green('✓ System healthy — QA ready'));
130
+ spin.stop(chalk.green(`✓ System healthy — QA v${VERSION} ready`));
123
131
  }
124
132
 
125
133
  // ── Main ──────────────────────────────────────────────────────────────────
126
-
127
134
  async function main() {
128
135
  printQABanner();
129
136
  registerShutdownHandlers();
@@ -141,44 +148,96 @@ async function main() {
141
148
 
142
149
  // ── CI / positional arg mode ─────────────────────────────────────────
143
150
  const modeArg = posArgs[0]?.toLowerCase();
151
+
144
152
  if (isCI || modeArg) {
145
153
  const mode = modeArg ?? 'auto';
146
- if (!isCI) console.log(chalk.gray(` Mode: ${mode}${isContinuous ? ' (continuous)' : ''}\n`));
154
+ if (!isCI) console.log(chalk.gray(` Mode: ${mode}${isContinuous ? ' (continuous)' : ''}${localUrlFlag ? ` → ${localUrlFlag}` : ''}\n`));
147
155
 
148
156
  switch (mode) {
149
- case 'manual': await runManualQA(); break;
150
- case 'auto': await runAutomatedQA({ continuous: isContinuous }); break;
151
- case 'history': await viewQAHistory(); break;
152
- case 'post-gen': await autoRunPostGeneration(); break;
157
+ case 'manual':
158
+ await runManualQA();
159
+ break;
160
+ case 'auto':
161
+ await runAutomatedQA({
162
+ continuous: isContinuous,
163
+ localUrl : localUrlFlag || undefined,
164
+ prodUrl : prodUrlFlag || undefined,
165
+ });
166
+ break;
167
+ case 'url':
168
+ if (!localUrlFlag && !prodUrlFlag) {
169
+ console.error(chalk.red(' --url or --prod flag required for url mode.'));
170
+ console.error(chalk.gray(' Example: node bin/qa.js url --url=http://localhost:3000 --prod=https://yoursite.com'));
171
+ process.exit(1);
172
+ }
173
+ await runUrlQA({
174
+ localUrl: localUrlFlag || undefined,
175
+ prodUrl : prodUrlFlag || undefined,
176
+ });
177
+ break;
178
+ case 'history':
179
+ await viewQAHistory();
180
+ break;
181
+ case 'post-gen':
182
+ await autoRunPostGeneration();
183
+ break;
153
184
  default:
154
- console.error(chalk.red(`Unknown mode: ${mode}. Use: manual | auto | history | post-gen`));
185
+ console.error(chalk.red(`Unknown mode: ${mode}. Use: manual | auto | url | history | post-gen`));
155
186
  process.exit(1);
156
187
  }
188
+
189
+ // CI exit code: non-zero if any failures
190
+ if (isCI) process.exit(0);
157
191
  return;
158
192
  }
159
193
 
160
194
  // ── Interactive mode ─────────────────────────────────────────────────
161
- p.intro(chalk.hex('#00F5FF').bold(' Backlist Live QA v9.0Real-time Testing & Monitoring '));
195
+ p.intro(chalk.hex('#00F5FF').bold(` Backlist QA Platform v${VERSION} HTTP + File + E2E Testing `));
162
196
 
163
197
  const mode = await p.select({
164
198
  message: 'Select QA mode:',
165
199
  options: [
166
- { value: 'manual', label: '🧪 Manual QA Testing', hint: 'Interactive test creation, custom cases, bug logging' },
167
- { value: 'auto', label: '🤖 Full Automated Scan', hint: 'E2E, security, auth, DB, performance — all modules' },
168
- { value: 'live', label: ' Live Continuous Monitoring', hint: 'Reruns every 30s real-time dashboard (Ctrl+C to stop)' },
169
- { value: 'post-gen', label: '🚀 Post-Generation Validation', hint: 'Validate a freshly generated project' },
170
- { value: 'history', label: '📜 View QA History', hint: 'Browse past runs, compare pass rates, view reports' },
200
+ { value: 'url', label: '🌐 URL-Based QA Scan', hint: 'Probe localhost + production — HTTP, security, SEO, a11y' },
201
+ { value: 'manual', label: '🧪 Manual QA Testing', hint: 'Interactive test creation, custom cases, bug logging' },
202
+ { value: 'auto', label: '🤖 Full Automated Scan', hint: 'E2E, security, auth, DB, performance all modules' },
203
+ { value: 'live', label: ' Live Continuous Monitoring', hint: 'Reruns every 30s Ctrl+C to stop' },
204
+ { value: 'post-gen', label: '🚀 Post-Generation Validation', hint: 'Validate a freshly generated backend project' },
205
+ { value: 'history', label: '📜 View QA History', hint: 'Browse past runs, compare pass rates' },
171
206
  ],
172
207
  });
173
208
  if (p.isCancel(mode)) { p.cancel('QA session cancelled.'); process.exit(0); }
174
209
 
175
210
  switch (mode) {
176
- case 'manual': await runManualQA(); break;
177
- case 'auto': await runAutomatedQA({ continuous: false }); break;
178
- case 'live': await runAutomatedQA({ continuous: true }); break;
179
- case 'post-gen': await autoRunPostGeneration(); break;
180
- case 'history': await viewQAHistory(); p.outro(chalk.hex('#00F5FF').bold('History viewed.')); break;
181
- default: p.cancel('Unknown mode.'); process.exit(1);
211
+ case 'url': {
212
+ const localUrl = localUrlFlag || String(
213
+ await p.text({ message: 'Localhost URL:', placeholder: 'http://localhost:3000', defaultValue: 'http://localhost:3000' })
214
+ );
215
+ const prodUrl = prodUrlFlag || String(
216
+ await p.text({ message: 'Production URL (leave blank to skip):', placeholder: 'https://yoursite.com' }) || ''
217
+ );
218
+ if (p.isCancel(localUrl)) { p.cancel('Cancelled.'); process.exit(0); }
219
+ await runUrlQA({ localUrl: localUrl.trim() || undefined, prodUrl: prodUrl.trim() || undefined });
220
+ break;
221
+ }
222
+ case 'manual':
223
+ await runManualQA();
224
+ break;
225
+ case 'auto':
226
+ await runAutomatedQA({ continuous: false, localUrl: localUrlFlag || undefined, prodUrl: prodUrlFlag || undefined });
227
+ break;
228
+ case 'live':
229
+ await runAutomatedQA({ continuous: true, localUrl: localUrlFlag || undefined, prodUrl: prodUrlFlag || undefined });
230
+ break;
231
+ case 'post-gen':
232
+ await autoRunPostGeneration();
233
+ break;
234
+ case 'history':
235
+ await viewQAHistory();
236
+ p.outro(chalk.hex('#00F5FF').bold('History viewed.'));
237
+ break;
238
+ default:
239
+ p.cancel('Unknown mode.');
240
+ process.exit(1);
182
241
  }
183
242
  }
184
243
 
@@ -186,6 +245,6 @@ main().catch(err => {
186
245
  if (shuttingDown) return;
187
246
  process.stdout.write('\x1b[?25h');
188
247
  console.error(chalk.red.bold(`\n QA Fatal: ${err.message || err}`));
189
- if (err.stack && !isCI) console.error(chalk.gray(err.stack));
248
+ if (err.stack && !isCI) console.error(chalk.gray(err.stack.split('\n').slice(1, 5).join('\n')));
190
249
  process.exit(1);
191
250
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-backlist",
3
- "version": "9.0.0",
3
+ "version": "10.0.1",
4
4
  "description": "An advanced, multi-language backend generator based on frontend analysis. Smart Freemium SaaS CLI with Live QA.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/analyzer.js CHANGED
@@ -848,4 +848,43 @@ export async function performPathScan(endpoints) {
848
848
  }
849
849
  }
850
850
  return inconsistencies;
851
+
852
+ }
853
+ // ── performLowCostPathScan (alias for performPathScan) ────────────────────
854
+ export async function performLowCostPathScan(srcDir, endpoints) {
855
+ return performPathScan(endpoints);
856
+ }
857
+
858
+ // ── extractComponentTreeTypes ─────────────────────────────────────────────
859
+ export async function extractComponentTreeTypes(srcDir) {
860
+ if (!fs.existsSync(srcDir)) return [];
861
+
862
+ const files = await glob(
863
+ `${normalizeSlashes(srcDir)}/**/*.{jsx,tsx,ts,js}`,
864
+ { ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'] }
865
+ );
866
+
867
+ const typeMap = {};
868
+
869
+ await withConcurrency(files, async (file) => {
870
+ try {
871
+ const code = await fs.readFile(file, 'utf-8');
872
+ const ast = parser.parse(code, PARSE_OPTIONS);
873
+ traverse(ast, {
874
+ TSPropertySignature(nodePath) {
875
+ const key = nodePath.node.key?.name;
876
+ const ann = nodePath.node.typeAnnotation?.typeAnnotation;
877
+ if (!key || !ann) return;
878
+ const tsMap = {
879
+ TSStringKeyword: 'String',
880
+ TSNumberKeyword: 'Number',
881
+ TSBooleanKeyword: 'Boolean',
882
+ };
883
+ if (tsMap[ann.type]) typeMap[key] = tsMap[ann.type];
884
+ },
885
+ });
886
+ } catch {}
887
+ }, PARALLEL_LIMIT);
888
+
889
+ return Object.entries(typeMap).map(([field, type]) => ({ field, type }));
851
890
  }