@rapidd/build 1.2.3 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +148 -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,200 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- /**
5
- * Generate relationships.json from Prisma DMMF
6
- * @param {Object} models - Models object from parser
7
- * @param {string} outputPath - Path to output relationships.json
8
- */
9
- function generateRelationships(models, outputPath) {
10
- const relationships = {};
11
-
12
- for (const [modelName, modelInfo] of Object.entries(models)) {
13
- relationships[modelName] = {};
14
-
15
- for (const relation of modelInfo.relations) {
16
- const relatedModel = models[relation.type];
17
-
18
- if (!relatedModel) {
19
- console.warn(`Warning: Related model ${relation.type} not found for ${modelName}.${relation.name}`);
20
- continue;
21
- }
22
-
23
- // Check if the related model has a composite primary key (many-to-many junction table)
24
- const compositeKeyFields = getCompositeKeyFromModel(relatedModel);
25
-
26
- // Only add field/fields if:
27
- // 1. Related model has composite key (junction table)
28
- // 2. It's a many relationship (array)
29
- // 3. The composite key includes this model's foreign key (proving it's a junction for this model)
30
- const currentModelFk = `${modelName.toLowerCase()}_id`;
31
- const isJunctionTable = compositeKeyFields &&
32
- compositeKeyFields.length > 1 &&
33
- relation.isArray &&
34
- compositeKeyFields.includes(currentModelFk);
35
-
36
- if (isJunctionTable) {
37
- // Many-to-many relationship through junction table
38
- // Reorder fields so current model's field comes first
39
- const reorderedFields = reorderFieldsForModel(compositeKeyFields, modelName);
40
-
41
- relationships[modelName][relation.name] = {
42
- 'object': relation.type,
43
- 'field': compositeKeyFields.join('_'), // e.g., "course_id_teacher_id"
44
- 'fields': reorderedFields
45
- };
46
- } else {
47
- // Simple one-to-one or one-to-many relationship
48
- // Find the foreign key field name
49
- const foreignKeyField = findForeignKeyField(relation, modelInfo, relatedModel);
50
-
51
- if (!foreignKeyField) {
52
- // Skip this relationship if we can't find the FK field
53
- // This usually means the FK is on the other side of the relation
54
- continue;
55
- }
56
-
57
- relationships[modelName][relation.name] = {
58
- 'object': relation.type,
59
- 'field': foreignKeyField
60
- };
61
- }
62
- }
63
- }
64
-
65
- // Ensure output directory exists
66
- const outputDir = path.dirname(outputPath);
67
- if (!fs.existsSync(outputDir)) {
68
- fs.mkdirSync(outputDir, { recursive: true });
69
- }
70
-
71
- fs.writeFileSync(outputPath, JSON.stringify(relationships, null, 4));
72
- console.log('Generated relationships.json');
73
- }
74
-
75
- /**
76
- * Find the foreign key field for a relation
77
- * @param {Object} relation - Relation object
78
- * @param {Object} currentModel - Current model info
79
- * @param {Object} relatedModel - Related model info
80
- * @returns {string|null} - Foreign key field name
81
- */
82
- function findForeignKeyField(relation, currentModel, relatedModel) {
83
- // IMPORTANT: The foreign key field is where the @relation(fields: [...]) is defined
84
-
85
- // For array relations (one-to-many), the FK is in the related model (child)
86
- if (relation.isArray) {
87
- // Find the corresponding relation in the related model that points back
88
- for (const relField of Object.values(relatedModel.fields)) {
89
- if (relField.kind === 'object' &&
90
- relField.relationName === relation.relationName &&
91
- relField.relationFromFields &&
92
- relField.relationFromFields.length > 0) {
93
- // This is the FK field in the child (related) model
94
- return relField.relationFromFields[0];
95
- }
96
- }
97
- // Fallback
98
- return null;
99
- }
100
-
101
- // For singular relations (many-to-one or one-to-one), check if THIS relation has fields defined
102
- if (relation.relationFromFields && relation.relationFromFields.length > 0) {
103
- // The FK is in the current model
104
- return relation.relationFromFields[0];
105
- }
106
-
107
- // For optional one-to-one relations where FK is on the other side
108
- // (e.g., users -> student_profiles? where userId is in student_profiles)
109
- if (!relation.isArray) {
110
- // Find the corresponding relation in the related model that points back
111
- for (const relField of Object.values(relatedModel.fields)) {
112
- if (relField.kind === 'object' &&
113
- relField.relationName === relation.relationName &&
114
- relField.relationFromFields &&
115
- relField.relationFromFields.length > 0) {
116
- // This is the FK field in the related model (pointing back to us)
117
- return relField.relationFromFields[0];
118
- }
119
- }
120
- }
121
-
122
- // If no fields on this side, the FK must be on the other side (shouldn't use this relation for filtering)
123
- return null;
124
- }
125
-
126
- /**
127
- * Get composite key fields from a model
128
- * @param {Object} modelInfo - Model information
129
- * @returns {Array|null} - Array of composite key field names or null
130
- */
131
- function getCompositeKeyFromModel(modelInfo) {
132
- // First check if the model has a compositeKey property (from parser)
133
- if (modelInfo.compositeKey && modelInfo.compositeKey.length > 1) {
134
- return modelInfo.compositeKey;
135
- }
136
-
137
- // Fallback: check for fields marked with isId (for schema parser)
138
- const compositeFields = [];
139
- for (const [fieldName, fieldInfo] of Object.entries(modelInfo.fields)) {
140
- if (fieldInfo.isId && !fieldInfo.isRelation) {
141
- compositeFields.push(fieldName);
142
- }
143
- }
144
-
145
- // If we found multiple ID fields, it's a composite key
146
- if (compositeFields.length > 1) {
147
- return compositeFields;
148
- }
149
-
150
- return null;
151
- }
152
-
153
- /**
154
- * Reorder composite key fields so the current model's field comes first
155
- * @param {Array} fields - Array of field names
156
- * @param {String} currentModelName - Name of the current model
157
- * @returns {Array} - Reordered array with current model's field first
158
- */
159
- function reorderFieldsForModel(fields, currentModelName) {
160
- const currentModelField = `${currentModelName.toLowerCase()}_id`;
161
- const index = fields.indexOf(currentModelField);
162
-
163
- if (index > 0) {
164
- // Move current model's field to the front
165
- const reordered = [...fields];
166
- reordered.splice(index, 1);
167
- reordered.unshift(currentModelField);
168
- return reordered;
169
- }
170
-
171
- return fields;
172
- }
173
-
174
- /**
175
- * Generate relationships.json from schema
176
- * @param {string} schemaPath - Path to Prisma schema file
177
- * @param {string} outputPath - Path to output relationships.json
178
- */
179
- function generateRelationshipsFromSchema(schemaPath, outputPath) {
180
- const { parsePrismaSchema } = require('../parsers/prismaParser');
181
- const parsedData = parsePrismaSchema(schemaPath);
182
- generateRelationships(parsedData.models, outputPath);
183
- }
184
-
185
- /**
186
- * Generate relationships.json from DMMF
187
- * @param {string} schemaPath - Path to Prisma schema file
188
- * @param {string} outputPath - Path to output relationships.json
189
- */
190
- async function generateRelationshipsFromDMMF(schemaPath, outputPath) {
191
- const { parsePrismaDMMF } = require('../parsers/prismaParser');
192
- const parsedData = await parsePrismaDMMF(schemaPath);
193
- generateRelationships(parsedData.models, outputPath);
194
- }
195
-
196
- module.exports = {
197
- generateRelationships,
198
- generateRelationshipsFromSchema,
199
- generateRelationshipsFromDMMF
200
- };
@@ -1,119 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- /**
5
- * Generate Express route for a single model
6
- * @param {string} modelName - Name of the model
7
- * @returns {string} - Generated route code
8
- */
9
- function generateRouteFile(modelName) {
10
- const className = modelName.split(/[^a-zA-Z0-9]+/) // split on any non-alphanumeric char
11
- .filter(Boolean) // remove empty parts
12
- .map(word => word.charAt(0).toUpperCase() + word.slice(1))
13
- .join('');
14
-
15
- return `const router = require('express').Router();
16
- const {${className}, QueryBuilder, prisma} = require('../../../src/Model/${className}');
17
-
18
- router.all('*', async (req, res, next) => {
19
- if(req.user){
20
- req.${className} = new ${className}({'user': req.user});
21
- next();
22
- }
23
- else{
24
- return res.sendError(401, "no_valid_session");
25
- }
26
- });
27
-
28
- // GET ALL
29
- router.get('/', async function(req, res) {
30
- try {
31
- const { q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc" } = req.query;
32
- const results = await req.${className}.getMany(q, include, limit, offset, sortBy, sortOrder);
33
- return res.sendList(results.data, results.meta);
34
- }
35
- catch(error){
36
- const response = QueryBuilder.errorHandler(error);
37
- return res.status(response.status_code).send(response);
38
- }
39
- });
40
-
41
- // GET BY ID
42
- router.get('/:id', async function(req, res) {
43
- try{
44
- const { include = ""} = req.query;
45
- const response = await req.${className}.get(req.params.id, include);
46
- return res.json(response);
47
- }
48
- catch(error){
49
- const response = QueryBuilder.errorHandler(error);
50
- return res.status(response.status_code).send(response);
51
- }
52
- });
53
-
54
- // CREATE
55
- router.post('/', async function(req, res) {
56
- const payload = req.body;
57
- try{
58
- const response = await req.${className}.create(payload);
59
- return res.status(201).json(response);
60
- }
61
- catch(error){
62
- const response = QueryBuilder.errorHandler(error, payload);
63
- return res.status(response.status_code).send(response);
64
- }
65
- });
66
-
67
- // UPDATE
68
- router.patch('/:id', async function(req, res) {
69
- const payload = req.body;
70
- try{
71
- const response = await req.${className}.update(req.params.id, payload);
72
- return res.json(response);
73
- }
74
- catch(error){
75
- const response = QueryBuilder.errorHandler(error, payload);
76
- return res.status(response.status_code).send(response);
77
- }
78
- });
79
-
80
- // DELETE
81
- router.delete('/:id', async (req, res)=>{
82
- try{
83
- await req.${className}.delete(req.params.id);
84
- return res.sendResponse(200, "object_deleted_successfully", {modelName: "${className}"});
85
- }
86
- catch(error){
87
- const response = QueryBuilder.errorHandler(error);
88
- return res.status(response.status_code).send(response);
89
- }
90
- });
91
-
92
- module.exports = router;
93
- `;
94
- }
95
-
96
- /**
97
- * Generate all route files
98
- * @param {Object} models - Models object from parser
99
- * @param {string} routesDir - Directory to output route files
100
- */
101
- function generateAllRoutes(models, routesDir) {
102
- // Create routes directory if it doesn't exist
103
- if (!fs.existsSync(routesDir)) {
104
- fs.mkdirSync(routesDir, { recursive: true });
105
- }
106
-
107
- // Generate individual route files
108
- for (const modelName of Object.keys(models)) {
109
- const routeCode = generateRouteFile(modelName);
110
- const routePath = path.join(routesDir, `${modelName}.js`);
111
- fs.writeFileSync(routePath, routeCode);
112
- console.log(`Generated route: ${modelName}.js`);
113
- }
114
- }
115
-
116
- module.exports = {
117
- generateAllRoutes,
118
- generateRouteFile
119
- };
@@ -1,121 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- // Load .env file if it exists
5
- try {
6
- require('dotenv').config({ path: path.join(process.cwd(), '.env') });
7
- } catch (e) {
8
- // dotenv not available, skip
9
- }
10
-
11
- /**
12
- * Try to load DATABASE_URL from prisma.config.ts (Prisma 7)
13
- * @returns {string|null} - Database URL or null if not found
14
- */
15
- function loadUrlFromPrismaConfig() {
16
- const configPath = path.join(process.cwd(), 'prisma.config.ts');
17
-
18
- if (!fs.existsSync(configPath)) {
19
- return null;
20
- }
21
-
22
- try {
23
- const configContent = fs.readFileSync(configPath, 'utf-8');
24
-
25
- // Look for env('DATABASE_URL') or similar patterns
26
- const envMatch = configContent.match(/env\(['"]([^'"]+)['"]\)/);
27
- if (envMatch) {
28
- const envVar = envMatch[1];
29
- return process.env[envVar] || null;
30
- }
31
-
32
- // Look for direct URL assignment
33
- const urlMatch = configContent.match(/url:\s*['"]([^'"]+)['"]/);
34
- if (urlMatch) {
35
- return urlMatch[1];
36
- }
37
- } catch (e) {
38
- // Failed to read config, return null
39
- }
40
-
41
- return null;
42
- }
43
-
44
- /**
45
- * Parse datasource configuration from Prisma schema
46
- * @param {string} schemaPath - Path to Prisma schema file
47
- * @returns {Object} - Datasource configuration with resolved URL
48
- */
49
- function parseDatasource(schemaPath) {
50
- const schemaContent = fs.readFileSync(schemaPath, 'utf-8');
51
-
52
- // Extract datasource block
53
- const datasourceRegex = /datasource\s+\w+\s*{([^}]*)}/;
54
- const match = schemaContent.match(datasourceRegex);
55
-
56
- if (!match) {
57
- throw new Error('No datasource block found in Prisma schema');
58
- }
59
-
60
- const datasourceBlock = match[1];
61
-
62
- // Extract provider
63
- const providerMatch = datasourceBlock.match(/provider\s*=\s*"([^"]+)"/);
64
- const provider = providerMatch ? providerMatch[1] : null;
65
-
66
- // Try to extract url from schema first
67
- let url = null;
68
- const urlMatch = datasourceBlock.match(/url\s*=\s*(.+)/);
69
-
70
- if (urlMatch) {
71
- url = urlMatch[1].trim();
72
-
73
- // Handle env() function
74
- const envMatch = url.match(/env\(["']([^"']+)["']\)/);
75
- if (envMatch) {
76
- const envVar = envMatch[1];
77
- url = process.env[envVar];
78
- } else {
79
- // Remove quotes if present
80
- url = url.replace(/^["']|["']$/g, '');
81
- }
82
- }
83
-
84
- // If no URL in schema, try prisma.config.ts (Prisma 7)
85
- if (!url) {
86
- url = loadUrlFromPrismaConfig();
87
- }
88
-
89
- // If still no URL, check DATABASE_URL environment variable directly
90
- if (!url) {
91
- url = process.env.DATABASE_URL || null;
92
- }
93
-
94
- // Detect PostgreSQL from provider OR from the actual connection URL
95
- // This is important because the schema might say "mysql" but DATABASE_URL could be postgresql://
96
- let isPostgreSQL = provider === 'postgresql' || provider === 'postgres';
97
-
98
- if (!isPostgreSQL && url) {
99
- // Check if URL starts with postgresql:// or postgres://
100
- isPostgreSQL = url.startsWith('postgresql://') || url.startsWith('postgres://');
101
- }
102
-
103
- // Explicitly detect MySQL to avoid false PostgreSQL detection
104
- const isMySQL = provider === 'mysql' || (url && url.startsWith('mysql://'));
105
-
106
- // If it's MySQL, ensure isPostgreSQL is false
107
- if (isMySQL) {
108
- isPostgreSQL = false;
109
- }
110
-
111
- return {
112
- provider,
113
- url,
114
- isPostgreSQL,
115
- isMySQL
116
- };
117
- }
118
-
119
- module.exports = {
120
- parseDatasource
121
- };