@rapidd/build 1.1.0 → 1.1.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.
@@ -1,322 +0,0 @@
1
- /**
2
- * Automatic PostgreSQL RLS to JavaScript Converter
3
- * Uses dynamic function analysis - no hardcoding!
4
- */
5
-
6
- /**
7
- * Create a converter with analyzed function mappings
8
- */
9
- function createConverter(functionMappings = {}, sessionVariables = {}) {
10
-
11
- /**
12
- * Convert PostgreSQL RLS to JavaScript
13
- */
14
- function convertToJavaScript(sql, dataVar = 'data', userVar = 'user') {
15
- if (!sql || sql.trim() === '') {
16
- return 'true';
17
- }
18
-
19
- sql = sql.trim().replace(/\s+/g, ' ').replace(/\n/g, ' ');
20
-
21
- // Handle CASE WHEN
22
- if (sql.toUpperCase().startsWith('CASE')) {
23
- return convertCaseWhen(sql, dataVar, userVar);
24
- }
25
-
26
- // Handle OR
27
- const orParts = splitLogicalOperator(sql, 'OR');
28
- if (orParts.length > 1) {
29
- return orParts.map(part => convertToJavaScript(part, dataVar, userVar)).join(' || ');
30
- }
31
-
32
- // Handle AND
33
- const andParts = splitLogicalOperator(sql, 'AND');
34
- if (andParts.length > 1) {
35
- return '(' + andParts.map(part => convertToJavaScript(part, dataVar, userVar)).join(' && ') + ')';
36
- }
37
-
38
- // Handle function = ANY (ARRAY[...])
39
- const funcAnyMatch = sql.match(/(\w+)\s*\([^)]*\)[^=]*=\s*ANY\s*\(\s*(?:\()?ARRAY\s*\[([^\]]+)\]/i);
40
- if (funcAnyMatch) {
41
- const funcName = funcAnyMatch[1];
42
- const arrayValues = extractArrayValues(funcAnyMatch[2]);
43
-
44
- // Use analyzed mapping or fallback
45
- const jsExpression = getFunctionMapping(funcName, userVar);
46
- return `[${arrayValues}].includes(${jsExpression})`;
47
- }
48
-
49
- // Handle function() = 'value' (e.g., get_current_user_role() = 'admin')
50
- const funcValueMatch = sql.match(/(\w+)\s*\([^)]*\)\s*=\s*'([^']+)'/i);
51
- if (funcValueMatch) {
52
- const funcName = funcValueMatch[1];
53
- const value = funcValueMatch[2];
54
-
55
- const jsExpression = getFunctionMapping(funcName, userVar);
56
- return `${jsExpression} === '${value}'`;
57
- }
58
-
59
- // Handle field = function()
60
- const fieldFuncMatch = sql.match(/(\w+)\s*=\s*(\w+)\s*\([^)]*\)/i);
61
- if (fieldFuncMatch) {
62
- const field = fieldFuncMatch[1];
63
- const funcName = fieldFuncMatch[2];
64
-
65
- const jsExpression = getFunctionMapping(funcName, userVar);
66
- return `${dataVar}?.${field} === ${jsExpression}`;
67
- }
68
-
69
- // Handle current_setting
70
- const settingMatch = sql.match(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'([^']+)'/i);
71
- if (settingMatch) {
72
- const field = settingMatch[1];
73
- const setting = settingMatch[2];
74
-
75
- const jsExpression = getSessionMapping(setting, userVar);
76
- return `${dataVar}?.${field} === ${jsExpression}`;
77
- }
78
-
79
- // Handle EXISTS
80
- if (sql.includes('EXISTS')) {
81
- // Try to extract table and conditions for better TODO comment
82
- const tableMatch = sql.match(/FROM\s+(\w+)/i);
83
- const table = tableMatch ? tableMatch[1] : 'related_table';
84
- return `true /* TODO: Check ${table} relationship */`;
85
- }
86
-
87
- // Handle boolean literals
88
- if (sql.toLowerCase() === 'true') return 'true';
89
- if (sql.toLowerCase() === 'false') return 'false';
90
-
91
- return `true /* Unhandled: ${sql.substring(0, 50)}... */`;
92
- }
93
-
94
- /**
95
- * Get JavaScript mapping for a PostgreSQL function
96
- */
97
- function getFunctionMapping(funcName, userVar) {
98
- // Check analyzed mappings first
99
- if (functionMappings[funcName]) {
100
- const mapping = functionMappings[funcName];
101
- if (mapping.javascript) {
102
- // Replace 'user' placeholder with actual variable name
103
- return mapping.javascript.replace(/user/g, userVar);
104
- }
105
- }
106
-
107
- // Special case for role functions - they should map to user?.role not user?.user_role
108
- if (funcName.toLowerCase() === 'get_current_user_role' ||
109
- funcName.toLowerCase() === 'current_user_role') {
110
- return `${userVar}?.role`;
111
- }
112
-
113
- // Fallback: infer from function name
114
- // get_current_X -> user?.X
115
- const getCurrentMatch = funcName.match(/get_current_(\w+)/i);
116
- if (getCurrentMatch) {
117
- return `${userVar}?.${getCurrentMatch[1]}`;
118
- }
119
-
120
- // current_X -> user?.X
121
- const currentMatch = funcName.match(/current_(\w+)/i);
122
- if (currentMatch) {
123
- return `${userVar}?.${currentMatch[1]}`;
124
- }
125
-
126
- // Unknown function
127
- return `${userVar}?.${funcName}()`;
128
- }
129
-
130
- /**
131
- * Get JavaScript mapping for a session variable
132
- */
133
- function getSessionMapping(setting, userVar) {
134
- // Check analyzed mappings
135
- if (sessionVariables[setting]) {
136
- return sessionVariables[setting].replace(/user/g, userVar);
137
- }
138
-
139
- // Fallback: infer from setting name
140
- if (setting.includes('user_id')) {
141
- return `${userVar}?.id`;
142
- }
143
-
144
- const parts = setting.split('.');
145
- const lastPart = parts[parts.length - 1];
146
- return `${userVar}?.${lastPart}`;
147
- }
148
-
149
- /**
150
- * Convert CASE WHEN
151
- */
152
- function convertCaseWhen(sql, dataVar, userVar) {
153
- const caseMatch = sql.match(/CASE\s+([^W]+)\s+((?:WHEN[\s\S]+?)+)(?:\s+ELSE\s+([\s\S]+?))?\s*END/i);
154
- if (!caseMatch) return 'false';
155
-
156
- const caseExpr = caseMatch[1].trim();
157
- const whenClauses = caseMatch[2];
158
- const elseClause = caseMatch[3];
159
-
160
- // Extract function name from CASE expression
161
- const funcMatch = caseExpr.match(/(\w+)\s*\(/);
162
- const testExpression = funcMatch ? getFunctionMapping(funcMatch[1], userVar) : caseExpr;
163
-
164
- const conditions = [];
165
- const whenPattern = /WHEN\s+'?([^':\s]+)'?(?:::\w+)?\s+THEN\s+((?:(?!WHEN|ELSE|END).)+)/gi;
166
-
167
- let match;
168
- while ((match = whenPattern.exec(whenClauses)) !== null) {
169
- const value = match[1];
170
- const thenExpr = match[2].trim();
171
-
172
- if (thenExpr.toLowerCase() === 'true') {
173
- conditions.push(`${testExpression} === '${value}'`);
174
- } else if (thenExpr.toLowerCase() !== 'false') {
175
- const thenJs = convertToJavaScript(thenExpr, dataVar, userVar);
176
- conditions.push(`(${testExpression} === '${value}' && ${thenJs})`);
177
- }
178
- }
179
-
180
- if (elseClause && elseClause.trim().toLowerCase() !== 'false') {
181
- conditions.push(convertToJavaScript(elseClause.trim(), dataVar, userVar));
182
- }
183
-
184
- return conditions.length > 0 ? `(${conditions.join(' || ')})` : 'false';
185
- }
186
-
187
- /**
188
- * Convert to Prisma filter
189
- */
190
- function convertToPrismaFilter(sql, userVar = 'user') {
191
- if (!sql || sql.trim() === '') return '{}';
192
-
193
- sql = sql.trim().replace(/\s+/g, ' ').replace(/\n/g, ' ');
194
-
195
- // Handle OR conditions
196
- const orParts = splitLogicalOperator(sql, 'OR');
197
- if (orParts.length > 1) {
198
- const orFilters = orParts
199
- .map(part => convertToPrismaFilter(part, userVar))
200
- .filter(f => f !== '{}');
201
-
202
- if (orFilters.length === 0) return '{}';
203
- if (orFilters.length === 1) return orFilters[0];
204
- return `{ OR: [${orFilters.join(', ')}] }`;
205
- }
206
-
207
- // Handle AND conditions
208
- const andParts = splitLogicalOperator(sql, 'AND');
209
- if (andParts.length > 1) {
210
- const andFilters = andParts
211
- .map(part => convertToPrismaFilter(part, userVar))
212
- .filter(f => f !== '{}');
213
-
214
- if (andFilters.length === 0) return '{}';
215
- if (andFilters.length === 1) return andFilters[0];
216
- return `{ AND: [${andFilters.join(', ')}] }`;
217
- }
218
-
219
- // Skip role-only checks (they can't be filtered in Prisma)
220
- if (sql.match(/^\s*(?:get_current_user_role|current_user_role)\s*\(\)/i)) {
221
- return '{}';
222
- }
223
-
224
- // Extract field comparisons
225
- const filters = [];
226
-
227
- // Handle: field = function()
228
- const fieldFuncMatch = sql.match(/^(\w+)\s*=\s*(\w+)\s*\([^)]*\)$/i);
229
- if (fieldFuncMatch) {
230
- const field = fieldFuncMatch[1];
231
- const funcName = fieldFuncMatch[2];
232
-
233
- // Skip role functions
234
- if (funcName.toLowerCase().includes('role')) {
235
- return '{}';
236
- }
237
-
238
- const jsExpression = getFunctionMapping(funcName, userVar);
239
- return `{ ${field}: ${jsExpression} }`;
240
- }
241
-
242
- // Handle: field = (current_setting('...'))
243
- const settingMatch = sql.match(/^(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'([^']+)'/i);
244
- if (settingMatch) {
245
- const field = settingMatch[1];
246
- const setting = settingMatch[2];
247
- const jsExpression = getSessionMapping(setting, userVar);
248
- return `{ ${field}: ${jsExpression} }`;
249
- }
250
-
251
- // Handle: field = 'value' (direct comparison)
252
- const directMatch = sql.match(/^(\w+)\s*=\s*'([^']+)'/i);
253
- if (directMatch) {
254
- const field = directMatch[1];
255
- const value = directMatch[2];
256
- return `{ ${field}: '${value}' }`;
257
- }
258
-
259
- // Handle: field = number
260
- const numberMatch = sql.match(/^(\w+)\s*=\s*(\d+)$/i);
261
- if (numberMatch) {
262
- const field = numberMatch[1];
263
- const value = numberMatch[2];
264
- return `{ ${field}: ${value} }`;
265
- }
266
-
267
- // Can't convert complex conditions to Prisma filters
268
- return '{}';
269
- }
270
-
271
- return { convertToJavaScript, convertToPrismaFilter };
272
- }
273
-
274
- /**
275
- * Helper: Split by logical operator respecting parentheses
276
- */
277
- function splitLogicalOperator(sql, operator) {
278
- const parts = [];
279
- let current = '';
280
- let depth = 0;
281
- let inQuotes = false;
282
- let i = 0;
283
-
284
- while (i < sql.length) {
285
- if (sql[i] === "'" && !inQuotes) {
286
- inQuotes = true;
287
- } else if (sql[i] === "'" && inQuotes) {
288
- inQuotes = false;
289
- } else if (!inQuotes) {
290
- if (sql[i] === '(') depth++;
291
- else if (sql[i] === ')') depth--;
292
- else if (depth === 0) {
293
- const upcoming = sql.substring(i, i + operator.length + 2);
294
- if (upcoming.match(new RegExp(`\\s+${operator}\\s+`, 'i'))) {
295
- if (current.trim()) parts.push(current.trim());
296
- current = '';
297
- i += upcoming.match(/\s+\w+\s+/)[0].length - 1;
298
- }
299
- }
300
- }
301
- current += sql[i];
302
- i++;
303
- }
304
-
305
- if (current.trim()) parts.push(current.trim());
306
- return parts.length > 1 ? parts : [sql];
307
- }
308
-
309
- /**
310
- * Helper: Extract array values
311
- */
312
- function extractArrayValues(arrayContent) {
313
- return arrayContent
314
- .split(',')
315
- .map(r => r.trim())
316
- .map(r => r.replace(/::[^,\]]+/g, ''))
317
- .map(r => r.replace(/^'|'$/g, ''))
318
- .map(r => `'${r}'`)
319
- .join(', ');
320
- }
321
-
322
- module.exports = { createConverter };
@@ -1,379 +0,0 @@
1
- /**
2
- * Dynamic PostgreSQL RLS to JavaScript/Prisma Converter
3
- * Handles any PostgreSQL RLS pattern without hardcoding assumptions
4
- */
5
-
6
- /**
7
- * Main converter function - PostgreSQL RLS to JavaScript
8
- */
9
- function convertToJavaScript(sql, dataVar = 'data', userVar = 'user') {
10
- if (!sql || sql.trim() === '') {
11
- return 'true';
12
- }
13
-
14
- sql = sql.trim();
15
-
16
- // Strip outer parentheses if they wrap the entire expression
17
- if (sql.startsWith('(') && sql.endsWith(')')) {
18
- let depth = 0;
19
- let matchesOuter = true;
20
- for (let i = 0; i < sql.length; i++) {
21
- if (sql[i] === '(') depth++;
22
- if (sql[i] === ')') depth--;
23
- if (depth === 0 && i < sql.length - 1) {
24
- matchesOuter = false;
25
- break;
26
- }
27
- }
28
- if (matchesOuter) {
29
- sql = sql.substring(1, sql.length - 1).trim();
30
- }
31
- }
32
-
33
- // Normalize whitespace
34
- sql = sql.replace(/\s+/g, ' ').replace(/\n/g, ' ');
35
-
36
- // Handle CASE WHEN expressions
37
- if (sql.toUpperCase().startsWith('CASE')) {
38
- return convertCaseWhen(sql, dataVar, userVar);
39
- }
40
-
41
- // Handle OR conditions
42
- const orMatch = sql.match(/\((.*?)\)\s+OR\s+\((.*?)\)/i);
43
- if (orMatch) {
44
- const left = convertToJavaScript(orMatch[1], dataVar, userVar);
45
- const right = convertToJavaScript(orMatch[2], dataVar, userVar);
46
- return `(${left} || ${right})`;
47
- }
48
-
49
- // Handle AND conditions
50
- const andMatch = sql.match(/\((.*?)\)\s+AND\s+\((.*?)\)/i);
51
- if (andMatch) {
52
- const left = convertToJavaScript(andMatch[1], dataVar, userVar);
53
- const right = convertToJavaScript(andMatch[2], dataVar, userVar);
54
- return `(${left} && ${right})`;
55
- }
56
-
57
- // Handle function calls with = ANY (ARRAY[...])
58
- const anyArrayMatch = sql.match(/(\w+)\s*\([^)]*\)[^=]*=\s*ANY\s*\(\s*(?:\()?ARRAY\s*\[([^\]]+)\]/i);
59
- if (anyArrayMatch) {
60
- const funcName = anyArrayMatch[1];
61
- const arrayValues = extractArrayValues(anyArrayMatch[2]);
62
-
63
- // Map function names to user properties dynamically
64
- const userProperty = mapFunctionToUserProperty(funcName);
65
- return `[${arrayValues}].includes(${userVar}?.${userProperty})`;
66
- }
67
-
68
- // Handle field = function() comparisons (with or without quotes)
69
- const funcCompareMatch = sql.match(/(?:"?(\w+)"?)\s*=\s*(\w+)\s*\([^)]*\)/i);
70
- if (funcCompareMatch) {
71
- const field = funcCompareMatch[1];
72
- const funcName = funcCompareMatch[2];
73
-
74
- // Map function to user property
75
- const userProperty = mapFunctionToUserProperty(funcName);
76
- return `${dataVar}?.${field} === ${userVar}?.${userProperty}`;
77
- }
78
-
79
- // Handle field = (current_setting(...))
80
- const currentSettingMatch = sql.match(/(?:"?(\w+)"?)\s*=\s*\(\s*current_setting\s*\(\s*'([^']+)'/i);
81
- if (currentSettingMatch) {
82
- const field = currentSettingMatch[1];
83
- const setting = currentSettingMatch[2];
84
-
85
- // Map setting to user property
86
- const userProperty = mapSettingToUserProperty(setting);
87
- return `${dataVar}?.${field} === ${userVar}?.${userProperty}`;
88
- }
89
-
90
- // Handle field = literal value (true, false, numbers, strings)
91
- const literalMatch = sql.match(/(?:"?(\w+)"?)\s*=\s*(true|false|\d+|'[^']+')/i);
92
- if (literalMatch) {
93
- const field = literalMatch[1];
94
- const value = literalMatch[2];
95
- return `${dataVar}?.${field} === ${value}`;
96
- }
97
-
98
- // Handle EXISTS subqueries
99
- if (sql.includes('EXISTS')) {
100
- return handleExistsSubquery(sql, dataVar, userVar);
101
- }
102
-
103
- // Handle simple boolean values
104
- if (sql.toLowerCase() === 'true') return 'true';
105
- if (sql.toLowerCase() === 'false') return 'false';
106
-
107
- // Unhandled pattern
108
- console.warn(`⚠ Unhandled RLS pattern: ${sql}`);
109
- return `true /* Unhandled RLS pattern: ${sql.substring(0, 60)}... */`;
110
- }
111
-
112
- /**
113
- * Map PostgreSQL function names to user object properties
114
- * This is where customization happens based on your PostgreSQL functions
115
- */
116
- function mapFunctionToUserProperty(funcName) {
117
- const mappings = {
118
- // Common patterns - can be customized per project
119
- 'get_current_user_role': 'role',
120
- 'get_current_user_id': 'id',
121
- 'current_user_id': 'id',
122
- 'get_current_teacher_id': 'teacher_id',
123
- 'get_current_student_id': 'student_id',
124
- 'get_current_agency_id': 'agency_id',
125
- 'get_current_school_id': 'school_id',
126
- // Add more mappings as needed
127
- };
128
-
129
- // If we have a mapping, use it
130
- if (mappings[funcName]) {
131
- return mappings[funcName];
132
- }
133
-
134
- // Try to infer from function name
135
- // get_current_X_id -> X_id
136
- const getIdMatch = funcName.match(/get_current_(\w+)_id/i);
137
- if (getIdMatch) {
138
- return `${getIdMatch[1]}_id`;
139
- }
140
-
141
- // get_current_X -> X
142
- const getCurrentMatch = funcName.match(/get_current_(\w+)/i);
143
- if (getCurrentMatch) {
144
- return getCurrentMatch[1];
145
- }
146
-
147
- // current_X -> X
148
- const currentMatch = funcName.match(/current_(\w+)/i);
149
- if (currentMatch) {
150
- return currentMatch[1];
151
- }
152
-
153
- // Default: use function name as-is
154
- return funcName;
155
- }
156
-
157
- /**
158
- * Map PostgreSQL settings to user properties
159
- */
160
- function mapSettingToUserProperty(setting) {
161
- // app.current_user_id -> id
162
- if (setting === 'app.current_user_id') {
163
- return 'id';
164
- }
165
-
166
- // app.current_X -> X
167
- const appMatch = setting.match(/app\.current_(\w+)/i);
168
- if (appMatch) {
169
- return appMatch[1];
170
- }
171
-
172
- // Default
173
- return setting.replace(/[^a-zA-Z0-9_]/g, '_');
174
- }
175
-
176
- /**
177
- * Extract and format array values
178
- */
179
- function extractArrayValues(arrayContent) {
180
- return arrayContent
181
- .split(',')
182
- .map(r => r.trim())
183
- .map(r => r.replace(/::[^,\]]+/g, '')) // Remove all type casts
184
- .map(r => r.replace(/^'|'$/g, '')) // Remove quotes
185
- .map(r => `'${r}'`)
186
- .join(', ');
187
- }
188
-
189
- /**
190
- * Convert CASE WHEN expressions
191
- */
192
- function convertCaseWhen(sql, dataVar, userVar) {
193
- const caseMatch = sql.match(/CASE\s+(\S+(?:\([^)]*\))?)\s+((?:WHEN[\s\S]+?)+)\s*(?:ELSE\s+([\s\S]+?))?\s*END/i);
194
-
195
- if (!caseMatch) {
196
- return `false /* Unparseable CASE expression */`;
197
- }
198
-
199
- const caseExpr = caseMatch[1];
200
- const whenClauses = caseMatch[2];
201
- const elseClause = caseMatch[3];
202
-
203
- // Extract the function being called in CASE
204
- let testProperty = 'unknown';
205
- const funcMatch = caseExpr.match(/(\w+)\s*\(/);
206
- if (funcMatch) {
207
- testProperty = mapFunctionToUserProperty(funcMatch[1]);
208
- }
209
-
210
- // Parse WHEN branches
211
- const whenPattern = /WHEN\s+'?([^':\s]+)'?(?:::\w+)?\s+THEN\s+((?:(?!WHEN\s+|ELSE\s+|END).)+)/gi;
212
- const conditions = [];
213
-
214
- let match;
215
- while ((match = whenPattern.exec(whenClauses)) !== null) {
216
- const value = match[1];
217
- const thenExpr = match[2].trim();
218
-
219
- if (thenExpr.toLowerCase() === 'true') {
220
- conditions.push(`${userVar}?.${testProperty} === '${value}'`);
221
- } else if (thenExpr.toLowerCase() === 'false') {
222
- // Skip false conditions
223
- } else {
224
- // Complex THEN expression - recursively convert
225
- const convertedThen = convertToJavaScript(thenExpr, dataVar, userVar);
226
- if (convertedThen !== 'false') {
227
- conditions.push(`(${userVar}?.${testProperty} === '${value}' && ${convertedThen})`);
228
- }
229
- }
230
- }
231
-
232
- // Handle ELSE clause
233
- if (elseClause && elseClause.trim().toLowerCase() !== 'false') {
234
- const elseConverted = convertToJavaScript(elseClause.trim(), dataVar, userVar);
235
- if (elseConverted !== 'false') {
236
- conditions.push(elseConverted);
237
- }
238
- }
239
-
240
- return conditions.length > 0 ? `(${conditions.join(' || ')})` : 'false';
241
- }
242
-
243
- /**
244
- * Handle EXISTS subqueries
245
- * These typically check relationships and need manual implementation
246
- */
247
- function handleExistsSubquery(sql, dataVar, userVar) {
248
- // Try to extract meaningful information from the EXISTS clause
249
- const existsMatch = sql.match(/EXISTS\s*\(\s*SELECT[^)]+FROM\s+(\w+)[^)]+WHERE\s+([^)]+)\)/i);
250
-
251
- if (existsMatch) {
252
- const tableName = existsMatch[1];
253
- const whereClause = existsMatch[2];
254
-
255
- // Look for common patterns in WHERE clause
256
- // Pattern: checking if related table's foreign key matches current context
257
- const fkMatch = whereClause.match(/(\w+)\.(\w+)\s*=\s*(\w+)\.(\w+)/gi);
258
- if (fkMatch) {
259
- return `true /* TODO: Check ${tableName} relationship via JOIN */`;
260
- }
261
-
262
- // Pattern: checking against current user functions
263
- if (whereClause.match(/get_current_\w+_id\(\)/i)) {
264
- return `true /* TODO: Verify ${tableName} access for current user context */`;
265
- }
266
- }
267
-
268
- return `true /* EXISTS subquery needs manual implementation */`;
269
- }
270
-
271
- /**
272
- * Convert to Prisma filter
273
- */
274
- function convertToPrismaFilter(sql, userVar = 'user') {
275
- if (!sql || sql.trim() === '') {
276
- return '{}';
277
- }
278
-
279
- sql = sql.trim().replace(/\s+/g, ' ').replace(/\n/g, ' ');
280
-
281
- // CASE WHEN - extract filterable conditions
282
- if (sql.toUpperCase().startsWith('CASE')) {
283
- return extractPrismaFiltersFromCase(sql, userVar);
284
- }
285
-
286
- // Handle field = function() patterns
287
- const funcCompareMatch = sql.match(/(\w+)\s*=\s*(\w+)\s*\([^)]*\)/i);
288
- if (funcCompareMatch) {
289
- const field = funcCompareMatch[1];
290
- const funcName = funcCompareMatch[2];
291
- const userProperty = mapFunctionToUserProperty(funcName);
292
-
293
- // Only create filter if it's a data field comparison
294
- if (!funcName.includes('role')) {
295
- return `{ ${field}: ${userVar}?.${userProperty} }`;
296
- }
297
- }
298
-
299
- // Handle current_setting patterns
300
- const settingMatch = sql.match(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'([^']+)'/i);
301
- if (settingMatch) {
302
- const field = settingMatch[1];
303
- const setting = settingMatch[2];
304
- const userProperty = mapSettingToUserProperty(setting);
305
- return `{ ${field}: ${userVar}?.${userProperty} }`;
306
- }
307
-
308
- // Handle OR conditions
309
- const orMatch = sql.match(/\((.*?)\)\s+OR\s+\((.*?)\)/i);
310
- if (orMatch) {
311
- const left = convertToPrismaFilter(orMatch[1], userVar);
312
- const right = convertToPrismaFilter(orMatch[2], userVar);
313
-
314
- if (left !== '{}' && right !== '{}') {
315
- return `{ OR: [${left}, ${right}] }`;
316
- } else if (left !== '{}') {
317
- return left;
318
- } else if (right !== '{}') {
319
- return right;
320
- }
321
- }
322
-
323
- // Role-based checks can't be filtered in Prisma
324
- if (sql.match(/get_current_\w+_role/i) || sql.includes('= ANY')) {
325
- return '{}';
326
- }
327
-
328
- return '{}';
329
- }
330
-
331
- /**
332
- * Extract Prisma filters from CASE WHEN
333
- */
334
- function extractPrismaFiltersFromCase(sql, userVar) {
335
- const filters = new Set();
336
-
337
- // Look for all field = function() patterns in the CASE statement
338
- const fieldMatches = sql.matchAll(/(\w+)\s*=\s*(\w+)\s*\([^)]*\)/gi);
339
-
340
- for (const match of fieldMatches) {
341
- const field = match[1];
342
- const funcName = match[2];
343
-
344
- // Skip role checks
345
- if (funcName.includes('role')) continue;
346
-
347
- const userProperty = mapFunctionToUserProperty(funcName);
348
- filters.add(`{ ${field}: ${userVar}?.${userProperty} }`);
349
- }
350
-
351
- // Look for current_setting patterns
352
- const settingMatches = sql.matchAll(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'([^']+)'/gi);
353
-
354
- for (const match of settingMatches) {
355
- const field = match[1];
356
- const setting = match[2];
357
- const userProperty = mapSettingToUserProperty(setting);
358
- filters.add(`{ ${field}: ${userVar}?.${userProperty} }`);
359
- }
360
-
361
- const filterArray = Array.from(filters);
362
-
363
- if (filterArray.length === 0) {
364
- return '{}';
365
- }
366
-
367
- if (filterArray.length === 1) {
368
- return filterArray[0];
369
- }
370
-
371
- return `{ OR: [${filterArray.join(', ')}] }`;
372
- }
373
-
374
- module.exports = {
375
- convertToJavaScript,
376
- convertToPrismaFilter,
377
- mapFunctionToUserProperty,
378
- mapSettingToUserProperty
379
- };