@rapidd/build 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.
@@ -0,0 +1,168 @@
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.charAt(0).toUpperCase() + modelName.slice(1);
13
+
14
+ return `const {Model, QueryBuilder, prisma} = require('../Model');
15
+ const {rls} = require('../../rapidd/rapidd');
16
+
17
+ class ${className} extends Model {
18
+ constructor(options){
19
+ super('${modelName}', options);
20
+ }
21
+
22
+ static queryBuilder = new QueryBuilder('${modelName}', rls.model.${modelName} || {});
23
+
24
+ static getAccessFilter(user) {
25
+ return rls.model.${modelName}?.getAccessFilter?.(user) || {};
26
+ }
27
+
28
+ static hasAccess(data, user) {
29
+ return rls.model.${modelName}?.hasAccess?.(data, user) || true;
30
+ }
31
+
32
+ /**
33
+ * @param {string} q
34
+ * @property {string|Object} include
35
+ * @param {number} limit
36
+ * @param {number} offset
37
+ * @param {string} sortBy
38
+ * @param {'asc'|'desc'} sortOrder
39
+ * @returns {Object[]}
40
+ */
41
+ async getMany(q = {}, include = "", limit = 25, offset = 0, sortBy = "id", sortOrder = "asc"){
42
+ return await this._getMany(q, include, Number(limit), Number(offset), sortBy, sortOrder);
43
+ }
44
+
45
+ /**
46
+ * @param {number} id
47
+ * @param {string | Object} include
48
+ * @returns {{} | null}
49
+ */
50
+ async get(id, include){
51
+ return await this._get(Number(id), include);
52
+ }
53
+
54
+ /**
55
+ * @param {Object} data
56
+ * @returns {Object}
57
+ */
58
+ async create(data){
59
+ return await this._create(data);
60
+ }
61
+
62
+ /**
63
+ * @param {number} id
64
+ * @param {{}} data
65
+ * @returns {Object}
66
+ */
67
+ async update(id, data){
68
+ return await this._update(Number(id), data);
69
+ }
70
+
71
+ /**
72
+ * @param {number} id
73
+ * @returns {Object}
74
+ */
75
+ async delete(id){
76
+ return await this._delete(Number(id));
77
+ }
78
+
79
+ /**
80
+ * @param {string | Object} include
81
+ * @returns {Object}
82
+ */
83
+ filter(include){
84
+ return {...this._filter(include), ...this.getAccessFilter()};
85
+ }
86
+
87
+ /**
88
+ * @param {string | Object} include
89
+ * @returns {Object}
90
+ */
91
+ include(include){
92
+ return this._include(include);
93
+ }
94
+ }
95
+
96
+ module.exports = {${className}, QueryBuilder, prisma};
97
+ `;
98
+ }
99
+
100
+ /**
101
+ * Map Prisma types to JavaScript types
102
+ * @param {string} prismaType - Prisma field type
103
+ * @returns {string} - JavaScript type
104
+ */
105
+ function mapPrismaTypeToJS(prismaType) {
106
+ const typeMap = {
107
+ 'String': 'string',
108
+ 'Int': 'number',
109
+ 'Float': 'number',
110
+ 'Decimal': 'number',
111
+ 'Boolean': 'boolean',
112
+ 'DateTime': 'Date',
113
+ 'Json': 'object',
114
+ 'Bytes': 'Buffer'
115
+ };
116
+
117
+ return typeMap[prismaType] || prismaType;
118
+ }
119
+
120
+ /**
121
+ * Generate all model files
122
+ * @param {Object} models - Models object from parser
123
+ * @param {string} modelDir - Directory to output model files
124
+ * @param {string} modelJsPath - Path to output Model.js
125
+ */
126
+ function generateAllModels(models, modelDir, modelJsPath) {
127
+ // Create model directory if it doesn't exist
128
+ if (!fs.existsSync(modelDir)) {
129
+ fs.mkdirSync(modelDir, { recursive: true });
130
+ }
131
+
132
+ // Generate individual model files
133
+ for (const [modelName, modelInfo] of Object.entries(models)) {
134
+ const modelCode = generateModelFile(modelName, modelInfo);
135
+ // Capitalize first letter for filename
136
+ const className = modelName.charAt(0).toUpperCase() + modelName.slice(1);
137
+ const modelPath = path.join(modelDir, `${className}.js`);
138
+ fs.writeFileSync(modelPath, modelCode);
139
+ console.log(`Generated model: ${className}.js`);
140
+ }
141
+
142
+ // Copy Model.js to output if it exists in the project
143
+ const sourceModelJs = path.join(process.cwd(), 'Model.js');
144
+ if (fs.existsSync(sourceModelJs)) {
145
+ fs.copyFileSync(sourceModelJs, modelJsPath);
146
+ console.log('Copied Model.js to output');
147
+ } else {
148
+ console.warn('Warning: Model.js not found in project root');
149
+ }
150
+
151
+ // Copy rapidd.js to output if it exists
152
+ const sourceRapiddJs = path.join(process.cwd(), 'rapidd', 'rapidd.js');
153
+ const outputRapiddDir = path.dirname(modelDir.replace(/src[\/\\]Model$/, 'rapidd'));
154
+ const outputRapiddJs = path.join(outputRapiddDir, 'rapidd.js');
155
+
156
+ if (fs.existsSync(sourceRapiddJs)) {
157
+ if (!fs.existsSync(outputRapiddDir)) {
158
+ fs.mkdirSync(outputRapiddDir, { recursive: true });
159
+ }
160
+ fs.copyFileSync(sourceRapiddJs, outputRapiddJs);
161
+ console.log('Copied rapidd.js to output');
162
+ }
163
+ }
164
+
165
+ module.exports = {
166
+ generateAllModels,
167
+ generateModelFile
168
+ };
@@ -0,0 +1,186 @@
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
+ relationships[modelName][relation.name] = {
52
+ 'object': relation.type,
53
+ 'field': foreignKeyField || `${relation.type}_id` // Use actual FK or fallback to convention
54
+ };
55
+ }
56
+ }
57
+ }
58
+
59
+ // Ensure output directory exists
60
+ const outputDir = path.dirname(outputPath);
61
+ if (!fs.existsSync(outputDir)) {
62
+ fs.mkdirSync(outputDir, { recursive: true });
63
+ }
64
+
65
+ fs.writeFileSync(outputPath, JSON.stringify(relationships, null, 4));
66
+ console.log('Generated relationships.json');
67
+ }
68
+
69
+ /**
70
+ * Find the foreign key field for a relation
71
+ * @param {Object} relation - Relation object
72
+ * @param {Object} currentModel - Current model info
73
+ * @param {Object} relatedModel - Related model info
74
+ * @returns {string|null} - Foreign key field name
75
+ */
76
+ function findForeignKeyField(relation, currentModel, relatedModel) {
77
+ // If relation has relationFromFields, use it
78
+ if (relation.relationFromFields && relation.relationFromFields.length > 0) {
79
+ return relation.relationFromFields[0];
80
+ }
81
+
82
+ // For array relations (one-to-many from parent), look for the FK in the related model
83
+ if (relation.isArray) {
84
+ // Find which field in the related model points back to current model
85
+ for (const [fieldName, fieldInfo] of Object.entries(relatedModel.fields)) {
86
+ if (fieldInfo.relationName === relation.relationName &&
87
+ fieldInfo.relationFromFields &&
88
+ fieldInfo.relationFromFields.length > 0) {
89
+ // This is the FK field in the related model
90
+ return fieldInfo.relationFromFields[0];
91
+ }
92
+ }
93
+
94
+ // Fallback: convention-based
95
+ return `${relation.type}_id`;
96
+ }
97
+
98
+ // For singular relations (many-to-one), find the FK in current model
99
+ for (const [fieldName, fieldInfo] of Object.entries(currentModel.fields)) {
100
+ if (fieldInfo.relationName === relation.relationName &&
101
+ fieldInfo.relationToFields &&
102
+ fieldInfo.relationToFields.length > 0) {
103
+ // Found the matching relation field, return its FK
104
+ return fieldName;
105
+ }
106
+ }
107
+
108
+ // Final fallback
109
+ return `${relation.type}_id`;
110
+ }
111
+
112
+ /**
113
+ * Get composite key fields from a model
114
+ * @param {Object} modelInfo - Model information
115
+ * @returns {Array|null} - Array of composite key field names or null
116
+ */
117
+ function getCompositeKeyFromModel(modelInfo) {
118
+ // First check if the model has a compositeKey property (from parser)
119
+ if (modelInfo.compositeKey && modelInfo.compositeKey.length > 1) {
120
+ return modelInfo.compositeKey;
121
+ }
122
+
123
+ // Fallback: check for fields marked with isId (for schema parser)
124
+ const compositeFields = [];
125
+ for (const [fieldName, fieldInfo] of Object.entries(modelInfo.fields)) {
126
+ if (fieldInfo.isId && !fieldInfo.isRelation) {
127
+ compositeFields.push(fieldName);
128
+ }
129
+ }
130
+
131
+ // If we found multiple ID fields, it's a composite key
132
+ if (compositeFields.length > 1) {
133
+ return compositeFields;
134
+ }
135
+
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Reorder composite key fields so the current model's field comes first
141
+ * @param {Array} fields - Array of field names
142
+ * @param {String} currentModelName - Name of the current model
143
+ * @returns {Array} - Reordered array with current model's field first
144
+ */
145
+ function reorderFieldsForModel(fields, currentModelName) {
146
+ const currentModelField = `${currentModelName.toLowerCase()}_id`;
147
+ const index = fields.indexOf(currentModelField);
148
+
149
+ if (index > 0) {
150
+ // Move current model's field to the front
151
+ const reordered = [...fields];
152
+ reordered.splice(index, 1);
153
+ reordered.unshift(currentModelField);
154
+ return reordered;
155
+ }
156
+
157
+ return fields;
158
+ }
159
+
160
+ /**
161
+ * Generate relationships.json from schema
162
+ * @param {string} schemaPath - Path to Prisma schema file
163
+ * @param {string} outputPath - Path to output relationships.json
164
+ */
165
+ function generateRelationshipsFromSchema(schemaPath, outputPath) {
166
+ const { parsePrismaSchema } = require('../parsers/prismaParser');
167
+ const parsedData = parsePrismaSchema(schemaPath);
168
+ generateRelationships(parsedData.models, outputPath);
169
+ }
170
+
171
+ /**
172
+ * Generate relationships.json from DMMF
173
+ * @param {string} prismaClientPath - Path to Prisma client
174
+ * @param {string} outputPath - Path to output relationships.json
175
+ */
176
+ async function generateRelationshipsFromDMMF(prismaClientPath, outputPath) {
177
+ const { parsePrismaDMMF } = require('../parsers/prismaParser');
178
+ const parsedData = await parsePrismaDMMF(prismaClientPath);
179
+ generateRelationships(parsedData.models, outputPath);
180
+ }
181
+
182
+ module.exports = {
183
+ generateRelationships,
184
+ generateRelationshipsFromSchema,
185
+ generateRelationshipsFromDMMF
186
+ };
@@ -0,0 +1,334 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { Client } = require('pg');
4
+ const { createConverter } = require('../parsers/autoRLSConverter');
5
+ const { analyzeFunctions, generateMappingConfig } = require('../parsers/functionAnalyzer');
6
+
7
+ /**
8
+ * Auto-detect user table name (case-insensitive search for user/users)
9
+ * @param {Object} models - Models object from parser
10
+ * @param {string} userTableOption - User-specified table name (optional)
11
+ * @returns {string} - Name of the user table
12
+ */
13
+ function detectUserTable(models, userTableOption) {
14
+ if (userTableOption) {
15
+ return userTableOption;
16
+ }
17
+
18
+ const modelNames = Object.keys(models);
19
+ const userTables = modelNames.filter(name =>
20
+ name.toLowerCase() === 'user' || name.toLowerCase() === 'users'
21
+ );
22
+
23
+ if (userTables.length === 0) {
24
+ throw new Error('No user table found (user/users). Please specify --user-table option.');
25
+ }
26
+
27
+ if (userTables.length > 1) {
28
+ throw new Error(`Multiple user tables found: ${userTables.join(', ')}. Please specify --user-table option.`);
29
+ }
30
+
31
+ return userTables[0];
32
+ }
33
+
34
+ /**
35
+ * Extract RLS policies from PostgreSQL
36
+ * @param {string} databaseUrl - PostgreSQL connection URL
37
+ * @param {Array} modelNames - Array of model names
38
+ * @returns {Object} - RLS policies for each table
39
+ */
40
+ async function extractPostgreSQLPolicies(databaseUrl, modelNames) {
41
+ const client = new Client({ connectionString: databaseUrl });
42
+
43
+ try {
44
+ await client.connect();
45
+
46
+ const policies = {};
47
+
48
+ // Initialize all models with empty policies
49
+ for (const modelName of modelNames) {
50
+ policies[modelName] = [];
51
+ }
52
+
53
+ // Query all RLS policies from pg_policies
54
+ const result = await client.query(`
55
+ SELECT
56
+ tablename,
57
+ policyname,
58
+ permissive,
59
+ roles,
60
+ cmd,
61
+ qual,
62
+ with_check
63
+ FROM pg_policies
64
+ WHERE schemaname = 'public'
65
+ ORDER BY tablename, policyname
66
+ `);
67
+
68
+ // Group policies by table
69
+ for (const row of result.rows) {
70
+ const tableName = row.tablename;
71
+ if (policies[tableName] !== undefined) {
72
+ policies[tableName].push({
73
+ name: row.policyname,
74
+ permissive: row.permissive === 'PERMISSIVE',
75
+ roles: row.roles,
76
+ command: row.cmd, // SELECT, INSERT, UPDATE, DELETE, ALL
77
+ using: row.qual, // USING expression
78
+ withCheck: row.with_check // WITH CHECK expression
79
+ });
80
+ }
81
+ }
82
+
83
+ await client.end();
84
+ return policies;
85
+
86
+ } catch (error) {
87
+ try {
88
+ await client.end();
89
+ } catch (e) {
90
+ // Ignore cleanup errors
91
+ }
92
+ throw error;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Generate RLS functions for a single model from PostgreSQL policies
98
+ * @param {string} modelName - Name of the model
99
+ * @param {Array} policies - Array of policy objects for this model
100
+ * @param {string} userTable - Name of the user table
101
+ * @returns {string} - JavaScript code for RLS functions
102
+ */
103
+ function generateModelRLS(modelName, policies, userTable) {
104
+ const hasPolicies = policies && policies.length > 0;
105
+
106
+ if (!hasPolicies) {
107
+ // No RLS policies - generate permissive access
108
+ return ` ${modelName}: {
109
+ canCreate: (user) => true,
110
+ hasAccess: (data, user) => true,
111
+ getAccessFilter: (user) => ({}),
112
+ getUpdateFilter: (user) => ({}),
113
+ getDeleteFilter: (user) => ({}),
114
+ getOmitFields: (user) => []
115
+ }`;
116
+ }
117
+
118
+ // Find policies by command type
119
+ const selectPolicies = policies.filter(p => p.command === 'SELECT' || p.command === 'ALL');
120
+ const insertPolicies = policies.filter(p => p.command === 'INSERT' || p.command === 'ALL');
121
+ const updatePolicies = policies.filter(p => p.command === 'UPDATE' || p.command === 'ALL');
122
+ const deletePolicies = policies.filter(p => p.command === 'DELETE' || p.command === 'ALL');
123
+
124
+ // Generate canCreate (INSERT policies with WITH CHECK)
125
+ const canCreateCode = generateCanCreate(insertPolicies);
126
+
127
+ // Generate hasAccess (SELECT policies with USING)
128
+ const hasAccessCode = generateHasAccess(selectPolicies);
129
+
130
+ // Generate getAccessFilter (SELECT policies)
131
+ const accessFilterCode = generateFilter(selectPolicies, 'using');
132
+
133
+ // Generate getUpdateFilter (UPDATE policies)
134
+ const updateFilterCode = generateFilter(updatePolicies, 'using');
135
+
136
+ // Generate getDeleteFilter (DELETE policies)
137
+ const deleteFilterCode = generateFilter(deletePolicies, 'using');
138
+
139
+ return ` ${modelName}: {
140
+ canCreate: (user) => {
141
+ ${canCreateCode}
142
+ },
143
+ hasAccess: (data, user) => {
144
+ ${hasAccessCode}
145
+ },
146
+ getAccessFilter: (user) => {
147
+ ${accessFilterCode}
148
+ },
149
+ getUpdateFilter: (user) => {
150
+ ${updateFilterCode}
151
+ },
152
+ getDeleteFilter: (user) => {
153
+ ${deleteFilterCode}
154
+ },
155
+ getOmitFields: (user) => []
156
+ }`;
157
+ }
158
+
159
+ /**
160
+ * Generate canCreate function from INSERT policies
161
+ */
162
+ function generateCanCreate(insertPolicies, converter) {
163
+ if (insertPolicies.length === 0) {
164
+ return 'return true;';
165
+ }
166
+
167
+ const conditions = [];
168
+
169
+ for (const policy of insertPolicies) {
170
+ const expr = policy.withCheck || policy.using;
171
+ if (expr) {
172
+ try {
173
+ const jsExpr = converter.convertToJavaScript(expr, 'data', 'user');
174
+ conditions.push(jsExpr);
175
+ } catch (e) {
176
+ conditions.push(`true /* Error parsing: ${expr.substring(0, 50)}... */`);
177
+ }
178
+ }
179
+ }
180
+
181
+ if (conditions.length === 0) {
182
+ return 'return true;';
183
+ }
184
+
185
+ // Policies are OR'd together (any policy allows)
186
+ return `return ${conditions.join(' || ')};`;
187
+ }
188
+
189
+ /**
190
+ * Generate hasAccess function from SELECT policies
191
+ */
192
+ function generateHasAccess(selectPolicies) {
193
+ if (selectPolicies.length === 0) {
194
+ return 'return true;';
195
+ }
196
+
197
+ const conditions = [];
198
+
199
+ for (const policy of selectPolicies) {
200
+ if (policy.using) {
201
+ try {
202
+ const jsExpr = convertToJavaScript(policy.using, 'data', 'user');
203
+ conditions.push(jsExpr);
204
+ } catch (e) {
205
+ conditions.push(`true /* Error parsing: ${policy.using.substring(0, 50)}... */`);
206
+ }
207
+ }
208
+ }
209
+
210
+ if (conditions.length === 0) {
211
+ return 'return true;';
212
+ }
213
+
214
+ // Policies are OR'd together (any policy allows)
215
+ return `return ${conditions.join(' || ')};`;
216
+ }
217
+
218
+ /**
219
+ * Generate Prisma filter function
220
+ */
221
+ function generateFilter(policies, expressionField) {
222
+ if (policies.length === 0) {
223
+ return 'return {};';
224
+ }
225
+
226
+ const filters = [];
227
+
228
+ for (const policy of policies) {
229
+ const expr = policy[expressionField];
230
+ if (expr) {
231
+ try {
232
+ const prismaFilter = convertToPrismaFilter(expr, 'user');
233
+ if (prismaFilter !== '{}') {
234
+ filters.push(prismaFilter);
235
+ }
236
+ } catch (e) {
237
+ // On error, return empty filter (permissive)
238
+ }
239
+ }
240
+ }
241
+
242
+ if (filters.length === 0) {
243
+ return 'return {};';
244
+ }
245
+
246
+ if (filters.length === 1) {
247
+ return `return ${filters[0]};`;
248
+ }
249
+
250
+ // Multiple policies are OR'd together
251
+ return `return { OR: [${filters.join(', ')}] };`;
252
+ }
253
+
254
+ /**
255
+ * Generate complete rls.js file
256
+ * @param {Object} models - Models object
257
+ * @param {string} outputPath - Path to output rls.js
258
+ * @param {string} databaseUrl - Database connection URL
259
+ * @param {boolean} isPostgreSQL - Whether database is PostgreSQL
260
+ * @param {string} userTableOption - User-specified table name
261
+ */
262
+ async function generateRLS(models, outputPath, databaseUrl, isPostgreSQL, userTableOption) {
263
+ const userTable = detectUserTable(models, userTableOption);
264
+ const modelNames = Object.keys(models);
265
+
266
+ let policies = {};
267
+ const timestamp = new Date().toISOString();
268
+
269
+ let rlsCode = `const rls = {\n model: {},\n lastUpdateDate: '${timestamp}'\n};\n\n`;
270
+
271
+ // Analyze PostgreSQL functions if available
272
+ let functionAnalysis = null;
273
+
274
+ if (isPostgreSQL && databaseUrl) {
275
+ console.log('PostgreSQL detected - analyzing database functions...');
276
+ try {
277
+ functionAnalysis = await analyzeFunctions(databaseUrl);
278
+ console.log(`✓ Analyzed ${Object.keys(functionAnalysis.functionMappings).length} PostgreSQL functions`);
279
+
280
+ // Save function analysis for debugging/manual adjustment
281
+ const configPath = path.join(path.dirname(outputPath), 'rls-mappings.json');
282
+ const mappingConfig = generateMappingConfig(functionAnalysis);
283
+ fs.writeFileSync(configPath, JSON.stringify(mappingConfig, null, 2));
284
+ console.log(`✓ Function mappings saved to ${configPath}`);
285
+ } catch (error) {
286
+ console.warn(`⚠ Could not analyze functions: ${error.message}`);
287
+ }
288
+
289
+ console.log('Extracting RLS policies from database...');
290
+ try {
291
+ policies = await extractPostgreSQLPolicies(databaseUrl, modelNames);
292
+ const totalPolicies = Object.values(policies).reduce((sum, p) => sum + p.length, 0);
293
+ console.log(`✓ Extracted ${totalPolicies} RLS policies from PostgreSQL`);
294
+ } catch (error) {
295
+ console.warn(`⚠ Failed to extract PostgreSQL RLS: ${error.message}`);
296
+ console.log('Generating permissive RLS for all models...');
297
+ // Initialize empty policies for all models
298
+ for (const modelName of modelNames) {
299
+ policies[modelName] = [];
300
+ }
301
+ }
302
+ } else {
303
+ if (!isPostgreSQL) {
304
+ console.log('Non-PostgreSQL database detected (MySQL/SQLite/etc) - RLS not supported');
305
+ }
306
+ console.log('Generating permissive RLS for all models...');
307
+ // Initialize empty policies for all models
308
+ for (const modelName of modelNames) {
309
+ policies[modelName] = [];
310
+ }
311
+ }
312
+
313
+ // Generate RLS for each model
314
+ rlsCode += 'rls.model = {\n';
315
+ const modelRLSCode = modelNames.map(modelName => {
316
+ return generateModelRLS(modelName, policies[modelName], userTable);
317
+ });
318
+ rlsCode += modelRLSCode.join(',\n');
319
+ rlsCode += '\n};\n\n';
320
+ rlsCode += 'module.exports = rls;\n';
321
+
322
+ // Ensure output directory exists
323
+ const outputDir = path.dirname(outputPath);
324
+ if (!fs.existsSync(outputDir)) {
325
+ fs.mkdirSync(outputDir, { recursive: true });
326
+ }
327
+
328
+ fs.writeFileSync(outputPath, rlsCode);
329
+ console.log('Generated rls.js');
330
+ }
331
+
332
+ module.exports = {
333
+ generateRLS
334
+ };