@rapidd/build 1.2.3 → 2.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 (71) hide show
  1. package/README.md +219 -68
  2. package/dist/bin/cli.d.ts +3 -0
  3. package/dist/bin/cli.d.ts.map +1 -0
  4. package/dist/bin/cli.js +31 -0
  5. package/dist/bin/cli.js.map +1 -0
  6. package/dist/index.d.ts +18 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +32 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/src/commands/build.d.ts +17 -0
  11. package/dist/src/commands/build.d.ts.map +1 -0
  12. package/dist/src/commands/build.js +236 -0
  13. package/dist/src/commands/build.js.map +1 -0
  14. package/dist/src/generators/aclGenerator.d.ts +6 -0
  15. package/dist/src/generators/aclGenerator.d.ts.map +1 -0
  16. package/dist/src/generators/aclGenerator.js +384 -0
  17. package/dist/src/generators/aclGenerator.js.map +1 -0
  18. package/dist/src/generators/index.d.ts +4 -0
  19. package/dist/src/generators/index.d.ts.map +1 -0
  20. package/dist/src/generators/index.js +13 -0
  21. package/dist/src/generators/index.js.map +1 -0
  22. package/dist/src/generators/modelGenerator.d.ts +10 -0
  23. package/dist/src/generators/modelGenerator.d.ts.map +1 -0
  24. package/dist/src/generators/modelGenerator.js +143 -0
  25. package/dist/src/generators/modelGenerator.js.map +1 -0
  26. package/dist/src/generators/routeGenerator.d.ts +10 -0
  27. package/dist/src/generators/routeGenerator.d.ts.map +1 -0
  28. package/dist/src/generators/routeGenerator.js +172 -0
  29. package/dist/src/generators/routeGenerator.js.map +1 -0
  30. package/dist/src/parsers/datasourceParser.d.ts +11 -0
  31. package/dist/src/parsers/datasourceParser.d.ts.map +1 -0
  32. package/dist/src/parsers/datasourceParser.js +131 -0
  33. package/dist/src/parsers/datasourceParser.js.map +1 -0
  34. package/dist/src/parsers/deepSQLAnalyzer.d.ts +85 -0
  35. package/dist/src/parsers/deepSQLAnalyzer.d.ts.map +1 -0
  36. package/dist/src/parsers/deepSQLAnalyzer.js +482 -0
  37. package/dist/src/parsers/deepSQLAnalyzer.js.map +1 -0
  38. package/dist/src/parsers/enhancedRLSConverter.d.ts +14 -0
  39. package/dist/src/parsers/enhancedRLSConverter.d.ts.map +1 -0
  40. package/dist/src/parsers/enhancedRLSConverter.js +168 -0
  41. package/dist/src/parsers/enhancedRLSConverter.js.map +1 -0
  42. package/dist/src/parsers/functionAnalyzer.d.ts +55 -0
  43. package/dist/src/parsers/functionAnalyzer.d.ts.map +1 -0
  44. package/dist/src/parsers/functionAnalyzer.js +274 -0
  45. package/dist/src/parsers/functionAnalyzer.js.map +1 -0
  46. package/dist/src/parsers/index.d.ts +13 -0
  47. package/dist/src/parsers/index.d.ts.map +1 -0
  48. package/dist/src/parsers/index.js +20 -0
  49. package/dist/src/parsers/index.js.map +1 -0
  50. package/dist/src/parsers/prismaFilterBuilder.d.ts +79 -0
  51. package/dist/src/parsers/prismaFilterBuilder.d.ts.map +1 -0
  52. package/dist/src/parsers/prismaFilterBuilder.js +322 -0
  53. package/dist/src/parsers/prismaFilterBuilder.js.map +1 -0
  54. package/dist/src/parsers/prismaParser.d.ts +14 -0
  55. package/dist/src/parsers/prismaParser.d.ts.map +1 -0
  56. package/dist/src/parsers/prismaParser.js +263 -0
  57. package/dist/src/parsers/prismaParser.js.map +1 -0
  58. package/package.json +21 -13
  59. package/bin/cli.js +0 -33
  60. package/index.js +0 -11
  61. package/src/commands/build.js +0 -638
  62. package/src/generators/aclGenerator.js +0 -394
  63. package/src/generators/modelGenerator.js +0 -174
  64. package/src/generators/relationshipsGenerator.js +0 -200
  65. package/src/generators/routeGenerator.js +0 -119
  66. package/src/parsers/datasourceParser.js +0 -121
  67. package/src/parsers/deepSQLAnalyzer.js +0 -554
  68. package/src/parsers/enhancedRLSConverter.js +0 -181
  69. package/src/parsers/functionAnalyzer.js +0 -302
  70. package/src/parsers/prismaFilterBuilder.js +0 -422
  71. package/src/parsers/prismaParser.js +0 -287
