@specverse/engines 4.1.25 → 4.1.27

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.
@@ -54,9 +54,9 @@ function generateBackendPackageJson(context) {
54
54
  "prisma": "^5.7.0",
55
55
  "vitest": "^3.0.0",
56
56
  "@vitest/coverage-v8": "^3.0.0",
57
- "eslint": "^8.55.0",
58
- "@typescript-eslint/eslint-plugin": "^6.15.0",
59
- "@typescript-eslint/parser": "^6.15.0"
57
+ "eslint": "^9.0.0",
58
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
59
+ "@typescript-eslint/parser": "^8.0.0"
60
60
  },
61
61
  engines: {
62
62
  node: ">=20.0.0"
@@ -99,6 +99,9 @@ ${serviceMap}
99
99
  credentials: true
100
100
  });
101
101
 
102
+ // Health check \u2014 used by smoke tests, container probes, monitors
103
+ app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
104
+
102
105
  // Spec endpoint \u2014 serves the specification for the runtime frontend
103
106
  app.get('/api/spec', async () => spec);
104
107
 
@@ -35,10 +35,10 @@ function generatePackageJson(context) {
35
35
  "tailwindcss": "^3.4.13",
36
36
  "typescript": "^5.2.0",
37
37
  "vite": "^6.0.0",
38
- "eslint": "^8.53.0",
39
- "@typescript-eslint/eslint-plugin": "^6.10.0",
40
- "@typescript-eslint/parser": "^6.10.0",
41
- "eslint-plugin-react-hooks": "^4.6.0",
38
+ "eslint": "^9.0.0",
39
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
40
+ "@typescript-eslint/parser": "^8.0.0",
41
+ "eslint-plugin-react-hooks": "^5.0.0",
42
42
  "eslint-plugin-react-refresh": "^0.4.4"
43
43
  }
44
44
  };
@@ -37,10 +37,10 @@ function generateRuntimePackageJson(context) {
37
37
  "tailwindcss": "^3.4.13",
38
38
  "typescript": "^5.2.0",
39
39
  "vite": "^6.0.0",
40
- "eslint": "^8.53.0",
41
- "@typescript-eslint/eslint-plugin": "^6.10.0",
42
- "@typescript-eslint/parser": "^6.10.0",
43
- "eslint-plugin-react-hooks": "^4.6.0",
40
+ "eslint": "^9.0.0",
41
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
42
+ "@typescript-eslint/parser": "^8.0.0",
43
+ "eslint-plugin-react-hooks": "^5.0.0",
44
44
  "eslint-plugin-react-refresh": "^0.4.4"
45
45
  }
46
46
  };
@@ -424,7 +424,10 @@ import { fileURLToPath } from 'url';`,
424
424
  }
425
425
 
426
426
  const projectName = name || 'my-project';
427
- const templateName = options.template || 'default';
427
+ // Commander applies the --template default ("full-stack"); the
428
+ // fallback here only fires if a caller invoked the action handler
429
+ // directly without going through commander.
430
+ const templateName = options.template || 'full-stack';
428
431
  const templateDir = join(templatesDir, templateName);
429
432
 
430
433
  if (!existsSync(templateDir)) {
@@ -443,7 +446,16 @@ import { fileURLToPath } from 'url';`,
443
446
 
444
447
  // Copy template with variable substitution
445
448
  const kebab = projectName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
