dzql 0.1.2 → 0.1.4
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 +21 -6
- package/package.json +4 -4
- package/src/compiler/cli/index.js +174 -0
- package/src/compiler/codegen/graph-rules-codegen.js +259 -0
- package/src/compiler/codegen/notification-codegen.js +232 -0
- package/src/compiler/codegen/operation-codegen.js +555 -0
- package/src/compiler/codegen/permission-codegen.js +310 -0
- package/src/compiler/compiler.js +228 -0
- package/src/compiler/index.js +11 -0
- package/src/compiler/parser/entity-parser.js +299 -0
- package/src/compiler/parser/path-parser.js +290 -0
- package/src/database/migrations/002_functions.sql +39 -2
- package/src/database/migrations/003_operations.sql +10 -16
- package/src/database/migrations/004_search.sql +7 -0
- package/src/database/migrations/005_entities.sql +112 -0
- package/GETTING_STARTED.md +0 -1104
- package/REFERENCE.md +0 -960
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Code Generator
|
|
3
|
+
* Generates PostgreSQL permission check functions from path ASTs
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { PathParser } from '../parser/path-parser.js';
|
|
7
|
+
|
|
8
|
+
export class PermissionCodegen {
|
|
9
|
+
constructor(tableName, permissionPaths) {
|
|
10
|
+
this.tableName = tableName;
|
|
11
|
+
this.permissionPaths = permissionPaths;
|
|
12
|
+
this.parser = new PathParser();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Generate all permission check functions
|
|
17
|
+
* @returns {string} SQL for permission functions
|
|
18
|
+
*/
|
|
19
|
+
generate() {
|
|
20
|
+
const functions = [];
|
|
21
|
+
|
|
22
|
+
// Always generate the 4 standard permission functions
|
|
23
|
+
const standardOperations = ['view', 'create', 'update', 'delete'];
|
|
24
|
+
|
|
25
|
+
for (const operation of standardOperations) {
|
|
26
|
+
// Clean the operation name (remove any comments or special characters)
|
|
27
|
+
const cleanOperation = this._cleanOperationName(operation);
|
|
28
|
+
|
|
29
|
+
// Get paths for this operation (checking both clean and original keys)
|
|
30
|
+
const paths = this.permissionPaths[operation]
|
|
31
|
+
|| this.permissionPaths[cleanOperation]
|
|
32
|
+
|| this._findPathsByPartialMatch(operation);
|
|
33
|
+
|
|
34
|
+
if (!paths || paths.length === 0) {
|
|
35
|
+
// Public access - always returns true
|
|
36
|
+
functions.push(this._generatePublicPermission(cleanOperation));
|
|
37
|
+
} else {
|
|
38
|
+
functions.push(this._generatePermissionFunction(cleanOperation, paths));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return functions.join('\n\n');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Clean operation name - remove comments, quotes, newlines
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
_cleanOperationName(operation) {
|
|
50
|
+
if (!operation || typeof operation !== 'string') {
|
|
51
|
+
return 'unknown';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Remove SQL comments (-- ... to end of line)
|
|
55
|
+
let clean = operation.replace(/--[^\n]*/g, '');
|
|
56
|
+
|
|
57
|
+
// Remove quotes
|
|
58
|
+
clean = clean.replace(/['"]/g, '');
|
|
59
|
+
|
|
60
|
+
// Remove newlines and extra whitespace
|
|
61
|
+
clean = clean.replace(/\s+/g, ' ').trim();
|
|
62
|
+
|
|
63
|
+
// Extract just the operation name (view, create, update, delete)
|
|
64
|
+
// Match common patterns
|
|
65
|
+
if (clean.includes('view')) return 'view';
|
|
66
|
+
if (clean.includes('create')) return 'create';
|
|
67
|
+
if (clean.includes('update')) return 'update';
|
|
68
|
+
if (clean.includes('delete')) return 'delete';
|
|
69
|
+
|
|
70
|
+
// If no match, return the first word
|
|
71
|
+
const firstWord = clean.split(/\s+/)[0].toLowerCase();
|
|
72
|
+
return firstWord || 'unknown';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Find paths by partial match in permission keys
|
|
77
|
+
* Handles cases where keys might have comments embedded
|
|
78
|
+
* @private
|
|
79
|
+
*/
|
|
80
|
+
_findPathsByPartialMatch(operation) {
|
|
81
|
+
for (const [key, paths] of Object.entries(this.permissionPaths)) {
|
|
82
|
+
const cleanKey = this._cleanOperationName(key);
|
|
83
|
+
if (cleanKey === operation) {
|
|
84
|
+
return paths;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Generate a permission function for an operation
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_generatePermissionFunction(operation, paths) {
|
|
95
|
+
const functionName = `can_${operation}_${this.tableName}`;
|
|
96
|
+
const checks = [];
|
|
97
|
+
|
|
98
|
+
for (const path of paths) {
|
|
99
|
+
const ast = this.parser.parse(path);
|
|
100
|
+
const sql = this._generatePathSQL(ast);
|
|
101
|
+
if (sql) {
|
|
102
|
+
checks.push(sql);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Combine checks with OR logic
|
|
107
|
+
const checkSQL = checks.length > 0
|
|
108
|
+
? checks.join('\n OR ')
|
|
109
|
+
: 'false';
|
|
110
|
+
|
|
111
|
+
return `-- Permission check: ${operation} on ${this.tableName}
|
|
112
|
+
CREATE OR REPLACE FUNCTION can_${operation}_${this.tableName}(
|
|
113
|
+
p_user_id INT,
|
|
114
|
+
p_record JSONB
|
|
115
|
+
) RETURNS BOOLEAN AS $$
|
|
116
|
+
BEGIN
|
|
117
|
+
RETURN (
|
|
118
|
+
${checkSQL}
|
|
119
|
+
);
|
|
120
|
+
END;
|
|
121
|
+
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Generate public permission (always true)
|
|
126
|
+
* @private
|
|
127
|
+
*/
|
|
128
|
+
_generatePublicPermission(operation) {
|
|
129
|
+
return `-- Permission check: ${operation} on ${this.tableName} (public access)
|
|
130
|
+
CREATE OR REPLACE FUNCTION can_${operation}_${this.tableName}(
|
|
131
|
+
p_user_id INT,
|
|
132
|
+
p_record JSONB
|
|
133
|
+
) RETURNS BOOLEAN AS $$
|
|
134
|
+
BEGIN
|
|
135
|
+
RETURN true; -- Public access
|
|
136
|
+
END;
|
|
137
|
+
$$ LANGUAGE plpgsql STABLE SECURITY DEFINER;`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Generate SQL for a path AST
|
|
142
|
+
* @private
|
|
143
|
+
*/
|
|
144
|
+
_generatePathSQL(ast) {
|
|
145
|
+
switch (ast.type) {
|
|
146
|
+
case 'empty':
|
|
147
|
+
return 'true'; // No restriction
|
|
148
|
+
|
|
149
|
+
case 'direct_field':
|
|
150
|
+
return this._generateDirectFieldCheck(ast);
|
|
151
|
+
|
|
152
|
+
case 'traversal':
|
|
153
|
+
return this._generateTraversalCheck(ast);
|
|
154
|
+
|
|
155
|
+
case 'dot_path':
|
|
156
|
+
return this._generateDotPathCheck(ast);
|
|
157
|
+
|
|
158
|
+
default:
|
|
159
|
+
console.warn('Unknown AST type:', ast.type);
|
|
160
|
+
return 'false';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generate direct field check: @owner_id
|
|
166
|
+
* @private
|
|
167
|
+
*/
|
|
168
|
+
_generateDirectFieldCheck(ast) {
|
|
169
|
+
return `(p_record->>'${ast.field}')::int = p_user_id`;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Generate traversal check: @org_id->acts_for[org_id=$]{active}.user_id
|
|
174
|
+
* @private
|
|
175
|
+
*/
|
|
176
|
+
_generateTraversalCheck(ast) {
|
|
177
|
+
const steps = ast.steps;
|
|
178
|
+
|
|
179
|
+
// Extract components from the path
|
|
180
|
+
let sourceField = null;
|
|
181
|
+
let targetTable = null;
|
|
182
|
+
let targetField = null;
|
|
183
|
+
let filters = [];
|
|
184
|
+
let temporal = false;
|
|
185
|
+
|
|
186
|
+
for (const step of steps) {
|
|
187
|
+
if (step.type === 'field_ref') {
|
|
188
|
+
if (!sourceField) {
|
|
189
|
+
// First field reference is the source
|
|
190
|
+
sourceField = step.field;
|
|
191
|
+
} else {
|
|
192
|
+
// Last field reference is the target
|
|
193
|
+
targetField = step.field;
|
|
194
|
+
}
|
|
195
|
+
} else if (step.type === 'table_ref') {
|
|
196
|
+
targetTable = step.table;
|
|
197
|
+
|
|
198
|
+
// Collect filter conditions
|
|
199
|
+
if (step.filter) {
|
|
200
|
+
filters = step.filter;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Check for temporal marker
|
|
204
|
+
if (step.temporal) {
|
|
205
|
+
temporal = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Get target field if specified in table ref
|
|
209
|
+
if (step.targetField) {
|
|
210
|
+
targetField = step.targetField;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Build WHERE conditions
|
|
216
|
+
const conditions = [];
|
|
217
|
+
|
|
218
|
+
// Add filter conditions
|
|
219
|
+
for (const filter of filters) {
|
|
220
|
+
if (filter.operator === '=' && filter.value.type === 'param') {
|
|
221
|
+
// field=$ means match the record's field value
|
|
222
|
+
conditions.push(`${targetTable}.${filter.field} = (p_record->>'${sourceField}')::int`);
|
|
223
|
+
} else if (filter.operator === '=') {
|
|
224
|
+
const value = this._formatValue(filter.value);
|
|
225
|
+
conditions.push(`${targetTable}.${filter.field} = ${value}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Add temporal condition
|
|
230
|
+
if (temporal) {
|
|
231
|
+
conditions.push(`${targetTable}.valid_to IS NULL`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Add user_id check (final target)
|
|
235
|
+
if (targetField) {
|
|
236
|
+
conditions.push(`${targetTable}.${targetField} = p_user_id`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Build EXISTS query
|
|
240
|
+
const whereClause = conditions.length > 0
|
|
241
|
+
? 'WHERE ' + conditions.join('\n AND ')
|
|
242
|
+
: '';
|
|
243
|
+
|
|
244
|
+
return `EXISTS (
|
|
245
|
+
SELECT 1 FROM ${targetTable}
|
|
246
|
+
${whereClause}
|
|
247
|
+
)`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Generate filter condition SQL
|
|
252
|
+
* @private
|
|
253
|
+
*/
|
|
254
|
+
_generateFilterCondition(condition, tableAlias) {
|
|
255
|
+
const field = `${tableAlias}.${condition.field}`;
|
|
256
|
+
const value = this._formatValue(condition.value);
|
|
257
|
+
|
|
258
|
+
switch (condition.operator) {
|
|
259
|
+
case '=':
|
|
260
|
+
if (condition.value.type === 'param') {
|
|
261
|
+
// Special case: field=$ means use the record's value
|
|
262
|
+
return `${field} = (p_record->>'${condition.field}')::int`;
|
|
263
|
+
}
|
|
264
|
+
return `${field} = ${value}`;
|
|
265
|
+
|
|
266
|
+
default:
|
|
267
|
+
return `${field} ${condition.operator} ${value}`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Format a value for SQL
|
|
273
|
+
* @private
|
|
274
|
+
*/
|
|
275
|
+
_formatValue(value) {
|
|
276
|
+
switch (value.type) {
|
|
277
|
+
case 'literal':
|
|
278
|
+
return `'${value.value}'`;
|
|
279
|
+
case 'number':
|
|
280
|
+
return value.value;
|
|
281
|
+
case 'field':
|
|
282
|
+
return `(p_record->>'${value.value}')`;
|
|
283
|
+
case 'param':
|
|
284
|
+
return '?'; // Will be replaced by caller
|
|
285
|
+
default:
|
|
286
|
+
return 'NULL';
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Generate dot path check (less common)
|
|
292
|
+
* @private
|
|
293
|
+
*/
|
|
294
|
+
_generateDotPathCheck(ast) {
|
|
295
|
+
// For now, treat as a field reference to the last field
|
|
296
|
+
const lastField = ast.fields[ast.fields.length - 1];
|
|
297
|
+
return `(p_record->>'${lastField}')::int = p_user_id`;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Generate permission check functions for an entity
|
|
303
|
+
* @param {string} tableName - Table name
|
|
304
|
+
* @param {Object} permissionPaths - Permission paths object
|
|
305
|
+
* @returns {string} SQL for permission functions
|
|
306
|
+
*/
|
|
307
|
+
export function generatePermissionFunctions(tableName, permissionPaths) {
|
|
308
|
+
const codegen = new PermissionCodegen(tableName, permissionPaths);
|
|
309
|
+
return codegen.generate();
|
|
310
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DZQL Compiler
|
|
3
|
+
* Main compiler class that orchestrates parsing and code generation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EntityParser } from './parser/entity-parser.js';
|
|
7
|
+
import { generatePermissionFunctions } from './codegen/permission-codegen.js';
|
|
8
|
+
import { generateOperations } from './codegen/operation-codegen.js';
|
|
9
|
+
import { generateNotificationFunction } from './codegen/notification-codegen.js';
|
|
10
|
+
import { generateGraphRuleFunctions } from './codegen/graph-rules-codegen.js';
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
|
|
13
|
+
export class DZQLCompiler {
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
this.options = {
|
|
16
|
+
includeComments: true,
|
|
17
|
+
includeChecksums: true,
|
|
18
|
+
...options
|
|
19
|
+
};
|
|
20
|
+
this.parser = new EntityParser();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Compile an entity definition to SQL
|
|
25
|
+
* @param {Object} entity - Entity configuration
|
|
26
|
+
* @returns {Object} Compilation result
|
|
27
|
+
*/
|
|
28
|
+
compile(entity) {
|
|
29
|
+
const startTime = Date.now();
|
|
30
|
+
|
|
31
|
+
// Normalize entity configuration
|
|
32
|
+
const normalizedEntity = this.parser.parseFromObject(entity);
|
|
33
|
+
|
|
34
|
+
// Generate SQL sections
|
|
35
|
+
const sections = [];
|
|
36
|
+
|
|
37
|
+
// Header
|
|
38
|
+
if (this.options.includeComments) {
|
|
39
|
+
sections.push(this._generateHeader(normalizedEntity));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Permission functions
|
|
43
|
+
const permissionSQL = generatePermissionFunctions(
|
|
44
|
+
normalizedEntity.tableName,
|
|
45
|
+
normalizedEntity.permissionPaths
|
|
46
|
+
);
|
|
47
|
+
sections.push(permissionSQL);
|
|
48
|
+
|
|
49
|
+
// Operation functions
|
|
50
|
+
const operationSQL = generateOperations(normalizedEntity);
|
|
51
|
+
sections.push(operationSQL);
|
|
52
|
+
|
|
53
|
+
// Notification path resolution (if needed)
|
|
54
|
+
if (normalizedEntity.notificationPaths &&
|
|
55
|
+
Object.keys(normalizedEntity.notificationPaths).length > 0) {
|
|
56
|
+
sections.push(this._generateNotificationFunction(normalizedEntity));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Graph rules (if needed)
|
|
60
|
+
if (normalizedEntity.graphRules &&
|
|
61
|
+
Object.keys(normalizedEntity.graphRules).length > 0) {
|
|
62
|
+
sections.push(this._generateGraphRuleFunctions(normalizedEntity));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Combine all sections
|
|
66
|
+
const sql = sections.join('\n\n');
|
|
67
|
+
|
|
68
|
+
// Calculate checksum
|
|
69
|
+
const checksum = this._calculateChecksum(sql);
|
|
70
|
+
|
|
71
|
+
const result = {
|
|
72
|
+
tableName: normalizedEntity.tableName,
|
|
73
|
+
sql,
|
|
74
|
+
checksum,
|
|
75
|
+
compilationTime: Date.now() - startTime,
|
|
76
|
+
generatedAt: new Date().toISOString()
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Compile multiple entities
|
|
84
|
+
* @param {Array} entities - Array of entity configurations
|
|
85
|
+
* @returns {Object} Compilation results
|
|
86
|
+
*/
|
|
87
|
+
compileAll(entities) {
|
|
88
|
+
const results = [];
|
|
89
|
+
const errors = [];
|
|
90
|
+
|
|
91
|
+
for (const entity of entities) {
|
|
92
|
+
try {
|
|
93
|
+
const result = this.compile(entity);
|
|
94
|
+
results.push(result);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
errors.push({
|
|
97
|
+
entity: entity.tableName || 'unknown',
|
|
98
|
+
error: error.message
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
results,
|
|
105
|
+
errors,
|
|
106
|
+
summary: {
|
|
107
|
+
total: entities.length,
|
|
108
|
+
successful: results.length,
|
|
109
|
+
failed: errors.length
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Compile from SQL file
|
|
116
|
+
* @param {string} sqlContent - SQL file content
|
|
117
|
+
* @returns {Object} Compilation results
|
|
118
|
+
*/
|
|
119
|
+
compileFromSQL(sqlContent) {
|
|
120
|
+
const registerCalls = sqlContent.match(/dzql\.register_entity\s*\([\s\S]*?\);/gi);
|
|
121
|
+
|
|
122
|
+
if (!registerCalls) {
|
|
123
|
+
return {
|
|
124
|
+
results: [],
|
|
125
|
+
errors: [],
|
|
126
|
+
summary: { total: 0, successful: 0, failed: 0 }
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const entities = [];
|
|
131
|
+
for (const call of registerCalls) {
|
|
132
|
+
try {
|
|
133
|
+
const entity = this.parser.parseFromSQL(call);
|
|
134
|
+
entities.push(entity);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.warn('Failed to parse entity:', error.message);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return this.compileAll(entities);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generate file header
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
_generateHeader(entity) {
|
|
148
|
+
return `-- ============================================================================
|
|
149
|
+
-- DZQL Compiled Functions for: ${entity.tableName}
|
|
150
|
+
-- Generated: ${new Date().toISOString()}
|
|
151
|
+
--
|
|
152
|
+
-- This file was automatically generated by the DZQL Compiler.
|
|
153
|
+
-- Do not edit directly - regenerate from entity definition.
|
|
154
|
+
-- ============================================================================`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Generate notification path resolution function
|
|
159
|
+
* @private
|
|
160
|
+
*/
|
|
161
|
+
_generateNotificationFunction(entity) {
|
|
162
|
+
return generateNotificationFunction(
|
|
163
|
+
entity.tableName,
|
|
164
|
+
entity.notificationPaths
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generate graph rule functions
|
|
170
|
+
* @private
|
|
171
|
+
*/
|
|
172
|
+
_generateGraphRuleFunctions(entity) {
|
|
173
|
+
return generateGraphRuleFunctions(
|
|
174
|
+
entity.tableName,
|
|
175
|
+
entity.graphRules
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Calculate SHA-256 checksum of SQL
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_calculateChecksum(sql) {
|
|
184
|
+
return crypto.createHash('sha256').update(sql).digest('hex');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Format SQL with proper indentation (basic)
|
|
189
|
+
* @private
|
|
190
|
+
*/
|
|
191
|
+
_formatSQL(sql) {
|
|
192
|
+
// Basic formatting - could be enhanced
|
|
193
|
+
return sql.trim();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Compile a single entity
|
|
199
|
+
* @param {Object} entity - Entity configuration
|
|
200
|
+
* @param {Object} options - Compiler options
|
|
201
|
+
* @returns {Object} Compilation result
|
|
202
|
+
*/
|
|
203
|
+
export function compile(entity, options = {}) {
|
|
204
|
+
const compiler = new DZQLCompiler(options);
|
|
205
|
+
return compiler.compile(entity);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Compile multiple entities
|
|
210
|
+
* @param {Array} entities - Array of entity configurations
|
|
211
|
+
* @param {Object} options - Compiler options
|
|
212
|
+
* @returns {Object} Compilation results
|
|
213
|
+
*/
|
|
214
|
+
export function compileAll(entities, options = {}) {
|
|
215
|
+
const compiler = new DZQLCompiler(options);
|
|
216
|
+
return compiler.compileAll(entities);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Compile from SQL file content
|
|
221
|
+
* @param {string} sqlContent - SQL file content
|
|
222
|
+
* @param {Object} options - Compiler options
|
|
223
|
+
* @returns {Object} Compilation results
|
|
224
|
+
*/
|
|
225
|
+
export function compileFromSQL(sqlContent, options = {}) {
|
|
226
|
+
const compiler = new DZQLCompiler(options);
|
|
227
|
+
return compiler.compileFromSQL(sqlContent);
|
|
228
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DZQL Compiler
|
|
3
|
+
* Transforms declarative entity definitions into optimized PostgreSQL stored procedures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { DZQLCompiler, compile, compileAll, compileFromSQL } from './compiler.js';
|
|
7
|
+
export { EntityParser, parseEntitiesFromSQL } from './parser/entity-parser.js';
|
|
8
|
+
export { PathParser, parsePath, parsePaths } from './parser/path-parser.js';
|
|
9
|
+
export { PermissionCodegen, generatePermissionFunctions } from './codegen/permission-codegen.js';
|
|
10
|
+
export { OperationCodegen, generateOperations } from './codegen/operation-codegen.js';
|
|
11
|
+
export { NotificationCodegen, generateNotificationFunction } from './codegen/notification-codegen.js';
|