offbyt 1.0.0 โ†’ 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js CHANGED
@@ -9,6 +9,7 @@ import { connectFrontendBackend } from './lib/modes/connect.js';
9
9
  import { runDoctor } from './lib/utils/doctor.js';
10
10
  import { getInteractiveSetup, displaySetupSummary } from './lib/modes/interactiveSetup.js';
11
11
  import { generateWithConfig } from './lib/modes/configBasedGenerator.js';
12
+ import { printBanner, printSection, printSuccess, printStep, printSummary, printFooter } from './lib/utils/cliFormatter.js';
12
13
 
13
14
  const program = new Command();
14
15
 
@@ -21,8 +22,17 @@ function parsePort(value) {
21
22
  return port;
22
23
  }
23
24
 
25
+ function parsePositiveInteger(value) {
26
+ const parsed = Number.parseInt(value, 10);
27
+ if (Number.isNaN(parsed) || parsed < 1) {
28
+ throw new Error('Value must be a positive integer');
29
+ }
30
+
31
+ return parsed;
32
+ }
33
+
24
34
  program
25
- .name('offbyt')
35
+ .name('offbyte')
26
36
  .description('Hybrid Backend Generator - Offline + AI Powered')
27
37
  .version('1.0.0');
28
38
 
@@ -32,8 +42,12 @@ program
32
42
  .option('--no-auto-connect', 'Skip auto-connect after generation')
33
43
  .option('--quick', 'Use default configuration (no questions)')
34
44
  .option('--no-api-detect', 'Skip automatic API detection from frontend')
45
+ .option('--no-verify', 'Skip Phase-2 backend verification (startup + health check)')
46
+ .option('--verify-timeout <ms>', 'Health-check timeout in milliseconds', parsePositiveInteger, 45000)
47
+ .option('--strict-verify', 'Fail command if Phase-2 verification does not pass')
35
48
  .action(async (projectPath, options) => {
36
49
  try {
50
+ printBanner();
37
51
  const workingPath = projectPath || process.cwd();
38
52
  let config;
39
53
 
@@ -49,7 +63,8 @@ program
49
63
  enableCaching: false,
50
64
  enableLogging: true
51
65
  };
52
- console.log(chalk.cyan('Using default configuration...\n'));
66
+ printSection('Quick Mode: Using Default Configuration');
67
+ printSuccess('Configuration ready - MongoDB + Express + Auth enabled');
53
68
  } else {
54
69
  // Interactive setup
55
70
  config = await getInteractiveSetup();
@@ -64,7 +79,7 @@ program
64
79
 
65
80
  // AUTOMATIC: Smart API detection & generation
66
81
  if (options.apiDetect !== false) {
67
- console.log(chalk.cyan('\n\n๐ŸŽฏ Running Smart API Detection...\n'));
82
+ console.log(chalk.cyan('\n\n[PIPELINE] Running Smart API Detection...\n'));
68
83
  const { generateSmartAPI } = await import('./lib/modes/generateApi.js');
69
84
  try {
70
85
  await generateSmartAPI(workingPath, { inject: true, config });
@@ -78,6 +93,48 @@ program
78
93
  }
79
94
  }
80
95
 
96
+ let shouldRunVerify = options.verify !== false;
97
+ if (options.verify !== false) {
98
+ const verifyChoice = await inquirer.prompt([
99
+ {
100
+ type: 'confirm',
101
+ name: 'runVerify',
102
+ message: 'Run post-generation health check (Phase-2)?',
103
+ default: true
104
+ }
105
+ ]);
106
+ shouldRunVerify = verifyChoice.runVerify;
107
+ }
108
+
109
+ if (shouldRunVerify) {
110
+ console.log(chalk.cyan('\n\n[VERIFY] Running Phase-2 verification...\n'));
111
+ const { verifyGeneratedBackend } = await import('./lib/utils/postGenerationVerifier.js');
112
+ const verification = await verifyGeneratedBackend(workingPath, {
113
+ timeoutMs: options.verifyTimeout,
114
+ maxAttempts: 2
115
+ });
116
+
117
+ if (verification.ok) {
118
+ console.log(chalk.green(`\n[OK] Phase-2 passed at http://localhost:${verification.port}${verification.healthEndpoint}\n`));
119
+ } else {
120
+ console.log(chalk.yellow('\n[WARN] Phase-2 verification did not fully pass.'));
121
+ console.log(chalk.yellow(` Reason: ${verification.issue}`));
122
+ if (verification.fixesApplied.length > 0) {
123
+ console.log(chalk.gray(' Auto-fixes attempted:'));
124
+ for (const fix of verification.fixesApplied) {
125
+ console.log(chalk.gray(` - ${fix}`));
126
+ }
127
+ }
128
+ console.log(chalk.gray(' You can skip this step next time using --no-verify.\n'));
129
+
130
+ if (options.strictVerify) {
131
+ throw new Error(`Phase-2 verification failed: ${verification.issue}`);
132
+ }
133
+ }
134
+ } else {
135
+ console.log(chalk.gray('\n[SKIP] Post-generation health check skipped by user choice.\n'));
136
+ }
137
+
81
138
  // Auto-connect if enabled
82
139
  if (options.autoConnect) {
83
140
  console.log(chalk.cyan('Auto-connecting frontend & backend...\n'));
@@ -189,18 +246,76 @@ program
189
246
  }
190
247
  });