446
- const component = projectName.charAt(0).toUpperCase() + projectName.slice(1);
449
+ // PascalCase: split on hyphens / underscores / whitespace, capitalize each segment, join.
450
+ // 'audit-backend-only' -> 'AuditBackendOnly', 'my_app' -> 'MyApp', 'foo' -> 'Foo'.
451
+ // NOTE: regex char class is double-escaped because this whole handler
452
+ // is embedded as a string in the generator template \u2014 \\s here ends
453
+ // up as s in the emitted source.
454
+ const component = projectName
455
+ .split(/[-_\\s]+/)
456
+ .filter(Boolean)
457
+ .map((s: string) => s.charAt(0).toUpperCase() + s.slice(1))
458
+ .join('');
447
459
  const vars: Record<string, string> = {
448
460
  '{{PROJECT_NAME}}': projectName,
449
461
  '{{projectName}}': projectName,
@@ -478,11 +490,29 @@ import { fileURLToPath } from 'url';`,
478
490
  console.log('Project created: ' + destDir);
479
491
  console.log('Template: ' + templateName);
480
492
  console.log('');
493
+
494
+ // Build the "Next steps" hint from the actual scripts the
495
+ // chosen template ships, so we never tell the user to run a
496
+ // command that doesn't exist in their package.json.
497
+ const destPkgPath = join(destDir, 'package.json');
498
+ const destPkg = existsSync(destPkgPath)
499
+ ? JSON.parse(readFileSync(destPkgPath, 'utf8'))
500
+ : { scripts: {} };
501
+ const scripts = destPkg.scripts || {};
481
502
  console.log('Next steps:');
482
503
  console.log(' cd ' + projectName);
483
- console.log(' npm run setup # validate, generate code, install deps, setup db');
484
- console.log(' npm run dev:backend # start backend (terminal 1)');
485
- console.log(' npm run dev:frontend # start frontend (terminal 2)');`
504
+ if (scripts.setup) {
505
+ console.log(' npm run setup # validate, generate code, install deps, setup db');
506
+ }
507
+ if (scripts['dev:backend'] && scripts['dev:frontend']) {
508
+ console.log(' npm run dev:backend # start backend (terminal 1)');
509
+ console.log(' npm run dev:frontend # start frontend (terminal 2)');
510
+ } else if (scripts.dev) {
511
+ console.log(' npm run dev # start dev server');
512
+ }
513
+ if (scripts['test:e2e']) {
514
+ console.log(' npm run test:e2e # run end-to-end tests (after starting dev servers)');
515
+ }`
486
516
  },
487
517
  // === gen subcommands ===
488
518
  "gen.diagrams": {
@@ -73,9 +73,9 @@ export default function generateBackendPackageJson(context: TemplateContext): st
73
73
  'prisma': '^5.7.0',
74
74
  'vitest': '^3.0.0',
75
75
  '@vitest/coverage-v8': '^3.0.0',
76
- 'eslint': '^8.55.0',
77
- '@typescript-eslint/eslint-plugin': '^6.15.0',
78
- '@typescript-eslint/parser': '^6.15.0'
76
+ 'eslint': '^9.0.0',
77
+ '@typescript-eslint/eslint-plugin': '^8.0.0',
78
+ '@typescript-eslint/parser': '^8.0.0'
79
79
  },
80
80
 
81
81
  engines: {
@@ -132,6 +132,9 @@ ${serviceMap}
132
132
  credentials: true
133
133
  });
134
134
 
135
+ // Health check — used by smoke tests, container probes, monitors
136
+ app.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
137
+
135
138
  // Spec endpoint — serves the specification for the runtime frontend
136
139
  app.get('/api/spec', async () => spec);
137
140
 
@@ -45,10 +45,10 @@ export default function generatePackageJson(context: TemplateContext): string {
45
45
  "tailwindcss": "^3.4.13",
46
46
  "typescript": "^5.2.0",
47
47
  "vite": "^6.0.0",
48
- "eslint": "^8.53.0",
49
- "@typescript-eslint/eslint-plugin": "^6.10.0",
50
- "@typescript-eslint/parser": "^6.10.0",
51
- "eslint-plugin-react-hooks": "^4.6.0",
48
+ "eslint": "^9.0.0",
49
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
50
+ "@typescript-eslint/parser": "^8.0.0",
51
+ "eslint-plugin-react-hooks": "^5.0.0",
52
52
  "eslint-plugin-react-refresh": "^0.4.4"
53
53
  }
54
54
  };
@@ -54,10 +54,10 @@ export default function generateRuntimePackageJson(context: TemplateContext): st
54
54
  "tailwindcss": "^3.4.13",
55
55
  "typescript": "^5.2.0",
56
56
  "vite": "^6.0.0",
57
- "eslint": "^8.53.0",
58
- "@typescript-eslint/eslint-plugin": "^6.10.0",
59
- "@typescript-eslint/parser": "^6.10.0",
60
- "eslint-plugin-react-hooks": "^4.6.0",
57
+ "eslint": "^9.0.0",
58
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
59
+ "@typescript-eslint/parser": "^8.0.0",
60
+ "eslint-plugin-react-hooks": "^5.0.0",
61
61
  "eslint-plugin-react-refresh": "^0.4.4"
62
62
  }
63
63
  };
@@ -479,7 +479,10 @@ import { fileURLToPath } from 'url';`,
479
479
  }