@@ -1,638 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const { parsePrismaSchema, parsePrismaDMMF } = require('../parsers/prismaParser');
4
- const { generateAllModels } = require('../generators/modelGenerator');
5
- const { generateRelationshipsFromDMMF, generateRelationshipsFromSchema } = require('../generators/relationshipsGenerator');
6
- const { generateACL } = require('../generators/aclGenerator');
7
- const { parseDatasource } = require('../parsers/datasourceParser');
8
- const { generateAllRoutes } = require('../generators/routeGenerator');
9
-
10
- /**
11
- * Generate rapidd/rapidd.js file
12
- * @param {string} rapiddJsPath - Path to rapidd.js
13
- * @param {boolean} isPostgreSQL - Whether the database is PostgreSQL
14
- */
15
- function generateRapiddFile(rapiddJsPath, isPostgreSQL = true) {
16
- let content;
17
-
18
- if (isPostgreSQL) {
19
- // PostgreSQL version with RLS support
20
- content = `const { PrismaClient, Prisma } = require('../prisma/client');
21
- const { AsyncLocalStorage } = require('async_hooks');
22
- const acl = require('./acl');
23
-
24
- // Request Context Storage
25
- const requestContext = new AsyncLocalStorage();
26
-
27
- // RLS Configuration aus Environment Variables
28
- const RLS_CONFIG = {
29
- namespace: process.env.RLS_NAMESPACE || 'app',
30
- userId: process.env.RLS_USER_ID || 'current_user_id',
31
- userRole: process.env.RLS_USER_ROLE || 'current_user_role',
32
- };
33
-
34
- // =====================================================
35
- // BASE PRISMA CLIENTS
36
- // =====================================================
37
-
38
- /**
39
- * ADMIN CLIENT - Bypasses ALL RLS
40
- * Uses DATABASE_URL_ADMIN connection (e.g., app_auth_proxy user)
41
- * Use ONLY for authentication operations:
42
- * - Login
43
- * - Register
44
- * - Email Verification
45
- * - Password Reset
46
- * - OAuth operations
47
- */
48
- const authPrisma = new PrismaClient({
49
- datasources: {
50
- db: {
51
- url: process.env.DATABASE_URL_ADMIN
52
- }
53
- },
54
- log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
55
- });
56
-
57
- /**
58
- * BASE CLIENT - Regular user with RLS
59
- * Uses DATABASE_URL connection
60
- * Use for all business operations
61
- */
62
- const basePrisma = new PrismaClient({
63
- datasources: {
64
- db: {
65
- url: process.env.DATABASE_URL
66
- }
67
- },
68
- log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
69
- });
70
-
71
- // =====================================================
72
- // RLS HELPER FUNCTIONS
73
- // =====================================================
74
-
75
- /**
76
- * Set RLS Session Variables in PostgreSQL
77
- * Execute each SET command separately to avoid prepared statement error
78
- */
79
- async function setRLSVariables(tx, userId, userRole) {
80
- const namespace = RLS_CONFIG.namespace;
81
- const userIdVar = RLS_CONFIG.userId;
82
- const userRoleVar = RLS_CONFIG.userRole;
83
-
84
- // Execute SET commands separately
85
- await tx.$executeRawUnsafe(\`SET LOCAL \${namespace}.\${userIdVar} = '\${userId}'\`);
86
- await tx.$executeRawUnsafe(\`SET LOCAL \${namespace}.\${userRoleVar} = '\${userRole}'\`);
87
- }
88
-
89
- /**
90
- * Reset RLS Session Variables
91
- */
92
- async function resetRLSVariables(tx) {
93
- const namespace = RLS_CONFIG.namespace;
94
- const userIdVar = RLS_CONFIG.userId;
95
- const userRoleVar = RLS_CONFIG.userRole;
96
-
97
- try {
98
- await tx.$executeRawUnsafe(\`RESET \${namespace}.\${userIdVar}\`);
99
- await tx.$executeRawUnsafe(\`RESET \${namespace}.\${userRoleVar}\`);
100
- } catch (e) {
101
- // Ignore errors on reset
102
- console.error('Failed to reset RLS variables:', e);
103
- }
104
- }
105
-
106
- // =====================================================
107
- // EXTENDED PRISMA WITH AUTOMATIC RLS
108
- // =====================================================
109
-
110
- /**
111
- * Extended Prisma Client with automatic RLS context
112
- * Automatically wraps all operations in RLS context from AsyncLocalStorage
113
- */
114
- const prisma = basePrisma.$extends({
115
- query: {
116
- async $allOperations({ operation, args, query, model }) {
117
- const context = requestContext.getStore();
118
-
119
- // No context = no RLS (e.g., system operations)
120
- if (!context?.userId || !context?.userRole) {
121
- return query(args);
122
- }
123
-
124
- const { userId, userRole } = context;
125
-
126
- // For operations that are already transactions, just set the variables
127
- if (operation === '$transaction') {
128
- return basePrisma.$transaction(async (tx) => {
129
- await setRLSVariables(tx, userId, userRole);
130
- return query(args);
131
- });
132
- }
133
-
134
- // For regular operations, wrap in transaction with RLS
135
- return basePrisma.$transaction(async (tx) => {
136
- // Set session variables
137
- await setRLSVariables(tx, userId, userRole);
138
-
139
- // Execute the original query using the transaction client
140
- if (model) {
141
- // Model query (e.g., user.findMany())
142
- return tx[model][operation](args);
143
- } else {
144
- // Raw query or special operation
145
- return tx[operation](args);
146
- }
147
- });
148
- },
149
- },
150
- });
151
-
152
- // =====================================================
153
- // TRANSACTION HELPERS
154
- // =====================================================
155
-
156
- /**
157
- * Helper for batch operations in single transaction
158
- */
159
- async function prismaTransaction(operations) {
160
- const context = requestContext.getStore();
161
-
162
- return basePrisma.$transaction(async (tx) => {
163
- if (context?.userId && context?.userRole) {
164
- await setRLSVariables(tx, context.userId, context.userRole);
165
- }
166
- return Promise.all(operations.map(op => op(tx)));
167
- });
168
- }
169
-
170
- // =====================================================
171
- // CONTEXT HELPERS
172
- // =====================================================
173
-
174
- /**
175
- * Express Middleware: Set RLS context from authenticated user
176
- * Use this AFTER your authentication middleware
177
- */
178
- function setRLSContext(req, res, next) {
179
- if (req.user) {
180
- // Set context for async operations
181
- requestContext.run(
182
- {
183
- userId: req.user.id,
184
- userRole: req.user.role
185
- },
186
- () => next()
187
- );
188
- } else {
189
- next();
190
- }
191
- }
192
-
193
- /**
194
- * Get RLS Config (for SQL generation)
195
- */
196
- function getRLSConfig() {
197
- return RLS_CONFIG;
198
- }
199
-
200
- // =====================================================
201
- // GRACEFUL SHUTDOWN
202
- // =====================================================
203
-
204
- async function disconnectAll() {
205
- await authPrisma.$disconnect();
206
- await basePrisma.$disconnect();
207
- }
208
-
209
- process.on('beforeExit', async () => {
210
- await disconnectAll();
211
- });
212
-
213
- // =====================================================
214
- // EXPORTS
215
- // =====================================================
216
-
217
- module.exports = {
218
- // Main clients
219
- prisma, // Use for regular operations with automatic RLS from context
220
- authPrisma, // Use ONLY for auth operations (login, register, etc.)
221
-
222
- // Transaction helpers
223
- prismaTransaction,
224
-
225
- // Context helpers
226
- requestContext,
227
- setRLSContext,
228
-
229
- // RLS utilities
230
- setRLSVariables,
231
- resetRLSVariables,
232
- getRLSConfig,
233
-
234
- // Utilities
235
- disconnectAll,
236
- PrismaClient,
237
- Prisma,
238
- acl
239
- };
240
- `;
241
- } else {
242
- // Non-PostgreSQL version (MySQL, SQLite, etc.) - simplified without RLS
243
- content = `const { PrismaClient } = require('../prisma/client');
244
- const acl = require('./acl');
245
-
246
- // Standard Prisma Client
247
- const prisma = new PrismaClient({
248
- log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
249
- });
250
-
251
- const prismaTransaction = async (operations) => prisma.$transaction(async (tx) => {
252
- return Promise.all(operations.map(op => op(tx)));
253
- });
254
-
255
- module.exports = {
256
- prisma,
257
- prismaTransaction,
258
- PrismaClient,
259
- acl
260
- };
261
- `;
262
- }
263
-
264
- // Ensure rapidd directory exists
265
- const rapiddDir = path.dirname(rapiddJsPath);
266
- if (!fs.existsSync(rapiddDir)) {
267
- fs.mkdirSync(rapiddDir, { recursive: true });
268
- }
269
-
270
- fs.writeFileSync(rapiddJsPath, content);
271
- console.log('✓ Generated rapidd/rapidd.js');
272
- }
273
-
274
- /**
275
- * Update relationships.json for a specific model
276
- */
277
- async function updateRelationshipsForModel(filteredModels, relationshipsPath, schemaPath, usedDMMF) {
278
- let existingRelationships = {};
279
-
280
- // Load existing relationships if file exists
281
- if (fs.existsSync(relationshipsPath)) {
282
- try {
283
- existingRelationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
284
- } catch (error) {
285
- console.warn('Could not parse existing relationships.json, will create new');
286
- }
287
- }
288
-
289
- // Generate relationships for the filtered model(s)
290
- let newRelationships = {};
291
- if (usedDMMF) {
292
- // Use DMMF to get relationships for specific model
293
- const { generateRelationshipsFromDMMF } = require('../generators/relationshipsGenerator');
294
- const tempPath = relationshipsPath + '.tmp';
295
- await generateRelationshipsFromDMMF(schemaPath, tempPath);
296
- const allRelationships = JSON.parse(fs.readFileSync(tempPath, 'utf8'));
297
- fs.unlinkSync(tempPath);
298
-
299
- // Extract only the filtered model's relationships
300
- for (const modelName of Object.keys(filteredModels)) {
301
- if (allRelationships[modelName]) {
302
- newRelationships[modelName] = allRelationships[modelName];
303
- }
304
- }
305
- } else {
306
- // Use schema parser
307
- const { generateRelationshipsFromSchema } = require('../generators/relationshipsGenerator');
308
- const tempPath = relationshipsPath + '.tmp';
309
- generateRelationshipsFromSchema(schemaPath, tempPath);
310
- const allRelationships = JSON.parse(fs.readFileSync(tempPath, 'utf8'));
311
- fs.unlinkSync(tempPath);
312
-
313
- // Extract only the filtered model's relationships
314
- for (const modelName of Object.keys(filteredModels)) {
315
- if (allRelationships[modelName]) {
316
- newRelationships[modelName] = allRelationships[modelName];
317
- }
318
- }
319
- }
320
-
321
- // Merge with existing relationships
322
- const updatedRelationships = { ...existingRelationships, ...newRelationships };
323
-
324
- // Write back to file
325
- fs.writeFileSync(relationshipsPath, JSON.stringify(updatedRelationships, null, 2));
326
- }
327
-
328
- /**
329
- * Update acl.js for a specific model
330
- */
331
- async function updateACLForModel(filteredModels, allModels, aclPath, datasource, userTable, relationships, debug = false) {
332
- const { generateACL } = require('../generators/aclGenerator');
333
-
334
- // Generate ACL for the filtered model (but pass all models for user table detection)
335
- const tempPath = aclPath + '.tmp';
336
- await generateACL(
337
- filteredModels,
338
- tempPath,
339
- datasource.url,
340
- datasource.isPostgreSQL,
341
- userTable,
342
- relationships,
343
- debug,
344
- allModels
345
- );
346
-
347
- // Read the generated ACL for the specific model
348
- const tempContent = fs.readFileSync(tempPath, 'utf8');
349
- fs.unlinkSync(tempPath);
350
-
351
- // Extract the model's ACL configuration
352
- const modelName = Object.keys(filteredModels)[0];
353
-
354
- // Find the start of the model definition
355
- const modelStart = tempContent.indexOf(`${modelName}:`);
356
- if (modelStart === -1) {
357
- throw new Error(`Could not find model ${modelName} in generated RLS`);
358
- }
359
-
360
- // Find the matching closing brace by counting braces
361
- let braceCount = 0;
362
- let inString = false;
363
- let stringChar = null;
364
- let i = tempContent.indexOf('{', modelStart);
365
- const contentStart = i;
366
-
367
- for (; i < tempContent.length; i++) {
368
- const char = tempContent[i];
369
- const prevChar = i > 0 ? tempContent[i - 1] : '';
370
-
371
- // Handle string literals
372
- if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
373
- if (!inString) {
374
- inString = true;
375
- stringChar = char;
376
- } else if (char === stringChar) {
377
- inString = false;
378
- stringChar = null;
379
- }
380
- }
381
-
382
- if (!inString) {
383
- if (char === '{') braceCount++;
384
- if (char === '}') braceCount--;
385
-
386
- if (braceCount === 0) {
387
- // Found the closing brace
388
- break;
389
- }
390
- }
391
- }
392
-
393
- if (braceCount !== 0) {
394
- throw new Error(`Could not extract ACL for model ${modelName} - unmatched braces`);
395
- }
396
-
397
- const modelAcl = tempContent.substring(modelStart, i + 1);
398
-
399
- // Read existing acl.js
400
- if (fs.existsSync(aclPath)) {
401
- let existingContent = fs.readFileSync(aclPath, 'utf8');
402
-
403
- // Check if model already exists in ACL
404
- const existingModelPattern = new RegExp(`${modelName}:\\s*\\{[\\s\\S]*?\\n \\}(?=,|\\n)`);
405
-
406
- if (existingModelPattern.test(existingContent)) {
407
- // Replace existing model ACL
408
- existingContent = existingContent.replace(existingModelPattern, modelAcl);
409
- } else {
410
- // Add new model ACL before the closing of acl.model
411
- // Find the last closing brace of a model object and add comma after it
412
- existingContent = existingContent.replace(
413
- /(\n \})\n(\};)/,
414
- `$1,\n ${modelAcl}\n$2`
415
- );
416
- }
417
-
418
- fs.writeFileSync(aclPath, existingContent);
419
- console.log(`✓ Updated RLS for model: ${modelName}`);
420
- } else {
421
- // If acl.js doesn't exist, create it with just this model
422
- await generateACL(
423
- filteredModels,
424
- aclPath,
425
- datasource.url,
426
- datasource.isPostgreSQL,
427
- userTable,
428
- relationships,
429
- debug,
430
- allModels
431
- );
432
- }
433
- }
434
-
435
- /**
436
- * Build models from Prisma schema
437
- * @param {Object} options - Build options
438
- * @param {string} options.schema - Path to Prisma schema file
439
- * @param {string} options.output - Output directory for generated models
440
- * @param {string} options.model - Optional: specific model to generate
441
- * @param {string} options.only - Optional: specific component to generate
442
- */
443
- async function buildModels(options) {
444
- const schemaPath = path.resolve(process.cwd(), options.schema);
445
- const outputBase = path.resolve(process.cwd(), options.output);
446
-
447
- // If output is "/", use process.cwd() as the base
448
- const baseDir = options.output === '/' ? process.cwd() : outputBase;
449
-
450
- // Construct paths
451
- const srcDir = path.join(baseDir, 'src');
452
- const modelDir = path.join(srcDir, 'Model');
453
- const modelJsPath = path.join(srcDir, 'Model.js');
454
- const rapiddDir = path.join(baseDir, 'rapidd');
455
- const relationshipsPath = path.join(rapiddDir, 'relationships.json');
456
- const aclPath = path.join(rapiddDir, 'acl.js');
457
- const rapiddJsPath = path.join(rapiddDir, 'rapidd.js');
458
- const routesDir = path.join(baseDir, 'routes', 'api', 'v1');
459
- const logsDir = path.join(baseDir, 'logs');
460
-
461
- console.log('Building Rapidd models...');
462
- console.log(`Schema: ${schemaPath}`);
463
- console.log(`Output: ${baseDir}`);
464
-
465
- // Create logs directory
466
- if (!fs.existsSync(logsDir)) {
467
- fs.mkdirSync(logsDir, { recursive: true });
468
- }
469
-
470
- // Check if schema file exists
471
- if (!fs.existsSync(schemaPath)) {
472
- throw new Error(`Prisma schema file not found at: ${schemaPath}`);
473
- }
474
-
475
- // Run npx prisma generate first
476
- console.log('\nRunning npx prisma generate...');
477
- const { execSync } = require('child_process');
478
- try {
479
- execSync(`npx prisma generate --schema=${schemaPath}`, {
480
- stdio: 'inherit',
481
- cwd: process.cwd()
482
- });
483
- console.log('✓ Prisma client generated successfully\n');
484
- } catch (error) {
485
- console.warn('⚠ Warning: Failed to generate Prisma client');
486
- console.warn('Continuing with schema parsing fallback...\n');
487
- }
488
-
489
- // Try to use Prisma DMMF first (using @prisma/internals getDMMF)
490
- let parsedData = null;
491
- let usedDMMF = false;
492
-
493
- try {
494
- parsedData = await parsePrismaDMMF(schemaPath);
495
- if (parsedData) {
496
- console.log('Using Prisma DMMF (via @prisma/internals)');
497
- usedDMMF = true;
498
- }
499
- } catch (error) {
500
- // Fall back to schema parsing
501
- }
502
-
503
- // If DMMF parsing failed, parse schema file directly
504
- if (!parsedData) {
505
- console.log('Parsing Prisma schema file...');
506
- parsedData = parsePrismaSchema(schemaPath);
507
- }
508
-
509
- const { models, enums } = parsedData;
510
-
511
- // Filter models if --model option is provided
512
- let filteredModels = models;
513
- if (options.model) {
514
- const modelName = options.model.toLowerCase();
515
- const matchedModel = Object.keys(models).find(m => m.toLowerCase() === modelName);
516
-
517
- if (!matchedModel) {
518
- throw new Error(`Model "${options.model}" not found in schema. Available models: ${Object.keys(models).join(', ')}`);
519
- }
520
-
521
- filteredModels = { [matchedModel]: models[matchedModel] };
522
- console.log(`Filtering to model: ${matchedModel}`);
523
- }
524
-
525
- console.log(`Found ${Object.keys(models).length} models${options.model ? ` (generating ${Object.keys(filteredModels).length})` : ''}`);
526
-
527
- // Determine which components to generate
528
- const shouldGenerate = {
529
- model: !options.only || options.only === 'model',
530
- route: !options.only || options.only === 'route',
531
- acl: !options.only || options.only === 'acl',
532
- relationship: !options.only || options.only === 'relationship'
533
- };
534
-
535
- // Validate --only option
536
- if (options.only && !['model', 'route', 'acl', 'relationship'].includes(options.only)) {
537
- throw new Error(`Invalid --only value "${options.only}". Must be one of: model, route, acl, relationship`);
538
- }
539
-
540
- // Generate model files
541
- if (shouldGenerate.model) {
542
- generateAllModels(filteredModels, modelDir, modelJsPath);
543
- }
544
-
545
- // Parse datasource to determine database type
546
- let datasource = { isPostgreSQL: true, url: null }; // Default to PostgreSQL
547
- try {
548
- datasource = parseDatasource(schemaPath);
549
- } catch (error) {
550
- // Only warn if it's not the expected "No url found" error in Prisma 7
551
- if (!error.message.includes('No url found')) {
552
- console.warn('Could not parse datasource, assuming PostgreSQL:', error.message);
553
- }
554
- }
555
-
556
- // Generate rapidd/rapidd.js if it doesn't exist
557
- if (!fs.existsSync(rapiddJsPath)) {
558
- console.log('Generating rapidd/rapidd.js...');
559
- generateRapiddFile(rapiddJsPath, datasource.isPostgreSQL);
560
- }
561
-
562
- // Generate relationships.json
563
- if (shouldGenerate.relationship) {
564
- console.log(`\nGenerating relationships.json...`);
565
-
566
- try {
567
- if (options.model) {
568
- // Update only specific model in relationships.json
569
- await updateRelationshipsForModel(filteredModels, relationshipsPath, schemaPath, usedDMMF);
570
- } else {
571
- // Generate all relationships
572
- if (usedDMMF) {
573
- await generateRelationshipsFromDMMF(schemaPath, relationshipsPath);
574
- } else {
575
- generateRelationshipsFromSchema(schemaPath, relationshipsPath);
576
- }
577
- }
578
- console.log(`✓ Relationships file generated at: ${relationshipsPath}`);
579
- } catch (error) {
580
- console.error('Failed to generate relationships.json:', error.message);
581
- console.log('Note: You may need to create relationships.json manually.');
582
- }
583
- }
584
-
585
- // Generate ACL configuration
586
- if (shouldGenerate.acl) {
587
- console.log(`\nGenerating ACL configuration...`);
588
-
589
- // Load relationships for Prisma filter building
590
- let relationships = {};
591
- try {
592
- if (fs.existsSync(relationshipsPath)) {
593
- relationships = JSON.parse(fs.readFileSync(relationshipsPath, 'utf8'));
594
- }
595
- } catch (error) {
596
- console.warn('Could not load relationships.json:', error.message);
597
- }
598
-
599
- try {
600
-
601
- // For non-PostgreSQL databases (MySQL, SQLite, etc.), generate permissive ACL
602
- if (!datasource.isPostgreSQL) {
603
- console.log(`${datasource.provider || 'Non-PostgreSQL'} database detected - generating permissive ACL...`);
604
- await generateACL(models, aclPath, null, false, options.userTable, relationships, options.debug);
605
- } else if (options.model) {
606
- // Update only specific model in acl.js
607
- await updateACLForModel(filteredModels, models, aclPath, datasource, options.userTable, relationships, options.debug);
608
- } else {
609
- // Generate ACL for all models
610
- await generateACL(
611
- models,
612
- aclPath,
613
- datasource.url,
614
- datasource.isPostgreSQL,
615
- options.userTable,
616
- relationships,
617
- options.debug
618
- );
619
- }
620
- } catch (error) {
621
- console.error('Failed to generate ACL:', error.message);
622
- console.log('Generating permissive ACL fallback...');
623
- // Pass null for URL and false for isPostgreSQL to skip database connection
624
- await generateACL(models, aclPath, null, false, options.userTable, relationships, options.debug);
625
- }
626
- }
627
-
628
- // Generate routes
629
- if (shouldGenerate.route) {
630
- generateAllRoutes(filteredModels, routesDir);
631
- }
632
-
633
- return { models, enums };
634
- }
635
-
636
- module.exports = {
637
- buildModels
638
- };