@rapidd/build 1.0.8 → 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,192 +0,0 @@
1
- /**
2
- * PostgreSQL RLS to JavaScript/Prisma Converter
3
- * Handles real-world PostgreSQL RLS patterns including CASE WHEN, EXISTS, type casts, etc.
4
- */
5
-
6
- /**
7
- * Convert PostgreSQL RLS expression to JavaScript
8
- * @param {string} sql - PostgreSQL RLS expression
9
- * @param {string} dataVar - Variable name for row data
10
- * @param {string} userVar - Variable name for user context
11
- * @returns {string} - JavaScript boolean expression
12
- */
13
- function convertToJavaScript(sql, dataVar = 'data', userVar = 'user') {
14
- if (!sql || sql.trim() === '') {
15
- return 'true';
16
- }
17
-
18
- sql = sql.trim();
19
-
20
- // Handle CASE WHEN expressions
21
- if (sql.toUpperCase().startsWith('CASE')) {
22
- return convertCaseWhen(sql, dataVar, userVar);
23
- }
24
-
25
- // Handle simple role checks: get_current_user_role() = ANY (ARRAY[...])
26
- const roleAnyMatch = sql.match(/get_current_user_role\(\)[^=]*=\s*ANY\s*\(\s*(?:\()?ARRAY\s*\[([^\]]+)\]/i);
27
- if (roleAnyMatch) {
28
- const roles = roleAnyMatch[1]
29
- .split(',')
30
- .map(r => r.trim())
31
- .map(r => r.replace(/::[^,\]]+/g, '')) // Remove type casts
32
- .map(r => r.replace(/^'|'$/g, '')) // Remove quotes
33
- .map(r => `'${r}'`)
34
- .join(', ');
35
- return `[${roles}].includes(${userVar}?.role)`;
36
- }
37
-
38
- // Handle EXISTS subqueries - these need manual implementation
39
- if (sql.toUpperCase().includes('EXISTS')) {
40
- return `true /* EXISTS subquery - requires manual implementation: ${sql.substring(0, 50)}... */`;
41
- }
42
-
43
- // Handle simple comparisons with current_setting
44
- const settingMatch = sql.match(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'app\.current_user_id'/i);
45
- if (settingMatch) {
46
- const field = settingMatch[1];
47
- return `${dataVar}?.${field} === ${userVar}?.id`;
48
- }
49
-
50
- // Fallback: return true with comment
51
- return `true /* Complex RLS - manual implementation needed: ${sql.substring(0, 50)}... */`;
52
- }
53
-
54
- /**
55
- * Convert CASE WHEN expression to JavaScript
56
- */
57
- function convertCaseWhen(sql, dataVar, userVar) {
58
- // Extract CASE conditions - improved regex to handle multiline
59
- const casePattern = /CASE\s+(\w+\([^)]*\))\s+(WHEN\s+[\s\S]+?)(?:ELSE\s+([\s\S]+?))?END/i;
60
- const match = sql.match(casePattern);
61
-
62
- if (!match) {
63
- return `false /* Unparseable CASE expression */`;
64
- }
65
-
66
- const caseExpr = match[1]; // e.g., get_current_user_role()
67
- const whenClauses = match[2];
68
- const elseClause = match[3];
69
-
70
- // Determine what function is being called
71
- let jsExpr = '';
72
- if (caseExpr.includes('get_current_user_role')) {
73
- jsExpr = `${userVar}?.role`;
74
- } else if (caseExpr.includes('current_user_id')) {
75
- jsExpr = `${userVar}?.id`;
76
- } else {
77
- return `false /* Unsupported CASE expression: ${caseExpr} */`;
78
- }
79
-
80
- // Parse WHEN clauses - improved regex for multiline THEN
81
- const whenPattern = /WHEN\s+'([^']+)'(?:::\w+)?\s+THEN\s+([\s\S]+?)(?=\s+WHEN\s+|$)/gi;
82
- const whenMatches = [...whenClauses.matchAll(whenPattern)];
83
- const conditions = [];
84
-
85
- for (const whenMatch of whenMatches) {
86
- const value = whenMatch[1]; // e.g., 'super_admin'
87
- let thenExpr = whenMatch[2].trim(); // e.g., 'true' or complex expression
88
-
89
- // Remove trailing text before next WHEN or ELSE
90
- thenExpr = thenExpr.replace(/\s+(?:WHEN|ELSE)[\s\S]*$/, '').trim();
91
-
92
- if (thenExpr.toLowerCase() === 'true') {
93
- conditions.push(`${jsExpr} === '${value}'`);
94
- } else {
95
- // Complex THEN expression - try to convert it
96
- const convertedThen = convertSimpleExpression(thenExpr, dataVar, userVar);
97
- conditions.push(`(${jsExpr} === '${value}' && ${convertedThen})`);
98
- }
99
- }
100
-
101
- if (conditions.length === 0) {
102
- return 'false';
103
- }
104
-
105
- return conditions.join(' || ');
106
- }
107
-
108
- /**
109
- * Convert simple PostgreSQL expressions
110
- */
111
- function convertSimpleExpression(expr, dataVar, userVar) {
112
- expr = expr.trim();
113
-
114
- // Remove outer parentheses
115
- if (expr.startsWith('(') && expr.endsWith(')')) {
116
- expr = expr.substring(1, expr.length - 1).trim();
117
- }
118
-
119
- // Handle EXISTS - mark as needing manual implementation (check FIRST before simple patterns)
120
- if (expr.toUpperCase().includes('EXISTS')) {
121
- return `true /* EXISTS subquery requires manual implementation */`;
122
- }
123
-
124
- // Handle: id = (current_setting('app.current_user_id')::integer)
125
- const settingMatch = expr.match(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'app\.current_user_id'[^)]*\)[^)]*\)/i);
126
- if (settingMatch) {
127
- const field = settingMatch[1];
128
- return `${dataVar}?.${field} === ${userVar}?.id`;
129
- }
130
-
131
- return 'true /* Requires manual implementation */';
132
- }
133
-
134
- /**
135
- * Convert PostgreSQL RLS expression to Prisma filter
136
- * @param {string} sql - PostgreSQL RLS expression
137
- * @param {string} userVar - Variable name for user context
138
- * @returns {string} - Prisma filter object as string
139
- */
140
- function convertToPrismaFilter(sql, userVar = 'user') {
141
- if (!sql || sql.trim() === '') {
142
- return '{}';
143
- }
144
-
145
- sql = sql.trim();
146
-
147
- // Handle CASE WHEN - convert to OR filter
148
- if (sql.toUpperCase().startsWith('CASE')) {
149
- return convertCaseWhenToPrisma(sql, userVar);
150
- }
151
-
152
- // Handle simple role checks - can't filter on user role in Prisma, return empty
153
- const roleAnyMatch = sql.match(/get_current_user_role\(\)[^=]*=\s*ANY\s*\(\s*(?:\()?ARRAY\s*\[([^\]]+)\]/i);
154
- if (roleAnyMatch) {
155
- // Role-based access can't be filtered in Prisma - must be checked in hasAccess
156
- return '{}';
157
- }
158
-
159
- // Handle simple comparisons with current_setting
160
- const settingMatch = sql.match(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'app\.current_user_id'/i);
161
- if (settingMatch) {
162
- const field = settingMatch[1];
163
- return `{ ${field}: ${userVar}?.id }`;
164
- }
165
-
166
- // Fallback
167
- return '{}';
168
- }
169
-
170
- /**
171
- * Convert CASE WHEN to Prisma OR filter
172
- */
173
- function convertCaseWhenToPrisma(sql, userVar) {
174
- // For CASE WHEN expressions, extract simple field comparisons
175
- const settingMatches = [...sql.matchAll(/(\w+)\s*=\s*\(\s*current_setting\s*\(\s*'app\.current_user_id'/gi)];
176
-
177
- if (settingMatches.length > 0) {
178
- const filters = settingMatches.map(m => `{ ${m[1]}: ${userVar}?.id }`);
179
- if (filters.length === 1) {
180
- return filters[0];
181
- }
182
- return `{ OR: [${filters.join(', ')}] }`;
183
- }
184
-
185
- // If no simple filters found, return empty (role-based checks handled in hasAccess)
186
- return '{}';
187
- }
188
-
189
- module.exports = {
190
- convertToJavaScript,
191
- convertToPrismaFilter
192
- };