@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.
- package/README.md +219 -68
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +31 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/src/commands/build.d.ts +17 -0
- package/dist/src/commands/build.d.ts.map +1 -0
- package/dist/src/commands/build.js +236 -0
- package/dist/src/commands/build.js.map +1 -0
- package/dist/src/generators/aclGenerator.d.ts +6 -0
- package/dist/src/generators/aclGenerator.d.ts.map +1 -0
- package/dist/src/generators/aclGenerator.js +384 -0
- package/dist/src/generators/aclGenerator.js.map +1 -0
- package/dist/src/generators/index.d.ts +4 -0
- package/dist/src/generators/index.d.ts.map +1 -0
- package/dist/src/generators/index.js +13 -0
- package/dist/src/generators/index.js.map +1 -0
- package/dist/src/generators/modelGenerator.d.ts +10 -0
- package/dist/src/generators/modelGenerator.d.ts.map +1 -0
- package/dist/src/generators/modelGenerator.js +143 -0
- package/dist/src/generators/modelGenerator.js.map +1 -0
- package/dist/src/generators/routeGenerator.d.ts +10 -0
- package/dist/src/generators/routeGenerator.d.ts.map +1 -0
- package/dist/src/generators/routeGenerator.js +172 -0
- package/dist/src/generators/routeGenerator.js.map +1 -0
- package/dist/src/parsers/datasourceParser.d.ts +11 -0
- package/dist/src/parsers/datasourceParser.d.ts.map +1 -0
- package/dist/src/parsers/datasourceParser.js +131 -0
- package/dist/src/parsers/datasourceParser.js.map +1 -0
- package/dist/src/parsers/deepSQLAnalyzer.d.ts +85 -0
- package/dist/src/parsers/deepSQLAnalyzer.d.ts.map +1 -0
- package/dist/src/parsers/deepSQLAnalyzer.js +482 -0
- package/dist/src/parsers/deepSQLAnalyzer.js.map +1 -0
- package/dist/src/parsers/enhancedRLSConverter.d.ts +14 -0
- package/dist/src/parsers/enhancedRLSConverter.d.ts.map +1 -0
- package/dist/src/parsers/enhancedRLSConverter.js +168 -0
- package/dist/src/parsers/enhancedRLSConverter.js.map +1 -0
- package/dist/src/parsers/functionAnalyzer.d.ts +55 -0
- package/dist/src/parsers/functionAnalyzer.d.ts.map +1 -0
- package/dist/src/parsers/functionAnalyzer.js +274 -0
- package/dist/src/parsers/functionAnalyzer.js.map +1 -0
- package/dist/src/parsers/index.d.ts +13 -0
- package/dist/src/parsers/index.d.ts.map +1 -0
- package/dist/src/parsers/index.js +20 -0
- package/dist/src/parsers/index.js.map +1 -0
- package/dist/src/parsers/prismaFilterBuilder.d.ts +79 -0
- package/dist/src/parsers/prismaFilterBuilder.d.ts.map +1 -0
- package/dist/src/parsers/prismaFilterBuilder.js +322 -0
- package/dist/src/parsers/prismaFilterBuilder.js.map +1 -0
- package/dist/src/parsers/prismaParser.d.ts +14 -0
- package/dist/src/parsers/prismaParser.d.ts.map +1 -0
- package/dist/src/parsers/prismaParser.js +263 -0
- package/dist/src/parsers/prismaParser.js.map +1 -0
- package/package.json +21 -13
- package/bin/cli.js +0 -33
- package/index.js +0 -11
- package/src/commands/build.js +0 -638
- package/src/generators/aclGenerator.js +0 -394
- package/src/generators/modelGenerator.js +0 -174
- package/src/generators/relationshipsGenerator.js +0 -200
- package/src/generators/routeGenerator.js +0 -119
- package/src/parsers/datasourceParser.js +0 -121
- package/src/parsers/deepSQLAnalyzer.js +0 -554
- package/src/parsers/enhancedRLSConverter.js +0 -181
- package/src/parsers/functionAnalyzer.js +0 -302
- package/src/parsers/prismaFilterBuilder.js +0 -422
- package/src/parsers/prismaParser.js +0 -287
|
@@ -1,394 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { Client } = require('pg');
|
|
4
|
-
const { createEnhancedConverter } = require('../parsers/enhancedRLSConverter');
|
|
5
|
-
const { analyzeFunctions, generateMappingConfig } = require('../parsers/functionAnalyzer');
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Auto-detect user table name (case-insensitive search for user/users)
|
|
9
|
-
*/
|
|
10
|
-
function detectUserTable(models, userTableOption) {
|
|
11
|
-
if (userTableOption) {
|
|
12
|
-
return userTableOption;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const modelNames = Object.keys(models);
|
|
16
|
-
const userTables = modelNames.filter(name =>
|
|
17
|
-
name.toLowerCase() === 'user' || name.toLowerCase() === 'users'
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
if (userTables.length === 0) {
|
|
21
|
-
throw new Error('No user table found (user/users). Please specify --user-table option.');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (userTables.length > 1) {
|
|
25
|
-
throw new Error(`Multiple user tables found: ${userTables.join(', ')}. Please specify --user-table option.`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return userTables[0];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Extract ACL policies from PostgreSQL RLS
|
|
33
|
-
*/
|
|
34
|
-
async function extractPostgreSQLPolicies(databaseUrl, models) {
|
|
35
|
-
const client = new Client({ connectionString: databaseUrl });
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await client.connect();
|
|
39
|
-
|
|
40
|
-
const policies = {};
|
|
41
|
-
|
|
42
|
-
// Create mapping from database table name to model name
|
|
43
|
-
const tableToModelMap = {};
|
|
44
|
-
for (const [modelName, modelData] of Object.entries(models)) {
|
|
45
|
-
const dbName = modelData.dbName || modelName.toLowerCase();
|
|
46
|
-
tableToModelMap[dbName] = modelName;
|
|
47
|
-
policies[modelName] = [];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Query all policies from PostgreSQL RLS (pg_policies)
|
|
51
|
-
const result = await client.query(`
|
|
52
|
-
SELECT
|
|
53
|
-
tablename,
|
|
54
|
-
policyname,
|
|
55
|
-
permissive,
|
|
56
|
-
roles,
|
|
57
|
-
cmd,
|
|
58
|
-
qual,
|
|
59
|
-
with_check
|
|
60
|
-
FROM pg_policies
|
|
61
|
-
WHERE schemaname = 'public'
|
|
62
|
-
ORDER BY tablename, policyname
|
|
63
|
-
`);
|
|
64
|
-
|
|
65
|
-
// Group policies by model (using table to model mapping)
|
|
66
|
-
for (const row of result.rows) {
|
|
67
|
-
const tableName = row.tablename;
|
|
68
|
-
const modelName = tableToModelMap[tableName];
|
|
69
|
-
|
|
70
|
-
if (modelName && policies[modelName] !== undefined) {
|
|
71
|
-
policies[modelName].push({
|
|
72
|
-
name: row.policyname,
|
|
73
|
-
permissive: row.permissive === 'PERMISSIVE',
|
|
74
|
-
roles: row.roles,
|
|
75
|
-
command: row.cmd,
|
|
76
|
-
using: row.qual,
|
|
77
|
-
withCheck: row.with_check
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
await client.end();
|
|
83
|
-
return policies;
|
|
84
|
-
|
|
85
|
-
} catch (error) {
|
|
86
|
-
try {
|
|
87
|
-
await client.end();
|
|
88
|
-
} catch (e) {}
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Generate ACL functions for a single model from PostgreSQL policies
|
|
95
|
-
*/
|
|
96
|
-
function generateModelACL(modelName, policies, converter) {
|
|
97
|
-
const hasPolicies = policies && policies.length > 0;
|
|
98
|
-
|
|
99
|
-
if (!hasPolicies) {
|
|
100
|
-
// No policies - generate permissive access
|
|
101
|
-
return ` ${modelName}: {
|
|
102
|
-
canCreate: (user) => true,
|
|
103
|
-
getAccessFilter: (user) => ({}),
|
|
104
|
-
getUpdateFilter: (user) => ({}),
|
|
105
|
-
getDeleteFilter: (user) => ({}),
|
|
106
|
-
getOmitFields: (user) => []
|
|
107
|
-
}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Find policies by command type
|
|
111
|
-
const selectPolicies = policies.filter(p => p.command === 'SELECT' || p.command === 'ALL');
|
|
112
|
-
const insertPolicies = policies.filter(p => p.command === 'INSERT' || p.command === 'ALL');
|
|
113
|
-
const updatePolicies = policies.filter(p => p.command === 'UPDATE' || p.command === 'ALL');
|
|
114
|
-
const deletePolicies = policies.filter(p => p.command === 'DELETE' || p.command === 'ALL');
|
|
115
|
-
|
|
116
|
-
// Generate each function
|
|
117
|
-
const canCreateCode = generateFunction(insertPolicies, 'withCheck', converter, modelName);
|
|
118
|
-
const accessFilterCode = generateFilter(selectPolicies, 'using', converter, modelName);
|
|
119
|
-
let updateFilterCode = generateFilter(updatePolicies, 'using', converter, modelName);
|
|
120
|
-
let deleteFilterCode = generateFilter(deletePolicies, 'using', converter, modelName);
|
|
121
|
-
|
|
122
|
-
// If update/delete filters are empty/false but access filter is not, copy access filter
|
|
123
|
-
if ((updateFilterCode === 'return false;' || updateFilterCode === 'return {};') &&
|
|
124
|
-
accessFilterCode !== 'return false;' && accessFilterCode !== 'return {};') {
|
|
125
|
-
updateFilterCode = accessFilterCode;
|
|
126
|
-
}
|
|
127
|
-
if ((deleteFilterCode === 'return false;' || deleteFilterCode === 'return {};') &&
|
|
128
|
-
accessFilterCode !== 'return false;' && accessFilterCode !== 'return {};') {
|
|
129
|
-
deleteFilterCode = accessFilterCode;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return ` ${modelName}: {
|
|
133
|
-
canCreate: (user) => {
|
|
134
|
-
${canCreateCode}
|
|
135
|
-
},
|
|
136
|
-
getAccessFilter: (user) => {
|
|
137
|
-
${accessFilterCode}
|
|
138
|
-
},
|
|
139
|
-
getUpdateFilter: (user) => {
|
|
140
|
-
${updateFilterCode}
|
|
141
|
-
},
|
|
142
|
-
getDeleteFilter: (user) => {
|
|
143
|
-
${deleteFilterCode}
|
|
144
|
-
},
|
|
145
|
-
getOmitFields: (user) => []
|
|
146
|
-
}`;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Generate JavaScript function from policies
|
|
151
|
-
*/
|
|
152
|
-
function generateFunction(policies, expressionField, converter, modelName) {
|
|
153
|
-
if (policies.length === 0) {
|
|
154
|
-
return 'return true;';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const conditions = [];
|
|
158
|
-
|
|
159
|
-
for (const policy of policies) {
|
|
160
|
-
const expr = expressionField === 'withCheck'
|
|
161
|
-
? (policy.withCheck || policy.using)
|
|
162
|
-
: policy[expressionField];
|
|
163
|
-
|
|
164
|
-
if (expr) {
|
|
165
|
-
try {
|
|
166
|
-
const jsExpr = converter.convertToJavaScript(expr, 'data', 'user', modelName);
|
|
167
|
-
console.log(`✓ Policy '${policy.name}': ${expr.substring(0, 50)}... -> ${jsExpr.substring(0, 80)}`);
|
|
168
|
-
conditions.push(jsExpr);
|
|
169
|
-
} catch (e) {
|
|
170
|
-
console.warn(`⚠ Failed to convert policy '${policy.name}' for ${modelName}: ${e.message}`);
|
|
171
|
-
console.warn(` SQL: ${expr}`);
|
|
172
|
-
conditions.push(`true /* TODO: Manual conversion needed for policy '${policy.name}' */`);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (conditions.length === 0) {
|
|
178
|
-
return 'return true;';
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// If any condition is 'true', the entire expression is true
|
|
182
|
-
if (conditions.some(c => c === 'true' || c.startsWith('true /*'))) {
|
|
183
|
-
return 'return true;';
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Policies are OR'd together (any policy allows)
|
|
187
|
-
return `return ${conditions.join(' || ')};`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Generate Prisma filter function
|
|
192
|
-
*/
|
|
193
|
-
function generateFilter(policies, expressionField, converter, modelName) {
|
|
194
|
-
if (policies.length === 0) {
|
|
195
|
-
return 'return false;';
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const filtersWithRoles = [];
|
|
199
|
-
|
|
200
|
-
for (const policy of policies) {
|
|
201
|
-
const expr = policy[expressionField];
|
|
202
|
-
if (expr) {
|
|
203
|
-
try {
|
|
204
|
-
const prismaFilter = converter.convertToPrismaFilter(expr, 'user', modelName);
|
|
205
|
-
const analysis = converter.analyzer ? converter.analyzer.analyzeSQLForFilters(expr) : null;
|
|
206
|
-
|
|
207
|
-
// Track role conditions and data filters separately
|
|
208
|
-
const roleConditions = analysis?.conditions?.filter(c =>
|
|
209
|
-
c.type === 'role_any' || c.type === 'role_equal'
|
|
210
|
-
) || [];
|
|
211
|
-
|
|
212
|
-
filtersWithRoles.push({
|
|
213
|
-
filter: prismaFilter,
|
|
214
|
-
roleConditions,
|
|
215
|
-
hasDataFilter: prismaFilter !== '{}'
|
|
216
|
-
});
|
|
217
|
-
} catch (e) {
|
|
218
|
-
console.warn(`⚠ Failed to convert filter policy '${policy.name}' for ${modelName}: ${e.message}`);
|
|
219
|
-
console.warn(` SQL: ${expr}`);
|
|
220
|
-
// On error, skip filter (fail-safe - no access)
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (filtersWithRoles.length === 0) {
|
|
226
|
-
return 'return false;';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Build conditional filter logic
|
|
230
|
-
return buildConditionalFilter(filtersWithRoles);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Build conditional filter with role checks
|
|
235
|
-
*/
|
|
236
|
-
function buildConditionalFilter(filtersWithRoles) {
|
|
237
|
-
const roleOnlyFilters = [];
|
|
238
|
-
const dataFilters = [];
|
|
239
|
-
|
|
240
|
-
for (const item of filtersWithRoles) {
|
|
241
|
-
if (item.roleConditions.length > 0 && !item.hasDataFilter) {
|
|
242
|
-
// Pure role check - return {} if role matches
|
|
243
|
-
roleOnlyFilters.push(...item.roleConditions);
|
|
244
|
-
} else if (item.roleConditions.length > 0 && item.hasDataFilter) {
|
|
245
|
-
// Has both role and data filter - already handled with if statement
|
|
246
|
-
if (item.filter.includes('if (')) {
|
|
247
|
-
return item.filter;
|
|
248
|
-
}
|
|
249
|
-
dataFilters.push(item.filter);
|
|
250
|
-
} else if (item.hasDataFilter) {
|
|
251
|
-
// Data filter only
|
|
252
|
-
dataFilters.push(item.filter);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Generate conditional code
|
|
257
|
-
const conditions = [];
|
|
258
|
-
|
|
259
|
-
// Collect all roles that grant full access
|
|
260
|
-
const rolesWithFullAccess = new Set();
|
|
261
|
-
for (const roleCond of roleOnlyFilters) {
|
|
262
|
-
if (roleCond.type === 'role_any') {
|
|
263
|
-
roleCond.roles.forEach(r => rolesWithFullAccess.add(r));
|
|
264
|
-
} else if (roleCond.type === 'role_equal') {
|
|
265
|
-
rolesWithFullAccess.add(roleCond.role);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Add single consolidated role check if needed
|
|
270
|
-
if (rolesWithFullAccess.size > 0) {
|
|
271
|
-
const roleArray = Array.from(rolesWithFullAccess);
|
|
272
|
-
if (roleArray.length === 1) {
|
|
273
|
-
conditions.push(`if (user?.role === '${roleArray[0]}') { return {}; }`);
|
|
274
|
-
} else {
|
|
275
|
-
conditions.push(`if ([${roleArray.map(r => `'${r}'`).join(', ')}].includes(user?.role)) { return {}; }`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Deduplicate data filters
|
|
280
|
-
const uniqueDataFilters = [...new Set(dataFilters)];
|
|
281
|
-
|
|
282
|
-
// Add final return with data filters
|
|
283
|
-
if (uniqueDataFilters.length === 0) {
|
|
284
|
-
conditions.push('return false;');
|
|
285
|
-
} else if (uniqueDataFilters.length === 1) {
|
|
286
|
-
conditions.push(`return ${uniqueDataFilters[0]};`);
|
|
287
|
-
} else {
|
|
288
|
-
conditions.push(`return { OR: [${uniqueDataFilters.join(', ')}] };`);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return conditions.join(' ');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Generate complete acl.js file
|
|
296
|
-
*/
|
|
297
|
-
async function generateACL(models, outputPath, databaseUrl, isPostgreSQL, userTableOption, relationships = {}, debug = false, allModels = null) {
|
|
298
|
-
// Use allModels for user table detection if provided (when filtering by model)
|
|
299
|
-
const modelsForUserDetection = allModels || models;
|
|
300
|
-
const userTable = detectUserTable(modelsForUserDetection, userTableOption);
|
|
301
|
-
const modelNames = Object.keys(models);
|
|
302
|
-
|
|
303
|
-
let policies = {};
|
|
304
|
-
const timestamp = new Date().toISOString();
|
|
305
|
-
|
|
306
|
-
let aclCode = `const acl = {\n model: {},\n lastUpdateDate: '${timestamp}'\n};\n\n`;
|
|
307
|
-
|
|
308
|
-
// Create enhanced converter with analyzed functions, models, and relationships
|
|
309
|
-
let converter = createEnhancedConverter({}, {}, models, relationships);
|
|
310
|
-
|
|
311
|
-
if (isPostgreSQL && databaseUrl) {
|
|
312
|
-
console.log('PostgreSQL detected - analyzing database...');
|
|
313
|
-
|
|
314
|
-
// Step 1: Analyze functions
|
|
315
|
-
try {
|
|
316
|
-
const functionAnalysis = await analyzeFunctions(databaseUrl);
|
|
317
|
-
console.log(`✓ Analyzed ${Object.keys(functionAnalysis.functionMappings).length} PostgreSQL functions`);
|
|
318
|
-
|
|
319
|
-
// Create enhanced converter with analyzed mappings, models, and relationships
|
|
320
|
-
converter = createEnhancedConverter(
|
|
321
|
-
functionAnalysis.functionMappings,
|
|
322
|
-
functionAnalysis.sessionVariables,
|
|
323
|
-
models,
|
|
324
|
-
relationships
|
|
325
|
-
);
|
|
326
|
-
|
|
327
|
-
// Save function analysis for debugging (only if --debug flag is set)
|
|
328
|
-
if (debug) {
|
|
329
|
-
const configPath = path.join(path.dirname(outputPath), 'acl-mappings.json');
|
|
330
|
-
const mappingConfig = generateMappingConfig(functionAnalysis);
|
|
331
|
-
fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
|
|
332
|
-
console.log(`✓ Function mappings saved to ${configPath}`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Also add user context requirements as a comment in acl.js
|
|
336
|
-
if (Object.keys(functionAnalysis.userContextRequirements).length > 0) {
|
|
337
|
-
aclCode = `/**
|
|
338
|
-
* User Context Requirements:
|
|
339
|
-
* The user object should contain:
|
|
340
|
-
${Object.entries(functionAnalysis.userContextRequirements)
|
|
341
|
-
.map(([field, req]) => ` * - ${field}: ${typeof req === 'object' ? req.description : 'required'}`)
|
|
342
|
-
.join('\n')}
|
|
343
|
-
*/
|
|
344
|
-
|
|
345
|
-
` + aclCode;
|
|
346
|
-
}
|
|
347
|
-
} catch (error) {
|
|
348
|
-
console.warn(`⚠ Could not analyze functions: ${error.message}`);
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Step 2: Extract policies
|
|
352
|
-
try {
|
|
353
|
-
policies = await extractPostgreSQLPolicies(databaseUrl, models);
|
|
354
|
-
const totalPolicies = Object.values(policies).reduce((sum, p) => sum + p.length, 0);
|
|
355
|
-
console.log(`✓ Extracted ${totalPolicies} policies from PostgreSQL RLS`);
|
|
356
|
-
} catch (error) {
|
|
357
|
-
console.warn(`⚠ Failed to extract PostgreSQL policies: ${error.message}`);
|
|
358
|
-
console.log('Generating permissive ACL for all models...');
|
|
359
|
-
for (const modelName of modelNames) {
|
|
360
|
-
policies[modelName] = [];
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
} else {
|
|
364
|
-
if (!isPostgreSQL) {
|
|
365
|
-
console.log('Non-PostgreSQL database detected - generating permissive ACL');
|
|
366
|
-
}
|
|
367
|
-
console.log('Generating permissive ACL for all models...');
|
|
368
|
-
for (const modelName of modelNames) {
|
|
369
|
-
policies[modelName] = [];
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Generate ACL for each model
|
|
374
|
-
aclCode += 'acl.model = {\n';
|
|
375
|
-
const modelACLCode = modelNames.map(modelName => {
|
|
376
|
-
return generateModelACL(modelName, policies[modelName], converter);
|
|
377
|
-
});
|
|
378
|
-
aclCode += modelACLCode.join(',\n');
|
|
379
|
-
aclCode += '\n};\n\n';
|
|
380
|
-
aclCode += 'module.exports = acl;\n';
|
|
381
|
-
|
|
382
|
-
// Ensure output directory exists
|
|
383
|
-
const outputDir = path.dirname(outputPath);
|
|
384
|
-
if (!fs.existsSync(outputDir)) {
|
|
385
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
fs.writeFileSync(outputPath, aclCode);
|
|
389
|
-
console.log('✓ Generated acl.js with dynamic function mappings');
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
module.exports = {
|
|
393
|
-
generateACL
|
|
394
|
-
};
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Generate a single model file
|
|
6
|
-
* @param {string} modelName - Name of the model
|
|
7
|
-
* @param {Object} modelInfo - Model information from parser
|
|
8
|
-
* @returns {string} - Generated model class code
|
|
9
|
-
*/
|
|
10
|
-
function generateModelFile(modelName, modelInfo) {
|
|
11
|
-
// Capitalize first letter for class name
|
|
12
|
-
const className = modelName.split(/[^a-zA-Z0-9]+/) // split on any non-alphanumeric char
|
|
13
|
-
.filter(Boolean) // remove empty parts
|
|
14
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
15
|
-
.join('');
|
|
16
|
-
|
|
17
|
-
return `const {Model, QueryBuilder, prisma} = require('../Model');
|
|
18
|
-
|
|
19
|
-
class ${className} extends Model {
|
|
20
|
-
constructor(options){
|
|
21
|
-
super('${modelName}', options);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param {string} q
|
|
26
|
-
* @property {string|Object} include
|
|
27
|
-
* @param {number} limit
|
|
28
|
-
* @param {number} offset
|
|
29
|
-
* @param {string} sortBy
|
|
30
|
-
* @param {'asc'|'desc'} sortOrder
|
|
31
|
-
* @returns {Promise<Object[]>}
|
|
32
|
-
*/
|
|
33
|
-
async getMany(q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc"){
|
|
34
|
-
return await this._getMany(q, include, Number(limit), Number(offset), sortBy, sortOrder);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* @param {number} id
|
|
39
|
-
* @param {string | Object} include
|
|
40
|
-
* @returns {Promise<Object | null>}
|
|
41
|
-
*/
|
|
42
|
-
async get(id, include){
|
|
43
|
-
return await this._get(id, include);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* @param {Object} data
|
|
48
|
-
* @returns {Promise<Object>}
|
|
49
|
-
*/
|
|
50
|
-
async create(data){
|
|
51
|
-
return await this._create(data);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @param {number} id
|
|
56
|
-
* @param {{}} data
|
|
57
|
-
* @returns {Promise<Object>}
|
|
58
|
-
*/
|
|
59
|
-
async update(id, data){
|
|
60
|
-
return await this._update(id, data);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @param {{}} data
|
|
65
|
-
* @param {string} [unique_key=this.primaryKey]
|
|
66
|
-
* @returns {Promise<Object>}
|
|
67
|
-
*/
|
|
68
|
-
async upsert(data, unique_key = this.primaryKey){
|
|
69
|
-
return await this._upsert(data, unique_key);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* @param {number} id
|
|
74
|
-
* @returns {Object}
|
|
75
|
-
*/
|
|
76
|
-
async delete(id){
|
|
77
|
-
return await this._delete(id);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @param {string | Object} include
|
|
82
|
-
* @returns {Object}
|
|
83
|
-
*/
|
|
84
|
-
filter(include){
|
|
85
|
-
return {...this._filter(include), ...this.getAccessFilter()};
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* @param {string | Object} include
|
|
90
|
-
* @returns {Object}
|
|
91
|
-
*/
|
|
92
|
-
include(include){
|
|
93
|
-
return this._include(include);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
static QueryBuilder = new QueryBuilder('${modelName}');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
module.exports = {${className}, QueryBuilder, prisma};
|
|
100
|
-
`;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Map Prisma types to JavaScript types
|
|
105
|
-
* @param {string} prismaType - Prisma field type
|
|
106
|
-
* @returns {string} - JavaScript type
|
|
107
|
-
*/
|
|
108
|
-
function mapPrismaTypeToJS(prismaType) {
|
|
109
|
-
const typeMap = {
|
|
110
|
-
'String': 'string',
|
|
111
|
-
'Int': 'number',
|
|
112
|
-
'Float': 'number',
|
|
113
|
-
'Decimal': 'number',
|
|
114
|
-
'Boolean': 'boolean',
|
|
115
|
-
'DateTime': 'Date',
|
|
116
|
-
'Json': 'object',
|
|
117
|
-
'Bytes': 'Buffer'
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
return typeMap[prismaType] || prismaType;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Generate all model files
|
|
125
|
-
* @param {Object} models - Models object from parser
|
|
126
|
-
* @param {string} modelDir - Directory to output model files
|
|
127
|
-
* @param {string} modelJsPath - Path to output Model.js
|
|
128
|
-
*/
|
|
129
|
-
function generateAllModels(models, modelDir, modelJsPath) {
|
|
130
|
-
// Create model directory if it doesn't exist
|
|
131
|
-
if (!fs.existsSync(modelDir)) {
|
|
132
|
-
fs.mkdirSync(modelDir, { recursive: true });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Generate individual model files
|
|
136
|
-
for (const [modelName, modelInfo] of Object.entries(models)) {
|
|
137
|
-
const modelCode = generateModelFile(modelName, modelInfo);
|
|
138
|
-
// Capitalize first letter for filename
|
|
139
|
-
const className = modelName.split(/[^a-zA-Z0-9]+/) // split on any non-alphanumeric char
|
|
140
|
-
.filter(Boolean) // remove empty parts
|
|
141
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
142
|
-
.join('');
|
|
143
|
-
const modelPath = path.join(modelDir, `${className}.js`);
|
|
144
|
-
fs.writeFileSync(modelPath, modelCode);
|
|
145
|
-
console.log(`Generated model: ${className}.js`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Copy Model.js to output if it exists in the project
|
|
149
|
-
const sourceModelJs = path.join(process.cwd(), 'Model.js');
|
|
150
|
-
if (fs.existsSync(sourceModelJs)) {
|
|
151
|
-
fs.copyFileSync(sourceModelJs, modelJsPath);
|
|
152
|
-
console.log('Copied Model.js to output');
|
|
153
|
-
} else {
|
|
154
|
-
console.warn('Warning: Model.js not found in project root');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Copy rapidd.js to output if it exists
|
|
158
|
-
const sourceRapiddJs = path.join(process.cwd(), 'rapidd', 'rapidd.js');
|
|
159
|
-
const outputRapiddDir = modelDir.replace(/src[\/\\]Model$/, 'rapidd');
|
|
160
|
-
const outputRapiddJs = path.join(outputRapiddDir, 'rapidd.js');
|
|
161
|
-
|
|
162
|
-
if (fs.existsSync(sourceRapiddJs)) {
|
|
163
|
-
if (!fs.existsSync(outputRapiddDir)) {
|
|
164
|
-
fs.mkdirSync(outputRapiddDir, { recursive: true });
|
|
165
|
-
}
|
|
166
|
-
fs.copyFileSync(sourceRapiddJs, outputRapiddJs);
|
|
167
|
-
console.log('Copied rapidd.js to output');
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
module.exports = {
|
|
172
|
-
generateAllModels,
|
|
173
|
-
generateModelFile
|
|
174
|
-
};
|