offbyt 1.0.0

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.
Files changed (103) hide show
  1. package/README.md +2 -0
  2. package/cli/index.js +2 -0
  3. package/cli.js +206 -0
  4. package/core/detector/detectAxios.js +107 -0
  5. package/core/detector/detectFetch.js +148 -0
  6. package/core/detector/detectForms.js +55 -0
  7. package/core/detector/detectSocket.js +341 -0
  8. package/core/generator/generateControllers.js +17 -0
  9. package/core/generator/generateModels.js +25 -0
  10. package/core/generator/generateRoutes.js +17 -0
  11. package/core/generator/generateServer.js +18 -0
  12. package/core/generator/generateSocket.js +160 -0
  13. package/core/index.js +14 -0
  14. package/core/ir/IRTypes.js +25 -0
  15. package/core/ir/buildIR.js +83 -0
  16. package/core/parser/parseJS.js +26 -0
  17. package/core/parser/parseTS.js +27 -0
  18. package/core/rules/relationRules.js +38 -0
  19. package/core/rules/resourceRules.js +32 -0
  20. package/core/rules/schemaInference.js +26 -0
  21. package/core/scanner/scanProject.js +58 -0
  22. package/deploy/cloudflare.js +41 -0
  23. package/deploy/cloudflareWorker.js +122 -0
  24. package/deploy/connect.js +198 -0
  25. package/deploy/flyio.js +51 -0
  26. package/deploy/index.js +322 -0
  27. package/deploy/netlify.js +29 -0
  28. package/deploy/railway.js +215 -0
  29. package/deploy/render.js +195 -0
  30. package/deploy/utils.js +383 -0
  31. package/deploy/vercel.js +29 -0
  32. package/index.js +18 -0
  33. package/lib/generator/advancedCrudGenerator.js +475 -0
  34. package/lib/generator/crudCodeGenerator.js +486 -0
  35. package/lib/generator/irBasedGenerator.js +360 -0
  36. package/lib/ir-builder/index.js +16 -0
  37. package/lib/ir-builder/irBuilder.js +330 -0
  38. package/lib/ir-builder/rulesEngine.js +353 -0
  39. package/lib/ir-builder/templateEngine.js +193 -0
  40. package/lib/ir-builder/templates/index.js +14 -0
  41. package/lib/ir-builder/templates/model.template.js +47 -0
  42. package/lib/ir-builder/templates/routes-generic.template.js +66 -0
  43. package/lib/ir-builder/templates/routes-user.template.js +105 -0
  44. package/lib/ir-builder/templates/routes.template.js +102 -0
  45. package/lib/ir-builder/templates/validation.template.js +15 -0
  46. package/lib/ir-integration.js +349 -0
  47. package/lib/modes/benchmark.js +162 -0
  48. package/lib/modes/configBasedGenerator.js +2258 -0
  49. package/lib/modes/connect.js +1125 -0
  50. package/lib/modes/doctorAi.js +172 -0
  51. package/lib/modes/generateApi.js +435 -0
  52. package/lib/modes/interactiveSetup.js +548 -0
  53. package/lib/modes/offline.clean.js +14 -0
  54. package/lib/modes/offline.enhanced.js +787 -0
  55. package/lib/modes/offline.js +295 -0
  56. package/lib/modes/offline.v2.js +13 -0
  57. package/lib/modes/sync.js +629 -0
  58. package/lib/scanner/apiEndpointExtractor.js +387 -0
  59. package/lib/scanner/authPatternDetector.js +54 -0
  60. package/lib/scanner/frontendScanner.js +642 -0
  61. package/lib/utils/apiClientGenerator.js +242 -0
  62. package/lib/utils/apiScanner.js +95 -0
  63. package/lib/utils/codeInjector.js +350 -0
  64. package/lib/utils/doctor.js +381 -0
  65. package/lib/utils/envGenerator.js +36 -0
  66. package/lib/utils/loadTester.js +61 -0
  67. package/lib/utils/performanceAnalyzer.js +298 -0
  68. package/lib/utils/resourceDetector.js +281 -0
  69. package/package.json +20 -0
  70. package/templates/.env.template +31 -0
  71. package/templates/advanced.model.template.js +201 -0
  72. package/templates/advanced.route.template.js +341 -0
  73. package/templates/auth.middleware.template.js +87 -0
  74. package/templates/auth.routes.template.js +238 -0
  75. package/templates/auth.user.model.template.js +78 -0
  76. package/templates/cache.middleware.js +34 -0
  77. package/templates/chat.models.template.js +260 -0
  78. package/templates/chat.routes.template.js +478 -0
  79. package/templates/compression.middleware.js +19 -0
  80. package/templates/database.config.js +74 -0
  81. package/templates/errorHandler.middleware.js +54 -0
  82. package/templates/express/controller.ejs +26 -0
  83. package/templates/express/model.ejs +9 -0
  84. package/templates/express/route.ejs +18 -0
  85. package/templates/express/server.ejs +16 -0
  86. package/templates/frontend.env.template +14 -0
  87. package/templates/model.template.js +86 -0
  88. package/templates/package.production.json +51 -0
  89. package/templates/package.template.json +41 -0
  90. package/templates/pagination.utility.js +110 -0
  91. package/templates/production.server.template.js +233 -0
  92. package/templates/rateLimiter.middleware.js +36 -0
  93. package/templates/requestLogger.middleware.js +19 -0
  94. package/templates/response.helper.js +179 -0
  95. package/templates/route.template.js +130 -0
  96. package/templates/security.middleware.js +78 -0
  97. package/templates/server.template.js +91 -0
  98. package/templates/socket.server.template.js +433 -0
  99. package/templates/utils.helper.js +157 -0
  100. package/templates/validation.middleware.js +63 -0
  101. package/templates/validation.schema.js +128 -0
  102. package/utils/fileWriter.js +15 -0
  103. package/utils/logger.js +18 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * User Routes Template - With Auth