480
480
 
481
481
  const projectName = name || 'my-project';
482
- const templateName = options.template || 'default';
482
+ // Commander applies the --template default ("full-stack"); the
483
+ // fallback here only fires if a caller invoked the action handler
484
+ // directly without going through commander.
485
+ const templateName = options.template || 'full-stack';
483
486
  const templateDir = join(templatesDir, templateName);
484
487
 
485
488
  if (!existsSync(templateDir)) {
@@ -498,7 +501,16 @@ import { fileURLToPath } from 'url';`,
498
501
 
499
502
  // Copy template with variable substitution
500
503
  const kebab = projectName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
501
- const component = projectName.charAt(0).toUpperCase() + projectName.slice(1);
504
+ // PascalCase: split on hyphens / underscores / whitespace, capitalize each segment, join.
505
+ // 'audit-backend-only' -> 'AuditBackendOnly', 'my_app' -> 'MyApp', 'foo' -> 'Foo'.
506
+ // NOTE: regex char class is double-escaped because this whole handler
507
+ // is embedded as a string in the generator template — \\s here ends
508
+ // up as \s in the emitted source.
509
+ const component = projectName
510
+ .split(/[-_\\s]+/)
511
+ .filter(Boolean)
512
+ .map((s: string) => s.charAt(0).toUpperCase() + s.slice(1))
513
+ .join('');
502
514
  const vars: Record<string, string> = {
503
515
  '{{PROJECT_NAME}}': projectName,
504
516
  '{{projectName}}': projectName,
@@ -533,11 +545,29 @@ import { fileURLToPath } from 'url';`,
533
545
  console.log('Project created: ' + destDir);
534
546
  console.log('Template: ' + templateName);
535
547
  console.log('');
548
+
549
+ // Build the "Next steps" hint from the actual scripts the
550
+ // chosen template ships, so we never tell the user to run a
551
+ // command that doesn't exist in their package.json.
552
+ const destPkgPath = join(destDir, 'package.json');
553
+ const destPkg = existsSync(destPkgPath)
554
+ ? JSON.parse(readFileSync(destPkgPath, 'utf8'))
555
+ : { scripts: {} };
556
+ const scripts = destPkg.scripts || {};
536
557
  console.log('Next steps:');
537
558
  console.log(' cd ' + projectName);
538
- console.log(' npm run setup # validate, generate code, install deps, setup db');
539
- console.log(' npm run dev:backend # start backend (terminal 1)');
540
- console.log(' npm run dev:frontend # start frontend (terminal 2)');`
559
+ if (scripts.setup) {
560
+ console.log(' npm run setup # validate, generate code, install deps, setup db');
561
+ }
562
+ if (scripts['dev:backend'] && scripts['dev:frontend']) {
563
+ console.log(' npm run dev:backend # start backend (terminal 1)');
564
+ console.log(' npm run dev:frontend # start frontend (terminal 2)');
565
+ } else if (scripts.dev) {
566
+ console.log(' npm run dev # start dev server');
567
+ }
568
+ if (scripts['test:e2e']) {
569
+ console.log(' npm run test:e2e # run end-to-end tests (after starting dev servers)');
570
+ }`
541
571
  },
542
572
  // === gen subcommands ===
543
573
  'gen.diagrams': {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@specverse/engines",
3
- "version": "4.1.25",
4
- "description": "SpecVerse toolchain parser, inference, realize, generators, AI, registry",
3
+ "version": "4.1.27",
4
+ "description": "SpecVerse toolchain \u2014 parser, inference, realize, generators, AI, registry",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -65,4 +65,4 @@
65
65
  "access": "public"
66
66
  },
67
67
  "license": "MIT"
68
- }
68
+ }