@coherent.js/cli 1.0.0-beta.3 → 1.0.0-beta.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/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // src/index.js
2
2
  import { Command as Command6 } from "commander";
3
- import { readFileSync as readFileSync7 } from "fs";
3
+ import { readFileSync as readFileSync8 } from "fs";
4
4
  import { fileURLToPath as fileURLToPath4 } from "url";
5
5
  import { dirname as dirname3, join as join8 } from "path";
6
6
  import picocolors6 from "picocolors";
@@ -12,9 +12,10 @@ import ora from "ora";
12
12
  import picocolors from "picocolors";
13
13
  import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
14
14
  import { resolve as resolve2 } from "path";
15
+ import { spawn } from "child_process";
15
16
 
16
17
  // src/generators/project-scaffold.js
17
- import { writeFileSync, mkdirSync } from "node:fs";
18
+ import { writeFileSync, mkdirSync, copyFileSync, constants, readFileSync as readFileSync2, appendFileSync } from "node:fs";
18
19
  import { join as join2, dirname as dirname2 } from "node:path";
19
20
  import { execSync } from "node:child_process";
20
21
 
@@ -22,6 +23,7 @@ import { execSync } from "node:child_process";
22
23
  import { readFileSync } from "fs";
23
24
  import { join, dirname } from "path";
24
25
  import { fileURLToPath } from "url";
26
+ import { env } from "node:process";
25
27
  var cachedVersion = null;
26
28
  function getCLIVersion() {
27
29
  if (cachedVersion) {
@@ -35,7 +37,7 @@ function getCLIVersion() {
35
37
  cachedVersion = packageJson.version;
36
38
  return cachedVersion;
37
39
  } catch {
38
- cachedVersion = process.env.COHERENT_CLI_VERSION || "1.0.0-beta.3";
40
+ cachedVersion = env.COHERENT_CLI_VERSION || "1.0.0-beta.5";
39
41
  return cachedVersion;
40
42
  }
41
43
  }
@@ -43,13 +45,16 @@ function getCLIVersion() {
43
45
  // src/generators/runtime-scaffold.js
44
46
  var cliVersion = getCLIVersion();
45
47
  function generateBuiltInServer(options = {}) {
46
- const { port = 3e3, hasApi = false, hasDatabase = false } = options;
48
+ const { port = 3e3, hasApi = false, hasDatabase = false, hasAuth = false } = options;
47
49
  const imports = [
48
- `import http from 'http';`,
50
+ `import http from 'node:http';`,
51
+ `import fs from 'node:fs';`,
52
+ `import path from 'node:path';`,
49
53
  `import { render } from '@coherent.js/core';`
50
54
  ];
51
55
  if (hasApi) imports.push(`import { setupRoutes } from './api/routes.js';`);
52
56
  if (hasDatabase) imports.push(`import { initDatabase } from './db/index.js';`);
57
+ if (hasAuth) imports.push(`import { setupAuthRoutes } from './api/auth.js';`);
53
58
  const server = `
54
59
  ${imports.join("\n")}
55
60
  import { HomePage } from './components/HomePage.js';
@@ -60,23 +65,60 @@ ${hasDatabase ? `// Initialize database
60
65
  await initDatabase();
61
66
  ` : ""}${hasApi ? `// Setup API routes
62
67
  const apiRoutes = setupRoutes();
68
+ ` : ""}${hasAuth ? `// Setup auth routes
69
+ const authRoutes = setupAuthRoutes();
63
70
  ` : ""}
64
71
  const server = http.createServer(async (req, res) => {
65
72
  const url = new URL(req.url, \`http://\${req.headers.host}\`);
66
73
 
67
- ${hasApi ? ` // Handle API routes
74
+ ${hasApi || hasAuth ? ` // Handle API routes
68
75
  if (url.pathname.startsWith('/api')) {
69
- const route = apiRoutes.find(r => r.path === url.pathname && r.method === req.method);
70
- if (route) {
71
- return route.handler(req, res);
76
+ const allRoutes = [...${hasApi ? "apiRoutes" : "[]"}, ...${hasAuth ? "authRoutes" : "[]"}];
77
+ for (const route of allRoutes) {
78
+ const match = matchRoute(route.path, url.pathname, req.method, route.method);
79
+ if (match) {
80
+ req.params = match.params;
81
+ return route.handler(req, res);
82
+ }
83
+ }
84
+ }
85
+
86
+ ` : ""} // Serve components for hydration
87
+ if (url.pathname.startsWith('/components/')) {
88
+ const filePath = path.join(process.cwd(), 'src', url.pathname);
89
+ try {
90
+ const content = await fs.promises.readFile(filePath);
91
+ res.writeHead(200, { 'Content-Type': 'text/javascript' });
92
+ return res.end(content);
93
+ } catch (err) {
94
+ res.writeHead(404);
95
+ return res.end('Not Found');
72
96
  }
73
97
  }
74
98
 
75
- ` : ""} // Serve static files
99
+ // Serve static files
76
100
  if (url.pathname.startsWith('/public')) {
77
- // Add your static file serving logic here
78
- res.writeHead(404);
79
- return res.end('Not Found');
101
+ const filePath = path.join(process.cwd(), url.pathname);
102
+ try {
103
+ const content = await fs.promises.readFile(filePath);
104
+ const ext = path.extname(filePath).toLowerCase();
105
+ const contentTypes = {
106
+ '.html': 'text/html',
107
+ '.js': 'text/javascript',
108
+ '.css': 'text/css',
109
+ '.json': 'application/json',
110
+ '.png': 'image/png',
111
+ '.jpg': 'image/jpeg',
112
+ '.gif': 'image/gif',
113
+ '.svg': 'image/svg+xml',
114
+ '.ico': 'image/x-icon'
115
+ };
116
+ res.writeHead(200, { 'Content-Type': contentTypes[ext] || 'application/octet-stream' });
117
+ return res.end(content);
118
+ } catch (err) {
119
+ res.writeHead(404);
120
+ return res.end('Not Found');
121
+ }
80
122
  }
81
123
 
82
124
  // Render page
@@ -101,6 +143,42 @@ ${hasApi ? ` // Handle API routes
101
143
  }
102
144
  });
103
145
 