191
248
 
249
+ program
250
+ .command('industry-validate [path]')
251
+ .description('Run offline Industry Mode contract validation and print report')
252
+ .option('--json', 'Print JSON output')
253
+ .action(async (projectPath, options) => {
254
+ try {
255
+ const {
256
+ runIndustryContractCheck,
257
+ printIndustryReport
258
+ } = await import('./lib/utils/industryMode.js');
259
+
260
+ const report = await runIndustryContractCheck(projectPath || process.cwd());
261
+ if (options.json) {
262
+ console.log(JSON.stringify(report, null, 2));
263
+ } else {
264
+ printIndustryReport(report);
265
+ }
266
+
267
+ process.exit(report.ok ? 0 : 1);
268
+ } catch (error) {
269
+ console.error(chalk.red('Error:', error.message));
270
+ process.exit(1);
271
+ }
272
+ });
273
+
274
+ program
275
+ .command('industry-smoke [path]')
276
+ .description('Run offline Industry Mode smoke checks and save report file')
277
+ .option('--timeout <ms>', 'Smoke timeout in milliseconds', parsePositiveInteger, 45000)
278
+ .option('--json', 'Print JSON output')
279
+ .action(async (projectPath, options) => {
280
+ try {
281
+ const {
282
+ runIndustrySmoke,
283
+ writeIndustryReport,
284
+ printIndustryReport
285
+ } = await import('./lib/utils/industryMode.js');
286
+
287
+ const workingPath = projectPath || process.cwd();
288
+ const report = await runIndustrySmoke(workingPath, {
289
+ timeoutMs: options.timeout
290
+ });
291
+
292
+ const filePath = writeIndustryReport(workingPath, report);
293
+ if (options.json) {
294
+ console.log(JSON.stringify({ ...report, reportFile: filePath }, null, 2));
295
+ } else {
296
+ printIndustryReport(report);
297
+ console.log(chalk.cyan(`Report saved: ${filePath}`));
298
+ }
299
+
300
+ process.exit(report.ok ? 0 : 1);
301
+ } catch (error) {
302
+ console.error(chalk.red('Error:', error.message));
303
+ process.exit(1);
304
+ }
305
+ });
306
+
192
307
  program.parse(process.argv);
193
308
 
194
309
  if (!process.argv.slice(2).length) {
195
310
  program.outputHelp();
196
311
  console.log(chalk.cyan('\nQuick Start:\n'));
197
312
  console.log(chalk.white(' Option 1 (Recommended):'));
198
- console.log(chalk.gray(' offbyt generate # Generate + Auto-connect\n'));
313
+ console.log(chalk.gray(' offbyte generate # Generate + Auto-connect\n'));
199
314
  console.log(chalk.white(' Option 2 (Skip auto-connect):'));
200
- console.log(chalk.gray(' offbyt generate --no-auto-connect # Generate only\n'));
315
+ console.log(chalk.gray(' offbyte generate --no-auto-connect # Generate only\n'));
201
316
  console.log(chalk.white(' Option 3 (Just connect):'));
202
- console.log(chalk.gray(' offbyt connect [path] # Auto-connect existing project\n'));
317
+ console.log(chalk.gray(' offbyte connect [path] # Auto-connect existing project\n'));
203
318
  console.log(chalk.white(' Option 4 (Deploy live):'));
204
- console.log(chalk.gray(' offbyt deploy [path] # Deploy + auto-connect URLs\n'));
319
+ console.log(chalk.gray(' offbyte deploy [path] # Deploy + auto-connect URLs\n'));
205
320
  }
