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,298 @@
1
+ /**
2
+ * Performance Analyzer - Analyzes load test results and generates insights
3
+ */
4
+
5
+ import chalk from 'chalk';
6
+
7
+ /**
8
+ * Analyze performance results
9
+ */
10
+ export function analyzePerformance(results, routes) {
11
+ const analysis = {
12
+ summary: {},
13
+ bottlenecks: [],
14
+ recommendations: [],
15
+ scalabilityScore: 0
16
+ };
17
+
18
+ // Analyze each load level
19
+ for (const levelResult of results) {
20
+ const level = levelResult.level;
21
+ const routeResults = levelResult.routes;
22
+
23
+ let totalLatency = 0;
24
+ let slowRoutes = [];
25
+ let errorRoutes = [];
26
+
27
+ for (const routeResult of routeResults) {
28
+ const avgLatency = routeResult.latency.mean;
29
+ totalLatency += avgLatency;
30
+
31
+ // Detect slow routes
32
+ if (avgLatency > 500) {
33
+ slowRoutes.push({
34
+ route: routeResult.route,
35
+ latency: avgLatency.toFixed(2)
36
+ });
37
+ }
38
+
39
+ // Detect errors
40
+ if (routeResult.errors > 0 || routeResult.non2xx > 0) {
41
+ errorRoutes.push({
42
+ route: routeResult.route,
43
+ errors: routeResult.errors,
44
+ non2xx: routeResult.non2xx
45
+ });
46
+ }
47
+ }
48
+
49
+ const avgLatency = routeResults.length > 0 ? totalLatency / routeResults.length : 0;
50
+
51
+ analysis.summary[level] = {
52
+ avgLatency: avgLatency.toFixed(2),
53
+ slowRoutes,
54
+ errorRoutes
55
+ };
56
+
57
+ // Identify bottlenecks
58
+ if (avgLatency > 500) {
59
+ analysis.bottlenecks.push({
60
+ level,
61
+ issue: `High average latency (${avgLatency.toFixed(2)}ms)`,
62
+ severity: 'critical'
63
+ });
64
+ } else if (avgLatency > 200) {
65
+ analysis.bottlenecks.push({
66
+ level,
67
+ issue: `Moderate latency (${avgLatency.toFixed(2)}ms)`,
68
+ severity: 'warning'
69
+ });
70
+ }
71
+
72
+ if (slowRoutes.length > 0) {
73
+ for (const slow of slowRoutes) {
74
+ analysis.bottlenecks.push({
75
+ level,
76
+ issue: `${slow.route} is slow (${slow.latency}ms)`,
77
+ severity: 'critical'
78
+ });
79
+ }
80
+ }
81
+
82
+ if (errorRoutes.length > 0) {
83
+ for (const err of errorRoutes) {
84
+ analysis.bottlenecks.push({
85
+ level,
86
+ issue: `${err.route} has errors (${err.errors} errors, ${err.non2xx} non-2xx)`,
87
+ severity: 'critical'
88
+ });
89
+ }
90
+ }
91
+ }
92
+
93
+ // Generate recommendations
94
+ analysis.recommendations = generateRecommendations(analysis);
95
+
96
+ // Calculate scalability score (0-100)
97
+ analysis.scalabilityScore = calculateScalabilityScore(analysis);
98
+
99
+ return analysis;
100
+ }
101
+
102
+ /**
103
+ * Generate recommendations based on analysis
104
+ */
105
+ function generateRecommendations(analysis) {
106
+ const recommendations = [];
107
+
108
+ // Check if there are critical bottlenecks
109
+ const criticalBottlenecks = analysis.bottlenecks.filter(b => b.severity === 'critical');
110
+
111
+ if (criticalBottlenecks.length > 0) {
112
+ recommendations.push({
113
+ priority: 'high',
114
+ issue: 'Critical performance bottlenecks detected',
115
+ suggestions: [
116
+ 'Add database indexes on frequently queried fields',
117
+ 'Implement caching (Redis/Memcached) for read-heavy operations',
118
+ 'Optimize database queries (avoid N+1 queries)',
119
+ 'Use connection pooling for database connections',
120
+ 'Consider horizontal scaling (load balancing)'
121
+ ]
122
+ });
123
+ }
124
+
125
+ // Check latency trends
126
+ const levels = Object.keys(analysis.summary).map(Number).sort((a, b) => a - b);
127
+ if (levels.length >= 2) {
128
+ const firstLatency = parseFloat(analysis.summary[levels[0]].avgLatency);
129
+ const lastLatency = parseFloat(analysis.summary[levels[levels.length - 1]].avgLatency);
130
+
131
+ if (lastLatency > firstLatency * 3) {
132
+ recommendations.push({
133
+ priority: 'high',
134
+ issue: 'Performance degrades significantly at scale',
135
+ suggestions: [
136
+ 'Implement API rate limiting',
137
+ 'Add request queuing for burst traffic',
138
+ 'Scale backend horizontally (multiple instances)',
139
+ 'Use a CDN for static assets',
140
+ 'Implement database read replicas'
141
+ ]
142
+ });
143
+ }
144
+ }
145
+
146
+ // General optimization recommendations
147
+ recommendations.push({
148
+ priority: 'medium',
149
+ issue: 'General optimizations',
150
+ suggestions: [
151
+ 'Enable GZIP compression for responses',
152
+ 'Implement pagination for list endpoints',
153
+ 'Use async/await properly to avoid blocking',
154
+ 'Monitor database query performance',
155
+ 'Set up proper logging and monitoring'
156
+ ]
157
+ });
158
+
159
+ return recommendations;
160
+ }
161
+
162
+ /**
163
+ * Calculate scalability score (0-100)
164
+ */
165
+ function calculateScalabilityScore(analysis) {
166
+ let score = 100;
167
+
168
+ // Deduct for bottlenecks
169
+ const criticalCount = analysis.bottlenecks.filter(b => b.severity === 'critical').length;
170
+ const warningCount = analysis.bottlenecks.filter(b => b.severity === 'warning').length;
171
+
172
+ score -= criticalCount * 15;
173
+ score -= warningCount * 5;
174
+
175
+ // Deduct for high latency
176
+ const levels = Object.keys(analysis.summary);
177
+ for (const level of levels) {
178
+ const avgLatency = parseFloat(analysis.summary[level].avgLatency);
179
+ if (avgLatency > 1000) score -= 10;
180
+ else if (avgLatency > 500) score -= 5;
181
+ else if (avgLatency > 200) score -= 2;
182
+ }
183
+
184
+ return Math.max(0, Math.min(100, score));
185
+ }
186
+
187
+ /**
188
+ * Generate formatted report
189
+ */
190
+ export function generateReport(analysis, startupMode = false) {
191
+ let report = '';
192
+
193
+ report += chalk.bold.cyan('\n╔════════════════════════════════════════════════════════════╗\n');
194
+ report += chalk.bold.cyan('â•' offbyt SCALABILITY REPORT â•'\n');
195
+ report += chalk.bold.cyan('╚════════════════════════════════════════════════════════════╝\n\n');
196
+
197
+ // Scalability Score
198
+ const score = analysis.scalabilityScore;
199
+ let scoreColor = chalk.green;
200
+ let scoreLabel = 'Excellent';
201
+
202
+ if (score < 50) {
203
+ scoreColor = chalk.red;
204
+ scoreLabel = 'Poor';
205
+ } else if (score < 70) {
206
+ scoreColor = chalk.yellow;
207
+ scoreLabel = 'Fair';
208
+ } else if (score < 85) {
209
+ scoreColor = chalk.blue;
210
+ scoreLabel = 'Good';
211
+ }
212
+
213
+ report += chalk.bold('📊 Scalability Score: ') + scoreColor.bold(`${score}/100 (${scoreLabel})`) + '\n\n';
214
+
215
+ // Summary for each load level
216
+ report += chalk.bold.yellow('📈 Performance Summary:\n\n');
217
+
218
+ const levels = Object.keys(analysis.summary).map(Number).sort((a, b) => a - b);
219
+ for (const level of levels) {
220
+ const summary = analysis.summary[level];
221
+ const latency = parseFloat(summary.avgLatency);
222
+
223
+ let icon = '✅';
224
+ let color = chalk.green;
225
+
226
+ if (latency > 500) {
227
+ icon = '❌';
228
+ color = chalk.red;
229
+ } else if (latency > 200) {
230
+ icon = '⚠️ ';
231
+ color = chalk.yellow;
232
+ }
233
+
234
+ report += color(`${icon} ${level} concurrent users → Avg latency: ${summary.avgLatency}ms\n`);
235
+
236
+ if (summary.slowRoutes.length > 0) {
237
+ for (const slow of summary.slowRoutes) {
238
+ report += color(` └─ ${slow.route} is slow (${slow.latency}ms)\n`);
239
+ }
240
+ }
241
+ }
242
+
243
+ report += '\n';
244
+
245
+ // Startup Growth Simulation (if enabled)
246
+ if (startupMode) {
247
+ report += chalk.bold.magenta('🚀 Startup Growth Simulation:\n\n');
248
+ report += chalk.gray('Month 1 → 100 users ✅ Stable\n');
249
+ report += chalk.gray('Month 3 → 1,000 users ✅ Stable\n');
250
+
251
+ if (score < 70) {
252
+ report += chalk.yellow('Month 6 → 10,000 users ⚠️ Performance issues expected\n');
253
+ report += chalk.red('Month 12 → 100,000 users ❌ System will struggle\n\n');
254
+ report += chalk.red.bold('⚠️ System needs optimization before scaling to 10k+ users\n');
255
+ } else {
256
+ report += chalk.green('Month 6 → 10,000 users ✅ Stable\n');
257
+ report += chalk.cyan('Month 12 → 100,000 users 🔵 Consider scaling plan\n');
258
+ }
259
+
260
+ report += '\n';
261
+ }
262
+
263
+ // Bottlenecks
264
+ if (analysis.bottlenecks.length > 0) {
265
+ report += chalk.bold.red('🔴 Detected Bottlenecks:\n\n');
266
+
267
+ for (const bottleneck of analysis.bottlenecks.slice(0, 10)) {
268
+ const icon = bottleneck.severity === 'critical' ? '❌' : '⚠️ ';
269
+ const color = bottleneck.severity === 'critical' ? chalk.red : chalk.yellow;
270
+ report += color(`${icon} ${bottleneck.issue} (at ${bottleneck.level} users)\n`);
271
+ }
272
+
273
+ report += '\n';
274
+ }
275
+
276
+ // Recommendations
277
+ report += chalk.bold.blue('💡 Recommended Optimizations:\n\n');
278
+
279
+ for (const rec of analysis.recommendations) {
280
+ const priorityLabel = rec.priority === 'high' ? chalk.red('[HIGH]') : chalk.yellow('[MEDIUM]');
281
+ report += `${priorityLabel} ${chalk.bold(rec.issue)}\n`;
282
+
283
+ for (const suggestion of rec.suggestions.slice(0, 5)) {
284
+ report += chalk.gray(` • ${suggestion}\n`);
285
+ }
286
+
287
+ report += '\n';
288
+ }
289
+
290
+ // Footer
291
+ report += chalk.cyan('─────────────────────────────────────────────────────────────\n');
292
+ report += chalk.gray('Generated by offbyt Benchmark Tool\n');
293
+
294
+ return report;
295
+ }
296
+
297
+ export default { analyzePerformance, generateReport };
298
+
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Resource Detector - Detects data resources from frontend code patterns
3
+ * Detects from: useState, useEffect, data arrays, forms, etc.
4
+ */
5
+
6
+ import fs from 'fs';
7
+ import path from 'path';
8
+ import { globSync } from 'glob';
9
+ import * as parser from '@babel/parser';
10
+ import traverse from '@babel/traverse';
11
+
12
+ /**
13
+ * Scan frontend and detect resources from state variables and data patterns
14
+ */
15
+ export function detectResourcesFromFrontend(projectPath) {
16
+ const resources = new Map();
17
+
18
+ // Find all frontend files
19
+ const patterns = [
20
+ 'src/**/*.{js,jsx,ts,tsx}',
21
+ 'app/**/*.{js,jsx,ts,tsx}',
22
+ 'pages/**/*.{js,jsx,ts,tsx}',
23
+ 'components/**/*.{js,jsx,ts,tsx}',
24
+ '*.{js,jsx,ts,tsx}'
25
+ ];
26
+
27
+ const files = [];
28
+ for (const pattern of patterns) {
29
+ files.push(...globSync(pattern, { nodir: true, cwd: projectPath }));
30
+ }
31
+
32
+ console.log(` 📁 Scanning ${files.length} files...`);
33
+
34
+ for (const file of files) {
35
+ try {
36
+ const fullPath = path.join(projectPath, file);
37
+ const content = fs.readFileSync(fullPath, 'utf8');
38
+
39
+ // Detect resources from code patterns
40
+ const detected = detectFromCode(content, file);
41
+
42
+ for (const resource of detected) {
43
+ if (!resources.has(resource.name)) {
44
+ resources.set(resource.name, {
45
+ name: resource.name,
46
+ singular: resource.singular,
47
+ fields: new Set(resource.fields || []),
48
+ sources: []
49
+ });
50
+ }
51
+
52
+ resources.get(resource.name).sources.push({
53
+ file,
54
+ type: resource.type
55
+ });
56
+
57
+ // Merge fields
58
+ if (resource.fields) {
59
+ for (const field of resource.fields) {
60
+ resources.get(resource.name).fields.add(field);
61
+ }
62
+ }
63
+ }
64
+ } catch (error) {
65
+ // Ignore parse errors
66
+ }
67
+ }
68
+
69
+ // Convert to array
70
+ return Array.from(resources.values()).map(r => ({
71
+ ...r,
72
+ fields: Array.from(r.fields).filter(field =>
73
+ field !== '_id' && field !== 'id' && field.toLowerCase() !== 'id'
74
+ )
75
+ }));
76
+ }
77
+
78
+ /**
79
+ * Detect resources from code content using AST and patterns
80
+ */
81
+ function detectFromCode(content, fileName) {
82
+ const resources = [];
83
+
84
+ // Pattern 1: useState hooks
85
+ // Example: const [products, setProducts] = useState([])
86
+ const statePattern = /const\s+\[(\w+),\s*set\w+\]\s*=\s*useState/g;
87
+ let match;
88
+ while ((match = statePattern.exec(content)) !== null) {
89
+ const varName = match[1];
90
+ if (isResourceVariable(varName)) {
91
+ resources.push({
92
+ name: varName,
93
+ singular: singularize(varName),
94
+ type: 'useState',
95
+ fields: extractFieldsFromVariable(content, varName)
96
+ });
97
+ }
98
+ }
99
+
100
+ // Pattern 2: Data arrays
101
+ // Example: const products = []
102
+ const arrayPattern = /const\s+(\w+)\s*=\s*\[\]/g;
103
+ while ((match = arrayPattern.exec(content)) !== null) {
104
+ const varName = match[1];
105
+ if (isResourceVariable(varName)) {
106
+ resources.push({
107
+ name: varName,
108
+ singular: singularize(varName),
109
+ type: 'array',
110
+ fields: extractFieldsFromVariable(content, varName)
111
+ });
112
+ }
113
+ }
114
+
115
+ // Pattern 3: map operations (indicates list rendering)
116
+ // Example: products.map(product => ...)
117
+ const mapPattern = /(\w+)\.map\s*\(\s*(?:\()?(\w+)(?:\))?\s*=>/g;
118
+ while ((match = mapPattern.exec(content)) !== null) {
119
+ const listName = match[1];
120
+ const itemName = match[2];
121
+
122
+ if (isResourceVariable(listName)) {
123
+ const fields = extractFieldsFromItemVariable(content, itemName);
124
+ resources.push({
125
+ name: listName,
126
+ singular: itemName,
127
+ type: 'map',
128
+ fields
129
+ });
130
+ }
131
+ }
132
+
133
+ // Pattern 4: Form fields (input names)
134
+ // Example: <input name="productName" />
135
+ const inputPattern = /name=["'](\w+)["']/g;
136
+ const formFields = [];
137
+ while ((match = inputPattern.exec(content)) !== null) {
138
+ formFields.push(match[1]);
139
+ }
140
+
141
+ if (formFields.length > 0) {
142
+ // Try to infer resource from form fields
143
+ const resourceName = inferResourceFromFields(formFields);
144
+ if (resourceName) {
145
+ resources.push({
146
+ name: resourceName,
147
+ singular: singularize(resourceName),
148
+ type: 'form',
149
+ fields: formFields
150
+ });
151
+ }
152
+ }
153
+
154
+ // Pattern 5: useEffect with fetch/axios placeholder
155
+ // Example: useEffect(() => { /* TODO: fetch products */ }, [])
156
+ const effectPattern = /useEffect\s*\([^)]*?(?:fetch|load|get)\s*(\w+)/gi;
157
+ while ((match = effectPattern.exec(content)) !== null) {
158
+ const varName = match[1];
159
+ if (isResourceVariable(varName)) {
160
+ resources.push({
161
+ name: varName,
162
+ singular: singularize(varName),
163
+ type: 'useEffect'
164
+ });
165
+ }
166
+ }
167
+
168
+ return resources;
169
+ }
170
+
171
+ /**
172
+ * Check if variable name looks like a resource
173
+ */
174
+ function isResourceVariable(name) {
175
+ // Skip common non-resource variables
176
+ const excluded = ['data', 'loading', 'error', 'fetching', 'isLoading', 'isError',
177
+ 'response', 'result', 'value', 'state', 'props', 'params', 'filter',
178
+ 'filtered', 'sorted', 'paginated', 'searched'];
179
+
180
+ // Skip derived/transformed variables (filteredTodos, sortedProducts, etc.)
181
+ const derivedPrefixes = ['filtered', 'sorted', 'paginated', 'searched', 'visible', 'active', 'selected', 'new'];
182
+ if (derivedPrefixes.some(prefix => name.toLowerCase().startsWith(prefix))) {
183
+ return false;
184
+ }
185
+
186
+ if (excluded.includes(name.toLowerCase())) return false;
187
+
188
+ // Should be plural or common resource name
189
+ return name.length > 2 && (
190
+ name.endsWith('s') ||
191
+ name.endsWith('List') ||
192
+ name.endsWith('Data') ||
193
+ isCommonResource(name)
194
+ );
195
+ }
196
+
197
+ /**
198
+ * Check if it's a common resource name
199
+ */
200
+ function isCommonResource(name) {
201
+ const common = ['user', 'product', 'order', 'cart', 'item', 'post', 'comment',
202
+ 'review', 'category', 'tag', 'customer', 'invoice', 'payment'];
203
+ const lowerName = name.toLowerCase();
204
+ // Check if name equals or ends with (singular or plural) a common resource
205
+ return common.some(r => {
206
+ const plural = r + 's';
207
+ return lowerName === r || lowerName === plural ||
208
+ lowerName.endsWith(r) && (lowerName[lowerName.length - r.length - 1] === 's' || lowerName.length === r.length);
209
+ });
210
+ }
211
+
212
+ /**
213
+ * Convert plural to singular (simple version)
214
+ */
215
+ function singularize(word) {
216
+ if (word.endsWith('ies')) return word.slice(0, -3) + 'y';
217
+ if (word.endsWith('ses')) return word.slice(0, -2);
218
+ if (word.endsWith('s')) return word.slice(0, -1);
219
+ return word;
220
+ }
221
+
222
+ /**
223
+ * Extract fields from variable usage in code
224
+ */
225
+ function extractFieldsFromVariable(content, varName) {
226
+ const fields = new Set();
227
+
228
+ // Pattern: varName.field
229
+ const dotPattern = new RegExp(`${varName}\\.\\w+\\.(\\w+)`, 'g');
230
+ let match;
231
+ while ((match = dotPattern.exec(content)) !== null) {
232
+ fields.add(match[1]);
233
+ }
234
+
235
+ return Array.from(fields).filter(field =>
236
+ field !== '_id' && field !== 'id' && field.toLowerCase() !== 'id'
237
+ );
238
+ }
239
+
240
+ /**
241
+ * Extract fields from item variable in map operations
242
+ */
243
+ function extractFieldsFromItemVariable(content, itemName) {
244
+ const fields = new Set();
245
+
246
+ // Pattern: item.fieldName
247
+ const fieldPattern = new RegExp(`${itemName}\\.([a-zA-Z_][a-zA-Z0-9_]*)`, 'g');
248
+ let match;
249
+ while ((match = fieldPattern.exec(content)) !== null) {
250
+ const field = match[1];
251
+ // Skip common methods and MongoDB _id
252
+ if (!['map', 'filter', 'reduce', 'forEach', 'length'].includes(field) &&
253
+ field !== '_id' && field !== 'id' && field.toLowerCase() !== 'id') {
254
+ fields.add(field);
255
+ }
256
+ }
257
+
258
+ return Array.from(fields);
259
+ }
260
+
261
+ /**
262
+ * Infer resource name from form fields
263
+ */
264
+ function inferResourceFromFields(fields) {
265
+ // Look for common patterns in field names
266
+ const patterns = {
267
+ product: ['productName', 'productPrice', 'productDescription'],
268
+ user: ['userName', 'userEmail', 'userPassword'],
269
+ order: ['orderDate', 'orderTotal', 'orderStatus']
270
+ };
271
+
272
+ for (const [resource, keywords] of Object.entries(patterns)) {
273
+ if (keywords.some(kw => fields.some(f => f.toLowerCase().includes(kw.toLowerCase())))) {
274
+ return resource + 's';
275
+ }
276
+ }
277
+
278
+ return null;
279
+ }
280
+
281
+ export default { detectResourcesFromFrontend };
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "offbyt",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "Code generation and deployment tool",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "offbyt": "cli.js"
9
+ },
10
+ "scripts": {
11
+ "start": "node cli.js",
12
+ "dev": "node cli.js"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "dependencies": {
18
+ "glob": "^13.0.6"
19
+ }
20
+ }
@@ -0,0 +1,31 @@
1
+ # ========== SERVER ==========
2
+ NODE_ENV=development
3
+ PORT=5000
4
+
5
+ # ========== DATABASE ==========
6
+ MONGODB_URI=mongodb://localhost:27017/<DB_NAME>
7
+ DB_NAME=<DB_NAME>
8
+
9
+ # ========== SECURITY ==========
10
+ JWT_SECRET=your_jwt_secret_key_here_change_in_production
11
+ JWT_EXPIRE=7d
12
+ JWT_EXPIRES_IN=7d
13
+ PASSWORD_SALT_ROUNDS=10
14
+
15
+ # ========== CORS ==========
16
+ CORS_ORIGIN=http://localhost:3000,http://localhost:5173
17
+
18
+ # ========== API ==========
19
+ API_VERSION=v1
20
+ API_RATE_LIMIT=100
21
+
22
+ # ========== LOGGING ==========
23
+ LOG_LEVEL=info
24
+ ENABLE_LOGGING=true
25
+ ENABLE_METRICS=false
26
+
27
+ # ========== CACHE ==========
28
+ CACHE_TTL=300
29
+
30
+ # ========== API KEYS (Optional) ==========
31
+ # API_KEY=your_api_key_here