146
+ // Route matching helper
147
+ function matchRoute(routePattern, urlPath, requestMethod, routeMethod) {
148
+ // Check HTTP method
149
+ if (requestMethod !== routeMethod) {
150
+ return null;
151
+ }
152
+
153
+ // Split paths into segments
154
+ const routeSegments = routePattern.split('/').filter(Boolean);
155
+ const urlSegments = urlPath.split('/').filter(Boolean);
156
+
157
+ // Check if lengths match
158
+ if (routeSegments.length !== urlSegments.length) {
159
+ return null;
160
+ }
161
+
162
+ const params = {};
163
+
164
+ // Match each segment
165
+ for (let i = 0; i < routeSegments.length; i++) {
166
+ const routeSegment = routeSegments[i];
167
+ const urlSegment = urlSegments[i];
168
+
169
+ // Check for parameter (e.g., :id)
170
+ if (routeSegment.startsWith(':')) {
171
+ const paramName = routeSegment.substring(1);
172
+ params[paramName] = urlSegment;
173
+ } else if (routeSegment !== urlSegment) {
174
+ // Literal segment doesn't match
175
+ return null;
176
+ }
177
+ }
178
+
179
+ return { params };
180
+ }
181
+
104
182
  server.listen(PORT, () => {
105
183
  console.log(\`Server running at http://localhost:\${PORT}\`);
106
184
  });
@@ -111,7 +189,7 @@ function generateExpressServer(options = {}) {
111
189
  const { port = 3e3, hasApi = false, hasDatabase = false, hasAuth = false } = options;
112
190
  const imports = [
113
191
  `import express from 'express';`,
114
- `import { setupCoherent } from '@coherent.js/express';`
192
+ `import { render } from '@coherent.js/core';`
115
193
  ];
116
194
  if (hasApi) imports.push(`import apiRoutes from './api/routes.js';`);
117
195
  if (hasDatabase) imports.push(`import { initDatabase } from './db/index.js';`);
@@ -132,15 +210,25 @@ ${hasDatabase ? `// Initialize database
132
210
  await initDatabase();
133
211
  ` : ""}${hasAuth ? `// Setup authentication
134
212
  app.use(authMiddleware);
135
- ` : ""}// Setup Coherent.js
136
- setupCoherent(app);
137
-
138
- ${hasApi ? `// API routes
139
- app.use('/api', apiRoutes);
140
213
  ` : ""}
141
- // Main route
214
+ ${hasApi ? `// API routes - convert Coherent.js router to Express middleware
215
+ app.use('/api', apiRoutes.toExpressRouter(express));
216
+ ` : ""}
217
+ // Main route - render Coherent.js component to HTML
142
218
  app.get('/', (req, res) => {
143
- res.render(HomePage({}));
219
+ const content = render(HomePage({}));
220
+ const html = \`<!DOCTYPE html>
221
+ <html lang="en">
222
+ <head>
223
+ <meta charset="UTF-8">
224
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
225
+ <title>Coherent.js App</title>
226
+ </head>
227
+ <body>
228
+ \${content}
229
+ </body>
230
+ </html>\`;
231
+ res.type('html').send(html);
144
232
  });
145
233
 
146
234
  // Error handling
@@ -189,9 +277,9 @@ await fastify.register(import('@fastify/static'), {
189
277
  ${hasApi ? `// API routes
190
278
  await fastify.register(apiRoutes, { prefix: '/api' });
191
279
  ` : ""}
192
- // Main route
280
+ // Main route - return Coherent.js component (auto-rendered by plugin)
193
281
  fastify.get('/', async (request, reply) => {
194
- return reply.render(HomePage({}));
282
+ return HomePage({});
195
283
  });
196
284
 
197
285
  // Start server
@@ -239,9 +327,9 @@ setupCoherent(app);
239
327
  ${hasApi ? `// API routes
240
328
  apiRoutes(router);
241
329
  ` : ""}
242
- // Main route
330
+ // Main route - set body to Coherent.js component (auto-rendered by middleware)
243
331
  router.get('/', async (ctx) => {
244
- ctx.render(HomePage({}));
332
+ ctx.body = HomePage({});
245
333
  });
246
334
 
247
335
  app.use(router.routes());
@@ -307,9 +395,12 @@ export const dbConfig = {
307
395
  user: process.env.DB_USER || 'postgres',
308
396
  password: process.env.DB_PASSWORD || 'postgres',
309
397
  // Connection pool settings
310
- max: 20,
311
- idleTimeoutMillis: 30000,
312
- connectionTimeoutMillis: 2000,
398
+ pool: {
399
+ min: 2,
400
+ max: 20,
401
+ idleTimeoutMillis: 30000,
402
+ connectionTimeoutMillis: 2000,
403
+ }
313
404
  };
314
405
  `,
315
406
  mysql: `
@@ -320,9 +411,12 @@ export const dbConfig = {
320
411
  user: process.env.DB_USER || 'root',
321
412
  password: process.env.DB_PASSWORD || 'password',
322
413
  // Connection pool settings
323
- connectionLimit: 10,
324
- waitForConnections: true,
325
- queueLimit: 0
414
+ pool: {
415
+ max: 10,
416
+ min: 0,
417
+ waitForConnections: true,
418
+ queueLimit: 0
419
+ }
326
420
  };
327
421
  `,
328
422
  sqlite: `
@@ -346,22 +440,33 @@ export const dbConfig = {
346
440
  };
347
441
  return configs[dbType] || "";
348
442
  }
349
- function generateDatabaseInit(dbType) {
443
+ function generateDatabaseInit(dbType, language = "javascript") {
444
+ const isTypeScript = language === "typescript";
445
+ const typeAnnotation = isTypeScript ? ": any" : "";
446
+ const returnType = isTypeScript ? ": Promise<any>" : "";
350
447
  const inits = {
351
448
  postgres: `
352
449
  import { setupDatabase } from '@coherent.js/database';
353
450
  import { dbConfig } from './config.js';
354
451
 
355
- let db;
452
+ let db${typeAnnotation};
356
453
 
357
- export async function initDatabase() {
454
+ export async function initDatabase()${returnType} {
358
455
  try {
359
456
  // Setup database with Coherent.js
360
457
  db = setupDatabase({
361
- type: 'postgres',
362
- ...dbConfig
458
+ type: 'postgresql',
459
+ host: dbConfig.host,
460
+ port: dbConfig.port,
461
+ database: dbConfig.database,
462
+ username: dbConfig.user,
463
+ password: dbConfig.password,
464
+ pool: dbConfig.pool
363
465
  });
364
466
 
467
+ // Wait for connection
468
+ await db.connect();
469
+
365
470
  console.log('\u2713 Connected to PostgreSQL database');
366
471
 
367
472
  return db;
@@ -371,7 +476,7 @@ export async function initDatabase() {
371
476
  }
372
477
  }
373
478
 
374
- export function getDatabase() {
479
+ export function getDatabase()${typeAnnotation} {
375
480
  if (!db) {
376
481
  throw new Error('Database not initialized. Call initDatabase() first.');
377
482
  }
@@ -391,16 +496,24 @@ process.on('SIGINT', async () => {
391
496
  import { setupDatabase } from '@coherent.js/database';
392
497
  import { dbConfig } from './config.js';
393
498
 
394
- let db;
499
+ let db${typeAnnotation};
395
500
 
396
- export async function initDatabase() {
501
+ export async function initDatabase()${returnType} {
397
502
  try {
398
503
  // Setup database with Coherent.js
399
504
  db = setupDatabase({
400
505
  type: 'mysql',
401
- ...dbConfig
506
+ host: dbConfig.host,
507
+ port: dbConfig.port,
508
+ database: dbConfig.database,
509
+ username: dbConfig.user,
510
+ password: dbConfig.password,
511
+ pool: dbConfig.pool
402
512
  });
403
513
 
514
+ // Wait for connection
515
+ await db.connect();
516
+
404
517
  console.log('\u2713 Connected to MySQL database');
405
518
 
406
519
  return db;
@@ -410,7 +523,7 @@ export async function initDatabase() {
410
523
  }
411
524
  }
412
525
 
413
- export function getDatabase() {
526
+ export function getDatabase()${typeAnnotation} {
414
527
  if (!db) {
415
528
  throw new Error('Database not initialized. Call initDatabase() first.');
416
529
  }
@@ -432,21 +545,24 @@ import { dbConfig } from './config.js';
432
545
  import { mkdirSync } from 'fs';
433
546
  import { dirname } from 'path';
434
547
 
435
- let db;
548
+ let db${typeAnnotation};
436
549
 
437
- export async function initDatabase() {
550
+ export async function initDatabase()${returnType} {
438
551
  try {
439
- // Ensure directory exists
440
- if (dbConfig.filename) {
552
+ // Ensure directory exists for SQLite file
553
+ if (dbConfig.filename && dbConfig.filename !== ':memory:') {
441
554
  mkdirSync(dirname(dbConfig.filename), { recursive: true });
442
555
  }
443
556
 
444
557
  // Setup database with Coherent.js
445
558
  db = setupDatabase({
446
559
  type: 'sqlite',
447
- ...dbConfig
560
+ database: dbConfig.filename || ':memory:'
448
561
  });
449
562
 
563
+ // Wait for connection
564
+ await db.connect();
565
+
450
566
  console.log('\u2713 Connected to SQLite database');
451
567
 
452
568
  return db;
@@ -456,7 +572,7 @@ export async function initDatabase() {
456
572
  }
457
573
  }
458
574
 
459
- export function getDatabase() {
575
+ export function getDatabase()${typeAnnotation} {
460
576
  if (!db) {
461
577
  throw new Error('Database not initialized. Call initDatabase() first.');
462
578
  }
@@ -464,9 +580,9 @@ export function getDatabase() {
464
580
  }
465
581
 
466
582
  // Graceful shutdown
467
- process.on('SIGINT', () => {
583
+ process.on('SIGINT', async () => {
468
584
  if (db) {
469
- db.close();
585
+ await db.disconnect();
470
586
  console.log('Database closed');
471
587
  }
472
588
  process.exit(0);
@@ -476,16 +592,20 @@ process.on('SIGINT', () => {
476
592
  import { setupDatabase } from '@coherent.js/database';
477
593
  import { dbConfig } from './config.js';
478
594
 
479
- let db;
595
+ let db${typeAnnotation};
480
596
 
481
- export async function initDatabase() {
597
+ export async function initDatabase()${returnType} {
482
598
  try {
483
599
  // Setup database with Coherent.js
484
600
  db = setupDatabase({
485
601
  type: 'mongodb',
486
- ...dbConfig
602
+ database: dbConfig.uri,
603
+ ...dbConfig.options
487
604
  });
488
605
 
606
+ // Wait for connection
607
+ await db.connect();
608
+
489
609
  console.log('\u2713 Connected to MongoDB database');
490
610
 
491
611
  return db;
@@ -495,7 +615,7 @@ export async function initDatabase() {
495
615
  }
496
616
  }
497
617
 
498
- export function getDatabase() {
618
+ export function getDatabase()${typeAnnotation} {
499
619
  if (!db) {
500
620
  throw new Error('Database not initialized. Call initDatabase() first.');
501
621
  }
@@ -514,47 +634,57 @@ process.on('SIGINT', async () => {
514
634
  };
515
635
  return inits[dbType] || "";
516
636
  }
517
- function generateExampleModel(dbType) {
637
+ function generateExampleModel(dbType, language = "javascript") {
638
+ const isTypeScript = language === "typescript";
639
+ const typeAnnotation = isTypeScript ? ": Promise<any>" : "";
640
+ const interfaceDef = isTypeScript ? `
641
+ interface UserData {
642
+ email: string;
643
+ name: string;
644
+ }` : "";
518
645
  const models = {
519
646
  postgres: `
520
- import { getDatabase } from './index.js';
647
+ import { getDatabase } from '../index.js';
648
+
649
+ ${interfaceDef}
521
650
 
522
651
  export class UserModel {
523
- static async createTable() {
652
+ static async createTable()${typeAnnotation} {
524
653
  const db = getDatabase();
525
654
  await db.query(\`
526
655
  CREATE TABLE IF NOT EXISTS users (
527
656
  id SERIAL PRIMARY KEY,
528
657
  email VARCHAR(255) UNIQUE NOT NULL,
529
658
  name VARCHAR(255) NOT NULL,
659
+ password_hash VARCHAR(255),
530
660
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
531
661
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
532
662
  )
533
663
  \`);
534
664
  }
535
665
 
536
- static async create(data) {
666
+ static async create(userData)${typeAnnotation} {
537
667
  const db = getDatabase();
538
668
  const result = await db.query(
539
669
  'INSERT INTO users (email, name) VALUES ($1, $2) RETURNING *',
540
- [data.email, data.name]
670
+ [userData.email, userData.name]
541
671
  );
542
672
  return result.rows[0];
543
673
  }
544
674
 
545
- static async findById(id) {
675
+ static async findById(id)${typeAnnotation} {
546
676
  const db = getDatabase();
547
677
  const result = await db.query('SELECT * FROM users WHERE id = $1', [id]);
548
678
  return result.rows[0];
549
679
  }
550
680
 
551
- static async findByEmail(email) {
681
+ static async findByEmail(email)${typeAnnotation} {
552
682
  const db = getDatabase();
553
683
  const result = await db.query('SELECT * FROM users WHERE email = $1', [email]);
554
684
  return result.rows[0];
555
685
  }
556
686
 
557
- static async update(id, data) {
687
+ static async update(id, data)${typeAnnotation} {
558
688
  const db = getDatabase();
559
689
  const result = await db.query(
560
690
  'UPDATE users SET email = $1, name = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $3 RETURNING *',
@@ -563,70 +693,75 @@ export class UserModel {
563
693
  return result.rows[0];
564
694
  }
565
695
 
566
- static async delete(id) {
696
+ static async delete(id)${typeAnnotation} {
567
697
  const db = getDatabase();
568
698
  await db.query('DELETE FROM users WHERE id = $1', [id]);
569
699
  }
570
700
  }
571
701
  `,
572
702
  mysql: `
573
- import { getDatabase } from './index.js';
703
+ import { getDatabase } from '../index.js';
704
+
705
+ ${interfaceDef}
574
706
 
575
707
  export class UserModel {
576
- static async createTable() {
708
+ static async createTable()${typeAnnotation} {
577
709
  const db = getDatabase();
578
- await db.execute(\`
710
+ await db.query(\`
579
711
  CREATE TABLE IF NOT EXISTS users (
580
712
  id INT AUTO_INCREMENT PRIMARY KEY,
581
713
  email VARCHAR(255) UNIQUE NOT NULL,
582
714
  name VARCHAR(255) NOT NULL,
715
+ password_hash VARCHAR(255),
583
716
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
584
717
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
585
718
  )
586
719
  \`);
587
720
  }
588
721
 
589
- static async create(data) {
722
+ static async create(userData)${typeAnnotation} {
590
723
  const db = getDatabase();
591
- const [result] = await db.execute(
724
+ const result = await db.query(
592
725
  'INSERT INTO users (email, name) VALUES (?, ?)',
593
- [data.email, data.name]
726
+ [userData.email, userData.name]
594
727
  );
595
- return { id: result.insertId, ...data };
728
+ return result[0];
596
729
  }
597
730
 
598
- static async findById(id) {
731
+ static async findById(id)${typeAnnotation} {
599
732
  const db = getDatabase();
600
- const [rows] = await db.execute('SELECT * FROM users WHERE id = ?', [id]);
601
- return rows[0];
733
+ const result = await db.query('SELECT * FROM users WHERE id = ?', [id]);
734
+ return result[0];
602
735
  }
603
736
 
604
- static async findByEmail(email) {
737
+ static async findByEmail(email)${typeAnnotation} {
605
738
  const db = getDatabase();
606
- const [rows] = await db.execute('SELECT * FROM users WHERE email = ?', [email]);
607
- return rows[0];
739
+ const result = await db.query('SELECT * FROM users WHERE email = ?', [email]);
740
+ return result[0];
608
741
  }
609
742
 
610
- static async update(id, data) {
743
+ static async update(id, data)${typeAnnotation} {
611
744
  const db = getDatabase();
612
- await db.execute(
613
- 'UPDATE users SET email = ?, name = ? WHERE id = ?',
745
+ const result = await db.query(
746
+ 'UPDATE users SET email = ?, name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?',
614
747
  [data.email, data.name, id]
615
748
  );
616
- return { id, ...data };
749
+ return result[0];
617
750
  }
618
751
 
619
- static async delete(id) {
752
+ static async delete(id)${typeAnnotation} {
620
753
  const db = getDatabase();
621
- await db.execute('DELETE FROM users WHERE id = ?', [id]);
754
+ await db.query('DELETE FROM users WHERE id = ?', [id]);
622
755
  }
623
756
  }
624
757
  `,
625
758
  sqlite: `
626
- import { getDatabase } from './index.js';
759
+ import { getDatabase } from '../index.js';
760
+
761
+ ${interfaceDef}
627
762
 
628
763
  export class UserModel {
629
- static createTable() {
764
+ static createTable()${typeAnnotation} {
630
765
  const db = getDatabase();
631
766
  db.exec(\`
632
767
  CREATE TABLE IF NOT EXISTS users (
@@ -639,94 +774,82 @@ export class UserModel {
639
774
  \`);
640
775
  }
641
776
 
642
- static create(data) {
777
+ static create(data)${typeAnnotation} {
643
778
  const db = getDatabase();
644
779
  const stmt = db.prepare('INSERT INTO users (email, name) VALUES (?, ?)');
645
780
  const result = stmt.run(data.email, data.name);
646
781
  return { id: result.lastInsertRowid, ...data };
647
782
  }
648
783
 
649
- static findById(id) {
784
+ static findById(id)${typeAnnotation} {
650
785
  const db = getDatabase();
651
786
  const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
652
787
  return stmt.get(id);
653
788
  }
654
789
 
655
- static findByEmail(email) {
790
+ static findByEmail(email)${typeAnnotation} {
656
791
  const db = getDatabase();
657
792
  const stmt = db.prepare('SELECT * FROM users WHERE email = ?');
658
793
  return stmt.get(email);
659
794
  }
660
795
 
661
- static update(id, data) {
796
+ static update(id, data)${typeAnnotation} {
662
797
  const db = getDatabase();
663
798
  const stmt = db.prepare('UPDATE users SET email = ?, name = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?');
664
799
  stmt.run(data.email, data.name, id);
665
- return { id, ...data };
800
+ return this.findById(id);
666
801
  }
667
802
 
668
- static delete(id) {
803
+ static delete(id)${typeAnnotation} {
669
804
  const db = getDatabase();
670
805
  const stmt = db.prepare('DELETE FROM users WHERE id = ?');
671
- stmt.run(id);
806
+ return stmt.run(id);
672
807
  }
673
808
  }
674
809
  `,
675
810
  mongodb: `
676
- import { getDatabase } from './index.js';
811
+ import { getDatabase } from '../index.js';
677
812
 
678
- export class UserModel {
679
- static collectionName = 'users';
813
+ ${interfaceDef}
680
814
 
681
- static getCollection() {
815
+ export class UserModel {
816
+ static async createCollection()${typeAnnotation} {
682
817
  const db = getDatabase();
683
- return db.collection(this.collectionName);
818
+ await db.createCollection('users');
819
+
820
+ // Create index for email uniqueness
821
+ await db.collection('users').createIndex({ email: 1 }, { unique: true });
684
822
  }
685
823
 
686
- static async create(data) {
687
- const collection = this.getCollection();
688
- const result = await collection.insertOne({
689
- email: data.email,
690
- name: data.name,
691
- createdAt: new Date(),
692
- updatedAt: new Date()
693
- });
694
- return { _id: result.insertedId, ...data };
824
+ static async create(userData)${typeAnnotation} {
825
+ const db = getDatabase();
826
+ const result = await db.collection('users').insertOne(userData);
827
+ return { _id: result.insertedId, ...userData };
695
828
  }
696
829
 
697
- static async findById(id) {
698
- const collection = this.getCollection();
699
- return await collection.findOne({ _id: id });
830
+ static async findById(id)${typeAnnotation} {
831
+ const db = getDatabase();
832
+ return await db.collection('users').findOne({ _id: id });
700
833
  }
701
834
 
702
- static async findByEmail(email) {
703
- const collection = this.getCollection();
704
- return await collection.findOne({ email });
835
+ static async findByEmail(email)${typeAnnotation} {
836
+ const db = getDatabase();
837
+ return await db.collection('users').findOne({ email });
705
838
  }
706
839
 
707
- static async update(id, data) {
708
- const collection = this.getCollection();
709
- await collection.updateOne(
840
+ static async update(id, data)${typeAnnotation} {
841
+ const db = getDatabase();
842
+ const result = await db.collection('users').updateOne(
710
843
  { _id: id },
711
- {
712
- $set: {
713
- email: data.email,
714
- name: data.name,
715
- updatedAt: new Date()
716
- }
717
- }
844
+ { $set: { ...data, updatedAt: new Date() } }
718
845
  );
719
- return { _id: id, ...data };
720
- }
721
-
722
- static async delete(id) {
723
- const collection = this.getCollection();
724
- await collection.deleteOne({ _id: id });
846
+ return result.modifiedCount > 0 ? this.findById(id) : null;
725
847
  }
726
848
 
727
- static async createIndexes() {
728
- const collection = this.getCollection();
729
- await collection.createIndex({ email: 1 }, { unique: true });
849
+ static async delete(id)${typeAnnotation} {
850
+ const db = getDatabase();
851
+ const result = await db.collection('users').deleteOne({ _id: id });
852
+ return result.deletedCount > 0;
730
853
  }
731
854
  }
732
855
  `
@@ -779,11 +902,11 @@ function getDatabaseDependencies(dbType) {
779
902
  };
780
903
  return deps[dbType] || {};
781
904
  }
782
- function generateDatabaseScaffolding(dbType) {
905
+ function generateDatabaseScaffolding(dbType, language = "javascript") {
783
906
  return {
784
907
  config: generateDatabaseConfig(dbType),
785
- init: generateDatabaseInit(dbType),
786
- model: generateExampleModel(dbType),
908
+ init: generateDatabaseInit(dbType, language),
909
+ model: generateExampleModel(dbType, language),
787
910
  env: generateEnvExample(dbType),
788
911
  dependencies: getDatabaseDependencies(dbType)
789
912
  };
@@ -1079,7 +1202,7 @@ function generateAuthRoutes(runtime, authType) {
1079
1202
  const jwtRoutes = {
1080
1203
  express: `
1081
1204
  import express from 'express';
1082
- import { generateToken } from '../middleware/auth.js';
1205
+ import { generateToken, authMiddleware } from '../middleware/auth.js';
1083
1206
  import { UserModel } from '../db/models/User.js';
1084
1207
 
1085
1208
  const router = express.Router();
@@ -1156,6 +1279,158 @@ router.get('/me', authMiddleware, async (req, res) => {
1156
1279
  });
1157
1280
 
1158
1281
  export default router;
1282
+ `,
1283
+ "built-in": `
1284
+ // Auth routes for built-in HTTP server
1285
+ import { generateToken, verifyToken } from '../middleware/auth.js';
1286
+ import { UserModel } from '../db/models/User.js';
1287
+
1288
+ // Register handler
1289
+ export async function registerHandler(req, res) {
1290
+ try {
1291
+ // Parse JSON body
1292
+ let body = '';
1293
+ req.on('data', chunk => {
1294
+ body += chunk.toString();
1295
+ });
1296
+
1297
+ req.on('end', async () => {
1298
+ try {
1299
+ const { email, name, password } = JSON.parse(body);
1300
+
1301
+ // Validate input
1302
+ if (!email || !name || !password) {
1303
+ res.writeHead(400, { 'Content-Type': 'application/json' });
1304
+ return res.end(JSON.stringify({ error: 'Missing required fields' }));
1305
+ }
1306
+
1307
+ // Check if user exists
1308
+ const existingUser = await UserModel.findByEmail(email);
1309
+ if (existingUser) {
1310
+ res.writeHead(400, { 'Content-Type': 'application/json' });
1311
+ return res.end(JSON.stringify({ error: 'User already exists' }));
1312
+ }
1313
+
1314
+ // Create user (you should hash the password!)
1315
+ const user = await UserModel.create({ email, name });
1316
+
1317
+ // Generate token
1318
+ const token = generateToken({ id: user.id, email: user.email });
1319
+
1320
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1321
+ res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name }, token }));
1322
+ } catch (error) {
1323
+ console.error('Register error:', error);
1324
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1325
+ res.end(JSON.stringify({ error: 'Registration failed' }));
1326
+ }
1327
+ });
1328
+ } catch (error) {
1329
+ console.error('Register error:', error);
1330
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1331
+ res.end(JSON.stringify({ error: 'Registration failed' }));
1332
+ }
1333
+ }
1334
+
1335
+ // Login handler
1336
+ export async function loginHandler(req, res) {
1337
+ try {
1338
+ // Parse JSON body
1339
+ let body = '';
1340
+ req.on('data', chunk => {
1341
+ body += chunk.toString();
1342
+ });
1343
+
1344
+ req.on('end', async () => {
1345
+ try {
1346
+ const { email, password } = JSON.parse(body);
1347
+
1348
+ // Validate input
1349
+ if (!email || !password) {
1350
+ res.writeHead(400, { 'Content-Type': 'application/json' });
1351
+ return res.end(JSON.stringify({ error: 'Missing required fields' }));
1352
+ }
1353
+
1354
+ // Find user
1355
+ const user = await UserModel.findByEmail(email);
1356
+ if (!user) {
1357
+ res.writeHead(401, { 'Content-Type': 'application/json' });
1358
+ return res.end(JSON.stringify({ error: 'Invalid credentials' }));
1359
+ }
1360
+
1361
+ // Verify password (you should implement proper password checking!)
1362
+
1363
+ // Generate token
1364
+ const token = generateToken({ id: user.id, email: user.email });
1365
+
1366
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1367
+ res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name }, token }));
1368
+ } catch (error) {
1369
+ console.error('Login error:', error);
1370
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1371
+ res.end(JSON.stringify({ error: 'Login failed' }));
1372
+ }
1373
+ });
1374
+ } catch (error) {
1375
+ console.error('Login error:', error);
1376
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1377
+ res.end(JSON.stringify({ error: 'Login failed' }));
1378
+ }
1379
+ }
1380
+
1381
+ // Get current user handler
1382
+ export async function meHandler(req, res) {
1383
+ try {
1384
+ // Verify token from Authorization header
1385
+ const authHeader = req.headers.authorization;
1386
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
1387
+ res.writeHead(401, { 'Content-Type': 'application/json' });
1388
+ return res.end(JSON.stringify({ error: 'No token provided' }));
1389
+ }
1390
+
1391
+ const token = authHeader.substring(7);
1392
+ const decoded = verifyToken(token);
1393
+
1394
+ if (!decoded) {
1395
+ res.writeHead(401, { 'Content-Type': 'application/json' });
1396
+ return res.end(JSON.stringify({ error: 'Invalid or expired token' }));
1397
+ }
1398
+
1399
+ const user = await UserModel.findById(decoded.id);
1400
+ if (!user) {
1401
+ res.writeHead(404, { 'Content-Type': 'application/json' });
1402
+ return res.end(JSON.stringify({ error: 'User not found' }));
1403
+ }
1404
+
1405
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1406
+ res.end(JSON.stringify({ user: { id: user.id, email: user.email, name: user.name } }));
1407
+ } catch (error) {
1408
+ console.error('Get user error:', error);
1409
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1410
+ res.end(JSON.stringify({ error: 'Failed to get user' }));
1411
+ }
1412
+ }
1413
+
1414
+ // For built-in HTTP server compatibility
1415
+ export function setupAuthRoutes() {
1416
+ return [
1417
+ {
1418
+ path: '/api/auth/register',
1419
+ method: 'POST',
1420
+ handler: registerHandler
1421
+ },
1422
+ {
1423
+ path: '/api/auth/login',
1424
+ method: 'POST',
1425
+ handler: loginHandler
1426
+ },
1427
+ {
1428
+ path: '/api/auth/me',
1429
+ method: 'GET',
1430
+ handler: meHandler
1431
+ }
1432
+ ];
1433
+ }
1159
1434
  `,
1160
1435
  fastify: `
1161
1436
  import { generateToken } from '../plugins/auth.js';
@@ -1327,11 +1602,7 @@ router.get('/me', authMiddleware, async (ctx) => {
1327
1602
  }
1328
1603
  });
1329
1604
 
1330
- export default function registerAuthRoutes(router) {
1331
- router.post('/auth/register', registerHandler);
1332
- router.post('/auth/login', loginHandler);
1333
- router.get('/auth/me', authMiddleware, meHandler);
1334
- }
1605
+ export default router;
1335
1606
  `
1336
1607
  };
1337
1608
  const sessionRoutes = {
@@ -1471,6 +1742,312 @@ function generateAuthScaffolding(authType, runtime) {
1471
1742
  };
1472
1743
  }
1473
1744
 
1745
+ // src/generators/docker-scaffold.js
1746
+ var _cliVersion = getCLIVersion();
1747
+ function generateDockerScaffolding(dbType, dockerConfig) {
1748
+ const { port, name, user, password } = dockerConfig;
1749
+ const dockerCompose = generateDockerCompose(dbType, port, name, user, password);
1750
+ const dockerfile = generateDockerfile();
1751
+ const dockerignore = generateDockerignore();
1752
+ const envConfig = generateDockerEnvConfig(dbType, port, name, user, password);
1753
+ return {
1754
+ "docker-compose.yml": dockerCompose,
1755
+ "Dockerfile": dockerfile,
1756
+ ".dockerignore": dockerignore,
1757
+ envConfig
1758
+ };
1759
+ }
1760
+ function generateDockerCompose(dbType, port, dbName, dbUser, dbPassword) {
1761
+ const configs = {
1762
+ postgres: `version: '3.8'
1763
+
1764
+ services:
1765
+ postgres:
1766
+ image: postgres:16-alpine
1767
+ container_name: coherent-postgres
1768
+ restart: unless-stopped
1769
+ environment:
1770
+ POSTGRES_DB: ${dbName}
1771
+ POSTGRES_USER: ${dbUser}
1772
+ POSTGRES_PASSWORD: ${dbPassword}
1773
+ ports:
1774
+ - "${port}:5432"
1775
+ volumes:
1776
+ - postgres_data:/var/lib/postgresql/data
1777
+ networks:
1778
+ - coherent-network
1779
+
1780
+ volumes:
1781
+ postgres_data:
1782
+
1783
+ networks:
1784
+ coherent-network:
1785
+ driver: bridge`,
1786
+ mysql: `version: '3.8'
1787
+
1788
+ services:
1789
+ mysql:
1790
+ image: mysql:8
1791
+ container_name: coherent-mysql
1792
+ restart: unless-stopped
1793
+ environment:
1794
+ MYSQL_DATABASE: ${dbName}
1795
+ MYSQL_USER: ${dbUser}
1796
+ MYSQL_PASSWORD: ${dbPassword}
1797
+ MYSQL_ROOT_PASSWORD: ${dbPassword}_root
1798
+ ports:
1799
+ - "${port}:3306"
1800
+ volumes:
1801
+ - mysql_data:/var/lib/mysql
1802
+ networks:
1803
+ - coherent-network
1804
+
1805
+ volumes:
1806
+ mysql_data:
1807
+
1808
+ networks:
1809
+ coherent-network:
1810
+ driver: bridge`,
1811
+ mongodb: `version: '3.8'
1812
+
1813
+ services:
1814
+ mongodb:
1815
+ image: mongo:7
1816
+ container_name: coherent-mongodb
1817
+ restart: unless-stopped
1818
+ environment:
1819
+ MONGO_INITDB_ROOT_USERNAME: ${dbUser}
1820
+ MONGO_INITDB_ROOT_PASSWORD: ${dbPassword}
1821
+ MONGO_INITDB_DATABASE: ${dbName}
1822
+ ports:
1823
+ - "${port}:27017"
1824
+ volumes:
1825
+ - mongodb_data:/data/db
1826
+ networks:
1827
+ - coherent-network
1828
+
1829
+ volumes:
1830
+ mongodb_data:
1831
+
1832
+ networks:
1833
+ coherent-network:
1834
+ driver: bridge`
1835
+ };
1836
+ return configs[dbType] || configs.postgres;
1837
+ }
1838
+ function generateDockerfile() {
1839
+ return `# Use Node.js 18 Alpine as base image
1840
+ FROM node:18-alpine
1841
+
1842
+ # Set working directory
1843
+ WORKDIR /app
1844
+
1845
+ # Copy package files
1846
+ COPY package*.json ./
1847
+
1848
+ # Install dependencies
1849
+ RUN npm ci --only=production
1850
+
1851
+ # Copy application code
1852
+ COPY . .
1853
+
1854
+ # Create non-root user
1855
+ RUN addgroup -g 1001 -S nodejs
1856
+ RUN adduser -S nodejs -u 1001
1857
+
1858
+ # Change ownership of the app directory
1859
+ RUN chown -R nodejs:nodejs /app
1860
+ USER nodejs
1861
+
1862
+ # Expose port
1863
+ EXPOSE 3000
1864
+
1865
+ # Health check
1866
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
1867
+ CMD node healthcheck.js || exit 1
1868
+
1869
+ # Start the application
1870
+ CMD ["npm", "start"]`;
1871
+ }
1872
+ function generateDockerignore() {
1873
+ return `# Dependencies
1874
+ node_modules
1875
+ npm-debug.log*
1876
+ yarn-debug.log*
1877
+ yarn-error.log*
1878
+
1879
+ # Runtime data
1880
+ pids
1881
+ *.pid
1882
+ *.seed
1883
+ *.pid.lock
1884
+
1885
+ # Coverage directory used by tools like istanbul
1886
+ coverage
1887
+
1888
+ # nyc test coverage
1889
+ .nyc_output
1890
+
1891
+ # Grunt intermediate storage
1892
+ .grunt
1893
+
1894
+ # Bower dependency directory
1895
+ bower_components
1896
+
1897
+ # node-waf configuration
1898
+ .lock-wscript
1899
+
1900
+ # Compiled binary addons
1901
+ build/Release
1902
+
1903
+ # Dependency directories
1904
+ jspm_packages/
1905
+
1906
+ # TypeScript cache
1907
+ *.tsbuildinfo
1908
+
1909
+ # Optional npm cache directory
1910
+ .npm
1911
+
1912
+ # Optional eslint cache
1913
+ .eslintcache
1914
+
1915
+ # Microbundle cache
1916
+ .rpt2_cache/
1917
+ .rts2_cache_cjs/
1918
+ .rts2_cache_es/
1919
+ .rts2_cache_umd/
1920
+
1921
+ # Optional REPL history
1922
+ .node_repl_history
1923
+
1924
+ # Output of 'npm pack'
1925
+ *.tgz
1926
+
1927
+ # Yarn Integrity file
1928
+ .yarn-integrity
1929
+
1930
+ # dotenv environment variables file
1931
+ .env
1932
+ .env.test
1933
+ .env.local
1934
+ .env.production
1935
+
1936
+ # parcel-bundler cache
1937
+ .cache
1938
+ .parcel-cache
1939
+
1940
+ # Next.js build output
1941
+ .next
1942
+
1943
+ # Nuxt.js build / generate output
1944
+ .nuxt
1945
+ dist
1946
+
1947
+ # Gatsby files
1948
+ .cache/
1949
+ public
1950
+
1951
+ # Storybook build outputs
1952
+ .out
1953
+ .storybook-out
1954
+
1955
+ # Temporary folders
1956
+ tmp/
1957
+ temp/
1958
+
1959
+ # Logs
1960
+ logs
1961
+ *.log
1962
+
1963
+ # Runtime data
1964
+ pids
1965
+ *.pid
1966
+ *.seed
1967
+
1968
+ # Coverage directory used by tools like istanbul
1969
+ coverage
1970
+
1971
+ # Grunt intermediate storage
1972
+ .grunt
1973
+
1974
+ # Compiled binary addons
1975
+ build/Release
1976
+
1977
+ # Users Environment Variables
1978
+ .lock-wscript
1979
+
1980
+ # IDE files
1981
+ .vscode/
1982
+ .idea/
1983
+ *.swp
1984
+ *.swo
1985
+
1986
+ # OS generated files
1987
+ .DS_Store
1988
+ .DS_Store?
1989
+ ._*
1990
+ .Spotlight-V100
1991
+ .Trashes
1992
+ ehthumbs.db
1993
+ Thumbs.db`;
1994
+ }
1995
+ function generateDockerEnvConfig(dbType, port, dbName, dbUser, dbPassword) {
1996
+ const configs = {
1997
+ postgres: {
1998
+ DB_HOST: "postgres",
1999
+ DB_PORT: port,
2000
+ DB_NAME: dbName,
2001
+ DB_USER: dbUser,
2002
+ DB_PASSWORD: dbPassword,
2003
+ DATABASE_URL: `postgresql://${dbUser}:${dbPassword}@postgres:${port}/${dbName}`
2004
+ },
2005
+ mysql: {
2006
+ DB_HOST: "mysql",
2007
+ DB_PORT: port,
2008
+ DB_NAME: dbName,
2009
+ DB_USER: dbUser,
2010
+ DB_PASSWORD: dbPassword,
2011
+ DATABASE_URL: `mysql://${dbUser}:${dbPassword}@mysql:${port}/${dbName}`
2012
+ },
2013
+ mongodb: {
2014
+ DB_HOST: "mongodb",
2015
+ DB_PORT: port,
2016
+ DB_NAME: dbName,
2017
+ DB_USER: dbUser,
2018
+ DB_PASSWORD: dbPassword,
2019
+ DATABASE_URL: `mongodb://${dbUser}:${dbPassword}@mongodb:${port}/${dbName}`
2020
+ }
2021
+ };
2022
+ return configs[dbType] || configs.postgres;
2023
+ }
2024
+ function generateHealthCheck() {
2025
+ return `import http from 'http';
2026
+
2027
+ const options = {
2028
+ host: 'localhost',
2029
+ port: process.env.PORT || 3000,
2030
+ path: '/health',
2031
+ timeout: 2000
2032
+ };
2033
+
2034
+ const request = http.request(options, (res) => {
2035
+ console.log(\`Health check status: \${res.statusCode}\`);
2036
+ if (res.statusCode === 200) {
2037
+ process.exit(0);
2038
+ } else {
2039
+ process.exit(1);
2040
+ }
2041
+ });
2042
+
2043
+ request.on('error', (err) => {
2044
+ console.log('Health check failed:', err.message);
2045
+ process.exit(1);
2046
+ });
2047
+
2048
+ request.end();`;
2049
+ }
2050
+
1474
2051
  // src/generators/package-scaffold.js
1475
2052
  var cliVersion3 = getCLIVersion();
1476
2053
  function generateApiScaffolding() {
@@ -1479,31 +2056,95 @@ import { createRouter } from '@coherent.js/api';
1479
2056
 
1480
2057
  const router = createRouter();
1481
2058
 
1482
- // Example route with validation
2059
+ // Business Logic
2060
+ async function getUserById(id) {
2061
+ return { id, name: 'Example User', email: 'user@example.com' };
2062
+ }
2063
+
2064
+ async function createUser(data) {
2065
+ return { id: 1, ...data };
2066
+ }
2067
+
2068
+ // Router Definitions (for Express/Fastify/Koa usage)
1483
2069
  router.get('/users/:id', {
1484
2070
  params: {
1485
2071
  id: { type: 'number', required: true }
1486
2072
  },
1487
2073
  handler: async (req, res) => {
1488
2074
  const { id } = req.params;
1489
- // Fetch user logic here
1490
- return { id, name: 'Example User', email: 'user@example.com' };
2075
+ return getUserById(id);
1491
2076
  }
1492
2077
  });
1493
2078
 
1494
- // Example POST route with body validation
1495
2079
  router.post('/users', {
1496
2080
  body: {
1497
2081
  name: { type: 'string', required: true, minLength: 2 },
1498
2082
  email: { type: 'string', required: true, pattern: /^[^@]+@[^@]+\\.[^@]+$/ }
1499
2083
  },
1500
2084
  handler: async (req, res) => {
1501
- const { name, email } = req.body;
1502
- // Create user logic here
1503
- return { id: 1, name, email };
2085
+ return createUser(req.body);
1504
2086
  }
1505
2087
  });
1506
2088
 
2089
+ // Handler for GET /api/users/:id (Built-in Server)
2090
+ export async function getUsersByIdHandler(req, res) {
2091
+ try {
2092
+ const { id } = req.params;
2093
+ const result = await getUserById(id);
2094
+
2095
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2096
+ res.end(JSON.stringify(result));
2097
+ } catch (error) {
2098
+ console.error('API Error:', error);
2099
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2100
+ res.end(JSON.stringify({ error: 'Internal server error' }));
2101
+ }
2102
+ }
2103
+
2104
+ // Handler for POST /api/users (Built-in Server)
2105
+ export async function postUsersHandler(req, res) {
2106
+ try {
2107
+ let body = '';
2108
+ req.on('data', chunk => {
2109
+ body += chunk.toString();
2110
+ });
2111
+
2112
+ req.on('end', async () => {
2113
+ try {
2114
+ const parsedBody = JSON.parse(body);
2115
+ const result = await createUser(parsedBody);
2116
+
2117
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2118
+ res.end(JSON.stringify(result));
2119
+ } catch (error) {
2120
+ console.error('API Error:', error);
2121
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2122
+ res.end(JSON.stringify({ error: 'Internal server error' }));
2123
+ }
2124
+ });
2125
+ } catch (error) {
2126
+ console.error('API Error:', error);
2127
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2128
+ res.end(JSON.stringify({ error: 'Internal server error' }));
2129
+ }
2130
+ }
2131
+
2132
+ // For built-in HTTP server compatibility
2133
+ export function setupRoutes() {
2134
+ return [
2135
+ {
2136
+ path: '/api/users/:id',
2137
+ method: 'GET',
2138
+ handler: getUsersByIdHandler
2139
+ },
2140
+ {
2141
+ path: '/api/users',
2142
+ method: 'POST',
2143
+ handler: postUsersHandler
2144
+ }
2145
+ ];
2146
+ }
2147
+
1507
2148
  export default router;
1508
2149
  `;
1509
2150
  return {
@@ -1527,7 +2168,7 @@ document.addEventListener('DOMContentLoaded', () => {
1527
2168
 
1528
2169
  try {
1529
2170
  // Dynamically import component
1530
- const module = await import(\`../components/\${componentName}.js\`);
2171
+ const module = await import(\`/components/\${componentName}.js\`);
1531
2172
  const Component = module.default || module[componentName];
1532
2173
 
1533
2174
  // Hydrate component
@@ -2009,7 +2650,9 @@ async function scaffoldProject(projectPath, options) {
2009
2650
  auth = null,
2010
2651
  packages = [],
2011
2652
  language = "javascript",
2012
- packageManager = "npm"
2653
+ packageManager = "npm",
2654
+ onProgress = () => {
2655
+ }
2013
2656
  } = options;
2014
2657
  const isTypeScript = language === "typescript";
2015
2658
  const fileExtension = isTypeScript ? ".ts" : ".js";
@@ -2040,8 +2683,10 @@ async function scaffoldProject(projectPath, options) {
2040
2683
  dirs.forEach((dir) => {
2041
2684
  mkdirSync(join2(projectPath, dir), { recursive: true });
2042
2685
  });
2686
+ onProgress("Created project structure");
2043
2687
  const packageJson = generatePackageJson(name, { template, runtime, database, auth, packages, language, packageManager });
2044
2688
  writeFileSync(join2(projectPath, "package.json"), JSON.stringify(packageJson, null, 2));
2689
+ onProgress("Generated package.json");
2045
2690
  if (isTypeScript) {
2046
2691
  const tsConfig = generateTsConfig();
2047
2692
  writeFileSync(join2(projectPath, "tsconfig.json"), JSON.stringify(tsConfig, null, 2));
@@ -2049,6 +2694,7 @@ async function scaffoldProject(projectPath, options) {
2049
2694
  const jsConfig = generateJsConfig();
2050
2695
  writeFileSync(join2(projectPath, "jsconfig.json"), JSON.stringify(jsConfig, null, 2));
2051
2696
  }
2697
+ onProgress("Created configuration files");
2052
2698
  const serverContent = generateServerFile(runtime, {
2053
2699
  port: 3e3,
2054
2700
  hasApi: packages.includes("api") || auth,
@@ -2056,23 +2702,54 @@ async function scaffoldProject(projectPath, options) {
2056
2702
  hasAuth: !!auth
2057
2703
  });
2058
2704
  writeFileSync(join2(projectPath, `src/index${fileExtension}`), serverContent);
2705
+ onProgress("Set up server");
2059
2706
  await generateHomePageComponent(projectPath, name, isTypeScript, fileExtension);
2707
+ onProgress("Created components");
2060
2708
  if (database) {
2061
- const dbScaffolding = generateDatabaseScaffolding(database);
2062
- writeFileSync(join2(projectPath, "src/db/config.js"), dbScaffolding.config);
2063
- writeFileSync(join2(projectPath, "src/db/index.js"), dbScaffolding.init);
2064
- writeFileSync(join2(projectPath, "src/db/models/User.js"), dbScaffolding.model);
2065
- const existingEnv = "";
2066
- writeFileSync(join2(projectPath, ".env.example"), existingEnv + dbScaffolding.env);
2709
+ const dbScaffolding = generateDatabaseScaffolding(database, language);
2710
+ writeFileSync(join2(projectPath, `src/db/config${fileExtension}`), dbScaffolding.config);
2711
+ writeFileSync(join2(projectPath, `src/db/index${fileExtension}`), dbScaffolding.init);
2712
+ writeFileSync(join2(projectPath, `src/db/models/User${fileExtension}`), dbScaffolding.model);
2713
+ if (options.dockerConfig && database !== "sqlite") {
2714
+ const dockerScaffolding = generateDockerScaffolding(database, options.dockerConfig);
2715
+ writeFileSync(join2(projectPath, "docker-compose.yml"), dockerScaffolding["docker-compose.yml"]);
2716
+ writeFileSync(join2(projectPath, "Dockerfile"), dockerScaffolding["Dockerfile"]);
2717
+ writeFileSync(join2(projectPath, ".dockerignore"), dockerScaffolding[".dockerignore"]);
2718
+ writeFileSync(join2(projectPath, `healthcheck${fileExtension}`), generateHealthCheck());
2719
+ let envContent = "";
2720
+ for (const [key, value] of Object.entries(dockerScaffolding.envConfig)) {
2721
+ envContent += `${key}=${value}
2722
+ `;
2723
+ }
2724
+ writeFileSync(join2(projectPath, ".env.example"), envContent);
2725
+ onProgress("Created Docker configuration");
2726
+ } else {
2727
+ const existingEnv = "";
2728
+ writeFileSync(join2(projectPath, ".env.example"), existingEnv + dbScaffolding.env);
2729
+ }
2730
+ try {
2731
+ copyFileSync(join2(projectPath, ".env.example"), join2(projectPath, ".env"), constants.COPYFILE_EXCL);
2732
+ } catch {
2733
+ }
2734
+ onProgress("Configured database");
2067
2735
  }
2068
2736
  if (auth) {
2069
2737
  const authScaffolding = generateAuthScaffolding(auth, runtime);
2070
2738
  const authDir = runtime === "fastify" ? "plugins" : "middleware";
2071
- writeFileSync(join2(projectPath, `src/${authDir}/auth.js`), authScaffolding.middleware);
2072
- writeFileSync(join2(projectPath, "src/api/auth.js"), authScaffolding.routes);
2739
+ writeFileSync(join2(projectPath, `src/${authDir}/auth${fileExtension}`), authScaffolding.middleware);
2740
+ writeFileSync(join2(projectPath, `src/api/auth${fileExtension}`), authScaffolding.routes);
2073
2741
  const envPath = join2(projectPath, ".env.example");
2074
2742
  const existingEnv = "";
2075
2743
  writeFileSync(envPath, existingEnv + authScaffolding.env);
2744
+ try {
2745
+ const envContent = readFileSync2(join2(projectPath, ".env"), "utf8");
2746
+ if (!envContent.includes("JWT_SECRET") && !envContent.includes("SESSION_SECRET")) {
2747
+ appendFileSync(join2(projectPath, ".env"), authScaffolding.env);
2748
+ }
2749
+ } catch {
2750
+ writeFileSync(join2(projectPath, ".env"), authScaffolding.env);
2751
+ }
2752
+ onProgress("Set up authentication");
2076
2753
  }
2077
2754
  if (packages.length > 0) {
2078
2755
  const { files } = generatePackageScaffolding(packages);
@@ -2143,6 +2820,9 @@ function generatePackageJson(name, options) {
2143
2820
  const tsDeps = getTypeScriptDependencies();
2144
2821
  Object.assign(base.devDependencies, tsDeps);
2145
2822
  base.devDependencies.tsx = "^4.19.2";
2823
+ if (auth) {
2824
+ base.devDependencies["@types/jsonwebtoken"] = "^9.0.7";
2825
+ }
2146
2826
  }
2147
2827
  if (packageManager === "pnpm") {
2148
2828
  base.packageManager = "pnpm@9.0.0";
@@ -2152,7 +2832,7 @@ function generatePackageJson(name, options) {
2152
2832
  const runtimeDeps = getRuntimeDependencies(runtime);
2153
2833
  Object.assign(base.dependencies, runtimeDeps);
2154
2834
  if (database) {
2155
- const { dependencies: dbDeps } = generateDatabaseScaffolding(database);
2835
+ const { dependencies: dbDeps } = generateDatabaseScaffolding(database, language);
2156
2836
  Object.assign(base.dependencies, dbDeps);
2157
2837
  }
2158
2838
  if (auth) {
@@ -2295,16 +2975,16 @@ A Coherent.js application built with pure JavaScript objects.
2295
2975
 
2296
2976
  \`\`\`bash
2297
2977
  # Install dependencies
2298
- npm install
2978
+ pnpm install
2299
2979
 
2300
2980
  # Start development server
2301
- npm run dev
2981
+ pnpm run dev
2302
2982
 
2303
2983
  # Build for production
2304
- npm run build
2984
+ pnpm run build
2305
2985
 
2306
2986
  # Run tests
2307
- npm test
2987
+ pnpm test
2308
2988
  \`\`\`
2309
2989
 
2310
2990
  ## Project Structure
@@ -2323,7 +3003,7 @@ tests/ # Test files
2323
3003
  ## Learn More
2324
3004
 
2325
3005
  - [Coherent.js Documentation](https://github.com/Tomdrouv1/coherent.js)
2326
- - [API Reference](https://github.com/Tomdrouv1/coherent.js/docs/api-reference.md)
3006
+ - [API Reference](https://github.com/Tomdrouv1/coherent.js/tree/main/docs/api-reference.md)
2327
3007
 
2328
3008
  ## License
2329
3009
 
@@ -2387,21 +3067,21 @@ logs
2387
3067
  .node_repl_history
2388
3068
  `;
2389
3069
  writeFileSync(join2(projectPath, ".gitignore"), gitignore);
2390
- const testFile = `import { test } from 'node:test';
2391
- import assert from 'node:assert';
3070
+ const testFile = `import { describe, it, expect } from 'vitest';
2392
3071
  import { render } from '@coherent.js/core';
2393
3072
 
2394
- test('renders basic component', () => {
2395
- const component = {
2396
- div: {
2397
- text: 'Hello, World!'
2398
- }
2399
- };
3073
+ describe('Basic Component Rendering', () => {
3074
+ it('renders basic component', () => {
3075
+ const component = {
3076
+ div: {
3077
+ text: 'Hello, World!'
3078
+ }
3079
+ };
2400
3080
 
2401
- const html = render(component);
2402
- assert(html.includes('Hello, World!'));
2403
- });
2404
- `;
3081
+ const html = render(component);
3082
+ expect(html).toContain('Hello, World!');
3083
+ });
3084
+ });`;
2405
3085
  writeFileSync(join2(projectPath, "tests/basic.test.js"), testFile);
2406
3086
  }
2407
3087
 
@@ -2482,7 +3162,15 @@ function validateComponentName(name) {
2482
3162
  }
2483
3163
 
2484
3164
  // src/commands/create.js
2485
- var createCommand = new Command("create").description("Create a new Coherent.js project").argument("[name]", "project name").option("-t, --template <template>", "project template", "basic").option("--skip-install", "skip npm install").option("--skip-git", "skip git initialization").action(async (name, options) => {
3165
+ function getDefaultDockerPort(database) {
3166
+ const ports = {
3167
+ postgres: 5432,
3168
+ mysql: 3306,
3169
+ mongodb: 27017
3170
+ };
3171
+ return ports[database] || 5432;
3172
+ }
3173
+ var createCommand = new Command("create").description("Create a new Coherent.js project").argument("[name]", "project name").option("-t, --template <template>", "project template", "basic").option("--runtime <runtime>", "runtime framework", "koa").option("--database <database>", "database type").option("--auth <auth>", "authentication type").option("--language <language>", "language", "javascript").option("--skip-install", "skip npm install").option("--skip-git", "skip git initialization").option("--skip-prompts", "skip interactive prompts").option("--use-docker", "include Docker configuration for database").option("--docker-db-port <port>", "Docker database port").option("--docker-db-name <name>", "Docker database name").option("--docker-db-user <user>", "Docker database user").option("--docker-db-password <password>", "Docker database password").action(async (name, options) => {
2486
3174
  let projectName = name;
2487
3175
  if (!projectName) {
2488
3176
  const response = await prompts({
@@ -2514,48 +3202,48 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2514
3202
  console.log(picocolors.gray("\u{1F4CD} Location:"), projectPath);
2515
3203
  console.log();
2516
3204
  let template = options.template;
2517
- if (!template || template === "basic") {
2518
- const response = await prompts({
3205
+ if (!template || template === "basic" && !options.skipPrompts) {
3206
+ if (!options.skipPrompts) {
3207
+ const response = await prompts({
3208
+ type: "select",
3209
+ name: "template",
3210
+ message: "Which template would you like to use?",
3211
+ choices: [
3212
+ { title: "\u{1F3C3}\u200D\u2642\uFE0F Basic App", value: "basic", description: "Simple SSR app with routing" },
3213
+ { title: "\u{1F310} Full Stack", value: "fullstack", description: "API + SSR with database and auth options" }
3214
+ ],
3215
+ initial: 0
3216
+ });
3217
+ if (!response.template) {
3218
+ console.log(picocolors.yellow("\u{1F44B} Project creation cancelled"));
3219
+ process.exit(0);
3220
+ }
3221
+ template = response.template;
3222
+ }
3223
+ }
3224
+ let runtime = options.runtime || "built-in";
3225
+ let database = options.database || null;
3226
+ let auth = options.auth || null;
3227
+ let packages = [];
3228
+ let language = options.language || "javascript";
3229
+ if (!options.skipPrompts) {
3230
+ const languageResponse = await prompts({
2519
3231
  type: "select",
2520
- name: "template",
2521
- message: "Which template would you like to use?",
3232
+ name: "language",
3233
+ message: "Would you like to use TypeScript or JavaScript?",
2522
3234
  choices: [
2523
- { title: "\u{1F3C3}\u200D\u2642\uFE0F Basic App", value: "basic", description: "Simple Coherent.js app with routing" },
2524
- { title: "\u{1F310} Full Stack", value: "fullstack", description: "API + SSR with database integration" },
2525
- { title: "\u26A1 Express Integration", value: "express", description: "Coherent.js with Express.js" },
2526
- { title: "\u{1F680} Fastify Integration", value: "fastify", description: "Coherent.js with Fastify" },
2527
- { title: "\u{1F4F1} Component Library", value: "components", description: "Reusable component library" },
2528
- { title: "\u{1F3A8} Custom Setup", value: "custom", description: "Choose your own runtime and packages" }
3235
+ { title: "\u{1F4D8} JavaScript", value: "javascript", description: "JavaScript with JSDoc type hints (recommended)" },
3236
+ { title: "\u{1F4D5} TypeScript", value: "typescript", description: "Full TypeScript with static type checking" }
2529
3237
  ],
2530
3238
  initial: 0
2531
3239
  });
2532
- if (!response.template) {
3240
+ if (!languageResponse.language) {
2533
3241
  console.log(picocolors.yellow("\u{1F44B} Project creation cancelled"));
2534
3242
  process.exit(0);
2535
3243
  }
2536
- template = response.template;
3244
+ language = languageResponse.language;
2537
3245
  }
2538
- let runtime = "built-in";
2539
- let database = null;
2540
- let auth = null;
2541
- let packages = [];
2542
- let language = "javascript";
2543
- const languageResponse = await prompts({
2544
- type: "select",
2545
- name: "language",
2546
- message: "Would you like to use TypeScript or JavaScript?",
2547
- choices: [
2548
- { title: "\u{1F4D8} JavaScript", value: "javascript", description: "JavaScript with JSDoc type hints (recommended)" },
2549
- { title: "\u{1F4D5} TypeScript", value: "typescript", description: "Full TypeScript with static type checking" }
2550
- ],
2551
- initial: 0
2552
- });
2553
- if (!languageResponse.language) {
2554
- console.log(picocolors.yellow("\u{1F44B} Project creation cancelled"));
2555
- process.exit(0);
2556
- }
2557
- language = languageResponse.language;
2558
- if (template === "custom" || template === "basic" || template === "fullstack" || template === "components") {
3246
+ if ((template === "basic" || template === "fullstack") && !options.skipPrompts) {
2559
3247
  const runtimeResponse = await prompts({
2560
3248
  type: "select",
2561
3249
  name: "runtime",
@@ -2573,46 +3261,50 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2573
3261
  process.exit(0);
2574
3262
  }
2575
3263
  runtime = runtimeResponse.runtime;
2576
- } else if (template === "express") {
2577
- runtime = "express";
2578
- } else if (template === "fastify") {
2579
- runtime = "fastify";
2580
3264
  }
2581
- if (template === "fullstack" || template === "custom") {
2582
- const dbResponse = await prompts({
2583
- type: "select",
2584
- name: "database",
2585
- message: "Which database would you like to use?",
2586
- choices: [
2587
- { title: "\u{1F418} PostgreSQL", value: "postgres", description: "Powerful, open source relational database" },
2588
- { title: "\u{1F42C} MySQL", value: "mysql", description: "Popular open source relational database" },
2589
- { title: "\u{1F4E6} SQLite", value: "sqlite", description: "Lightweight, file-based database" },
2590
- { title: "\u{1F343} MongoDB", value: "mongodb", description: "NoSQL document database" },
2591
- { title: "\u274C None", value: "none", description: "Skip database setup" }
2592
- ],
2593
- initial: 0
2594
- });
2595
- if (!dbResponse.database) {
2596
- console.log(picocolors.yellow("\u{1F44B} Project creation cancelled"));
2597
- process.exit(0);
3265
+ if (template === "fullstack") {
3266
+ if (!options.skipPrompts) {
3267
+ const dbResponse = await prompts({
3268
+ type: "select",
3269
+ name: "database",
3270
+ message: "Which database would you like to use?",
3271
+ choices: [
3272
+ { title: "\u{1F418} PostgreSQL", value: "postgres", description: "Powerful, open source relational database" },
3273
+ { title: "\u{1F42C} MySQL", value: "mysql", description: "Popular open source relational database" },
3274
+ { title: "\u{1F4E6} SQLite", value: "sqlite", description: "Lightweight, file-based database" },
3275
+ { title: "\u{1F343} MongoDB", value: "mongodb", description: "NoSQL document database" },
3276
+ { title: "\u274C None", value: "none", description: "Skip database setup" }
3277
+ ],
3278
+ initial: 0
3279
+ });
3280
+ if (!dbResponse.database) {
3281
+ console.log(picocolors.yellow("\u{1F44B} Project creation cancelled"));
3282
+ process.exit(0);
3283
+ }
3284
+ database = dbResponse.database === "none" ? null : dbResponse.database;
3285
+ } else {
3286
+ database = options.database || null;
2598
3287
  }
2599
- database = dbResponse.database === "none" ? null : dbResponse.database;
2600
- const pkgResponse = await prompts({
2601
- type: "multiselect",
2602
- name: "packages",
2603
- message: "Select optional packages (space to select, enter to confirm):",
2604
- choices: [
2605
- { title: "@coherent.js/api", value: "api", description: "API framework with validation & OpenAPI", selected: template === "fullstack" },
2606
- { title: "@coherent.js/client", value: "client", description: "Client-side hydration & progressive enhancement" },
2607
- { title: "@coherent.js/i18n", value: "i18n", description: "Internationalization utilities" },
2608
- { title: "@coherent.js/forms", value: "forms", description: "Form handling utilities" },
2609
- { title: "@coherent.js/devtools", value: "devtools", description: "Development tools & debugging" },
2610
- { title: "@coherent.js/seo", value: "seo", description: "SEO optimization utilities" },
2611
- { title: "@coherent.js/testing", value: "testing", description: "Testing utilities & helpers" }
2612
- ]
2613
- });
2614
- packages = pkgResponse.packages || [];
2615
- if (packages.includes("api") || database) {
3288
+ if (!options.skipPrompts) {
3289
+ const pkgResponse = await prompts({
3290
+ type: "multiselect",
3291
+ name: "packages",
3292
+ message: "Select optional packages (space to select, enter to confirm):",
3293
+ choices: [
3294
+ { title: "@coherent.js/api", value: "api", description: "API framework with validation & OpenAPI", selected: template === "fullstack" },
3295
+ { title: "@coherent.js/client", value: "client", description: "Client-side hydration & progressive enhancement" },
3296
+ { title: "@coherent.js/i18n", value: "i18n", description: "Internationalization utilities" },
3297
+ { title: "@coherent.js/forms", value: "forms", description: "Form handling utilities" },
3298
+ { title: "@coherent.js/devtools", value: "devtools", description: "Development tools & debugging" },
3299
+ { title: "@coherent.js/seo", value: "seo", description: "SEO optimization utilities" },
3300
+ { title: "@coherent.js/testing", value: "testing", description: "Testing utilities & helpers" }
3301
+ ]
3302
+ });
3303
+ packages = pkgResponse.packages || [];
3304
+ } else {
3305
+ packages = template === "fullstack" ? ["api"] : [];
3306
+ }
3307
+ if (!options.skipPrompts && (packages.includes("api") || database)) {
2616
3308
  const authResponse = await prompts({
2617
3309
  type: "select",
2618
3310
  name: "auth",
@@ -2626,7 +3318,7 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2626
3318
  });
2627
3319
  auth = authResponse.auth === "none" ? null : authResponse.auth;
2628
3320
  }
2629
- } else if (template === "basic" || template === "components") {
3321
+ } else if (template === "basic" && !options.skipPrompts) {
2630
3322
  const pkgResponse = await prompts({
2631
3323
  type: "multiselect",
2632
3324
  name: "packages",
@@ -2641,7 +3333,7 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2641
3333
  packages = pkgResponse.packages || [];
2642
3334
  }
2643
3335
  let packageManager = "npm";
2644
- if (!options.skipInstall) {
3336
+ if (!options.skipInstall && !options.skipPrompts) {
2645
3337
  const pmResponse = await prompts({
2646
3338
  type: "select",
2647
3339
  name: "packageManager",
@@ -2659,6 +3351,56 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2659
3351
  }
2660
3352
  packageManager = pmResponse.packageManager;
2661
3353
  }
3354
+ let dockerConfig = null;
3355
+ if (database && database !== "sqlite" && !options.skipPrompts) {
3356
+ const dockerResponse = await prompts({
3357
+ type: "confirm",
3358
+ name: "useDocker",
3359
+ message: "Would you like to include Docker configuration for the database?",
3360
+ initial: true
3361
+ });
3362
+ if (dockerResponse.useDocker) {
3363
+ const dockerDetailsResponse = await prompts([
3364
+ {
3365
+ type: "number",
3366
+ name: "dbPort",
3367
+ message: "What port should the database use?",
3368
+ initial: getDefaultDockerPort(database)
3369
+ },
3370
+ {
3371
+ type: "text",
3372
+ name: "dbName",
3373
+ message: "What should the database be named?",
3374
+ initial: "coherent_db"
3375
+ },
3376
+ {
3377
+ type: "text",
3378
+ name: "dbUser",
3379
+ message: "What should the database user be?",
3380
+ initial: "coherent_user"
3381
+ },
3382
+ {
3383
+ type: "text",
3384
+ name: "dbPassword",
3385
+ message: "What should the database password be?",
3386
+ initial: "coherent_pass"
3387
+ }
3388
+ ]);
3389
+ dockerConfig = {
3390
+ port: dockerDetailsResponse.dbPort || getDefaultDockerPort(database),
3391
+ name: dockerDetailsResponse.dbName,
3392
+ user: dockerDetailsResponse.dbUser,
3393
+ password: dockerDetailsResponse.dbPassword
3394
+ };
3395
+ }
3396
+ } else if (database && database !== "sqlite" && options.useDocker) {
3397
+ dockerConfig = {
3398
+ port: options.dockerDbPort || getDefaultDockerPort(database),
3399
+ name: options.dockerDbName || "coherent_db",
3400
+ user: options.dockerDbUser || "coherent_user",
3401
+ password: options.dockerDbPassword || "coherent_pass"
3402
+ };
3403
+ }
2662
3404
  const spinner = ora("Scaffolding project...").start();
2663
3405
  try {
2664
3406
  mkdirSync2(projectPath, { recursive: true });
@@ -2671,30 +3413,77 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2671
3413
  packages,
2672
3414
  language,
2673
3415
  packageManager,
3416
+ dockerConfig,
2674
3417
  skipInstall: options.skipInstall,
2675
- skipGit: options.skipGit
3418
+ skipGit: options.skipGit,
3419
+ onProgress: (msg) => {
3420
+ spinner.text = msg;
3421
+ }
2676
3422
  });
2677
3423
  spinner.succeed("Project created successfully!");
2678
3424
  console.log();
2679
- console.log(picocolors.green("\u2705 Project created successfully!"));
3425
+ console.log(picocolors.green("Project created successfully!"));
2680
3426
  console.log();
2681
- console.log(picocolors.cyan("\u{1F4CB} Project Configuration:"));
2682
- console.log(picocolors.gray(" Language:"), picocolors.bold(language === "typescript" ? "TypeScript" : "JavaScript"));
2683
- console.log(picocolors.gray(" Runtime:"), picocolors.bold(runtime));
3427
+ console.log(picocolors.cyan("Configuration:"));
3428
+ console.log(picocolors.gray(" Template:"), picocolors.white(template === "fullstack" ? "Full Stack" : "Basic"));
3429
+ console.log(picocolors.gray(" Language:"), picocolors.white(language === "typescript" ? "TypeScript" : "JavaScript"));
3430
+ console.log(picocolors.gray(" Runtime:"), picocolors.white(runtime));
2684
3431
  if (database) {
2685
- console.log(picocolors.gray(" Database:"), picocolors.bold(database));
3432
+ console.log(picocolors.gray(" Database:"), picocolors.white(database));
2686
3433
  }
2687
3434
  if (auth) {
2688
- console.log(picocolors.gray(" Authentication:"), picocolors.bold(auth.toUpperCase()));
3435
+ console.log(picocolors.gray(" Auth:"), picocolors.white(auth.toUpperCase()));
2689
3436
  }
2690
3437
  if (packages.length > 0) {
2691
- console.log(picocolors.gray(" Packages:"), picocolors.bold(packages.join(", ")));
3438
+ const formattedPackages = packages.map((p) => `@coherent.js/${p}`).join(", ");
3439
+ console.log(picocolors.gray(" Packages:"), picocolors.white(formattedPackages));
2692
3440
  }
2693
3441
  console.log();
2694
- console.log(picocolors.cyan("Next steps:"));
2695
- console.log(picocolors.gray(" cd"), picocolors.bold(projectName));
3442
+ const fileExt = language === "typescript" ? ".ts" : ".js";
3443
+ console.log(picocolors.cyan("Project structure:"));
3444
+ console.log(picocolors.white(` ${projectName}/`));
3445
+ console.log(picocolors.gray(` src/`));
3446
+ console.log(picocolors.gray(` index${fileExt}`));
3447
+ console.log(picocolors.gray(` components/`));
2696
3448
  if (database) {
2697
- console.log(picocolors.gray(" # Configure database in .env.example"));
3449
+ console.log(picocolors.gray(` db/`));
3450
+ }
3451
+ if (auth || packages.includes("api")) {
3452
+ console.log(picocolors.gray(` api/`));
3453
+ }
3454
+ console.log(picocolors.gray(` public/`));
3455
+ console.log(picocolors.gray(` package.json`));
3456
+ if (database) {
3457
+ console.log(picocolors.gray(` .env.example`), picocolors.yellow("<-- Configure this!"));
3458
+ }
3459
+ console.log();
3460
+ const needsEnvConfig = database && database !== "sqlite";
3461
+ const envVars = [];
3462
+ if (needsEnvConfig) {
3463
+ if (database === "mongodb") {
3464
+ envVars.push("MONGODB_URI");
3465
+ } else {
3466
+ envVars.push("DB_HOST", "DB_PORT", "DB_NAME", "DB_USER", "DB_PASSWORD");
3467
+ }
3468
+ }
3469
+ if (auth === "jwt") {
3470
+ envVars.push("JWT_SECRET");
3471
+ }
3472
+ if (auth === "session") {
3473
+ envVars.push("SESSION_SECRET");
3474
+ }
3475
+ if (envVars.length > 0) {
3476
+ console.log(picocolors.yellow("Environment variables to configure:"));
3477
+ envVars.forEach((v) => console.log(picocolors.gray(` ${v}`)));
3478
+ console.log();
3479
+ }
3480
+ console.log(picocolors.cyan("Next steps:"));
3481
+ let stepNum = 1;
3482
+ console.log(picocolors.white(` ${stepNum}.`), picocolors.gray(`cd ${projectName}`));
3483
+ stepNum++;
3484
+ if (needsEnvConfig) {
3485
+ console.log(picocolors.white(` ${stepNum}.`), picocolors.gray("Edit .env with your database credentials"));
3486
+ stepNum++;
2698
3487
  }
2699
3488
  const pmCommands = {
2700
3489
  npm: { install: "npm install", dev: "npm run dev" },
@@ -2702,14 +3491,41 @@ var createCommand = new Command("create").description("Create a new Coherent.js
2702
3491
  pnpm: { install: "pnpm install", dev: "pnpm dev" }
2703
3492
  };
2704
3493
  const commands = pmCommands[packageManager] || pmCommands.npm;
2705
- if (!options.skipInstall) {
2706
- console.log(picocolors.gray(` ${commands.dev}`));
2707
- } else {
2708
- console.log(picocolors.gray(` ${commands.install}`));
2709
- console.log(picocolors.gray(` ${commands.dev}`));
3494
+ if (options.skipInstall) {
3495
+ console.log(picocolors.white(` ${stepNum}.`), picocolors.gray(commands.install));
3496
+ stepNum++;
2710
3497
  }
3498
+ console.log(picocolors.white(` ${stepNum}.`), picocolors.gray(commands.dev));
2711
3499
  console.log();
2712
- console.log(picocolors.gray("Happy coding! \u{1F389}"));
3500
+ if (!options.skipPrompts && !options.skipInstall) {
3501
+ const startResponse = await prompts({
3502
+ type: "confirm",
3503
+ name: "startDev",
3504
+ message: "Start development server now?",
3505
+ initial: true
3506
+ });
3507
+ if (startResponse.startDev) {
3508
+ console.log();
3509
+ console.log(picocolors.cyan("Starting development server..."));
3510
+ console.log(picocolors.gray(` ${commands.dev}`));
3511
+ console.log();
3512
+ try {
3513
+ const devProcess = spawn(commands.dev, [], {
3514
+ cwd: projectPath,
3515
+ stdio: "inherit",
3516
+ shell: true,
3517
+ detached: true
3518
+ });
3519
+ devProcess.unref();
3520
+ return;
3521
+ } catch {
3522
+ console.log(picocolors.yellow("Could not start development server automatically."));
3523
+ console.log(picocolors.gray(` Run manually: cd ${projectName} && ${commands.dev}`));
3524
+ console.log();
3525
+ }
3526
+ }
3527
+ }
3528
+ console.log(picocolors.gray("Happy coding!"));
2713
3529
  } catch (_error) {
2714
3530
  spinner.fail("Failed to create project");
2715
3531
  console.error(picocolors.red("\u274C Error:"), _error.message);
@@ -2776,7 +3592,7 @@ function generateBasicComponent(name) {
2776
3592
 
2777
3593
  /**
2778
3594
  * ${name} component
2779
- *
3595
+ *
2780
3596
  * @param {Object} props - Component properties
2781
3597
  * @param {string} props.className - CSS class name
2782
3598
  * @param {Array|Object} props.children - Child elements
@@ -2803,7 +3619,7 @@ function generateFunctionalComponent(name) {
2803
3619
 
2804
3620
  /**
2805
3621
  * ${name} - Functional component with business logic
2806
- *
3622
+ *
2807
3623
  * @param {Object} props - Component properties
2808
3624
  * @param {Array} props.items - Items to display
2809
3625
  * @param {Function} props.onItemClick - Callback for item clicks
@@ -2821,11 +3637,11 @@ export const ${name} = createComponent(({ items = [], onItemClick, className = '
2821
3637
  div: {
2822
3638
  className: \`${name.toLowerCase()} \${className}\`.trim(),
2823
3639
  children: [
2824
- {
2825
- h3: {
3640
+ {
3641
+ h3: {
2826
3642
  className: '${name.toLowerCase()}__title',
2827
- text: '${name}'
2828
- }
3643
+ text: '${name}'
3644
+ }
2829
3645
  },
2830
3646
  {
2831
3647
  ul: {
@@ -2868,17 +3684,17 @@ function generateInteractiveComponent(name) {
2868
3684
 
2869
3685
  /**
2870
3686
  * ${name} - Interactive component with state management
2871
- *
3687
+ *
2872
3688
  * @param {Object} props - Component properties
2873
3689
  * @param {*} props.initialValue - Initial value
2874
3690
  * @param {Function} props.onChange - Change callback
2875
3691
  * @param {string} props.className - CSS class name
2876
3692
  */
2877
- export const ${name} = createComponent(({
2878
- initialValue = '',
3693
+ export const ${name} = createComponent(({
3694
+ initialValue = '',
2879
3695
  onChange,
2880
3696
  className = '',
2881
- ...props
3697
+ ...props
2882
3698
  }) => {
2883
3699
  // Component state (handled by Coherent.js hydration)
2884
3700
  const state = {
@@ -2955,7 +3771,7 @@ function generateLayoutComponent(name) {
2955
3771
 
2956
3772
  /**
2957
3773
  * ${name} - Layout component for page structure
2958
- *
3774
+ *
2959
3775
  * @param {Object} props - Component properties
2960
3776
  * @param {string} props.title - Page title
2961
3777
  * @param {Array|Object} props.children - Child content
@@ -2963,7 +3779,7 @@ function generateLayoutComponent(name) {
2963
3779
  * @param {Object} props.footer - Footer content
2964
3780
  * @param {string} props.className - CSS class name
2965
3781
  */
2966
- export const ${name} = createComponent(({
3782
+ export const ${name} = createComponent(({
2967
3783
  title = 'Page Title',
2968
3784
  children = [],
2969
3785
  header = null,
@@ -3037,38 +3853,39 @@ export const ${name} = createComponent(({
3037
3853
  `;
3038
3854
  }
3039
3855
  function generateTestContent(name) {
3040
- return `import { test } from 'node:test';
3041
- import assert from 'node:assert';
3856
+ return `import { describe, it, expect } from 'vitest';
3042
3857
  import { render } from '@coherent.js/core';
3043
3858
  import { ${name} } from './${name}.js';
3044
3859
 
3045
- test('${name} renders correctly', () => {
3046
- const component = ${name}({});
3047
- const html = render(component);
3048
-
3049
- assert(typeof html === 'string');
3050
- assert(html.length > 0);
3051
- assert(html.includes('${name.toLowerCase()}'));
3052
- });
3860
+ describe('${name}', () => {
3861
+ it('renders correctly', () => {
3862
+ const component = ${name}({});
3863
+ const html = render(component);
3053
3864
 
3054
- test('${name} accepts className prop', () => {
3055
- const component = ${name}({ className: 'test-class' });
3056
- const html = render(component);
3057
-
3058
- assert(html.includes('test-class'));
3059
- });
3865
+ expect(typeof html).toBe('string');
3866
+ expect(html.length).toBeGreaterThan(0);
3867
+ expect(html).toContain('${name.toLowerCase()}');
3868
+ });
3060
3869
 
3061
- test('${name} renders children correctly', () => {
3062
- const children = [
3063
- { p: { text: 'Test child content' } }
3064
- ];
3065
-
3066
- const component = ${name}({ children });
3067
- const html = render(component);
3068
-
3069
- assert(html.includes('Test child content'));
3870
+ it('accepts className prop', () => {
3871
+ const component = ${name}({ className: 'test-class' });
3872
+ const html = render(component);
3873
+
3874
+ expect(html).toContain('test-class');
3875
+ });
3070
3876
  });
3071
- `;
3877
+
3878
+ it('renders children correctly', () => {
3879
+ const children = [
3880
+ { p: { text: 'Test child content' } }
3881
+ ];
3882
+
3883
+ const component = ${name}({ children });
3884
+ const html = render(component);
3885
+
3886
+ expect(html).toContain('Test child content');
3887
+ });
3888
+ });`;
3072
3889
  }
3073
3890
  function generateStoryContent(name) {
3074
3891
  return `import { ${name} } from './${name}.js';
@@ -3161,7 +3978,7 @@ function generateBasicPage(name) {
3161
3978
  /**
3162
3979
  * ${name} Page Component
3163
3980
  * Route: /${routeName}
3164
- *
3981
+ *
3165
3982
  * @param {Object} props - Page properties
3166
3983
  * @param {Object} props.params - Route parameters
3167
3984
  * @param {Object} props.query - Query parameters
@@ -3179,17 +3996,17 @@ export const ${name} = createComponent(({ params = {}, query = {}, request, ...p
3179
3996
  head: {
3180
3997
  children: [
3181
3998
  { title: { text: pageTitle } },
3182
- {
3183
- meta: {
3999
+ {
4000
+ meta: {
3184
4001
  name: 'description',
3185
4002
  content: pageDescription
3186
- }
4003
+ }
3187
4004
  },
3188
- {
3189
- meta: {
3190
- name: 'viewport',
3191
- content: 'width=device-width, initial-scale=1.0'
3192
- }
4005
+ {
4006
+ meta: {
4007
+ name: 'viewport',
4008
+ content: 'width=device-width, initial-scale=1.0'
4009
+ }
3193
4010
  }
3194
4011
  ]
3195
4012
  }
@@ -3300,10 +4117,10 @@ ${name}.description = '${name} page description';
3300
4117
 
3301
4118
  // Usage in router:
3302
4119
  // app.get('/${routeName}', (req, res) => {
3303
- // const html = render(${name}({
3304
- // params: req.params,
4120
+ // const html = render(${name}({
4121
+ // params: req.params,
3305
4122
  // query: req.query,
3306
- // request: req
4123
+ // request: req
3307
4124
  // }));
3308
4125
  // res.send(html);
3309
4126
  // });
@@ -3331,11 +4148,11 @@ export const ${name} = createComponent(({ stats = {}, user = null }) => {
3331
4148
  head: {
3332
4149
  children: [
3333
4150
  { title: { text: '${name} Dashboard' } },
3334
- {
3335
- meta: {
3336
- name: 'viewport',
3337
- content: 'width=device-width, initial-scale=1.0'
3338
- }
4151
+ {
4152
+ meta: {
4153
+ name: 'viewport',
4154
+ content: 'width=device-width, initial-scale=1.0'
4155
+ }
3339
4156
  }
3340
4157
  ]
3341
4158
  }
@@ -3486,11 +4303,11 @@ export const ${name} = createComponent(({ initialData = {}, errors = {} }) => {
3486
4303
  head: {
3487
4304
  children: [
3488
4305
  { title: { text: '${name} Form' } },
3489
- {
3490
- meta: {
3491
- name: 'viewport',
3492
- content: 'width=device-width, initial-scale=1.0'
3493
- }
4306
+ {
4307
+ meta: {
4308
+ name: 'viewport',
4309
+ content: 'width=device-width, initial-scale=1.0'
4310
+ }
3494
4311
  }
3495
4312
  ]
3496
4313
  }
@@ -3630,42 +4447,42 @@ export const ${name} = createComponent(({ initialData = {}, errors = {} }) => {
3630
4447
  `;
3631
4448
  }
3632
4449
  function generateTestContent2(name) {
3633
- return `import { test } from 'node:test';
3634
- import assert from 'node:assert';
4450
+ return `import { describe, it, expect } from 'vitest';
3635
4451
  import { render } from '@coherent.js/core';
3636
4452
  import { ${name} } from './${name}.js';
3637
4453
 
3638
- test('${name} page renders correctly', () => {
3639
- const page = ${name}({});
3640
- const html = render(page);
3641
-
3642
- assert(typeof html === 'string');
3643
- assert(html.length > 0);
3644
- assert(html.includes('<html>'));
3645
- assert(html.includes('${name}'));
3646
- });
4454
+ describe('${name} Page', () => {
4455
+ it('renders correctly', () => {
4456
+ const page = ${name}({});
4457
+ const html = render(page);
3647
4458
 
3648
- test('${name} page includes proper head elements', () => {
4459
+ expect(typeof html).toBe('string');
4460
+ expect(html.length).toBeGreaterThan(0);
4461
+ expect(html).toContain('<html>');
4462
+ expect(html).toContain('${name}');
4463
+ });
4464
+
4465
+ it('includes proper head elements', () => {
3649
4466
  const page = ${name}({});
3650
- const html = render(page);
3651
-
3652
- assert(html.includes('<title>'));
3653
- assert(html.includes('<meta'));
3654
- assert(html.includes('viewport'));
3655
- });
4467
+ const html = render(page);
4468
+
4469
+ expect(html).toContain('<title>');
4470
+ expect(html).toContain('<meta');
4471
+ expect(html).toContain('viewport');
4472
+ });
4473
+
4474
+ it('renders with custom props', () => {
4475
+ const props = {
4476
+ params: { id: '123' },
4477
+ query: { search: 'test' }
4478
+ };
3656
4479
 
3657
- test('${name} page renders with custom props', () => {
3658
- const props = {
3659
- params: { id: '123' },
3660
- query: { search: 'test' }
3661
- };
3662
-
3663
4480
  const page = ${name}(props);
3664
- const html = render(page);
3665
-
3666
- assert(html.includes('${name}'));
3667
- });
3668
- `;
4481
+ const html = render(page);
4482
+
4483
+ expect(html).toContain('${name}');
4484
+ });
4485
+ });`;
3669
4486
  }
3670
4487
  function toPascalCase2(str) {
3671
4488
  return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : "").replace(/^(.)/, (_, c) => c.toUpperCase());
@@ -3722,7 +4539,7 @@ function generateRESTAPI(apiName, originalName) {
3722
4539
  /**
3723
4540
  * ${className} API Routes
3724
4541
  * REST API for ${apiName} resources
3725
- *
4542
+ *
3726
4543
  * Base URL: /api/${apiName}
3727
4544
  */
3728
4545
 
@@ -3742,14 +4559,14 @@ const sampleData = [
3742
4559
  const ${apiName}Schema = {
3743
4560
  type: 'object',
3744
4561
  properties: {
3745
- name: {
3746
- type: 'string',
4562
+ name: {
4563
+ type: 'string',
3747
4564
  minLength: 1,
3748
- maxLength: 100
4565
+ maxLength: 100
3749
4566
  },
3750
- description: {
4567
+ description: {
3751
4568
  type: 'string',
3752
- maxLength: 500
4569
+ maxLength: 500
3753
4570
  }
3754
4571
  },
3755
4572
  required: ['name'],
@@ -3759,14 +4576,14 @@ const ${apiName}Schema = {
3759
4576
  const ${apiName}UpdateSchema = {
3760
4577
  type: 'object',
3761
4578
  properties: {
3762
- name: {
3763
- type: 'string',
4579
+ name: {
4580
+ type: 'string',
3764
4581
  minLength: 1,
3765
- maxLength: 100
4582
+ maxLength: 100
3766
4583
  },
3767
- description: {
4584
+ description: {
3768
4585
  type: 'string',
3769
- maxLength: 500
4586
+ maxLength: 500
3770
4587
  }
3771
4588
  },
3772
4589
  additionalProperties: false,
@@ -3781,21 +4598,21 @@ const ${apiName}UpdateSchema = {
3781
4598
  */
3782
4599
  ${camelCaseApiName}API.get('/', (req, res) => {
3783
4600
  const { page = 1, limit = 10, search } = req.query;
3784
-
4601
+
3785
4602
  let data = [...sampleData];
3786
-
4603
+
3787
4604
  // Apply search filter
3788
4605
  if (search) {
3789
- data = data.filter(item =>
4606
+ data = data.filter(item =>
3790
4607
  item.name.toLowerCase().includes(search.toLowerCase())
3791
4608
  );
3792
4609
  }
3793
-
4610
+
3794
4611
  // Apply pagination
3795
4612
  const startIndex = (page - 1) * limit;
3796
4613
  const endIndex = startIndex + parseInt(limit);
3797
4614
  const paginatedData = data.slice(startIndex, endIndex);
3798
-
4615
+
3799
4616
  return {
3800
4617
  data: paginatedData,
3801
4618
  pagination: {
@@ -3814,14 +4631,14 @@ ${camelCaseApiName}API.get('/', (req, res) => {
3814
4631
  ${camelCaseApiName}API.get('/:id', (req, res) => {
3815
4632
  const { id } = req.params;
3816
4633
  const item = sampleData.find(item => item.id === id);
3817
-
4634
+
3818
4635
  if (!item) {
3819
4636
  return res.status(404).json({
3820
4637
  _error: '${className} not found',
3821
4638
  code: 'NOT_FOUND'
3822
4639
  });
3823
4640
  }
3824
-
4641
+
3825
4642
  return { data: item };
3826
4643
  });
3827
4644
 
@@ -3829,11 +4646,11 @@ ${camelCaseApiName}API.get('/:id', (req, res) => {
3829
4646
  * POST /${apiName}
3830
4647
  * Create a new ${apiName} item
3831
4648
  */
3832
- ${camelCaseApiName}API.post('/',
4649
+ ${camelCaseApiName}API.post('/',
3833
4650
  withValidation(${apiName}Schema),
3834
4651
  (req, res) => {
3835
4652
  const { name, description } = req.body;
3836
-
4653
+
3837
4654
  const newItem = {
3838
4655
  id: String(Date.now()),
3839
4656
  name,
@@ -3841,9 +4658,9 @@ ${camelCaseApiName}API.post('/',
3841
4658
  createdAt: new Date().toISOString(),
3842
4659
  updatedAt: new Date().toISOString()
3843
4660
  };
3844
-
4661
+
3845
4662
  sampleData.push(newItem);
3846
-
4663
+
3847
4664
  return res.status(201).json({
3848
4665
  data: newItem,
3849
4666
  message: '${className} created successfully'
@@ -3860,22 +4677,22 @@ ${camelCaseApiName}API.put('/:id',
3860
4677
  (req, res) => {
3861
4678
  const { id } = req.params;
3862
4679
  const itemIndex = sampleData.findIndex(item => item.id === id);
3863
-
4680
+
3864
4681
  if (itemIndex === -1) {
3865
4682
  return res.status(404).json({
3866
4683
  _error: '${className} not found',
3867
4684
  code: 'NOT_FOUND'
3868
4685
  });
3869
4686
  }
3870
-
4687
+
3871
4688
  const updatedItem = {
3872
4689
  ...sampleData[itemIndex],
3873
4690
  ...req.body,
3874
4691
  updatedAt: new Date().toISOString()
3875
4692
  };
3876
-
4693
+
3877
4694
  sampleData[itemIndex] = updatedItem;
3878
-
4695
+
3879
4696
  return {
3880
4697
  data: updatedItem,
3881
4698
  message: '${className} updated successfully'
@@ -3890,16 +4707,16 @@ ${camelCaseApiName}API.put('/:id',
3890
4707
  ${camelCaseApiName}API.delete('/:id', (req, res) => {
3891
4708
  const { id } = req.params;
3892
4709
  const itemIndex = sampleData.findIndex(item => item.id === id);
3893
-
4710
+
3894
4711
  if (itemIndex === -1) {
3895
4712
  return res.status(404).json({
3896
4713
  _error: '${className} not found',
3897
4714
  code: 'NOT_FOUND'
3898
4715
  });
3899
4716
  }
3900
-
4717
+
3901
4718
  const deletedItem = sampleData.splice(itemIndex, 1)[0];
3902
-
4719
+
3903
4720
  return {
3904
4721
  data: deletedItem,
3905
4722
  message: '${className} deleted successfully'
@@ -3920,7 +4737,7 @@ export default ${camelCaseApiName}API;
3920
4737
  // Usage example:
3921
4738
  // import express from 'express';
3922
4739
  // import ${camelCaseApiName}API from './api/${apiName}.js';
3923
- //
4740
+ //
3924
4741
  // const app = express();
3925
4742
  // app.use(express.json());
3926
4743
  // app.use('/api', ${camelCaseApiName}API.toExpress());
@@ -3941,7 +4758,7 @@ function generateRPCAPI(apiName, originalName) {
3941
4758
  /**
3942
4759
  * ${className} RPC API
3943
4760
  * Remote Procedure Call API for ${apiName}
3944
- *
4761
+ *
3945
4762
  * Base URL: /rpc/${apiName}
3946
4763
  */
3947
4764
 
@@ -3964,10 +4781,10 @@ sampleData.set('2', { id: '2', name: 'Sample ${className} 2', createdAt: new Dat
3964
4781
  ${camelCaseApiName}RPC.post('/list', (req, res) => {
3965
4782
  const { params = {} } = req.body;
3966
4783
  const { limit = 10, offset = 0 } = params;
3967
-
4784
+
3968
4785
  const items = Array.from(sampleData.values())
3969
4786
  .slice(offset, offset + limit);
3970
-
4787
+
3971
4788
  return {
3972
4789
  jsonrpc: '2.0',
3973
4790
  result: {
@@ -3982,7 +4799,7 @@ ${camelCaseApiName}RPC.post('/list', (req, res) => {
3982
4799
  * RPC Method: ${apiName}.get
3983
4800
  * Get a specific ${apiName} item
3984
4801
  */
3985
- ${camelCaseApiName}RPC.post('/get',
4802
+ ${camelCaseApiName}RPC.post('/get',
3986
4803
  withValidation({
3987
4804
  type: 'object',
3988
4805
  properties: {
@@ -3999,7 +4816,7 @@ ${camelCaseApiName}RPC.post('/get',
3999
4816
  (req, res) => {
4000
4817
  const { params } = req.body;
4001
4818
  const item = sampleData.get(params.id);
4002
-
4819
+
4003
4820
  if (!item) {
4004
4821
  return {
4005
4822
  jsonrpc: '2.0',
@@ -4010,7 +4827,7 @@ ${camelCaseApiName}RPC.post('/get',
4010
4827
  id: req.body.id
4011
4828
  };
4012
4829
  }
4013
-
4830
+
4014
4831
  return {
4015
4832
  jsonrpc: '2.0',
4016
4833
  result: item,
@@ -4041,16 +4858,16 @@ ${camelCaseApiName}RPC.post('/create',
4041
4858
  (req, res) => {
4042
4859
  const { params } = req.body;
4043
4860
  const id = String(Date.now());
4044
-
4861
+
4045
4862
  const newItem = {
4046
4863
  id,
4047
4864
  ...params,
4048
4865
  createdAt: new Date(),
4049
4866
  updatedAt: new Date()
4050
4867
  };
4051
-
4868
+
4052
4869
  sampleData.set(id, newItem);
4053
-
4870
+
4054
4871
  return {
4055
4872
  jsonrpc: '2.0',
4056
4873
  result: newItem,
@@ -4082,7 +4899,7 @@ ${camelCaseApiName}RPC.post('/update',
4082
4899
  (req, res) => {
4083
4900
  const { params } = req.body;
4084
4901
  const existing = sampleData.get(params.id);
4085
-
4902
+
4086
4903
  if (!existing) {
4087
4904
  return {
4088
4905
  jsonrpc: '2.0',
@@ -4093,15 +4910,15 @@ ${camelCaseApiName}RPC.post('/update',
4093
4910
  id: req.body.id
4094
4911
  };
4095
4912
  }
4096
-
4913
+
4097
4914
  const updated = {
4098
4915
  ...existing,
4099
4916
  ...params,
4100
4917
  updatedAt: new Date()
4101
4918
  };
4102
-
4919
+
4103
4920
  sampleData.set(params.id, updated);
4104
-
4921
+
4105
4922
  return {
4106
4923
  jsonrpc: '2.0',
4107
4924
  result: updated,
@@ -4131,7 +4948,7 @@ ${camelCaseApiName}RPC.post('/delete',
4131
4948
  (req, res) => {
4132
4949
  const { params } = req.body;
4133
4950
  const item = sampleData.get(params.id);
4134
-
4951
+
4135
4952
  if (!item) {
4136
4953
  return {
4137
4954
  jsonrpc: '2.0',
@@ -4142,9 +4959,9 @@ ${camelCaseApiName}RPC.post('/delete',
4142
4959
  id: req.body.id
4143
4960
  };
4144
4961
  }
4145
-
4962
+
4146
4963
  sampleData.delete(params.id);
4147
-
4964
+
4148
4965
  return {
4149
4966
  jsonrpc: '2.0',
4150
4967
  result: { success: true, deleted: item },
@@ -4158,17 +4975,17 @@ export default ${camelCaseApiName}RPC;
4158
4975
  }
4159
4976
  function generateTestContent3(apiName, originalName) {
4160
4977
  const className = toPascalCase3(originalName);
4161
- return `import { test } from 'node:test';
4162
- import assert from 'node:assert';
4978
+ return `import { describe, it, expect } from 'vitest';
4163
4979
  import ${apiName}API from './${apiName}.js';
4164
4980
 
4165
- test('${className} API should be defined', () => {
4166
- assert(typeof ${apiName}API === 'object');
4167
- assert(typeof ${apiName}API.get === 'function');
4168
- assert(typeof ${apiName}API.post === 'function');
4981
+ describe('${className} API', () => {
4982
+ it('should be defined', () => {
4983
+ expect(typeof ${apiName}API).toBe('object');
4984
+ expect(typeof ${apiName}API.get).toBe('function');
4985
+ expect(typeof ${apiName}API.post).toBe('function');
4169
4986
  });
4170
4987
 
4171
- test('${className} API should handle GET requests', async () => {
4988
+ it('should handle GET requests', async () => {
4172
4989
  const mockReq = {
4173
4990
  query: {}
4174
4991
  };
@@ -4176,15 +4993,15 @@ test('${className} API should handle GET requests', async () => {
4176
4993
  status: (code) => mockRes,
4177
4994
  json: (data) => data
4178
4995
  };
4179
-
4996
+
4180
4997
  // This is a basic test structure
4181
4998
  // In a real test, you'd use a testing framework like supertest
4182
- assert(true); // Placeholder
4999
+ expect(true).toBe(true); // Placeholder
4183
5000
  });
4184
5001
 
4185
5002
  // Add more specific tests for your API endpoints
4186
5003
  // Example:
4187
- // test('POST /${apiName} should create new item', async () => {
5004
+ // it('POST /${apiName} should create new item', async () => {
4188
5005
  // // Test implementation
4189
5006
  // });
4190
5007
  //
@@ -4316,7 +5133,7 @@ import { Command as Command3 } from "commander";
4316
5133
  import ora3 from "ora";
4317
5134
  import picocolors3 from "picocolors";
4318
5135
  import { execSync as execSync2 } from "child_process";
4319
- import { existsSync as existsSync6, readFileSync as readFileSync2 } from "fs";
5136
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "fs";
4320
5137
  import { join as join6 } from "path";
4321
5138
  var buildCommand = new Command3("build").description("Build the project for production").option("-w, --watch", "watch for changes").option("--analyze", "analyze bundle size").option("--no-minify", "disable minification").option("--no-optimize", "disable optimizations").action(async (options) => {
4322
5139
  console.log(picocolors3.cyan("\u{1F3D7}\uFE0F Building Coherent.js project..."));
@@ -4328,7 +5145,7 @@ var buildCommand = new Command3("build").description("Build the project for prod
4328
5145
  }
4329
5146
  let packageJson;
4330
5147
  try {
4331
- packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
5148
+ packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
4332
5149
  } catch {
4333
5150
  console.error(picocolors3.red("\u274C Failed to read package.json"));
4334
5151
  process.exit(1);
@@ -4422,8 +5239,8 @@ var buildCommand = new Command3("build").description("Build the project for prod
4422
5239
  import { Command as Command4 } from "commander";
4423
5240
  import ora4 from "ora";
4424
5241
  import picocolors4 from "picocolors";
4425
- import { spawn } from "child_process";
4426
- import { existsSync as existsSync7, readFileSync as readFileSync3 } from "fs";
5242
+ import { spawn as spawn2 } from "child_process";
5243
+ import { existsSync as existsSync7, readFileSync as readFileSync4 } from "fs";
4427
5244
  import { join as join7 } from "path";
4428
5245
  var devCommand = new Command4("dev").description("Start development server with hot reload").option("-p, --port <port>", "port number", "3000").option("-h, --host <host>", "host address", "localhost").option("--open", "open browser automatically").option("--no-hmr", "disable hot module replacement").action(async (options) => {
4429
5246
  console.log(picocolors4.cyan("\u{1F680} Starting Coherent.js development server..."));
@@ -4435,7 +5252,7 @@ var devCommand = new Command4("dev").description("Start development server with
4435
5252
  }
4436
5253
  let packageJson;
4437
5254
  try {
4438
- packageJson = JSON.parse(readFileSync3(packageJsonPath, "utf-8"));
5255
+ packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
4439
5256
  } catch {
4440
5257
  console.error(picocolors4.red("\u274C Failed to read package.json"));
4441
5258
  process.exit(1);
@@ -4445,7 +5262,7 @@ var devCommand = new Command4("dev").description("Start development server with
4445
5262
  let devProcess;
4446
5263
  if (packageJson.scripts && packageJson.scripts.dev) {
4447
5264
  spinner.text = "Running dev script...";
4448
- devProcess = spawn("npm", ["run", "dev"], {
5265
+ devProcess = spawn2("npm", ["run", "dev"], {
4449
5266
  stdio: "inherit",
4450
5267
  cwd: process.cwd(),
4451
5268
  shell: true,
@@ -4458,19 +5275,19 @@ var devCommand = new Command4("dev").description("Start development server with
4458
5275
  } else {
4459
5276
  spinner.text = "Starting default dev server...";
4460
5277
  if (existsSync7("vite.config.js") || existsSync7("vite.config.ts")) {
4461
- devProcess = spawn("npx", ["vite", "--port", options.port, "--host", options.host], {
5278
+ devProcess = spawn2("npx", ["vite", "--port", options.port, "--host", options.host], {
4462
5279
  stdio: "inherit",
4463
5280
  cwd: process.cwd(),
4464
5281
  shell: true
4465
5282
  });
4466
5283
  } else if (existsSync7("webpack.config.js")) {
4467
- devProcess = spawn("npx", ["webpack", "serve", "--port", options.port, "--host", options.host], {
5284
+ devProcess = spawn2("npx", ["webpack", "serve", "--port", options.port, "--host", options.host], {
4468
5285
  stdio: "inherit",
4469
5286
  cwd: process.cwd(),
4470
5287
  shell: true
4471
5288
  });
4472
5289
  } else if (packageJson.type === "module" || existsSync7("src/index.js")) {
4473
- devProcess = spawn("npx", ["nodemon", "src/index.js"], {
5290
+ devProcess = spawn2("npx", ["nodemon", "src/index.js"], {
4474
5291
  stdio: "inherit",
4475
5292
  cwd: process.cwd(),
4476
5293
  shell: true,
@@ -4535,11 +5352,12 @@ import { Command as Command5 } from "commander";
4535
5352
  import prompts3 from "prompts";
4536
5353
  import ora5 from "ora";
4537
5354
  import picocolors5 from "picocolors";
4538
- import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
5355
+ import process2, { env as env2 } from "node:process";
5356
+ import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
4539
5357
  import { resolve as resolve5 } from "path";
4540
5358
 
4541
5359
  // src/analyzers/component-analyzer.js
4542
- import { readFileSync as readFileSync4, existsSync as existsSync8 } from "fs";
5360
+ import { readFileSync as readFileSync5, existsSync as existsSync8 } from "fs";
4543
5361
  import { resolve as resolve3 } from "path";
4544
5362
 
4545
5363
  // ../../node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
@@ -4753,7 +5571,7 @@ function expand_(str, isTop) {
4753
5571
  return expansions;
4754
5572
  }
4755
5573
 
4756
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js
5574
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/assert-valid-pattern.js
4757
5575
  var MAX_PATTERN_LENGTH = 1024 * 64;
4758
5576
  var assertValidPattern = (pattern) => {
4759
5577
  if (typeof pattern !== "string") {
@@ -4764,7 +5582,7 @@ var assertValidPattern = (pattern) => {
4764
5582
  }
4765
5583
  };
4766
5584
 
4767
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/brace-expressions.js
5585
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/brace-expressions.js
4768
5586
  var posixClasses = {
4769
5587
  "[:alnum:]": ["\\p{L}\\p{Nl}\\p{Nd}", true],
4770
5588
  "[:alpha:]": ["\\p{L}\\p{Nl}", true],
@@ -4873,12 +5691,15 @@ var parseClass = (glob2, position) => {
4873
5691
  return [comb, uflag, endPos - pos, true];
4874
5692
  };
4875
5693
 
4876
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/unescape.js
4877
- var unescape = (s, { windowsPathsNoEscape = false } = {}) => {
4878
- return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
5694
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/unescape.js
5695
+ var unescape = (s, { windowsPathsNoEscape = false, magicalBraces = true } = {}) => {
5696
+ if (magicalBraces) {
5697
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\])\]/g, "$1$2").replace(/\\([^\/])/g, "$1");
5698
+ }
5699
+ return windowsPathsNoEscape ? s.replace(/\[([^\/\\{}])\]/g, "$1") : s.replace(/((?!\\).|^)\[([^\/\\{}])\]/g, "$1$2").replace(/\\([^\/{}])/g, "$1");
4879
5700
  };
4880
5701
 
4881
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/ast.js
5702
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/ast.js
4882
5703
  var types = /* @__PURE__ */ new Set(["!", "?", "+", "*", "@"]);
4883
5704
  var isExtglobType = (c) => types.has(c);
4884
5705
  var startNoTraversal = "(?!(?:^|/)\\.\\.?(?:$|/))";
@@ -5229,7 +6050,7 @@ var AST = class _AST {
5229
6050
  if (this.#root === this)
5230
6051
  this.#fillNegs();
5231
6052
  if (!this.type) {
5232
- const noEmpty = this.isStart() && this.isEnd();
6053
+ const noEmpty = this.isStart() && this.isEnd() && !this.#parts.some((s) => typeof s !== "string");
5233
6054
  const src = this.#parts.map((p) => {
5234
6055
  const [re, _, hasMagic2, uflag] = typeof p === "string" ? _AST.#parseGlob(p, this.#hasMagic, noEmpty) : p.toRegExpSource(allowDot);
5235
6056
  this.#hasMagic = this.#hasMagic || hasMagic2;
@@ -5339,10 +6160,7 @@ var AST = class _AST {
5339
6160
  }
5340
6161
  }
5341
6162
  if (c === "*") {
5342
- if (noEmpty && glob2 === "*")
5343
- re += starNoEmpty;
5344
- else
5345
- re += star;
6163
+ re += noEmpty && glob2 === "*" ? starNoEmpty : star;
5346
6164
  hasMagic2 = true;
5347
6165
  continue;
5348
6166
  }
@@ -5357,12 +6175,15 @@ var AST = class _AST {
5357
6175
  }
5358
6176
  };
5359
6177
 
5360
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/escape.js
5361
- var escape = (s, { windowsPathsNoEscape = false } = {}) => {
6178
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/escape.js
6179
+ var escape = (s, { windowsPathsNoEscape = false, magicalBraces = false } = {}) => {
6180
+ if (magicalBraces) {
6181
+ return windowsPathsNoEscape ? s.replace(/[?*()[\]{}]/g, "[$&]") : s.replace(/[?*()[\]\\{}]/g, "\\$&");
6182
+ }
5362
6183
  return windowsPathsNoEscape ? s.replace(/[?*()[\]]/g, "[$&]") : s.replace(/[?*()[\]\\]/g, "\\$&");
5363
6184
  };
5364
6185
 
5365
- // ../../node_modules/.pnpm/minimatch@10.0.3/node_modules/minimatch/dist/esm/index.js
6186
+ // ../../node_modules/.pnpm/minimatch@10.1.1/node_modules/minimatch/dist/esm/index.js
5366
6187
  var minimatch = (p, pattern, options = {}) => {
5367
6188
  assertValidPattern(pattern);
5368
6189
  if (!options.nocomment && pattern.charAt(0) === "#") {
@@ -5427,7 +6248,7 @@ var path = {
5427
6248
  };
5428
6249
  var sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
5429
6250
  minimatch.sep = sep;
5430
- var GLOBSTAR = Symbol("globstar **");
6251
+ var GLOBSTAR = /* @__PURE__ */ Symbol("globstar **");
5431
6252
  minimatch.GLOBSTAR = GLOBSTAR;
5432
6253
  var qmark2 = "[^/]";
5433
6254
  var star2 = qmark2 + "*?";
@@ -5999,16 +6820,27 @@ var Minimatch = class {
5999
6820
  pp[i] = twoStar;
6000
6821
  }
6001
6822
  } else if (next === void 0) {
6002
- pp[i - 1] = prev + "(?:\\/|" + twoStar + ")?";
6823
+ pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + ")?";
6003
6824
  } else if (next !== GLOBSTAR) {
6004
6825
  pp[i - 1] = prev + "(?:\\/|\\/" + twoStar + "\\/)" + next;
6005
6826
  pp[i + 1] = GLOBSTAR;
6006
6827
  }
6007
6828
  });
6008
- return pp.filter((p) => p !== GLOBSTAR).join("/");
6829
+ const filtered = pp.filter((p) => p !== GLOBSTAR);
6830
+ if (this.partial && filtered.length >= 1) {
6831
+ const prefixes = [];
6832
+ for (let i = 1; i <= filtered.length; i++) {
6833
+ prefixes.push(filtered.slice(0, i).join("/"));
6834
+ }
6835
+ return "(?:" + prefixes.join("|") + ")";
6836
+ }
6837
+ return filtered.join("/");
6009
6838
  }).join("|");
6010
6839
  const [open, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
6011
6840
  re = "^" + open + re + close + "$";
6841
+ if (this.partial) {
6842
+ re = "^(?:\\/|" + open + re.slice(1, -1) + close + ")$";
6843
+ }
6012
6844
  if (this.negate)
6013
6845
  re = "^(?!" + re + ").+$";
6014
6846
  try {
@@ -6080,10 +6912,10 @@ minimatch.Minimatch = Minimatch;
6080
6912
  minimatch.escape = escape;
6081
6913
  minimatch.unescape = unescape;
6082
6914
 
6083
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/glob.js
6915
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/glob.js
6084
6916
  import { fileURLToPath as fileURLToPath3 } from "node:url";
6085
6917
 
6086
- // ../../node_modules/.pnpm/lru-cache@11.2.1/node_modules/lru-cache/dist/esm/index.js
6918
+ // ../../node_modules/.pnpm/lru-cache@11.2.2/node_modules/lru-cache/dist/esm/index.js
6087
6919
  var defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
6088
6920
  var warned = /* @__PURE__ */ new Set();
6089
6921
  var PROCESS = typeof process === "object" && !!process ? process : {};
@@ -6127,7 +6959,6 @@ if (typeof AC === "undefined") {
6127
6959
  };
6128
6960
  }
6129
6961
  var shouldWarn = (code) => !warned.has(code);
6130
- var TYPE = Symbol("type");
6131
6962
  var isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
6132
6963
  var getUintArray = (max) => !isPosInt(max) ? null : max <= Math.pow(2, 8) ? Uint8Array : max <= Math.pow(2, 16) ? Uint16Array : max <= Math.pow(2, 32) ? Uint32Array : max <= Number.MAX_SAFE_INTEGER ? ZeroArray : null;
6133
6964
  var ZeroArray = class extends Array {
@@ -7129,7 +7960,8 @@ var LRUCache = class _LRUCache {
7129
7960
  return fetchFail(ac.signal.reason);
7130
7961
  }
7131
7962
  const bf2 = p;
7132
- if (this.#valList[index] === p) {
7963
+ const vl = this.#valList[index];
7964
+ if (vl === p || ignoreAbort && updateCache && vl === void 0) {
7133
7965
  if (v2 === void 0) {
7134
7966
  if (bf2.__staleWhileFetching !== void 0) {
7135
7967
  this.#valList[index] = bf2.__staleWhileFetching;
@@ -7483,7 +8315,7 @@ var LRUCache = class _LRUCache {
7483
8315
  }
7484
8316
  };
7485
8317
 
7486
- // ../../node_modules/.pnpm/path-scurry@2.0.0/node_modules/path-scurry/dist/esm/index.js
8318
+ // ../../node_modules/.pnpm/path-scurry@2.0.1/node_modules/path-scurry/dist/esm/index.js
7487
8319
  import { posix, win32 } from "node:path";
7488
8320
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7489
8321
  import { lstatSync, readdir as readdirCB, readdirSync, readlinkSync, realpathSync as rps } from "fs";
@@ -7502,37 +8334,37 @@ var isStream = (s) => !!s && typeof s === "object" && (s instanceof Minipass ||
7502
8334
  var isReadable = (s) => !!s && typeof s === "object" && s instanceof EventEmitter && typeof s.pipe === "function" && // node core Writable streams have a pipe() method, but it throws
7503
8335
  s.pipe !== Stream.Writable.prototype.pipe;
7504
8336
  var isWritable = (s) => !!s && typeof s === "object" && s instanceof EventEmitter && typeof s.write === "function" && typeof s.end === "function";
7505
- var EOF = Symbol("EOF");
7506
- var MAYBE_EMIT_END = Symbol("maybeEmitEnd");
7507
- var EMITTED_END = Symbol("emittedEnd");
7508
- var EMITTING_END = Symbol("emittingEnd");
7509
- var EMITTED_ERROR = Symbol("emittedError");
7510
- var CLOSED = Symbol("closed");
7511
- var READ = Symbol("read");
7512
- var FLUSH = Symbol("flush");
7513
- var FLUSHCHUNK = Symbol("flushChunk");
7514
- var ENCODING = Symbol("encoding");
7515
- var DECODER = Symbol("decoder");
7516
- var FLOWING = Symbol("flowing");
7517
- var PAUSED = Symbol("paused");
7518
- var RESUME = Symbol("resume");
7519
- var BUFFER = Symbol("buffer");
7520
- var PIPES = Symbol("pipes");
7521
- var BUFFERLENGTH = Symbol("bufferLength");
7522
- var BUFFERPUSH = Symbol("bufferPush");
7523
- var BUFFERSHIFT = Symbol("bufferShift");
7524
- var OBJECTMODE = Symbol("objectMode");
7525
- var DESTROYED = Symbol("destroyed");
7526
- var ERROR = Symbol("error");
7527
- var EMITDATA = Symbol("emitData");
7528
- var EMITEND = Symbol("emitEnd");
7529
- var EMITEND2 = Symbol("emitEnd2");
7530
- var ASYNC = Symbol("async");
7531
- var ABORT = Symbol("abort");
7532
- var ABORTED = Symbol("aborted");
7533
- var SIGNAL = Symbol("signal");
7534
- var DATALISTENERS = Symbol("dataListeners");
7535
- var DISCARDED = Symbol("discarded");
8337
+ var EOF = /* @__PURE__ */ Symbol("EOF");
8338
+ var MAYBE_EMIT_END = /* @__PURE__ */ Symbol("maybeEmitEnd");
8339
+ var EMITTED_END = /* @__PURE__ */ Symbol("emittedEnd");
8340
+ var EMITTING_END = /* @__PURE__ */ Symbol("emittingEnd");
8341
+ var EMITTED_ERROR = /* @__PURE__ */ Symbol("emittedError");
8342
+ var CLOSED = /* @__PURE__ */ Symbol("closed");
8343
+ var READ = /* @__PURE__ */ Symbol("read");
8344
+ var FLUSH = /* @__PURE__ */ Symbol("flush");
8345
+ var FLUSHCHUNK = /* @__PURE__ */ Symbol("flushChunk");
8346
+ var ENCODING = /* @__PURE__ */ Symbol("encoding");
8347
+ var DECODER = /* @__PURE__ */ Symbol("decoder");
8348
+ var FLOWING = /* @__PURE__ */ Symbol("flowing");
8349
+ var PAUSED = /* @__PURE__ */ Symbol("paused");
8350
+ var RESUME = /* @__PURE__ */ Symbol("resume");
8351
+ var BUFFER = /* @__PURE__ */ Symbol("buffer");
8352
+ var PIPES = /* @__PURE__ */ Symbol("pipes");
8353
+ var BUFFERLENGTH = /* @__PURE__ */ Symbol("bufferLength");
8354
+ var BUFFERPUSH = /* @__PURE__ */ Symbol("bufferPush");
8355
+ var BUFFERSHIFT = /* @__PURE__ */ Symbol("bufferShift");
8356
+ var OBJECTMODE = /* @__PURE__ */ Symbol("objectMode");
8357
+ var DESTROYED = /* @__PURE__ */ Symbol("destroyed");
8358
+ var ERROR = /* @__PURE__ */ Symbol("error");
8359
+ var EMITDATA = /* @__PURE__ */ Symbol("emitData");
8360
+ var EMITEND = /* @__PURE__ */ Symbol("emitEnd");
8361
+ var EMITEND2 = /* @__PURE__ */ Symbol("emitEnd2");
8362
+ var ASYNC = /* @__PURE__ */ Symbol("async");
8363
+ var ABORT = /* @__PURE__ */ Symbol("abort");
8364
+ var ABORTED = /* @__PURE__ */ Symbol("aborted");
8365
+ var SIGNAL = /* @__PURE__ */ Symbol("signal");
8366
+ var DATALISTENERS = /* @__PURE__ */ Symbol("dataListeners");
8367
+ var DISCARDED = /* @__PURE__ */ Symbol("discarded");
7536
8368
  var defer = (fn) => Promise.resolve().then(fn);
7537
8369
  var nodefer = (fn) => fn();
7538
8370
  var isEndish = (ev) => ev === "end" || ev === "finish" || ev === "prefinish";
@@ -8368,7 +9200,7 @@ var Minipass = class extends EventEmitter {
8368
9200
  }
8369
9201
  };
8370
9202
 
8371
- // ../../node_modules/.pnpm/path-scurry@2.0.0/node_modules/path-scurry/dist/esm/index.js
9203
+ // ../../node_modules/.pnpm/path-scurry@2.0.1/node_modules/path-scurry/dist/esm/index.js
8372
9204
  var realpathSync = rps.native;
8373
9205
  var defaultFS = {
8374
9206
  lstatSync,
@@ -8413,7 +9245,7 @@ var ENOREALPATH = 512;
8413
9245
  var ENOCHILD = ENOTDIR | ENOENT | ENOREALPATH;
8414
9246
  var TYPEMASK = 1023;
8415
9247
  var entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
8416
- var normalizeCache = /* @__PURE__ */ new Map();
9248
+ var normalizeCache = new LRUCache({ max: 2 ** 12 });
8417
9249
  var normalize = (s) => {
8418
9250
  const c = normalizeCache.get(s);
8419
9251
  if (c)
@@ -8422,7 +9254,7 @@ var normalize = (s) => {
8422
9254
  normalizeCache.set(s, n);
8423
9255
  return n;
8424
9256
  };
8425
- var normalizeNocaseCache = /* @__PURE__ */ new Map();
9257
+ var normalizeNocaseCache = new LRUCache({ max: 2 ** 12 });
8426
9258
  var normalizeNocase = (s) => {
8427
9259
  const c = normalizeNocaseCache.get(s);
8428
9260
  if (c)
@@ -8445,7 +9277,7 @@ var ChildrenCache = class extends LRUCache {
8445
9277
  });
8446
9278
  }
8447
9279
  };
8448
- var setAsCwd = Symbol("PathScurry setAsCwd");
9280
+ var setAsCwd = /* @__PURE__ */ Symbol("PathScurry setAsCwd");
8449
9281
  var PathBase = class {
8450
9282
  /**
8451
9283
  * the basename of this path
@@ -8579,6 +9411,7 @@ var PathBase = class {
8579
9411
  get parentPath() {
8580
9412
  return (this.parent || this).fullpath();
8581
9413
  }
9414
+ /* c8 ignore start */
8582
9415
  /**
8583
9416
  * Deprecated alias for Dirent['parentPath'] Somewhat counterintuitively,
8584
9417
  * this property refers to the *parent* path, not the path object itself.
@@ -8588,6 +9421,7 @@ var PathBase = class {
8588
9421
  get path() {
8589
9422
  return this.parentPath;
8590
9423
  }
9424
+ /* c8 ignore stop */
8591
9425
  /**
8592
9426
  * Do not create new Path objects directly. They should always be accessed
8593
9427
  * via the PathScurry class or other methods on the Path class.
@@ -9910,7 +10744,7 @@ var PathScurryBase = class {
9910
10744
  const dirs = /* @__PURE__ */ new Set();
9911
10745
  const queue = [entry];
9912
10746
  let processing = 0;
9913
- const process2 = () => {
10747
+ const process3 = () => {
9914
10748
  let paused = false;
9915
10749
  while (!paused) {
9916
10750
  const dir = queue.shift();
@@ -9951,9 +10785,9 @@ var PathScurryBase = class {
9951
10785
  }
9952
10786
  }
9953
10787
  if (paused && !results.flowing) {
9954
- results.once("drain", process2);
10788
+ results.once("drain", process3);
9955
10789
  } else if (!sync2) {
9956
- process2();
10790
+ process3();
9957
10791
  }
9958
10792
  };
9959
10793
  let sync2 = true;
@@ -9961,7 +10795,7 @@ var PathScurryBase = class {
9961
10795
  sync2 = false;
9962
10796
  }
9963
10797
  };
9964
- process2();
10798
+ process3();
9965
10799
  return results;
9966
10800
  }
9967
10801
  streamSync(entry = this.cwd, opts = {}) {
@@ -9979,7 +10813,7 @@ var PathScurryBase = class {
9979
10813
  }
9980
10814
  const queue = [entry];
9981
10815
  let processing = 0;
9982
- const process2 = () => {
10816
+ const process3 = () => {
9983
10817
  let paused = false;
9984
10818
  while (!paused) {
9985
10819
  const dir = queue.shift();
@@ -10013,9 +10847,9 @@ var PathScurryBase = class {
10013
10847
  }
10014
10848
  }
10015
10849
  if (paused && !results.flowing)
10016
- results.once("drain", process2);
10850
+ results.once("drain", process3);
10017
10851
  };
10018
- process2();
10852
+ process3();
10019
10853
  return results;
10020
10854
  }
10021
10855
  chdir(path2 = this.cwd) {
@@ -10094,7 +10928,7 @@ var PathScurryDarwin = class extends PathScurryPosix {
10094
10928
  var Path = process.platform === "win32" ? PathWin32 : PathPosix;
10095
10929
  var PathScurry = process.platform === "win32" ? PathScurryWin32 : process.platform === "darwin" ? PathScurryDarwin : PathScurryPosix;
10096
10930
 
10097
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/pattern.js
10931
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/pattern.js
10098
10932
  var isPatternList = (pl) => pl.length >= 1;
10099
10933
  var isGlobList = (gl) => gl.length >= 1;
10100
10934
  var Pattern = class _Pattern {
@@ -10259,7 +11093,7 @@ var Pattern = class _Pattern {
10259
11093
  }
10260
11094
  };
10261
11095
 
10262
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/ignore.js
11096
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/ignore.js
10263
11097
  var defaultPlatform2 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
10264
11098
  var Ignore = class {
10265
11099
  relative;
@@ -10346,7 +11180,7 @@ var Ignore = class {
10346
11180
  }
10347
11181
  };
10348
11182
 
10349
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/processor.js
11183
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/processor.js
10350
11184
  var HasWalkedCache = class _HasWalkedCache {
10351
11185
  store;
10352
11186
  constructor(store = /* @__PURE__ */ new Map()) {
@@ -10567,7 +11401,7 @@ var Processor = class _Processor {
10567
11401
  }
10568
11402
  };
10569
11403
 
10570
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/walker.js
11404
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/walker.js
10571
11405
  var makeIgnore = (ignore, opts) => typeof ignore === "string" ? new Ignore([ignore], opts) : Array.isArray(ignore) ? new Ignore(ignore, opts) : ignore;
10572
11406
  var GlobUtil = class {
10573
11407
  path;
@@ -10894,7 +11728,7 @@ var GlobStream = class extends GlobUtil {
10894
11728
  }
10895
11729
  };
10896
11730
 
10897
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/glob.js
11731
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/glob.js
10898
11732
  var defaultPlatform3 = typeof process === "object" && process && typeof process.platform === "string" ? process.platform : "linux";
10899
11733
  var Glob = class {
10900
11734
  absolute;
@@ -11094,7 +11928,7 @@ var Glob = class {
11094
11928
  }
11095
11929
  };
11096
11930
 
11097
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/has-magic.js
11931
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/has-magic.js
11098
11932
  var hasMagic = (pattern, options = {}) => {
11099
11933
  if (!Array.isArray(pattern)) {
11100
11934
  pattern = [pattern];
@@ -11106,7 +11940,7 @@ var hasMagic = (pattern, options = {}) => {
11106
11940
  return false;
11107
11941
  };
11108
11942
 
11109
- // ../../node_modules/.pnpm/glob@11.0.3/node_modules/glob/dist/esm/index.js
11943
+ // ../../node_modules/.pnpm/glob@11.1.0/node_modules/glob/dist/esm/index.js
11110
11944
  function globStreamSync(pattern, options = {}) {
11111
11945
  return new Glob(pattern, options).streamSync();
11112
11946
  }
@@ -11268,7 +12102,7 @@ async function analyzeComponentFile(filePath, _options = {}) {
11268
12102
  });
11269
12103
  return analysis;
11270
12104
  }
11271
- const content = readFileSync4(resolve3(filePath), "utf-8");
12105
+ const content = readFileSync5(resolve3(filePath), "utf-8");
11272
12106
  analysis.size = Buffer.byteLength(content, "utf8");
11273
12107
  analysis.lines = content.split("\n").length;
11274
12108
  const syntaxAnalysis = analyzeSyntax(content, filePath);
@@ -11629,7 +12463,7 @@ async function analyzeHydration(options = {}) {
11629
12463
  }
11630
12464
 
11631
12465
  // src/validators/project-validator.js
11632
- import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
12466
+ import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
11633
12467
  import { resolve as resolve4 } from "path";
11634
12468
  async function validateProject(_options = {}) {
11635
12469
  const validation = {
@@ -11696,7 +12530,7 @@ function validatePackageJson() {
11696
12530
  const issues = [];
11697
12531
  const recommendations = [];
11698
12532
  try {
11699
- const packageJson = JSON.parse(readFileSync5("package.json", "utf-8"));
12533
+ const packageJson = JSON.parse(readFileSync6("package.json", "utf-8"));
11700
12534
  const requiredFields = ["name", "version", "description"];
11701
12535
  requiredFields.forEach((field) => {
11702
12536
  const hasField = packageJson[field];
@@ -11852,7 +12686,7 @@ var debugCommand = new Command5("debug").description("Debug and analyze Coherent
11852
12686
  });
11853
12687
  if (!response.target) {
11854
12688
  console.log(picocolors5.yellow("\u{1F44B} Debug cancelled"));
11855
- process.exit(0);
12689
+ process2.exit(0);
11856
12690
  }
11857
12691
  debugTarget = response.target;
11858
12692
  }
@@ -11894,14 +12728,14 @@ var debugCommand = new Command5("debug").description("Debug and analyze Coherent
11894
12728
  console.log();
11895
12729
  console.log(picocolors5.red("\u274C Debug Error:"));
11896
12730
  console.log(picocolors5.gray(` ${error.message}`));
11897
- if (process.env.DEBUG) {
12731
+ if (env2.DEBUG) {
11898
12732
  console.log();
11899
12733
  console.log(picocolors5.gray("Stack trace:"));
11900
12734
  console.log(picocolors5.gray(error.stack));
11901
12735
  } else {
11902
12736
  console.log(picocolors5.gray(" Run with DEBUG=1 for detailed error information"));
11903
12737
  }
11904
- process.exit(1);
12738
+ process2.exit(1);
11905
12739
  }
11906
12740
  });
11907
12741
  debugCommand.command("component [name]").description("Analyze component structure and performance").option("-f, --file <file>", "component file path").option("--props <props>", "test component with specific props (JSON)").option("--deep", "perform deep structural analysis").action(async (name, options) => {
@@ -11980,7 +12814,7 @@ async function analyzeConfiguration(_options = {}) {
11980
12814
  };
11981
12815
  if (existsSync10("package.json")) {
11982
12816
  try {
11983
- const packageJson = JSON.parse(readFileSync6("package.json", "utf-8"));
12817
+ const packageJson = JSON.parse(readFileSync7("package.json", "utf-8"));
11984
12818
  const hasCoherentDeps = Object.keys(packageJson.dependencies || {}).some((dep) => dep.startsWith("@coherent.js/"));
11985
12819
  if (!hasCoherentDeps) {
11986
12820
  analysis.summary.issues.push("No Coherent.js dependencies found");
@@ -12089,12 +12923,12 @@ async function generateHTMLReport(result) {
12089
12923
  <h1>\u{1F50D} Coherent.js Debug Report</h1>
12090
12924
  <p>Generated: ${(/* @__PURE__ */ new Date()).toLocaleString()}</p>
12091
12925
  </div>
12092
-
12926
+
12093
12927
  <div class="section">
12094
12928
  <h2>Summary</h2>
12095
12929
  <pre>${JSON.stringify(result.summary, null, 2)}</pre>
12096
12930
  </div>
12097
-
12931
+
12098
12932
  ${result.issues ? `
12099
12933
  <div class="section">
12100
12934
  <h2>Issues</h2>
@@ -12103,7 +12937,7 @@ async function generateHTMLReport(result) {
12103
12937
  `).join("")}
12104
12938
  </div>
12105
12939
  ` : ""}
12106
-
12940
+
12107
12941
  ${result.recommendations ? `
12108
12942
  <div class="section">
12109
12943
  <h2>Recommendations</h2>
@@ -12129,7 +12963,7 @@ var __dirname = dirname3(__filename);
12129
12963
  var version = "1.0.1";
12130
12964
  try {
12131
12965
  const packagePath = join8(__dirname, "..", "package.json");
12132
- const packageJson = JSON.parse(readFileSync7(packagePath, "utf-8"));
12966
+ const packageJson = JSON.parse(readFileSync8(packagePath, "utf-8"));
12133
12967
  version = packageJson.version;
12134
12968
  } catch {
12135
12969
  }