206
321
 
@@ -0,0 +1,22 @@
1
+ {
2
+ "requiredContract": [
3
+ "database",
4
+ "framework",
5
+ "auth",
6
+ "resources"
7
+ ],
8
+ "minResources": 1,
9
+ "requireAuth": true,
10
+ "requiredSecurity": [
11
+ "validation",
12
+ "rateLimit",
13
+ "helmet",
14
+ "cors"
15
+ ],
16
+ "smoke": {
17
+ "checkHealth": true,
18
+ "checkResourceList": true,
19
+ "maxResources": 10,
20
+ "timeoutMs": 45000
21
+ }
22
+ }
@@ -21,61 +21,61 @@ export async function generateWithConfig(projectPath, config) {
21
21
  // Step 1: Create structure
22
22
  const step1 = ora('Creating backend structure...').start();
23
23
  createBackendStructure(backendPath, config);
24
- step1.succeed('รขล“โ€ฆ Backend structure created');
24
+ step1.succeed('Backend structure created');
25
25
 
26
26
  // Step 2: Setup database
27
27
  const step2 = ora('Setting up database configuration...').start();
28
28
  setupDatabaseConfig(backendPath, config);
29
- step2.succeed('รขล“โ€ฆ Database configured');
29
+ step2.succeed('Database configured');
30
30
 
31
31
  // Step 3: Create middleware
32
32
  const step3 = ora('Setting up middleware...').start();
33
33
  setupMiddleware(backendPath, config);
34
- step3.succeed('รขล“โ€ฆ Middleware configured');
34
+ step3.succeed('Middleware configured');
35
35
 
36
36
  // Step 4: Create authentication (if enabled)
37
37
  if (config.enableAuth) {
38
38
  const step4 = ora('Setting up authentication system...').start();
39
39
  setupAuthentication(backendPath, config);
40
- step4.succeed('รขล“โ€ฆ Authentication configured');
40
+ step4.succeed('Authentication configured');
41
41
  }
42
42
 
43
43
  // Step 5: Create main server
44
44
  const step5 = ora('Creating main server file...').start();
45
45
  createServerFile(backendPath, config);
46
- step5.succeed('รขล“โ€ฆ Server created');
46
+ step5.succeed('Server created');
47
47
 
48
48
  // Step 6: Setup Socket.io (if enabled)
49
49
  if (config.enableSocket) {
50
50
  const step6 = ora('Setting up realtime sockets...').start();
51
51
  setupSockets(backendPath, config);
52
- step6.succeed('รขล“โ€ฆ Sockets configured');
52
+ step6.succeed('Sockets configured');
53
53
  }
54
54
 
55
55
  // Step 7: Create package.json
56
56
  const step7 = ora('Creating package.json...').start();
57
57
  createPackageJson(backendPath, config);
58
- step7.succeed('รขล“โ€ฆ Dependencies configured');
58
+ step7.succeed('Dependencies configured');
59
59
 
60
60
  // Step 8: Create .env
61
61
  const step8 = ora('Creating environment configuration...').start();
62
62
  createEnvFile(backendPath, config);
63
- step8.succeed('รขล“โ€ฆ Environment files created (.env, .env.example, .gitignore)');
63
+ step8.succeed('Environment files created (.env, .env.example, .gitignore)');
64
64
 
65
65
  // Step 9: Scan frontend and generate detected resources (skip - will be done by smart API)
66
66
  const step9 = ora('Skipping sample generation (using Smart API detection instead)...').start();
67
- step9.succeed('รขล“โ€ฆ Backend structure ready for Smart API generation');
67
+ step9.succeed('Backend structure ready for Smart API generation');
68
68
 
69
69
  // Step 10: Generate SQL files for SQL databases
70
70
  if (['mysql', 'postgresql', 'sqlite'].includes(config.database)) {
71
71
  const step10 = ora('Generating SQL scripts...').start();
72
- step10.succeed(`รขล“โ€ฆ SQL scripts created in backend/sql/ (ready for ${config.database.toUpperCase()})`);
72
+ step10.succeed(`SQL scripts created in backend/sql/ (ready for ${config.database.toUpperCase()})`);
73
73
  }
74
74
 
75
- console.log(chalk.cyan('\nรฐลธลฝโ€ฐ Backend structure created!\n'));
76
- console.log(chalk.cyan('รขล“โ€ฆ Smart API detection will run automatically next...\n'));
75
+ console.log(chalk.cyan('\n[OK] Backend structure created.\n'));
76
+ console.log(chalk.cyan('[OK] Smart API detection will run automatically next...\n'));
77
77
 
78
- console.log(chalk.yellow('รฐลธโ€œย Next steps:'));
78
+ console.log(chalk.yellow('Next steps:'));
79
79
  console.log(chalk.yellow(` 1. cd ${projectPath}/backend`));
80
80
  console.log(chalk.yellow(' 2. npm install'));
81
81
  console.log(chalk.yellow(' 3. Review & update .env file:'));
@@ -95,10 +95,10 @@ export async function generateWithConfig(projectPath, config) {
95
95
  console.log(chalk.yellow(' 4. npm run dev'));
96
96
  }
97
97
 
98
- console.log(chalk.cyan('\nรฐลธโ€™ยก Tip: Never commit .env to git! Use .env.example instead.\n'));
98
+ console.log(chalk.cyan('\nTip: Never commit .env to git! Use .env.example instead.\n'));
99
99
 
100
100
  } catch (error) {
101
- console.error(chalk.red('รขยล’ Error generating backend:'), error.message);
101
+ console.error(chalk.red('Error generating backend:'), error.message);
102
102
  throw error;
103
103
  }
