dzql 0.5.32 → 0.6.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.
- package/.env.sample +28 -0
- package/compose.yml +28 -0
- package/dist/client/index.ts +1 -0
- package/dist/client/stores/useMyProfileStore.ts +114 -0
- package/dist/client/stores/useOrgDashboardStore.ts +131 -0
- package/dist/client/stores/useVenueDetailStore.ts +117 -0
- package/dist/client/ws.ts +716 -0
- package/dist/db/migrations/000_core.sql +92 -0
- package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
- package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
- package/dist/runtime/manifest.json +1562 -0
- package/docs/README.md +293 -36
- package/docs/feature-requests/applyPatch-bug-report.md +85 -0
- package/docs/feature-requests/connection-ready-profile.md +57 -0
- package/docs/feature-requests/hidden-bug-report.md +111 -0
- package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
- package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
- package/docs/feature-requests/todo.md +146 -0
- package/docs/for_ai.md +641 -0
- package/docs/project-setup.md +432 -0
- package/examples/blog.ts +50 -0
- package/examples/invalid.ts +18 -0
- package/examples/venues.js +485 -0
- package/package.json +23 -60
- package/src/cli/codegen/client.ts +99 -0
- package/src/cli/codegen/manifest.ts +95 -0
- package/src/cli/codegen/pinia.ts +174 -0
- package/src/cli/codegen/realtime.ts +58 -0
- package/src/cli/codegen/sql.ts +698 -0
- package/src/cli/codegen/subscribable_sql.ts +547 -0
- package/src/cli/codegen/subscribable_store.ts +184 -0
- package/src/cli/codegen/types.ts +142 -0
- package/src/cli/compiler/analyzer.ts +52 -0
- package/src/cli/compiler/graph_rules.ts +251 -0
- package/src/cli/compiler/ir.ts +233 -0
- package/src/cli/compiler/loader.ts +132 -0
- package/src/cli/compiler/permissions.ts +227 -0
- package/src/cli/index.ts +164 -0
- package/src/client/index.ts +1 -0
- package/src/client/ws.ts +286 -0
- package/src/create/.env.example +8 -0
- package/src/create/README.md +101 -0
- package/src/create/compose.yml +14 -0
- package/src/create/domain.ts +153 -0
- package/src/create/package.json +24 -0
- package/src/create/server.ts +18 -0
- package/src/create/setup.sh +11 -0
- package/src/create/tsconfig.json +15 -0
- package/src/runtime/auth.ts +39 -0
- package/src/runtime/db.ts +33 -0
- package/src/runtime/errors.ts +51 -0
- package/src/runtime/index.ts +98 -0
- package/src/runtime/js_functions.ts +63 -0
- package/src/runtime/manifest_loader.ts +29 -0
- package/src/runtime/namespace.ts +483 -0
- package/src/runtime/server.ts +87 -0
- package/src/runtime/ws.ts +197 -0
- package/src/shared/ir.ts +197 -0
- package/tests/client.test.ts +38 -0
- package/tests/codegen.test.ts +71 -0
- package/tests/compiler.test.ts +45 -0
- package/tests/graph_rules.test.ts +173 -0
- package/tests/integration/db.test.ts +174 -0
- package/tests/integration/e2e.test.ts +65 -0
- package/tests/integration/features.test.ts +922 -0
- package/tests/integration/full_stack.test.ts +262 -0
- package/tests/integration/setup.ts +45 -0
- package/tests/ir.test.ts +32 -0
- package/tests/namespace.test.ts +395 -0
- package/tests/permissions.test.ts +55 -0
- package/tests/pinia.test.ts +48 -0
- package/tests/realtime.test.ts +22 -0
- package/tests/runtime.test.ts +80 -0
- package/tests/subscribable_gen.test.ts +72 -0
- package/tests/subscribable_reactivity.test.ts +258 -0
- package/tests/venues_gen.test.ts +25 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/README.md +0 -90
- package/bin/cli.js +0 -727
- package/docs/compiler/ADVANCED_FILTERS.md +0 -183
- package/docs/compiler/CODING_STANDARDS.md +0 -415
- package/docs/compiler/COMPARISON.md +0 -673
- package/docs/compiler/QUICKSTART.md +0 -326
- package/docs/compiler/README.md +0 -134
- package/docs/examples/README.md +0 -38
- package/docs/examples/blog.sql +0 -160
- package/docs/examples/venue-detail-simple.sql +0 -8
- package/docs/examples/venue-detail-subscribable.sql +0 -45
- package/docs/for-ai/claude-guide.md +0 -1210
- package/docs/getting-started/quickstart.md +0 -125
- package/docs/getting-started/subscriptions-quick-start.md +0 -203
- package/docs/getting-started/tutorial.md +0 -1104
- package/docs/guides/atomic-updates.md +0 -299
- package/docs/guides/client-stores.md +0 -730
- package/docs/guides/composite-primary-keys.md +0 -158
- package/docs/guides/custom-functions.md +0 -362
- package/docs/guides/drop-semantics.md +0 -554
- package/docs/guides/field-defaults.md +0 -240
- package/docs/guides/interpreter-vs-compiler.md +0 -237
- package/docs/guides/many-to-many.md +0 -929
- package/docs/guides/subscriptions.md +0 -537
- package/docs/reference/api.md +0 -1373
- package/docs/reference/client.md +0 -224
- package/src/client/stores/index.js +0 -8
- package/src/client/stores/useAppStore.js +0 -285
- package/src/client/stores/useWsStore.js +0 -289
- package/src/client/ws.js +0 -762
- package/src/compiler/cli/compile-example.js +0 -33
- package/src/compiler/cli/compile-subscribable.js +0 -43
- package/src/compiler/cli/debug-compile.js +0 -44
- package/src/compiler/cli/debug-parse.js +0 -26
- package/src/compiler/cli/debug-path-parser.js +0 -18
- package/src/compiler/cli/debug-subscribable-parser.js +0 -21
- package/src/compiler/cli/index.js +0 -174
- package/src/compiler/codegen/auth-codegen.js +0 -153
- package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
- package/src/compiler/codegen/graph-rules-codegen.js +0 -450
- package/src/compiler/codegen/notification-codegen.js +0 -232
- package/src/compiler/codegen/operation-codegen.js +0 -1382
- package/src/compiler/codegen/permission-codegen.js +0 -318
- package/src/compiler/codegen/subscribable-codegen.js +0 -827
- package/src/compiler/compiler.js +0 -371
- package/src/compiler/index.js +0 -11
- package/src/compiler/parser/entity-parser.js +0 -440
- package/src/compiler/parser/path-parser.js +0 -290
- package/src/compiler/parser/subscribable-parser.js +0 -244
- package/src/database/dzql-core.sql +0 -161
- package/src/database/migrations/001_schema.sql +0 -60
- package/src/database/migrations/002_functions.sql +0 -890
- package/src/database/migrations/003_operations.sql +0 -1135
- package/src/database/migrations/004_search.sql +0 -581
- package/src/database/migrations/005_entities.sql +0 -730
- package/src/database/migrations/006_auth.sql +0 -94
- package/src/database/migrations/007_events.sql +0 -133
- package/src/database/migrations/008_hello.sql +0 -18
- package/src/database/migrations/008a_meta.sql +0 -172
- package/src/database/migrations/009_subscriptions.sql +0 -240
- package/src/database/migrations/010_atomic_updates.sql +0 -157
- package/src/database/migrations/010_fix_m2m_events.sql +0 -94
- package/src/index.js +0 -40
- package/src/server/api.js +0 -9
- package/src/server/db.js +0 -442
- package/src/server/index.js +0 -317
- package/src/server/logger.js +0 -259
- package/src/server/mcp.js +0 -594
- package/src/server/meta-route.js +0 -251
- package/src/server/namespace.js +0 -292
- package/src/server/subscriptions.js +0 -351
- package/src/server/ws.js +0 -573
|
@@ -1,440 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Entity Definition Parser
|
|
3
|
-
* Parses entity registration calls and extracts configuration
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export class EntityParser {
|
|
7
|
-
/**
|
|
8
|
-
* Parse a dzql.register_entity() call from SQL
|
|
9
|
-
* @param {string} sql - SQL containing register_entity call
|
|
10
|
-
* @param {number} startOffset - Optional starting position in SQL
|
|
11
|
-
* @returns {Object} Parsed entity configuration with custom functions
|
|
12
|
-
*/
|
|
13
|
-
parseFromSQL(sql, startOffset = 0) {
|
|
14
|
-
// Extract the register_entity call
|
|
15
|
-
const searchSql = sql.substring(startOffset);
|
|
16
|
-
const registerMatch = searchSql.match(/dzql\.register_entity\s*\(([\s\S]*?)\);/i);
|
|
17
|
-
if (!registerMatch) {
|
|
18
|
-
throw new Error('No register_entity call found in SQL');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const params = this._parseParameters(registerMatch[1]);
|
|
22
|
-
const config = this._buildEntityConfig(params);
|
|
23
|
-
|
|
24
|
-
// Extract custom functions after this register_entity call
|
|
25
|
-
const registerEndPos = startOffset + registerMatch.index + registerMatch[0].length;
|
|
26
|
-
config.customFunctions = this._extractCustomFunctions(sql, registerEndPos);
|
|
27
|
-
|
|
28
|
-
return config;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Parse parameters from register_entity call
|
|
33
|
-
* @private
|
|
34
|
-
*/
|
|
35
|
-
_parseParameters(paramsString) {
|
|
36
|
-
// Split by commas that are not inside quotes, parentheses, or brackets
|
|
37
|
-
const params = [];
|
|
38
|
-
let currentParam = '';
|
|
39
|
-
let depth = 0;
|
|
40
|
-
let inString = false;
|
|
41
|
-
let stringChar = null;
|
|
42
|
-
|
|
43
|
-
for (let i = 0; i < paramsString.length; i++) {
|
|
44
|
-
const char = paramsString[i];
|
|
45
|
-
const prevChar = i > 0 ? paramsString[i - 1] : '';
|
|
46
|
-
|
|
47
|
-
if ((char === "'" || char === '"') && prevChar !== '\\') {
|
|
48
|
-
if (!inString) {
|
|
49
|
-
inString = true;
|
|
50
|
-
stringChar = char;
|
|
51
|
-
} else if (char === stringChar) {
|
|
52
|
-
inString = false;
|
|
53
|
-
stringChar = null;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!inString) {
|
|
58
|
-
if (char === '(' || char === '{' || char === '[') depth++;
|
|
59
|
-
if (char === ')' || char === '}' || char === ']') depth--;
|
|
60
|
-
|
|
61
|
-
if (char === ',' && depth === 0) {
|
|
62
|
-
params.push(currentParam.trim());
|
|
63
|
-
currentParam = '';
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
currentParam += char;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (currentParam.trim()) {
|
|
72
|
-
params.push(currentParam.trim());
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Strip SQL comments (-- ...) from each parameter
|
|
76
|
-
return params.map(param => {
|
|
77
|
-
// Remove everything after -- (SQL line comment)
|
|
78
|
-
return param.split('\n').map(line => {
|
|
79
|
-
const commentIndex = line.indexOf('--');
|
|
80
|
-
if (commentIndex !== -1) {
|
|
81
|
-
return line.substring(0, commentIndex);
|
|
82
|
-
}
|
|
83
|
-
return line;
|
|
84
|
-
}).join('\n').trim();
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Build entity configuration from parsed parameters
|
|
90
|
-
* @private
|
|
91
|
-
*/
|
|
92
|
-
_buildEntityConfig(params) {
|
|
93
|
-
const graphRules = params[8] ? this._parseJSON(params[8]) : {};
|
|
94
|
-
|
|
95
|
-
// Extract many_to_many from graph_rules if present
|
|
96
|
-
const manyToMany = graphRules.many_to_many || {};
|
|
97
|
-
|
|
98
|
-
// Extract primary_key from graph_rules if present (defaults to ['id'])
|
|
99
|
-
const primaryKey = graphRules.primary_key || ['id'];
|
|
100
|
-
|
|
101
|
-
const config = {
|
|
102
|
-
tableName: this._cleanString(params[0]),
|
|
103
|
-
labelField: this._cleanString(params[1]),
|
|
104
|
-
searchableFields: this._parseArray(params[2]),
|
|
105
|
-
fkIncludes: params[3] ? this._parseJSON(params[3]) : {},
|
|
106
|
-
softDelete: params[4] ? this._parseBoolean(params[4]) : false,
|
|
107
|
-
temporalFields: params[5] ? this._parseJSON(params[5]) : {},
|
|
108
|
-
notificationPaths: params[6] ? this._parseJSON(params[6]) : {},
|
|
109
|
-
permissionPaths: params[7] ? this._parseJSON(params[7]) : {},
|
|
110
|
-
graphRules: graphRules,
|
|
111
|
-
fieldDefaults: params[9] ? this._parseJSON(params[9]) : {},
|
|
112
|
-
manyToMany: manyToMany,
|
|
113
|
-
primaryKey: Array.isArray(primaryKey) ? primaryKey : [primaryKey]
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
return config;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Clean a string parameter (remove quotes)
|
|
121
|
-
* @private
|
|
122
|
-
*/
|
|
123
|
-
_cleanString(str) {
|
|
124
|
-
if (!str) return '';
|
|
125
|
-
// Remove SQL comments first, then outer quotes
|
|
126
|
-
let cleaned = str.replace(/--[^\n]*/g, ''); // Remove SQL comments
|
|
127
|
-
cleaned = cleaned.trim(); // Remove whitespace
|
|
128
|
-
cleaned = cleaned.replace(/^['"]|['"]$/g, ''); // Remove outer quotes
|
|
129
|
-
return cleaned;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Parse an array parameter
|
|
134
|
-
* @private
|
|
135
|
-
*/
|
|
136
|
-
_parseArray(str) {
|
|
137
|
-
if (!str || str === 'array[]::text[]') return [];
|
|
138
|
-
|
|
139
|
-
// Handle array['item1', 'item2'] format
|
|
140
|
-
// Use greedy match to get everything up to the last ] before optional ::type
|
|
141
|
-
const match = str.match(/array\[(.*)\](?:::.*)?$/i);
|
|
142
|
-
if (!match) return [];
|
|
143
|
-
|
|
144
|
-
// Split on commas that are not inside brackets or quotes
|
|
145
|
-
const items = [];
|
|
146
|
-
let current = '';
|
|
147
|
-
let depth = 0;
|
|
148
|
-
let inString = false;
|
|
149
|
-
let stringChar = null;
|
|
150
|
-
|
|
151
|
-
for (let i = 0; i < match[1].length; i++) {
|
|
152
|
-
const char = match[1][i];
|
|
153
|
-
const prev = i > 0 ? match[1][i - 1] : '';
|
|
154
|
-
|
|
155
|
-
if ((char === "'" || char === '"') && prev !== '\\') {
|
|
156
|
-
if (!inString) {
|
|
157
|
-
inString = true;
|
|
158
|
-
stringChar = char;
|
|
159
|
-
} else if (char === stringChar) {
|
|
160
|
-
inString = false;
|
|
161
|
-
stringChar = null;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (!inString) {
|
|
166
|
-
if (char === '[') depth++;
|
|
167
|
-
if (char === ']') depth--;
|
|
168
|
-
|
|
169
|
-
if (char === ',' && depth === 0) {
|
|
170
|
-
items.push(current.trim().replace(/^['"]|['"]$/g, ''));
|
|
171
|
-
current = '';
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
current += char;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (current.trim()) {
|
|
180
|
-
items.push(current.trim().replace(/^['"]|['"]$/g, ''));
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return items.filter(item => item.length > 0);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Parse a JSONB parameter
|
|
188
|
-
* @private
|
|
189
|
-
*/
|
|
190
|
-
_parseJSON(str) {
|
|
191
|
-
if (!str || str === '{}' || str === "'{}'") return {};
|
|
192
|
-
|
|
193
|
-
// Strip SQL comments before parsing
|
|
194
|
-
str = str.replace(/--[^\n]*/g, '').trim();
|
|
195
|
-
|
|
196
|
-
// Handle jsonb_build_object(...) calls
|
|
197
|
-
if (str.includes('jsonb_build_object')) {
|
|
198
|
-
return this._parseJSONBuildObject(str);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Handle JSON string literals
|
|
202
|
-
if (str.startsWith("'") && str.endsWith("'")) {
|
|
203
|
-
try {
|
|
204
|
-
// Remove outer quotes and unescape SQL quotes
|
|
205
|
-
const jsonStr = str.slice(1, -1).replace(/''/g, "'");
|
|
206
|
-
return JSON.parse(jsonStr);
|
|
207
|
-
} catch (e) {
|
|
208
|
-
console.warn('Failed to parse JSON:', str.substring(0, 100) + '...', e);
|
|
209
|
-
return {};
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return {};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Parse jsonb_build_object(...) calls recursively
|
|
218
|
-
* @private
|
|
219
|
-
*/
|
|
220
|
-
_parseJSONBuildObject(str) {
|
|
221
|
-
// Find the matching closing parenthesis for jsonb_build_object(
|
|
222
|
-
// Cannot use greedy regex as it matches the LAST ) not the matching one
|
|
223
|
-
const startMatch = str.match(/jsonb_build_object\s*\(/i);
|
|
224
|
-
if (!startMatch) return {};
|
|
225
|
-
|
|
226
|
-
const startIndex = startMatch.index + startMatch[0].length;
|
|
227
|
-
let depth = 1;
|
|
228
|
-
let inString = false;
|
|
229
|
-
let stringChar = null;
|
|
230
|
-
let endIndex = startIndex;
|
|
231
|
-
|
|
232
|
-
for (let i = startIndex; i < str.length && depth > 0; i++) {
|
|
233
|
-
const char = str[i];
|
|
234
|
-
const prev = i > 0 ? str[i - 1] : '';
|
|
235
|
-
|
|
236
|
-
if ((char === "'" || char === '"') && prev !== '\\') {
|
|
237
|
-
if (!inString) {
|
|
238
|
-
inString = true;
|
|
239
|
-
stringChar = char;
|
|
240
|
-
} else if (char === stringChar) {
|
|
241
|
-
inString = false;
|
|
242
|
-
stringChar = null;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (!inString) {
|
|
247
|
-
if (char === '(') depth++;
|
|
248
|
-
if (char === ')') depth--;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
endIndex = i;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const content = str.substring(startIndex, endIndex);
|
|
255
|
-
const params = this._parseParameters(content);
|
|
256
|
-
const result = {};
|
|
257
|
-
|
|
258
|
-
// Process key-value pairs
|
|
259
|
-
for (let i = 0; i < params.length; i += 2) {
|
|
260
|
-
if (i + 1 >= params.length) break;
|
|
261
|
-
|
|
262
|
-
const key = this._cleanString(params[i]);
|
|
263
|
-
const value = params[i + 1];
|
|
264
|
-
|
|
265
|
-
// Handle nested jsonb_build_object
|
|
266
|
-
if (value.includes('jsonb_build_object')) {
|
|
267
|
-
result[key] = this._parseJSONBuildObject(value);
|
|
268
|
-
}
|
|
269
|
-
// Handle jsonb_build_array
|
|
270
|
-
else if (value.includes('jsonb_build_array')) {
|
|
271
|
-
result[key] = this._parseJSONBuildArray(value);
|
|
272
|
-
}
|
|
273
|
-
// Handle array literal
|
|
274
|
-
else if (value.startsWith('array[')) {
|
|
275
|
-
result[key] = this._parseArray(value);
|
|
276
|
-
}
|
|
277
|
-
// Handle simple values
|
|
278
|
-
else {
|
|
279
|
-
const cleanValue = this._cleanString(value);
|
|
280
|
-
result[key] = cleanValue;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
return result;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Parse jsonb_build_array(...) calls
|
|
289
|
-
* @private
|
|
290
|
-
*/
|
|
291
|
-
_parseJSONBuildArray(str) {
|
|
292
|
-
// Find the matching closing parenthesis for jsonb_build_array(
|
|
293
|
-
// Cannot use greedy regex as it matches the LAST ) not the matching one
|
|
294
|
-
const startMatch = str.match(/jsonb_build_array\s*\(/i);
|
|
295
|
-
if (!startMatch) return [];
|
|
296
|
-
|
|
297
|
-
const startIndex = startMatch.index + startMatch[0].length;
|
|
298
|
-
let depth = 1;
|
|
299
|
-
let inString = false;
|
|
300
|
-
let stringChar = null;
|
|
301
|
-
let endIndex = startIndex;
|
|
302
|
-
|
|
303
|
-
for (let i = startIndex; i < str.length && depth > 0; i++) {
|
|
304
|
-
const char = str[i];
|
|
305
|
-
const prev = i > 0 ? str[i - 1] : '';
|
|
306
|
-
|
|
307
|
-
if ((char === "'" || char === '"') && prev !== '\\') {
|
|
308
|
-
if (!inString) {
|
|
309
|
-
inString = true;
|
|
310
|
-
stringChar = char;
|
|
311
|
-
} else if (char === stringChar) {
|
|
312
|
-
inString = false;
|
|
313
|
-
stringChar = null;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (!inString) {
|
|
318
|
-
if (char === '(') depth++;
|
|
319
|
-
if (char === ')') depth--;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
endIndex = i;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const content = str.substring(startIndex, endIndex);
|
|
326
|
-
const params = this._parseParameters(content);
|
|
327
|
-
|
|
328
|
-
return params.map(param => {
|
|
329
|
-
if (param.includes('jsonb_build_object')) {
|
|
330
|
-
return this._parseJSONBuildObject(param);
|
|
331
|
-
}
|
|
332
|
-
return this._cleanString(param);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* Parse boolean value
|
|
338
|
-
* @private
|
|
339
|
-
*/
|
|
340
|
-
_parseBoolean(str) {
|
|
341
|
-
const cleaned = str.trim().toLowerCase();
|
|
342
|
-
return cleaned === 'true' || cleaned === 't';
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Extract custom functions defined after register_entity() call
|
|
347
|
-
* @private
|
|
348
|
-
* @param {string} sql - Full SQL content
|
|
349
|
-
* @param {number} startPos - Position after register_entity call
|
|
350
|
-
* @returns {Array<string>} Array of custom function SQL statements
|
|
351
|
-
*/
|
|
352
|
-
_extractCustomFunctions(sql, startPos) {
|
|
353
|
-
// Find the next register_entity call or end of file
|
|
354
|
-
const nextEntityMatch = sql.substring(startPos).match(/dzql\.register_entity\s*\(/i);
|
|
355
|
-
const endPos = nextEntityMatch ? startPos + nextEntityMatch.index : sql.length;
|
|
356
|
-
|
|
357
|
-
// Extract the SQL between this entity and the next
|
|
358
|
-
const customSql = sql.substring(startPos, endPos).trim();
|
|
359
|
-
if (!customSql) return [];
|
|
360
|
-
|
|
361
|
-
const functions = [];
|
|
362
|
-
|
|
363
|
-
// Extract CREATE [OR REPLACE] FUNCTION statements
|
|
364
|
-
// Match from CREATE to the final semicolon of the function (including $$ delimiters)
|
|
365
|
-
const functionPattern = /CREATE\s+(?:OR\s+REPLACE\s+)?FUNCTION\s+[\s\S]*?(?:\$\$|\$[A-Za-z_][A-Za-z0-9_]*\$)\s*(?:LANGUAGE|;)[\s\S]*?;/gi;
|
|
366
|
-
let match;
|
|
367
|
-
while ((match = functionPattern.exec(customSql)) !== null) {
|
|
368
|
-
functions.push(match[0].trim());
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Extract INSERT INTO dzql.registry statements
|
|
372
|
-
const registryPattern = /INSERT\s+INTO\s+dzql\.registry\s+[\s\S]*?;/gi;
|
|
373
|
-
while ((match = registryPattern.exec(customSql)) !== null) {
|
|
374
|
-
functions.push(match[0].trim());
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Extract SELECT dzql.register_function() calls
|
|
378
|
-
const registerFunctionPattern = /SELECT\s+dzql\.register_function\s*\([\s\S]*?\)\s*;/gi;
|
|
379
|
-
while ((match = registerFunctionPattern.exec(customSql)) !== null) {
|
|
380
|
-
functions.push(match[0].trim());
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return functions;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/**
|
|
387
|
-
* Parse entity definition from JS object (for programmatic use)
|
|
388
|
-
* @param {Object} entity - Entity definition object
|
|
389
|
-
* @returns {Object} Normalized entity configuration
|
|
390
|
-
*/
|
|
391
|
-
parseFromObject(entity) {
|
|
392
|
-
const graphRules = entity.graphRules || {};
|
|
393
|
-
const manyToMany = entity.manyToMany || graphRules.many_to_many || {};
|
|
394
|
-
// Primary key can be specified directly on entity or in graphRules (defaults to ['id'])
|
|
395
|
-
const primaryKey = entity.primaryKey || graphRules.primary_key || ['id'];
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
tableName: entity.tableName || entity.table,
|
|
399
|
-
labelField: entity.labelField || 'name',
|
|
400
|
-
searchableFields: entity.searchableFields || [],
|
|
401
|
-
fkIncludes: entity.fkIncludes || {},
|
|
402
|
-
softDelete: entity.softDelete || false,
|
|
403
|
-
temporalFields: entity.temporalFields || {},
|
|
404
|
-
notificationPaths: entity.notificationPaths || {},
|
|
405
|
-
permissionPaths: entity.permissionPaths || {},
|
|
406
|
-
graphRules: graphRules,
|
|
407
|
-
fieldDefaults: entity.fieldDefaults || {},
|
|
408
|
-
manyToMany: manyToMany,
|
|
409
|
-
primaryKey: Array.isArray(primaryKey) ? primaryKey : [primaryKey],
|
|
410
|
-
customFunctions: entity.customFunctions || []
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Parse all entities from a SQL file
|
|
417
|
-
* @param {string} sql - SQL file content
|
|
418
|
-
* @returns {Array} Array of parsed entity configurations
|
|
419
|
-
*/
|
|
420
|
-
export function parseEntitiesFromSQL(sql) {
|
|
421
|
-
const parser = new EntityParser();
|
|
422
|
-
const entities = [];
|
|
423
|
-
|
|
424
|
-
// Find all register_entity calls with their positions
|
|
425
|
-
const registerPattern = /dzql\.register_entity\s*\(/gi;
|
|
426
|
-
let match;
|
|
427
|
-
let currentPos = 0;
|
|
428
|
-
|
|
429
|
-
while ((match = registerPattern.exec(sql)) !== null) {
|
|
430
|
-
try {
|
|
431
|
-
const entity = parser.parseFromSQL(sql, match.index);
|
|
432
|
-
entities.push(entity);
|
|
433
|
-
currentPos = match.index + 1; // Move past this match
|
|
434
|
-
} catch (error) {
|
|
435
|
-
console.warn('Failed to parse entity:', error.message);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
return entities;
|
|
440
|
-
}
|