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
package/README.md
CHANGED
|
@@ -4,12 +4,10 @@ PostgreSQL-powered framework with automatic CRUD operations and real-time WebSoc
|
|
|
4
4
|
|
|
5
5
|
## Documentation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- **[
|
|
10
|
-
- **[
|
|
11
|
-
- **[REFERENCE.md](REFERENCE.md)** - Complete API reference
|
|
12
|
-
- **[CLAUDE.md](../../docs/CLAUDE.md)** - Development guide for AI assistants
|
|
7
|
+
- **[Getting Started Guide](docs/GETTING_STARTED.md)** - Complete tutorial with working todo app
|
|
8
|
+
- **[API Reference](docs/REFERENCE.md)** - Complete API documentation
|
|
9
|
+
- **[Compiler Documentation](docs/compiler/)** - Entity compilation guide and coding standards
|
|
10
|
+
- **[Claude Guide](docs/CLAUDE.md)** - Development guide for AI assistants
|
|
13
11
|
- **[Venues Example](../venues/)** - Full working application
|
|
14
12
|
|
|
15
13
|
## Quick Install
|
|
@@ -33,6 +31,23 @@ const user = await ws.api.save.users({ name: 'Alice' });
|
|
|
33
31
|
const results = await ws.api.search.users({ filters: { name: 'alice' } });
|
|
34
32
|
```
|
|
35
33
|
|
|
34
|
+
## DZQL Compiler
|
|
35
|
+
|
|
36
|
+
Transform declarative entity definitions into optimized PostgreSQL stored procedures:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Via CLI
|
|
40
|
+
dzql compile database/init_db/009_venues_domain.sql -o compiled/
|
|
41
|
+
|
|
42
|
+
# Programmatically
|
|
43
|
+
import { DZQLCompiler } from 'dzql/compiler';
|
|
44
|
+
|
|
45
|
+
const compiler = new DZQLCompiler();
|
|
46
|
+
const result = compiler.compileFromSQL(sqlContent);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See **[Compiler Documentation](docs/compiler/)** for complete usage guide, coding standards, and advanced features.
|
|
50
|
+
|
|
36
51
|
## License
|
|
37
52
|
|
|
38
53
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dzql",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "PostgreSQL-powered framework with zero boilerplate CRUD operations and real-time WebSocket synchronization",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server/index.js",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
".": "./src/server/index.js",
|
|
9
9
|
"./client": "./src/client/ws.js",
|
|
10
10
|
"./server": "./src/server/index.js",
|
|
11
|
-
"./db": "./src/server/db.js"
|
|
11
|
+
"./db": "./src/server/db.js",
|
|
12
|
+
"./compiler": "./src/compiler/index.js"
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
15
|
"src/**/*.js",
|
|
@@ -20,13 +21,12 @@
|
|
|
20
21
|
],
|
|
21
22
|
"scripts": {
|
|
22
23
|
"test": "bun test",
|
|
23
|
-
"prepublishOnly": "echo '✅ Publishing DZQL v0.1.
|
|
24
|
+
"prepublishOnly": "echo '✅ Publishing DZQL v0.1.4...'"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"jose": "^6.1.0",
|
|
27
28
|
"postgres": "^3.4.7"
|
|
28
29
|
},
|
|
29
|
-
|
|
30
30
|
"keywords": [
|
|
31
31
|
"postgresql",
|
|
32
32
|
"postgres",
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* DZQL Compiler CLI
|
|
4
|
+
* Command-line interface for compiling entity definitions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
8
|
+
import { resolve, dirname, basename } from 'path';
|
|
9
|
+
import { DZQLCompiler } from '../compiler.js';
|
|
10
|
+
|
|
11
|
+
const USAGE = `
|
|
12
|
+
DZQL Compiler - Transform entity definitions into PostgreSQL functions
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
dzql-compile <input-file> [options]
|
|
16
|
+
|
|
17
|
+
Options:
|
|
18
|
+
-o, --output <dir> Output directory (default: ./compiled)
|
|
19
|
+
-w, --watch Watch for changes and recompile
|
|
20
|
+
-v, --verbose Verbose output
|
|
21
|
+
-h, --help Show this help message
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
dzql-compile entities/venues.sql
|
|
25
|
+
dzql-compile database/init_db/009_venues_domain.sql -o compiled/
|
|
26
|
+
dzql-compile entities/*.sql -o dist/compiled/
|
|
27
|
+
|
|
28
|
+
Environment Variables:
|
|
29
|
+
DZQL_COMPILER_VERBOSE Enable verbose output
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
class CLI {
|
|
33
|
+
constructor() {
|
|
34
|
+
this.args = process.argv.slice(2);
|
|
35
|
+
this.options = {
|
|
36
|
+
output: './compiled',
|
|
37
|
+
watch: false,
|
|
38
|
+
verbose: process.env.DZQL_COMPILER_VERBOSE === 'true'
|
|
39
|
+
};
|
|
40
|
+
this.compiler = new DZQLCompiler();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
run() {
|
|
44
|
+
this.parseArgs();
|
|
45
|
+
|
|
46
|
+
if (this.options.help || this.args.length === 0) {
|
|
47
|
+
console.log(USAGE);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const inputFile = this.args[0];
|
|
52
|
+
|
|
53
|
+
if (!existsSync(inputFile)) {
|
|
54
|
+
console.error(`Error: File not found: ${inputFile}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.compileFile(inputFile);
|
|
59
|
+
|
|
60
|
+
if (this.options.watch) {
|
|
61
|
+
this.watchFile(inputFile);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
parseArgs() {
|
|
66
|
+
for (let i = 0; i < this.args.length; i++) {
|
|
67
|
+
const arg = this.args[i];
|
|
68
|
+
|
|
69
|
+
switch (arg) {
|
|
70
|
+
case '-o':
|
|
71
|
+
case '--output':
|
|
72
|
+
this.options.output = this.args[++i];
|
|
73
|
+
break;
|
|
74
|
+
|
|
75
|
+
case '-w':
|
|
76
|
+
case '--watch':
|
|
77
|
+
this.options.watch = true;
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case '-v':
|
|
81
|
+
case '--verbose':
|
|
82
|
+
this.options.verbose = true;
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case '-h':
|
|
86
|
+
case '--help':
|
|
87
|
+
this.options.help = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
compileFile(inputFile) {
|
|
94
|
+
try {
|
|
95
|
+
console.log(`\n🔨 Compiling: ${inputFile}`);
|
|
96
|
+
|
|
97
|
+
// Read input file
|
|
98
|
+
const sqlContent = readFileSync(inputFile, 'utf-8');
|
|
99
|
+
|
|
100
|
+
// Compile
|
|
101
|
+
const result = this.compiler.compileFromSQL(sqlContent);
|
|
102
|
+
|
|
103
|
+
// Display results
|
|
104
|
+
console.log(`\n📊 Compilation Summary:`);
|
|
105
|
+
console.log(` Total entities: ${result.summary.total}`);
|
|
106
|
+
console.log(` Successful: ${result.summary.successful}`);
|
|
107
|
+
console.log(` Failed: ${result.summary.failed}`);
|
|
108
|
+
|
|
109
|
+
if (result.errors.length > 0) {
|
|
110
|
+
console.log(`\n❌ Errors:`);
|
|
111
|
+
for (const error of result.errors) {
|
|
112
|
+
console.log(` - ${error.entity}: ${error.error}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Write output files
|
|
117
|
+
if (result.results.length > 0) {
|
|
118
|
+
this.writeOutputFiles(result.results);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log(`\n✅ Compilation complete!\n`);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(`\n❌ Compilation failed:`, error.message);
|
|
124
|
+
if (this.options.verbose) {
|
|
125
|
+
console.error(error.stack);
|
|
126
|
+
}
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
writeOutputFiles(results) {
|
|
132
|
+
// Ensure output directory exists
|
|
133
|
+
if (!existsSync(this.options.output)) {
|
|
134
|
+
mkdirSync(this.options.output, { recursive: true });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log(`\n📝 Writing compiled files to: ${this.options.output}`);
|
|
138
|
+
|
|
139
|
+
const checksums = {};
|
|
140
|
+
|
|
141
|
+
for (const result of results) {
|
|
142
|
+
const outputFile = resolve(this.options.output, `${result.tableName}.sql`);
|
|
143
|
+
|
|
144
|
+
// Write SQL file
|
|
145
|
+
writeFileSync(outputFile, result.sql, 'utf-8');
|
|
146
|
+
|
|
147
|
+
// Store checksum
|
|
148
|
+
checksums[result.tableName] = {
|
|
149
|
+
checksum: result.checksum,
|
|
150
|
+
generatedAt: result.generatedAt,
|
|
151
|
+
compilationTime: result.compilationTime
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
console.log(` ✓ ${result.tableName}.sql (${result.checksum.substring(0, 8)}...)`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Write checksums file
|
|
158
|
+
const checksumsFile = resolve(this.options.output, 'checksums.json');
|
|
159
|
+
writeFileSync(checksumsFile, JSON.stringify(checksums, null, 2), 'utf-8');
|
|
160
|
+
|
|
161
|
+
console.log(` ✓ checksums.json`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
watchFile(inputFile) {
|
|
165
|
+
console.log(`\n👀 Watching for changes...`);
|
|
166
|
+
|
|
167
|
+
// TODO: Implement file watching
|
|
168
|
+
console.log(` (Watch mode not yet implemented)`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Run CLI
|
|
173
|
+
const cli = new CLI();
|
|
174
|
+
cli.run();
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph Rules Code Generator
|
|
3
|
+
* Generates PostgreSQL functions for graph rule execution
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class GraphRulesCodegen {
|
|
7
|
+
constructor(tableName, graphRules) {
|
|
8
|
+
this.tableName = tableName;
|
|
9
|
+
this.graphRules = graphRules;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate all graph rule functions
|
|
14
|
+
* @returns {string} SQL for graph rule functions
|
|
15
|
+
*/
|
|
16
|
+
generate() {
|
|
17
|
+
if (!this.graphRules || Object.keys(this.graphRules).length === 0) {
|
|
18
|
+
return ''; // No functions if no rules
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const functions = [];
|
|
22
|
+
|
|
23
|
+
// Generate function for each trigger (on_create, on_update, on_delete)
|
|
24
|
+
for (const [trigger, rules] of Object.entries(this.graphRules)) {
|
|
25
|
+
const functionSQL = this._generateTriggerFunction(trigger, rules);
|
|
26
|
+
if (functionSQL) {
|
|
27
|
+
functions.push(functionSQL);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return functions.join('\n\n');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate function for a specific trigger
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
_generateTriggerFunction(trigger, rules) {
|
|
39
|
+
const operation = trigger.replace('on_', ''); // on_create -> create
|
|
40
|
+
const functionName = `_graph_${this.tableName}_${trigger}`;
|
|
41
|
+
|
|
42
|
+
const actionBlocks = [];
|
|
43
|
+
|
|
44
|
+
// Process each rule
|
|
45
|
+
for (const [ruleName, ruleConfig] of Object.entries(rules)) {
|
|
46
|
+
const description = ruleConfig.description || ruleName;
|
|
47
|
+
const actions = Array.isArray(ruleConfig.actions)
|
|
48
|
+
? ruleConfig.actions
|
|
49
|
+
: (ruleConfig.actions ? [ruleConfig.actions] : []);
|
|
50
|
+
|
|
51
|
+
for (const action of actions) {
|
|
52
|
+
const actionSQL = this._generateAction(action, ruleName, description);
|
|
53
|
+
if (actionSQL) {
|
|
54
|
+
actionBlocks.push(actionSQL);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (actionBlocks.length === 0) {
|
|
60
|
+
return null; // No actions, no function
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Determine parameters based on operation - p_user_id ALWAYS FIRST
|
|
64
|
+
const params = operation === 'delete'
|
|
65
|
+
? `p_user_id INT,\n p_old_record JSONB`
|
|
66
|
+
: operation === 'update'
|
|
67
|
+
? `p_user_id INT,\n p_old_record JSONB,\n p_new_record JSONB`
|
|
68
|
+
: `p_user_id INT,\n p_record JSONB`;
|
|
69
|
+
|
|
70
|
+
return `-- Graph rules: ${trigger} on ${this.tableName}
|
|
71
|
+
CREATE OR REPLACE FUNCTION ${functionName}(
|
|
72
|
+
${params}
|
|
73
|
+
) RETURNS VOID AS $$
|
|
74
|
+
BEGIN
|
|
75
|
+
${actionBlocks.join('\n\n')}
|
|
76
|
+
END;
|
|
77
|
+
$$ LANGUAGE plpgsql SECURITY DEFINER;`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generate SQL for a single action
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
_generateAction(action, ruleName, description) {
|
|
85
|
+
const comment = ` -- ${description}`;
|
|
86
|
+
|
|
87
|
+
switch (action.type) {
|
|
88
|
+
case 'create':
|
|
89
|
+
return this._generateCreateAction(action, comment);
|
|
90
|
+
|
|
91
|
+
case 'update':
|
|
92
|
+
return this._generateUpdateAction(action, comment);
|
|
93
|
+
|
|
94
|
+
case 'delete':
|
|
95
|
+
return this._generateDeleteAction(action, comment);
|
|
96
|
+
|
|
97
|
+
case 'validate':
|
|
98
|
+
return this._generateValidateAction(action, comment);
|
|
99
|
+
|
|
100
|
+
case 'execute':
|
|
101
|
+
return this._generateExecuteAction(action, comment);
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
console.warn('Unknown action type:', action.type);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Generate CREATE action
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
_generateCreateAction(action, comment) {
|
|
114
|
+
const entity = action.entity;
|
|
115
|
+
const data = action.data;
|
|
116
|
+
|
|
117
|
+
const fields = [];
|
|
118
|
+
const values = [];
|
|
119
|
+
|
|
120
|
+
for (const [field, value] of Object.entries(data)) {
|
|
121
|
+
fields.push(field);
|
|
122
|
+
values.push(this._resolveValue(value));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return `${comment}
|
|
126
|
+
INSERT INTO ${entity} (${fields.join(', ')})
|
|
127
|
+
VALUES (${values.join(', ')});`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate UPDATE action
|
|
132
|
+
* @private
|
|
133
|
+
*/
|
|
134
|
+
_generateUpdateAction(action, comment) {
|
|
135
|
+
const entity = action.entity;
|
|
136
|
+
const data = action.data;
|
|
137
|
+
const match = action.match;
|
|
138
|
+
|
|
139
|
+
const setClauses = [];
|
|
140
|
+
for (const [field, value] of Object.entries(data)) {
|
|
141
|
+
setClauses.push(`${field} = ${this._resolveValue(value)}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const whereClauses = [];
|
|
145
|
+
for (const [field, value] of Object.entries(match)) {
|
|
146
|
+
whereClauses.push(`${field} = ${this._resolveValue(value)}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return `${comment}
|
|
150
|
+
UPDATE ${entity}
|
|
151
|
+
SET ${setClauses.join(', ')}
|
|
152
|
+
WHERE ${whereClauses.join(' AND ')};`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Generate DELETE action
|
|
157
|
+
* @private
|
|
158
|
+
*/
|
|
159
|
+
_generateDeleteAction(action, comment) {
|
|
160
|
+
const entity = action.entity;
|
|
161
|
+
const match = action.match;
|
|
162
|
+
|
|
163
|
+
const whereClauses = [];
|
|
164
|
+
for (const [field, value] of Object.entries(match)) {
|
|
165
|
+
whereClauses.push(`${field} = ${this._resolveValue(value)}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return `${comment}
|
|
169
|
+
DELETE FROM ${entity}
|
|
170
|
+
WHERE ${whereClauses.join(' AND ')};`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate VALIDATE action
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
_generateValidateAction(action, comment) {
|
|
178
|
+
const functionName = action.function;
|
|
179
|
+
const params = action.params || {};
|
|
180
|
+
const errorMessage = action.error_message || 'Validation failed';
|
|
181
|
+
|
|
182
|
+
const paramList = [];
|
|
183
|
+
for (const [key, value] of Object.entries(params)) {
|
|
184
|
+
paramList.push(`${key} => ${this._resolveValue(value)}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const paramSQL = paramList.length > 0 ? paramList.join(', ') : '';
|
|
188
|
+
|
|
189
|
+
return `${comment}
|
|
190
|
+
IF NOT ${functionName}(${paramSQL}) THEN
|
|
191
|
+
RAISE EXCEPTION '${errorMessage}';
|
|
192
|
+
END IF;`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Generate EXECUTE action
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
_generateExecuteAction(action, comment) {
|
|
200
|
+
const functionName = action.function;
|
|
201
|
+
const params = action.params || {};
|
|
202
|
+
|
|
203
|
+
const paramList = [];
|
|
204
|
+
for (const [key, value] of Object.entries(params)) {
|
|
205
|
+
paramList.push(`${key} => ${this._resolveValue(value)}`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const paramSQL = paramList.length > 0 ? paramList.join(', ') : '';
|
|
209
|
+
|
|
210
|
+
return `${comment}
|
|
211
|
+
PERFORM ${functionName}(${paramSQL});`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolve a value (variable reference or literal)
|
|
216
|
+
* @private
|
|
217
|
+
*/
|
|
218
|
+
_resolveValue(value) {
|
|
219
|
+
if (typeof value !== 'string') {
|
|
220
|
+
// Number or other type
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Handle special variables
|
|
225
|
+
if (value.startsWith('@')) {
|
|
226
|
+
const varName = value.substring(1);
|
|
227
|
+
|
|
228
|
+
// Special keywords
|
|
229
|
+
switch (varName) {
|
|
230
|
+
case 'user_id':
|
|
231
|
+
return 'p_user_id';
|
|
232
|
+
|
|
233
|
+
case 'today':
|
|
234
|
+
return 'CURRENT_DATE';
|
|
235
|
+
|
|
236
|
+
case 'now':
|
|
237
|
+
return 'NOW()';
|
|
238
|
+
|
|
239
|
+
default:
|
|
240
|
+
// Field reference from record
|
|
241
|
+
return `(p_record->>'${varName}')`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// String literal
|
|
246
|
+
return `'${value}'`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Generate graph rule functions for an entity
|
|
252
|
+
* @param {string} tableName - Table name
|
|
253
|
+
* @param {Object} graphRules - Graph rules object
|
|
254
|
+
* @returns {string} SQL for graph rule functions
|
|
255
|
+
*/
|
|
256
|
+
export function generateGraphRuleFunctions(tableName, graphRules) {
|
|
257
|
+
const codegen = new GraphRulesCodegen(tableName, graphRules);
|
|
258
|
+
return codegen.generate();
|
|
259
|
+
}
|