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,486 @@
1
+ /**
2
+ * CRUD Code Generator
3
+ * Generates complete CRUD model and routes for any resource
4
+ */
5
+
6
+ export function generateCrudModel(resourceName, fields = [], hasAuth = false) {
7
+ const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
8
+ const collectionName = resourceName.toLowerCase();
9
+
10
+ const fieldDefinitions = generateFieldDefinitions(fields);
11
+
12
+ return `import mongoose from 'mongoose';
13
+
14
+ const ${modelName}Schema = new mongoose.Schema(
15
+ {
16
+ ${fieldDefinitions}
17
+ },
18
+ {
19
+ timestamps: true,
20
+ collection: '${collectionName}'
21
+ }
22
+ );
23
+
24
+ ${hasAuth ? `
25
+ // Add userId reference if authenticated resources
26
+ ${modelName}Schema.add({
27
+ userId: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
28
+ });
29
+ ` : ''}
30
+
31
+ ${modelName}Schema.index({ createdAt: -1 });
32
+
33
+ const ${modelName} = mongoose.model('${modelName}', ${modelName}Schema);
34
+
35
+ export default ${modelName};
36
+ `;
37
+ }
38
+
39
+ /**
40
+ * Generate Mongoose field definitions from field names
41
+ */
42
+ function generateFieldDefinitions(fields) {
43
+ const fieldDefs = [];
44
+
45
+ for (const field of fields) {
46
+ // Skip MongoDB's automatic _id field and id variations
47
+ if (field === '_id' || field === 'id' || field.toLowerCase() === 'id') {
48
+ continue;
49
+ }
50
+
51
+ const normalized = field.toLowerCase();
52
+
53
+ // Determine field type based on name
54
+ let fieldDef = '';
55
+ if (normalized === 'email') {
56
+ fieldDef = ` ${field}: { type: String, lowercase: true, match: /.+\\@.+\\..+/ }`;
57
+ } else if (normalized === 'price' || normalized === 'cost' || normalized === 'rating') {
58
+ fieldDef = ` ${field}: { type: Number, default: 0 }`;
59
+ } else if (normalized === 'status') {
60
+ fieldDef = ` ${field}: { type: String, enum: ['active', 'inactive', 'pending'], default: 'active' }`;
61
+ } else if (normalized === 'views' || normalized === 'likes' || normalized === 'count') {
62
+ fieldDef = ` ${field}: { type: Number, default: 0 }`;
63
+ } else if (normalized === 'userid' || normalized === 'authorid' || normalized === 'postid') {
64
+ fieldDef = ` ${field}: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }`;
65
+ } else if (normalized === 'duedate' || normalized === 'deadline' || normalized === 'startdate') {
66
+ fieldDef = ` ${field}: { type: Date }`;
67
+ } else if (normalized === 'completed' || normalized === 'active' || normalized === 'published') {
68
+ fieldDef = ` ${field}: { type: Boolean, default: false }`;
69
+ } else if (normalized === 'tags') {
70
+ fieldDef = ` ${field}: [{ type: String }]`;
71
+ } else if (normalized === 'items' || normalized === 'comments' || normalized === 'reviews') {
72
+ fieldDef = ` ${field}: [{ type: mongoose.Schema.Types.ObjectId }]`;
73
+ } else {
74
+ // Default to string
75
+ fieldDef = ` ${field}: { type: String }`;
76
+ }
77
+
78
+ fieldDefs.push(fieldDef);
79
+ }
80
+
81
+ return fieldDefs.length > 0 ? fieldDefs.join(',\n') : ' _id: mongoose.Schema.Types.ObjectId';
82
+ }
83
+
84
+ /**
85
+ * Generate action-specific routes
86
+ * Creates routes for actions like /dashboard, /users, /analytics, /usage, etc.
87
+ */
88
+ function generateActionRoutes(resourceName, modelName, actions = [], endpoints = [], hasAuth = false) {
89
+ if (!actions || actions.length === 0) return '';
90
+
91
+ const authMiddleware = hasAuth ? ', authenticateToken' : '';
92
+ let routesCode = `// ===== ACTION ROUTES =====\n`;
93
+
94
+ for (const action of actions) {
95
+ const endpoint = endpoints.find(ep => ep.action === action);
96
+ const method = endpoint ? endpoint.method : 'GET';
97
+ const requiresAuth = endpoint ? endpoint.requiresAuth : hasAuth;
98
+ const auth = requiresAuth ? ', authenticateToken' : '';
99
+
100
+ // Generate response based on action type
101
+ let responseData = '';
102
+ let responseKey = action.toLowerCase();
103
+
104
+ if (action === 'dashboard') {
105
+ responseData = `
106
+ // TODO: Implement dashboard stats logic
107
+ const stats = {
108
+ totalUsers: await ${modelName}.countDocuments({}),
109
+ activeSubscriptions: 0,
110
+ revenue: 0,
111
+ newUsers: 0
112
+ };
113
+
114
+ res.status(200).json({
115
+ success: true,
116
+ stats: stats
117
+ });`;
118
+ } else if (action === 'users') {
119
+ responseData = `
120
+ // Fetch all users
121
+ const users = await ${modelName}.find({}).sort({ createdAt: -1 });
122
+
123
+ res.status(200).json({
124
+ success: true,
125
+ count: users.length,
126
+ users: users
127
+ });`;
128
+ } else if (action === 'subscriptions') {
129
+ responseData = `
130
+ // TODO: Implement subscriptions logic
131
+ const subscriptions = [];
132
+
133
+ res.status(200).json({
134
+ success: true,
135
+ count: subscriptions.length,
136
+ subscriptions: subscriptions
137
+ });`;
138
+ } else if (action === 'analytics') {
139
+ responseData = `
140
+ // TODO: Implement analytics logic
141
+ const analytics = {
142
+ dailySignups: [],
143
+ revenue: []
144
+ };
145
+
146
+ res.status(200).json({
147
+ success: true,
148
+ analytics: analytics
149
+ });`;
150
+ } else if (action === 'usage') {
151
+ responseData = `
152
+ // Fetch user usage data
153
+ const usage = {
154
+ apiCalls: 0,
155
+ storage: 0,
156
+ users: 0
157
+ };
158
+
159
+ res.status(200).json({
160
+ success: true,
161
+ usage: usage
162
+ });`;
163
+ } else if (action === 'subscription') {
164
+ responseData = `
165
+ // Fetch user subscription
166
+ // TODO: Implement subscription logic with actual Subscription model
167
+ const subscription = {
168
+ plan: 'free',
169
+ status: 'active',
170
+ renewalDate: new Date()
171
+ };
172
+
173
+ res.status(200).json({
174
+ success: true,
175
+ subscription: subscription
176
+ });`;
177
+ } else if (action === 'invoices') {
178
+ responseData = `
179
+ // Fetch user invoices
180
+ // TODO: Implement invoices logic
181
+ const invoices = [];
182
+
183
+ res.status(200).json({
184
+ success: true,
185
+ count: invoices.length,
186
+ invoices: invoices
187
+ });`;
188
+ } else if (action === 'settings') {
189
+ responseData = `
190
+ // Fetch or update user settings
191
+ ${method === 'PUT' ? `
192
+ const updatedSettings = req.body;
193
+ // TODO: Save settings to database
194
+
195
+ res.status(200).json({
196
+ success: true,
197
+ message: 'Settings updated successfully',
198
+ settings: updatedSettings
199
+ });` : `
200
+ // TODO: Fetch settings from database
201
+ const settings = {};
202
+
203
+ res.status(200).json({
204
+ success: true,
205
+ settings: settings
206
+ });`}`;
207
+ } else if (action === 'status' && method === 'PUT') {
208
+ // Special handling for /users/:id/status
209
+ responseData = `
210
+ const { status } = req.body;
211
+ const item = await ${modelName}.findByIdAndUpdate(
212
+ req.params.id,
213
+ { status },
214
+ { new: true }
215
+ );
216
+
217
+ res.status(200).json({
218
+ success: true,
219
+ message: 'Status updated successfully',
220
+ data: item
221
+ });`;
222
+ } else {
223
+ // Generic action
224
+ responseData = `
225
+ // TODO: Implement ${action} logic
226
+ res.status(200).json({
227
+ success: true,
228
+ message: '${action} endpoint',
229
+ ${responseKey}: []
230
+ });`;
231
+ }
232
+
233
+ // Determine route path
234
+ let routePath = `/${action}`;
235
+ if (action === 'status') {
236
+ routePath = '/:id/status';
237
+ } else if (action === 'upgrade') {
238
+ routePath = '/subscription/upgrade';
239
+ }
240
+
241
+ const httpMethod = method.toLowerCase();
242
+
243
+ routesCode += `
244
+ router.${httpMethod}('${routePath}'${auth}, async (req, res) => {
245
+ try {${responseData}
246
+ } catch (error) {
247
+ res.status(500).json({
248
+ success: false,
249
+ message: 'Error processing ${action}',
250
+ error: error.message
251
+ });
252
+ }
253
+ });
254
+ `;
255
+ }
256
+
257
+ return routesCode;
258
+ }
259
+
260
+ /**
261
+ * Generate CRUD routes for a resource
262
+ */
263
+ export function generateCrudRoutes(resourceName, fields = [], hasAuth = false, actions = [], endpoints = []) {
264
+ const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
265
+ const routePath = `/${resourceName.toLowerCase()}`;
266
+
267
+ // Determine if this resource is user-specific (needs auth for ALL operations)
268
+ const userSpecificResources = ['cart', 'orders', 'order'];
269
+ const isUserSpecific = userSpecificResources.includes(resourceName.toLowerCase());
270
+
271
+ // For user-specific resources, all routes need auth
272
+ // For public resources (products, reviews, categories), only write operations need auth
273
+ const authMiddlewareWrite = hasAuth ? ', authenticateToken' : '';
274
+ const authMiddlewareRead = (hasAuth && isUserSpecific) ? ', authenticateToken' : '';
275
+
276
+ const userIdAssignment = hasAuth ? `
277
+ if (req.user) {
278
+ data.userId = req.user.id;
279
+ }` : '';
280
+
281
+ // Determine response key for list endpoint
282
+ // Cart -> "items", Dashboard -> "stats", Others -> resource name
283
+ let responseKey = resourceName.toLowerCase();
284
+ if (responseKey === 'cart') responseKey = 'items';
285
+ else if (responseKey === 'dashboard') responseKey = 'stats';
286
+
287
+ return `import express from 'express';
288
+ import ${modelName} from '../models/${modelName}.js';
289
+ ${hasAuth ? "import { authenticateToken } from '../middleware/auth.js';" : ''}
290
+
291
+ const router = express.Router();
292
+
293
+ ${generateActionRoutes(resourceName, modelName, actions, endpoints, hasAuth)}
294
+
295
+ // ===== CREATE =====
296
+ router.post('/'${authMiddlewareWrite}, async (req, res) => {
297
+ try {
298
+ const data = req.body;
299
+ ${userIdAssignment}
300
+
301
+ const item = new ${modelName}(data);
302
+ await item.save();
303
+
304
+ res.status(201).json({
305
+ success: true,
306
+ message: '${modelName} created successfully',
307
+ data: item
308
+ });
309
+ } catch (error) {
310
+ res.status(400).json({
311
+ success: false,
312
+ message: 'Error creating ${modelName}',
313
+ error: error.message
314
+ });
315
+ }
316
+ });
317
+
318
+ // ===== READ ALL (LIST) =====
319
+ router.get('/'${authMiddlewareRead}, async (req, res) => {
320
+ try {
321
+ const query = ${isUserSpecific && hasAuth ? "req.user ? { userId: req.user.id } : {}" : "{}"};
322
+ const items = await ${modelName}.find(query).sort({ createdAt: -1 });
323
+
324
+ res.status(200).json({
325
+ success: true,
326
+ count: items.length,
327
+ ${responseKey}: items
328
+ });
329
+ } catch (error) {
330
+ res.status(500).json({
331
+ success: false,
332
+ message: 'Error fetching ${modelName}s',
333
+ error: error.message
334
+ });
335
+ }
336
+ });
337
+
338
+ // ===== READ SINGLE =====
339
+ router.get('/:id'${authMiddlewareRead}, async (req, res) => {
340
+ try {
341
+ const item = await ${modelName}.findById(req.params.id);
342
+
343
+ if (!item) {
344
+ return res.status(404).json({
345
+ success: false,
346
+ message: '${modelName} not found'
347
+ });
348
+ }
349
+
350
+ res.status(200).json({
351
+ success: true,
352
+ data: item
353
+ });
354
+ } catch (error) {
355
+ res.status(500).json({
356
+ success: false,
357
+ message: 'Error fetching ${modelName}',
358
+ error: error.message
359
+ });
360
+ }
361
+ });
362
+
363
+ // ===== UPDATE =====
364
+ router.put('/:id'${authMiddlewareWrite}, async (req, res) => {
365
+ try {
366
+ const item = await ${modelName}.findByIdAndUpdate(
367
+ req.params.id,
368
+ req.body,
369
+ { new: true, runValidators: true }
370
+ );
371
+
372
+ if (!item) {
373
+ return res.status(404).json({
374
+ success: false,
375
+ message: '${modelName} not found'
376
+ });
377
+ }
378
+
379
+ res.status(200).json({
380
+ success: true,
381
+ message: '${modelName} updated successfully',
382
+ data: item
383
+ });
384
+ } catch (error) {
385
+ res.status(400).json({
386
+ success: false,
387
+ message: 'Error updating ${modelName}',
388
+ error: error.message
389
+ });
390
+ }
391
+ });
392
+
393
+ // ===== DELETE =====
394
+ router.delete('/:id'${authMiddlewareWrite}, async (req, res) => {
395
+ try {
396
+ const item = await ${modelName}.findByIdAndDelete(req.params.id);
397
+
398
+ if (!item) {
399
+ return res.status(404).json({
400
+ success: false,
401
+ message: '${modelName} not found'
402
+ });
403
+ }
404
+
405
+ res.status(200).json({
406
+ success: true,
407
+ message: '${modelName} deleted successfully'
408
+ });
409
+ } catch (error) {
410
+ res.status(500).json({
411
+ success: false,
412
+ message: 'Error deleting ${modelName}',
413
+ error: error.message
414
+ });
415
+ }
416
+ });
417
+
418
+ export default router;
419
+ `;
420
+ }
421
+
422
+ /**
423
+ * Generate server imports and route registrations
424
+ */
425
+ export function generateServerRouteRegistration(resources, hasAuth = false) {
426
+ const imports = [];
427
+ const registrations = [];
428
+
429
+ for (const [resourceName, resource] of Object.entries(resources)) {
430
+ if (resourceName === 'api') continue; // Skip generic api resource
431
+
432
+ const modelName = resourceName.charAt(0).toUpperCase() + resourceName.slice(1).replace(/s$/, '');
433
+ imports.push(`import ${resourceName}Routes from './routes/${resourceName}.routes.js';`);
434
+ registrations.push(`app.use('/api/${resourceName}', ${resourceName}Routes);`);
435
+ }
436
+
437
+ let code = '';
438
+
439
+ if (imports.length > 0) {
440
+ code += '// Resource Routes\\n' + imports.join('\\n') + '\\n\\n';
441
+ }
442
+
443
+ if (registrations.length > 0) {
444
+ code += '// Register Routes\\n' + registrations.join('\\n');
445
+ }
446
+
447
+ return code;
448
+ }
449
+
450
+ /**
451
+ * Generate updated package.json dependencies for CRUD
452
+ */
453
+ export function getRequiredCrudDependencies() {
454
+ return {
455
+ 'express': '^4.18.2',
456
+ 'mongoose': '^8.0.0',
457
+ 'bcryptjs': '^2.4.3',
458
+ 'jsonwebtoken': '^9.1.0',
459
+ 'cors': '^2.8.5',
460
+ 'dotenv': '^16.3.1'
461
+ };
462
+ }
463
+
464
+ /**
465
+ * Validate if resource has proper CRUD structure
466
+ */
467
+ export function validateCrudResource(resource) {
468
+ const errors = [];
469
+
470
+ if (!resource.name) {
471
+ errors.push('Resource must have a name');
472
+ }
473
+
474
+ if (resource.methods.length === 0) {
475
+ errors.push('Resource must have at least one HTTP method');
476
+ }
477
+
478
+ if (resource.fields.length === 0) {
479
+ errors.push('Resource should have at least one field defined');
480
+ }
481
+
482
+ return {
483
+ isValid: errors.length === 0,
484
+ errors
485
+ };
486
+ }