3
+ */
4
+
5
+ export const ROUTES_USER_TEMPLATE = `import express from 'express';
6
+ import User from '../models/User.model.js';
7
+ import bcryptjs from 'bcryptjs';
8
+ import jwt from 'jsonwebtoken';
9
+
10
+ const router = express.Router();
11
+ const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-key';
12
+ const JWT_EXPIRE = process.env.JWT_EXPIRE || process.env.JWT_EXPIRES_IN || '7d';
13
+
14
+ // POST signup
15
+ router.post('/signup', async (req, res) => {
16
+ try {
17
+ const { username, email, password, firstName, lastName, role } = req.body;
18
+ const existing = await User.findOne({ email });
19
+ if (existing) return res.status(400).json({ success: false, error: 'Email already registered' });
20
+ const hashedPassword = await bcryptjs.hash(password, 10);
21
+ const userRole = role || 'student';
22
+ const data = new User({ username, email, password: hashedPassword, firstName, lastName, role: userRole });
23
+ await data.save();
24
+ const token = jwt.sign({ id: data._id, email: data.email }, JWT_SECRET, { expiresIn: JWT_EXPIRE });
25
+ res.status(201).json({ success: true, message: 'User created', data: { id: data._id, email: data.email, firstName: data.firstName, lastName: data.lastName, role: userRole }, token });
26
+ } catch (error) {
27
+ res.status(400).json({ success: false, error: error.message });
28
+ }
29
+ });
30
+
31
+ // POST login
32
+ router.post('/login', async (req, res) => {
33
+ try {
34
+ const { email, password } = req.body;
35
+ const user = await User.findOne({ email });
36
+ if (!user) return res.status(401).json({ success: false, error: 'Invalid credentials' });
37
+ const isValid = await bcryptjs.compare(password, user.password);
38
+ if (!isValid) return res.status(401).json({ success: false, error: 'Invalid credentials' });
39
+ const token = jwt.sign({ id: user._id, email: user.email }, JWT_SECRET, { expiresIn: JWT_EXPIRE });
40
+ res.json({ success: true, message: 'Login successful', data: { id: user._id, email: user.email, firstName: user.firstName, lastName: user.lastName, role: user.role }, token });
41
+ } catch (error) {
42
+ res.status(500).json({ success: false, error: error.message });
43
+ }
44
+ });
45
+
46
+ // GET all users
47
+ router.get('/', async (req, res) => {
48
+ try {
49
+ const { skip = 0, limit = 100 } = req.query;
50
+ const data = await User.find({ isDeleted: false }).skip(parseInt(skip)).limit(parseInt(limit));
51
+ const total = await User.countDocuments({ isDeleted: false });
52
+ res.json({ success: true, data, total, skip: parseInt(skip), limit: parseInt(limit) });
53
+ } catch (error) {
54
+ res.status(500).json({ success: false, error: error.message });
55
+ }
56
+ });
57
+
58
+ // GET single user
59
+ router.get('/:id', async (req, res) => {
60
+ try {
61
+ const data = await User.findById(req.params.id);
62
+ if (!data) return res.status(404).json({ success: false, error: 'Not found' });
63
+ res.json({ success: true, data });
64
+ } catch (error) {
65
+ res.status(500).json({ success: false, error: error.message });
66
+ }
67
+ });
68
+
69
+ // POST create user
70
+ router.post('/', async (req, res) => {
71
+ try {
72
+ const data = new User(req.body);
73
+ if (data.password) {
74
+ data.password = await bcryptjs.hash(data.password, 10);
75
+ }
76
+ await data.save();
77
+ res.status(201).json({ success: true, data, message: 'Created successfully' });
78
+ } catch (error) {
79
+ res.status(400).json({ success: false, error: error.message });
80
+ }
81
+ });
82
+
83
+ // PUT update user
84
+ router.put('/:id', async (req, res) => {
85
+ try {
86
+ const data = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
87
+ if (!data) return res.status(404).json({ success: false, error: 'Not found' });
88
+ res.json({ success: true, data, message: 'Updated successfully' });
89
+ } catch (error) {
90
+ res.status(400).json({ success: false, error: error.message });
91
+ }
92
+ });
93
+
94
+ // DELETE user
95
+ router.delete('/:id', async (req, res) => {
96
+ try {
97
+ await User.findByIdAndUpdate(req.params.id, { isDeleted: true });
98
+ res.json({ success: true, message: 'Deleted successfully' });
99
+ } catch (error) {
100
+ res.status(500).json({ success: false, error: error.message });
101
+ }
102
+ });
103
+
104
+ export default router;
105
+ `;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Express Routes Template
3
+ */
4
+
5
+ export const ROUTES_TEMPLATE = `import express from 'express';
6
+ import <%= capitalize(resource.singular) %> from '../models/<%= capitalize(resource.singular) %>.model.js';
7
+ import bcryptjs from 'bcryptjs';
8
+ import jwt from 'jsonwebtoken';
9
+
10
+ const router = express.Router();
11
+ const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-key';
12
+ const JWT_EXPIRE = process.env.JWT_EXPIRE || process.env.JWT_EXPIRES_IN || '7d';
13
+
14
+ // POST signup (for users resource only)
15
+ router.post('/signup', async (req, res) => {
16
+ try {
17
+ const { username, email, password, firstName, lastName } = req.body;
18
+ const existing = await <%= capitalize(resource.singular) %>.findOne({ email });
19
+ if (existing) return res.status(400).json({ success: false, error: 'Email already registered' });
20
+ const hashedPassword = await bcryptjs.hash(password, 10);
21
+ const data = new <%= capitalize(resource.singular) %>({ username, email, password: hashedPassword, firstName, lastName, role: 'student' });
22
+ await data.save();
23
+ const token = jwt.sign({ id: data._id, email: data.email }, JWT_SECRET, { expiresIn: JWT_EXPIRE });
24
+ res.status(201).json({ success: true, message: 'User created', data: { id: data._id, email: data.email, firstName: data.firstName, lastName: data.lastName }, token });
25
+ } catch (error) {
26
+ res.status(400).json({ success: false, error: error.message });
27
+ }
28
+ });
29
+
30
+ // POST login (for users resource only)
31
+ router.post('/login', async (req, res) => {
32
+ try {
33
+ const { email, password } = req.body;
34
+ const user = await <%= capitalize(resource.singular) %>.findOne({ email });
35
+ if (!user) return res.status(401).json({ success: false, error: 'Invalid credentials' });
36
+ const isValid = await bcryptjs.compare(password, user.password);
37
+ if (!isValid) return res.status(401).json({ success: false, error: 'Invalid credentials' });
38
+ const token = jwt.sign({ id: user._id, email: user.email }, JWT_SECRET, { expiresIn: JWT_EXPIRE });
39
+ res.json({ success: true, message: 'Login successful', data: { id: user._id, email: user.email, firstName: user.firstName, lastName: user.lastName }, token });
40
+ } catch (error) {
41
+ res.status(500).json({ success: false, error: error.message });
42
+ }
43
+ });
44
+
45
+ // GET all <%= resource.plural %>
46
+ router.get('/', async (req, res) => {
47
+ try {
48
+ const { skip = 0, limit = 100 } = req.query;
49
+ const data = await <%= capitalize(resource.singular) %>.find({ isDeleted: false }).skip(parseInt(skip)).limit(parseInt(limit)).populate('user').populate('event').populate('club').populate('organizer').populate('admin').populate('members');
50
+ const total = await <%= capitalize(resource.singular) %>.countDocuments({ isDeleted: false });
51
+ res.json({ success: true, data, total, skip: parseInt(skip), limit: parseInt(limit) });
52
+ } catch (error) {
53
+ res.status(500).json({ success: false, error: error.message });
54
+ }
55
+ });
56
+
57
+ // GET single <%= resource.singular %>
58
+ router.get('/:id', async (req, res) => {
59
+ try {
60
+ const data = await <%= capitalize(resource.singular) %>.findById(req.params.id).populate('user').populate('event').populate('club').populate('organizer').populate('admin').populate('members');
61
+ if (!data) return res.status(404).json({ success: false, error: 'Not found' });
62
+ res.json({ success: true, data });
63
+ } catch (error) {
64
+ res.status(500).json({ success: false, error: error.message });
65
+ }
66
+ });
67
+
68
+ // POST create <%= resource.singular %>
69
+ router.post('/', async (req, res) => {
70
+ try {
71
+ const data = new <%= capitalize(resource.singular) %>(req.body);
72
+ await data.save();
73
+ await data.populate('user').populate('event').populate('club').populate('organizer').populate('admin').populate('members');
74
+ res.status(201).json({ success: true, data, message: 'Created successfully' });
75
+ } catch (error) {
76
+ res.status(400).json({ success: false, error: error.message });
77
+ }
78
+ });
79
+
80
+ // PUT update <%= resource.singular %>
81
+ router.put('/:id', async (req, res) => {
82
+ try {
83
+ const data = await <%= capitalize(resource.singular) %>.findByIdAndUpdate(req.params.id, req.body, { new: true }).populate('user').populate('event').populate('club').populate('organizer').populate('admin').populate('members');
84
+ if (!data) return res.status(404).json({ success: false, error: 'Not found' });
85
+ res.json({ success: true, data, message: 'Updated successfully' });
86
+ } catch (error) {
87
+ res.status(400).json({ success: false, error: error.message });
88
+ }
89
+ });
90
+
91
+ // DELETE <%= resource.singular %>
92
+ router.delete('/:id', async (req, res) => {
93
+ try {
94
+ await <%= capitalize(resource.singular) %>.findByIdAndUpdate(req.params.id, { isDeleted: true });
95
+ res.json({ success: true, message: 'Deleted successfully' });
96
+ } catch (error) {
97
+ res.status(500).json({ success: false, error: error.message });
98
+ }
99
+ });
100
+
101
+ export default router;
102
+ `;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Validation Schema Template
3
+ */
4
+
5
+ export const VALIDATION_TEMPLATE = `import Joi from 'joi';
6
+
7
+ const validate<%= capitalize(resource.singular) %> = {
8
+ create: Joi.object({
9
+ name: Joi.string().required(),
10
+ email: Joi.string().email().optional()
11
+ })
12
+ };
13
+
14
+ export default validate<%= capitalize(resource.singular) %>;
15
+ `;
@@ -0,0 +1,349 @@
1
+ /**
2
+ * IR Integration - Connects Scanner → IR → Generator
3
+ *
4
+ * Professional pipeline: Scan → Build IR → Generate Code
5
+ */
6
+
7
+ import fs from 'fs';
8
+ import path from 'path';
9
+ import { scanFrontendCode } from './scanner/frontendScanner.js';
10
+ import { buildIR, validateIR, printIR } from './ir-builder/irBuilder.js';
11
+ import {
12
+ generateBackendFromScanner,
13
+ writeGeneratedFiles,
14
+ generateBackendScaffold,
15
+ printGenerationSummary
16
+ } from './generator/irBasedGenerator.js';
17
+
18
+ /**
19
+ * Complete pipeline: Frontend → IR → Backend
20
+ */
21
+ export async function offbyteWithIR(frontendPath, outputPath, options = {}) {
22
+ console.log('\n' + '='.repeat(70));
23
+ console.log('🚀 offbyt - IR-Based Backend Generation');
24
+ console.log('='.repeat(70) + '\n');
25
+
26
+ try {
27
+ // Step 1: Scan frontend
28
+ console.log('📱 STEP 1: Scanning Frontend Code\n');
29
+ const detectedApis = scanFrontendCode(frontendPath);
30
+
31
+ if (detectedApis.length === 0) {
32
+ console.warn('⚠️ No APIs detected. Make sure frontend has API calls.');
33
+ return;
34
+ }
35
+
36
+ console.log(`✅ Detected ${detectedApis.length} API calls\n`);
37
+
38
+ // Step 2: Save detected APIs
39
+ const apisPath = path.join(outputPath, 'detected-apis.json');
40
+ if (!fs.existsSync(path.dirname(apisPath))) {
41
+ fs.mkdirSync(path.dirname(apisPath), { recursive: true });
42
+ }
43
+ fs.writeFileSync(apisPath, JSON.stringify(detectedApis, null, 2));
44
+ console.log(`💾 Saved API detections to: detectedapis.json\n`);
45
+
46
+ // Step 3: Build IR
47
+ console.log('🧠 STEP 2: Building Intermediate Representation (IR)\n');
48
+ const ir = buildIR(detectedApis, {
49
+ hasAuth: options.hasAuth ?? true,
50
+ dbType: options.dbType ?? 'mongodb',
51
+ apiVersion: options.apiVersion ?? 'v1',
52
+ projectName: options.projectName || path.basename(frontendPath)
53
+ });
54
+
55
+ // Validate IR
56
+ const validation = validateIR(ir);
57
+ if (!validation.valid) {
58
+ console.error('❌ IR Validation Failed:');
59
+ validation.errors.forEach(err => console.error(` - ${err}`));
60
+ throw new Error('Invalid IR structure');
61
+ }
62
+
63
+ // Save IR
64
+ const irPath = path.join(outputPath, 'project.ir.json');
65
+ fs.writeFileSync(irPath, JSON.stringify(ir, null, 2));
66
+ console.log(`💾 Saved IR to: project.ir.json`);
67
+
68
+ console.log(`✅ IR built with ${ir.resources.length} resources\n`);
69
+
70
+ // Step 4: Generate backend
71
+ console.log('🔨 STEP 3: Generating Backend Code\n');
72
+ const result = await generateBackendFromScanner(detectedApis, {
73
+ hasAuth: options.hasAuth ?? true,
74
+ dbType: options.dbType ?? 'mongodb'
75
+ });
76
+
77
+ // Step 5: Write files
78
+ console.log('📁 STEP 4: Writing Generated Files\n');
79
+
80
+ // Create models, routes, validations
81
+ const backendDir = path.join(outputPath, 'generated');
82
+ await writeGeneratedFiles(result.generated, backendDir);
83
+
84
+ // Create scaffold (server.js, package.json, etc.)
85
+ await generateBackendScaffold(result.ir, backendDir);
86
+
87
+ // Create README
88
+ createReadme(backendDir, ir, options);
89
+
90
+ // Step 6: Summary
91
+ printGenerationSummary(result);
92
+
93
+ // Additional info
94
+ console.log('📊 IR Structure Breakdown:\n');
95
+ ir.resources.forEach(resource => {
96
+ console.log(` 📦 ${resource.singular}:`);
97
+ console.log(` Fields: ${resource.fields.map(f => f.name).join(', ')}`);
98
+ console.log(` Routes: ${resource.routes.join(', ')}`);
99
+ if (resource.validations && Object.keys(resource.validations).length > 0) {
100
+ console.log(` Validations: ${Object.keys(resource.validations).join(', ')}`);
101
+ }
102
+ });
103
+
104
+ console.log('\n' + '='.repeat(70));
105
+ console.log('✨ GENERATION COMPLETE!');
106
+ console.log('='.repeat(70) + '\n');
107
+
108
+ console.log('📂 Generated Structure:');
109
+ console.log(` ${backendDir}/`);
110
+ console.log(` ├── models/ (Mongoose models)`);
111
+ console.log(` ├── routes/ (Express routes)`);
112
+ console.log(` ├── validations/ (Input validators)`);
113
+ console.log(` ├── middleware/ (Middleware handlers)`);
114
+ console.log(` ├── config/ (Database config)`);
115
+ console.log(` ├── server.js (Express server)`);
116
+ console.log(` ├── package.json (Dependencies)`);
117
+ console.log(` ├── .env.example (Environment variables)`);
118
+ console.log(` ├── README.md (Documentation)`);
119
+ console.log(` └── ir-schemas/ (IR JSON files)\n`);
120
+
121
+ console.log('🚀 Next Steps:');
122
+ console.log(` 1. cd ${backendDir}`);
123
+ console.log(` 2. npm install`);
124
+ console.log(` 3. Configure .env file`);
125
+ console.log(` 4. npm run dev\n`);
126
+
127
+ return {
128
+ success: true,
129
+ ir,
130
+ generatedDir: backendDir,
131
+ stats: {
132
+ resourcesGenerated: ir.resources.length,
133
+ endpointsGenerated: ir.resources.reduce((sum, r) => sum + r.endpoints.length, 0),
134
+ fieldsDefinedGenerated: ir.resources.reduce((sum, r) => sum + r.fields.length, 0)
135
+ }
136
+ };
137
+
138
+ } catch (error) {
139
+ console.error('\n❌ Generation Failed:');
140
+ console.error(` ${error.message}\n`);
141
+ throw error;
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Create README for generated project
147
+ */
148
+ function createReadme(outputDir, ir, options) {
149
+ const resources = ir.resources.map(r =>
150
+ `- **${r.singular}** (\`${r.plural}\`): ${r.fields.map(f => f.name).join(', ')}`
151
+ ).join('\n');
152
+
153
+ const readme = `# Generated Backend
154
+
155
+ Auto-generated with **offbyt IR Architecture**
156
+
157
+ Generated on: ${new Date().toLocaleString()}
158
+
159
+ ## 📦 Resources
160
+
161
+ ${resources}
162
+
163
+ ## 🚀 Getting Started
164
+
165
+ \`\`\`bash
166
+ npm install
167
+ \`\`\`
168
+
169
+ ### Configuration
170
+
171
+ Create a \`.env\` file (copy from \`.env.example\`):
172
+
173
+ \`\`\`env
174
+ MONGODB_URI=mongodb://localhost:27017/offbyt-${ir.settings.apiVersion}
175
+ PORT=3000
176
+ NODE_ENV=development
177
+ JWT_SECRET=your-secret-key
178
+ \`\`\`
179
+
180
+ ### Development
181
+
182
+ \`\`\`bash
183
+ npm run dev
184
+ \`\`\`
185
+
186
+ Server will start on \`http://localhost:3000\`
187
+
188
+ ## 📚 API Documentation
189
+
190
+ ### Base URL
191
+
192
+ \`/api/${ir.settings.apiVersion}\`
193
+
194
+ ### Endpoints
195
+
196
+ ${ir.resources.map(r => {
197
+ const endpoints = r.endpoints.map(e => {
198
+ const method = e.method;
199
+ const path = `/api/` + r.plural + (method === 'GET' && e.path.includes(':id') ? '/:id' : '');
200
+ return '- `' + method + ' ' + path + '`';
201
+ }).join('\n');
202
+ return '#### ' + r.singular + '\n\n' + endpoints;
203
+ }).join('\n\n')}
204
+
205
+ ## 🔒 Authentication
206
+
207
+ ${ir.settings.hasAuth ? 'Authentication is enabled. Include JWT token in Authorization header.' : 'No authentication configured.'}
208
+
209
+ ## 📂 Project Structure
210
+
211
+ \`\`\`
212
+ .
213
+ ├── models/ Mongoose schemas
214
+ ├── routes/ Express route handlers
215
+ ├── validations/ Input validation schemas
216
+ ├── middleware/ Custom middleware
217
+ ├── config/ Database & environment config
218
+ ├── server.js Express server entry point
219
+ └── package.json Dependencies
220
+ \`\`\`
221
+
222
+ ## 🔧 Customization
223
+
224
+ Generated files are ready for customization:
225
+
226
+ 1. **Add Business Logic**: Edit files in \`models/\` and \`routes/\`
227
+ 2. **Extend Validation**: Modify validation files
228
+ 3. **Add Middleware**: Create new middleware in \`middleware/\`
229
+ 4. **Database Hooks**: Add Mongoose hooks in model files
230
+
231
+ ## 🧪 Testing
232
+
233
+ \`\`\`bash
234
+ npm test
235
+ \`\`\`
236
+
237
+ ## 📝 Environment Variables
238
+
239
+ See \`.env.example\` for all configuration options.
240
+
241
+ ## 🚀 Deployment
242
+
243
+ ### Using Docker
244
+
245
+ Create \`Dockerfile\`:
246
+
247
+ \`\`\`dockerfile
248
+ FROM node:18-alpine
249
+
250
+ WORKDIR /app
251
+
252
+ COPY package*.json ./
253
+ RUN npm ci --only=production
254
+
255
+ COPY . .
256
+
257
+ EXPOSE 3000
258
+ CMD ["npm", "start"]
259
+ \`\`\`
260
+
261
+ Build and run:
262
+
263
+ \`\`\`bash
264
+ docker build -t offbyt-app .
265
+ docker run -p 3000:3000 offbyt-app
266
+ \`\`\`
267
+
268
+ ### Using Node.js
269
+
270
+ \`\`\`bash
271
+ npm install
272
+ NODE_ENV=production npm start
273
+ \`\`\`
274
+
275
+ ## 📊 Tech Stack
276
+
277
+ - **Runtime**: Node.js 18+
278
+ - **Framework**: Express.js
279
+ - **Database**: MongoDB + Mongoose
280
+ - **Validation**: Joi
281
+ - **Logger**: Built-in request logger
282
+
283
+ ## 📄 License
284
+
285
+ Generated by offbyt IR Architecture
286
+
287
+ ---
288
+
289
+ **Generated by**: offbyt
290
+ **Generation Date**: ${new Date().toISOString()}
291
+ **IR Version**: ${ir.version}
292
+ `;
293
+
294
+ const readmePath = path.join(outputDir, 'README.md');
295
+ fs.writeFileSync(readmePath, readme);
296
+ console.log(` ✅ ${path.relative(process.cwd(), readmePath)}`);
297
+ }
298
+
299
+ /**
300
+ * Quick start: Directory → Full Backend
301
+ */
302
+ export async function quickGenerate(frontendPath, options = {}) {
303
+ const projectName = path.basename(frontendPath);
304
+ const outputPath = `./offbyt-${projectName}`;
305
+
306
+ return offbyteWithIR(frontendPath, outputPath, {
307
+ projectName,
308
+ ...options
309
+ });
310
+ }
311
+
312
+ /**
313
+ * CLI Helper
314
+ */
315
+ export async function runFromCLI(args) {
316
+ const [command, frontendPath, outputPath] = args;
317
+
318
+ if (!frontendPath) {
319
+ console.log('\n📖 Usage: node ir-integration.js <frontend-path> [output-path]\n');
320
+ console.log('Examples:');
321
+ console.log(' node ir-integration.js ./my-app');
322
+ console.log(' node ir-integration.js ./frontend ./generated-backend\n');
323
+ process.exit(1);
324
+ }
325
+
326
+ if (!fs.existsSync(frontendPath)) {
327
+ console.error(`❌ Frontend path not found: ${frontendPath}`);
328
+ process.exit(1);
329
+ }
330
+
331
+ const out = outputPath || `./offbyt-${path.basename(frontendPath)}`;
332
+
333
+ try {
334
+ await offbyteWithIR(frontendPath, out, {
335
+ hasAuth: true,
336
+ projectName: path.basename(frontendPath)
337
+ });
338
+ } catch (error) {
339
+ process.exit(1);
340
+ }
341
+ }
342
+
343
+ // Run if called directly
344
+ if (import.meta.url === `file://${process.argv[1]}`) {
345
+ runFromCLI(process.argv.slice(2));
346
+ }
347
+
348
+ export default offbyteWithIR;
349
+