104
104
  }
@@ -364,9 +364,9 @@ app.use(requestLogger);
364
364
 
365
365
  // Database Connection
366
366
  connectDatabase().then(() => {
367
- console.log('รขล“โ€ฆ Database connected');
367
+ console.log('[OK] Database connected');
368
368
  }).catch(error => {
369
- console.error('รขยล’ Database connection failed:', error);
369
+ console.error('[ERR] Database connection failed:', error);
370
370
  process.exit(1);
371
371
  });
372
372
 
@@ -397,11 +397,11 @@ io.on('connection', (socket) => {
397
397
  });
398
398
 
399
399
  server.listen(PORT, () => {
400
- console.log(\`รฐลธลกโ‚ฌ Server running on http://localhost:\${PORT}\`);
400
+ console.log(\`[OK] Server running on http://localhost:\${PORT}\`);
401
401
  });`;
402
402
  } else {
403
403
  serverContent += `app.listen(PORT, () => {
404
- console.log(\`รฐลธลกโ‚ฌ Server running on http://localhost:\${PORT}\`);
404
+ console.log(\`[OK] Server running on http://localhost:\${PORT}\`);
405
405
  });`;
406
406
  }
407
407
 
@@ -430,9 +430,9 @@ await fastify.register(fastifyCors, { origin: '*' });
430
430
 
431
431
  // Database Connection
432
432
  connectDatabase().then(() => {
433
- fastify.log.info('รขล“โ€ฆ Database connected');
433
+ fastify.log.info('[OK] Database connected');
434
434
  }).catch(error => {
435
- fastify.log.error('รขยล’ Database connection failed:', error);
435
+ fastify.log.error('[ERR] Database connection failed:', error);
436
436
  process.exit(1);
437
437
  });
438
438
 
@@ -468,7 +468,7 @@ io.on('connection', (socket) => {
468
468
  serverContent += `// Start server
469
469
  try {
470
470
  await fastify.listen({ port: PORT, host: '0.0.0.0' });
471
- console.log(\`รฐลธลกโ‚ฌ Fastify server running on http://localhost:\${PORT}\`);
471
+ console.log(\`[OK] Fastify server running on http://localhost:\${PORT}\`);
472
472
  } catch (err) {
473
473
  fastify.log.error(err);
474
474
  process.exit(1);
@@ -535,7 +535,7 @@ async function bootstrap() {
535
535
  });
536
536
  });
537
537
  ` : ''}
538
- console.log(\`รฐลธลกโ‚ฌ NestJS server running on http://localhost:\${PORT}\`);
538
+ console.log(\`[OK] NestJS server running on http://localhost:\${PORT}\`);
539
539
  }
540
540
 
541
541
  bootstrap();`;
@@ -553,14 +553,14 @@ ${['postgresql', 'mysql', 'sqlite'].includes(config.database) ? `import { TypeOr
553
553
  ConfigModule.forRoot({
554
554
  isGlobal: true
555
555
  }),
556
- ${config.database === 'mongodb' ? ` MongooseModule.forRoot(process.env.MONGODB_URI || 'mongodb://localhost:27017/offbyt'),` : ''}
556
+ ${config.database === 'mongodb' ? ` MongooseModule.forRoot(process.env.MONGODB_URI || 'mongodb://localhost:27017/offbyte'),` : ''}
557
557
  ${config.database === 'postgresql' ? ` TypeOrmModule.forRoot({
558
558
  type: 'postgres',
559
559
  host: process.env.DB_HOST || 'localhost',
560
560
  port: parseInt(process.env.DB_PORT) || 5432,
561
561
  username: process.env.DB_USER || 'postgres',
562
562
  password: process.env.DB_PASSWORD || 'password',
563
- database: process.env.DB_NAME || 'offbyt',
563
+ database: process.env.DB_NAME || 'offbyte',
564
564
  autoLoadEntities: true,
565
565
  synchronize: process.env.NODE_ENV === 'development'
566
566
  }),` : ''}
@@ -570,7 +570,7 @@ ${config.database === 'mysql' ? ` TypeOrmModule.forRoot({
570
570
  port: parseInt(process.env.DB_PORT) || 3306,
571
571
  username: process.env.DB_USER || 'root',
572
572
  password: process.env.DB_PASSWORD || 'password',
573
- database: process.env.DB_NAME || 'offbyt',
573
+ database: process.env.DB_NAME || 'offbyte',
574
574
  autoLoadEntities: true,
575
575
  synchronize: process.env.NODE_ENV === 'development'
576
576
  }),` : ''}
@@ -695,9 +695,9 @@ function createPackageJson(backendPath, config) {
695
695
  }
696
696
 
697
697
  const packageJson = {
698
- name: 'offbyt-backend',
698
+ name: 'offbyte-backend',
699
699
  version: '1.0.0',
700
- description: 'Production-ready backend generated by offbyt',
700
+ description: 'Production-ready backend generated by offbyte',
701
701
  type: config.framework === 'nestjs' ? 'commonjs' : 'module',
702
702
  main: config.framework === 'nestjs' ? 'dist/main.js' : 'server.js',
703
703
  scripts,
@@ -815,7 +815,7 @@ async function detectAndGenerateResources(projectPath, backendPath, config) {
815
815
  generateRoute(backendPath, resourceName, resourceInfo, config, apiAnalysis);
816
816
  generatedResources.push(resourceName);
817
817
  } catch (e) {
818
- console.warn(`รขลกย รฏยธย Skipped ${resourceName}:`, e.message);
818
+ console.warn(`[WARN] Skipped ${resourceName}:`, e.message);
819
819
  }
820
820
  });
821
821
 
@@ -847,7 +847,7 @@ async function detectAndGenerateResources(projectPath, backendPath, config) {
847
847
 
848
848
  return { resources: generatedResources };
849
849
  } catch (error) {
850
- console.warn('รขลกย รฏยธย Auto-detection error:', error.message);
850
+ console.warn('[WARN] Auto-detection error:', error.message);
851
851
  createSampleResources(backendPath, config);
852
852
  return { resources: ['User (sample)'] };
853
853
  }
@@ -1652,7 +1652,7 @@ function generateSchemaSQL(resources, dbType, apiAnalysis = { relationships: []
1652
1652
  const isMySQL = dbType === 'mysql';
1653
1653
 
1654
1654
  let sql = `-- ============================================
1655
- -- offbyt ${dbType.toUpperCase()} Schema
1655
+ -- offbyte ${dbType.toUpperCase()} Schema
1656
1656
  -- Generated for ${dbType === 'mysql' ? 'MySQL' : dbType === 'postgresql' ? 'PostgreSQL' : 'SQLite'} Database
1657
1657
  -- Auto-generated from detected frontend resources
1658
1658
  -- ============================================
@@ -1921,7 +1921,7 @@ function generateConversationRelationships(dbType) {
1921
1921
 
1922
1922
  function generateCRUDSQL(resources, dbType, apiAnalysis = { relationships: [] }) {
1923
1923
  let sql = `-- ============================================\n`;
1924
- sql += `-- offbyt ${dbType.toUpperCase()} CRUD Operations\n`;
1924
+ sql += `-- offbyte ${dbType.toUpperCase()} CRUD Operations\n`;
1925
1925
  sql += `-- All Create, Read, Update, Delete Queries\n`;
1926
1926
  sql += `-- Ready to use in your application\n`;
1927
1927
  sql += `-- ============================================\n\n`;
@@ -2000,7 +2000,7 @@ function generateSampleValue(fieldName) {
2000
2000
 
2001
2001
  function generateJoinSQL(resources, dbType, apiAnalysis = { relationships: [] }) {
2002
2002
  let sql = `-- ============================================\n`;
2003
- sql += `-- offbyt ${dbType.toUpperCase()} Relationships & JOINs\n`;
2003
+ sql += `-- offbyte ${dbType.toUpperCase()} Relationships & JOINs\n`;
2004
2004
  sql += `-- Complex queries with table relationships\n`;
2005
2005
  sql += `-- ============================================\n\n`;
2006
2006
 
@@ -2059,7 +2059,7 @@ function generateJoinSQL(resources, dbType, apiAnalysis = { relationships: [] })
2059
2059
 
2060
2060
  function generateSeedSQL(resources, dbType, apiAnalysis = { relationships: [] }) {
2061
2061
  let sql = `-- ============================================\n`;
2062
- sql += `-- offbyt ${dbType.toUpperCase()} Sample Data\n`;
2062
+ sql += `-- offbyte ${dbType.toUpperCase()} Sample Data\n`;
2063
2063
  sql += `-- Seed data for testing and development\n`;
2064
2064
  sql += `-- ============================================\n\n`;
2065
2065
 
@@ -2182,7 +2182,7 @@ export const User = sequelize.define('User', {
2182
2182
  const router = express.Router();
2183
2183
 
2184
2184
  router.get('/', (req, res) => {
2185
- res.json({ message: 'Welcome to offbyt API' });
2185
+ res.json({ message: 'Welcome to offbyte API' });
2186
2186
  });
2187
2187
 
2188
2188
  export default router;`;
@@ -2211,9 +2211,9 @@ export const createUser = async (req, res) => {
2211
2211
  fs.writeFileSync(path.join(backendPath, 'controllers', 'userController.js'), sampleController);
2212
2212
 
2213
2213
  // README
2214
- const readme = `# offbyt Generated Backend
2214
+ const readme = `# offbyte Generated Backend
2215
2215
 
2216
- This is a production-ready backend generated by offbyt.
2216
+ This is a production-ready backend generated by offbyte.
2217
2217
 
2218
2218
  ## Configuration
2219
2219
  - Database: ${config.database}
@@ -2251,7 +2251,7 @@ npm run dev
2251
2251
  ## Environment Variables
2252
2252
  See \`../.env\` for required environment variables.
2253
2253
 
2254
- Generated by offbyt รขลกยก`;
2254
+ Generated by offbyte`;
2255
2255
 
2256
2256
  fs.writeFileSync(path.join(backendPath, 'README.md'), readme);
2257
2257
  }
@@ -18,36 +18,38 @@ import ora from 'ora';
18
18
  // ============================================================
19
19
  export async function connectFrontendBackend(projectPath) {
20
20
  try {
21
- console.log(chalk.cyan('\n๐Ÿ”— offbyt Auto-Connection Engine\n'));
22
- console.log(chalk.gray('Scanner: Frontend โ†’ Backend Connection Analysis\n'));
21
+ console.log(chalk.cyan('\n============================================================'));
22
+ console.log(chalk.cyan('OFFBYTE AUTO-CONNECTION ENGINE'));
23
+ console.log(chalk.cyan('============================================================\n'));
24
+ console.log(chalk.gray('Scanner: Frontend -> Backend Connection Analysis\n'));
23
25
 
24
26
  const backendPath = path.join(projectPath, 'backend');
25
27
 
26
28
  // Verify project structure
27
29
  if (!fs.existsSync(backendPath)) {
28
- console.error(chalk.red('โŒ Backend not found. Run "offbyt generate" first.\n'));
30
+ console.error(chalk.red('Backend not found. Run "offbyte generate" first.\n'));
29
31
  process.exit(1);
30
32
  }
31
33
 
32
34
  // Step 1: Scan React components
33
35
  const step1 = ora('Step 1/5: Scanning React components...').start();
34
36
  const reactAnalysis = scanReactComponents(projectPath);
35
- step1.succeed(`โœ… Found ${reactAnalysis.components.length} API components`);
37
+ step1.succeed(`Found ${reactAnalysis.components.length} API components`);
36
38
 
37
39
  // Step 2: Scan backend structure
38
40
  const step2 = ora('Step 2/5: Analyzing backend routes...').start();
39
41
  const backendAnalysis = scanBackendRoutes(backendPath);
40
- step2.succeed(`โœ… Found ${backendAnalysis.routes.length} backend routes`);
42
+ step2.succeed(`Found ${backendAnalysis.routes.length} backend routes`);
41
43
 
42
44
  // Step 3: Detect mismatches
43
45
  const step3 = ora('Step 3/5: Analyzing mismatches...').start();
44
46
  const mismatches = detectMismatches(reactAnalysis, backendAnalysis);
45
- step3.succeed(`โœ… Detected ${mismatches.issues.length} issues`);
47
+ step3.succeed(`Detected ${mismatches.issues.length} issues`);
46
48
 
47
49
  // Step 4: Auto-fix issues
48
50
  const step4 = ora('Step 4/5: Auto-fixing components...').start();
49
51
  const fixResults = applyAutoFixes(projectPath, backendPath, mismatches, reactAnalysis, backendAnalysis);
50
- step4.succeed(`โœ… Fixed ${fixResults.fixed} issues`);
52
+ step4.succeed(`Fixed ${fixResults.fixed} issues`);
51
53
 
52
54
  // Detect Vite
53
55
  let isVite = false;
@@ -64,18 +66,18 @@ export async function connectFrontendBackend(projectPath) {
64
66
  // Step 5: Create/Update .env
65
67
  const step5 = ora('Step 5/5: Configuring environment...').start();
66
68
  createFrontendEnv(projectPath, backendAnalysis.serverPort, isVite);
67
- step5.succeed('โœ… .env file configured');
69
+ step5.succeed('.env file configured');
68
70
 
69
71
  // Summary
70
- console.log(chalk.green('\nโœ… Auto-Connection Complete!\n'));
71
- console.log(chalk.gray('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”'));
72
+ console.log(chalk.green('\n[OK] Auto-Connection Complete!\n'));
73
+ console.log(chalk.gray('------------------------------------------------------------'));
72
74
  console.log(chalk.white(' Components Updated:'), reactAnalysis.components.length);
73
75
  console.log(chalk.white(' Issues Fixed:'), fixResults.fixed);
74
76
  console.log(chalk.white(' API URL Configured:'), `http://localhost:${backendAnalysis.serverPort}`);
75
- console.log(chalk.gray('โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\n'));
77
+ console.log(chalk.gray('------------------------------------------------------------\n'));
76
78
 
77
79
  if (mismatches.issues.length > 0) {
78
- console.log(chalk.yellow('โš ๏ธ Issues Found (Auto-Fixed):\n'));
80
+ console.log(chalk.yellow('[WARN] Issues Found (Auto-Fixed):\n'));
79
81
  mismatches.issues.forEach((issue, i) => {
80
82
  console.log(chalk.gray(` ${i+1}. ${issue.type}`));
81
83
  console.log(chalk.gray(` File: ${issue.file}`));
@@ -83,13 +85,13 @@ export async function connectFrontendBackend(projectPath) {
83
85
  });
84
86
  }
85
87
 
86
- console.log(chalk.cyan('๐Ÿš€ Ready to start:\n'));
88
+ console.log(chalk.cyan('Ready to start:\n'));
87
89
  console.log(chalk.white(' Frontend:', chalk.bold(`npm start`)));
88
90
  console.log(chalk.white(' Backend:', chalk.bold(`npm start`)));
89
91
  console.log(chalk.cyan('\n'));
90
92
 
91
93
  } catch (error) {
92
- console.error(chalk.red('โŒ Connection Error:', error.message));
94
+ console.error(chalk.red('Connection Error:'), error.message);
93
95
  process.exit(1);
94
96
  }
95
97
  }
@@ -152,7 +154,7 @@ function scanReactComponents(projectPath) {
152
154
  });
153
155
  }
154
156
  } catch (error) {
155
- console.warn(chalk.gray(` โš ๏ธ Skipped: ${path.relative(projectPath, file)}`));
157
+ console.warn(chalk.gray(` [WARN] Skipped: ${path.relative(projectPath, file)}`));
156
158
  }
157
159
  });
158
160
 
@@ -306,7 +308,7 @@ function scanBackendRoutes(backendPath) {
306
308
 
307
309
  return { routes, models, serverPort };
308
310
  } catch (error) {
309
- console.warn(chalk.yellow(`โš ๏ธ Backend scan error: ${error.message}`));
311
+ console.warn(chalk.yellow(`[WARN] Backend scan error: ${error.message}`));
310
312
  return { routes: [], models: [], serverPort: 5000 };
311
313
  }
312
314
  }
@@ -581,7 +583,7 @@ function applyAutoFixes(projectPath, backendPath, mismatches, reactAnalysis, bac
581
583
  }
582
584
  }
583
585
  } catch (error) {
584
- console.warn(chalk.gray(` โš ๏ธ Could not fix: ${issue.file} - ${error.message}`));
586
+ console.warn(chalk.gray(` [WARN] Could not fix: ${issue.file} - ${error.message}`));
585
587
  }
586
588
  });
587
589
 
@@ -613,10 +615,10 @@ function applyAutoFixes(projectPath, backendPath, mismatches, reactAnalysis, bac
613
615
  function fixResponseParsing(content) {
614
616
  // Fix common response parsing patterns
615
617
 
616
- // Pattern 1: res.data.data.token โ†’ res.data.token (if response is {data: {...}})
618
+ // Pattern 1: res.data.data.token รขโ€ โ€™ res.data.token (if response is {data: {...}})
617
619
  content = content.replace(/data\.data\.(\w+)/g, 'data.$1');
618
620
 
619
- // Pattern 2: response.data.data.user โ†’ response.data.user
621
+ // Pattern 2: response.data.data.user รขโ€ โ€™ response.data.user
620
622
  content = content.replace(/response\.data\.data\.(\w+)/g, 'response.data.$1');
621
623
 
622
624
  // Pattern 3: const x = await response.json();
@@ -639,7 +641,7 @@ function fixApiUrls(content, isVite = false) {
639
641
  // Ensure API calls use environment variables for base URL
640
642
  const API_URL = isVite ? 'import.meta.env.VITE_API_URL' : 'process.env.REACT_APP_API_URL';
641
643
 
642
- // Pattern 1: fetch('/api/... โ†’ fetch(`/api/...
644
+ // Pattern 1: fetch('/api/... รขโ€ โ€™ fetch(`/api/...
643
645
  content = content.replace(
644
646
  /fetch\s*\(\s*['"`](\/api\/[^'"`]+)['"`]/g,
645
647
  `fetch(\`\${${API_URL}}$1\``
@@ -661,7 +663,7 @@ function fixApiUrls(content, isVite = false) {
661
663
  `fetch(\`\${${API_URL}}$1\``
662
664
  );
663
665
 
664
- // Pattern 3: const url = '/api/... โ†’ const url = `/api/...
666
+ // Pattern 3: const url = '/api/... รขโ€ โ€™ const url = `/api/...
665
667
  content = content.replace(
666
668
  /(const|let|var)\s+(\w+)\s*=\s*['"`](\/api\/[^'"`]+)['"`]/g,
667
669
  `$1 $2 = \`\${${API_URL}}$3\``
@@ -1020,9 +1022,9 @@ function escapeRegex(string) {
1020
1022
  /**
1021
1023
  * Parse API path to extract resource name and route path
1022
1024
  * Examples:
1023
- * /api/admin/dashboard โ†’ {resourceName: 'admin', routePath: '/dashboard'}
1024
- * /api/user/invoices โ†’ {resourceName: 'user', routePath: '/invoices'}
1025
- * /api/products/:id โ†’ {resourceName: 'products', routePath: '/:id'}
1025
+ * /api/admin/dashboard รขโ€ โ€™ {resourceName: 'admin', routePath: '/dashboard'}
1026
+ * /api/user/invoices รขโ€ โ€™ {resourceName: 'user', routePath: '/invoices'}
1027
+ * /api/products/:id รขโ€ โ€™ {resourceName: 'products', routePath: '/:id'}
1026
1028
  */
1027
1029
  function parseApiPath(apiPath) {
1028
1030
  const normalizedPath = normalizeRoutePath(apiPath);
@@ -1122,4 +1124,3 @@ router.${methodLower}('${cleanPath}', async (req, res) => {
1122
1124
  }
1123
1125
  });`;
1124
1126
  }
1125
-