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.
Files changed (150) hide show
  1. package/.env.sample +28 -0
  2. package/compose.yml +28 -0
  3. package/dist/client/index.ts +1 -0
  4. package/dist/client/stores/useMyProfileStore.ts +114 -0
  5. package/dist/client/stores/useOrgDashboardStore.ts +131 -0
  6. package/dist/client/stores/useVenueDetailStore.ts +117 -0
  7. package/dist/client/ws.ts +716 -0
  8. package/dist/db/migrations/000_core.sql +92 -0
  9. package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
  10. package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
  11. package/dist/runtime/manifest.json +1562 -0
  12. package/docs/README.md +293 -36
  13. package/docs/feature-requests/applyPatch-bug-report.md +85 -0
  14. package/docs/feature-requests/connection-ready-profile.md +57 -0
  15. package/docs/feature-requests/hidden-bug-report.md +111 -0
  16. package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
  17. package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
  18. package/docs/feature-requests/todo.md +146 -0
  19. package/docs/for_ai.md +641 -0
  20. package/docs/project-setup.md +432 -0
  21. package/examples/blog.ts +50 -0
  22. package/examples/invalid.ts +18 -0
  23. package/examples/venues.js +485 -0
  24. package/package.json +23 -60
  25. package/src/cli/codegen/client.ts +99 -0
  26. package/src/cli/codegen/manifest.ts +95 -0
  27. package/src/cli/codegen/pinia.ts +174 -0
  28. package/src/cli/codegen/realtime.ts +58 -0
  29. package/src/cli/codegen/sql.ts +698 -0
  30. package/src/cli/codegen/subscribable_sql.ts +547 -0
  31. package/src/cli/codegen/subscribable_store.ts +184 -0
  32. package/src/cli/codegen/types.ts +142 -0
  33. package/src/cli/compiler/analyzer.ts +52 -0
  34. package/src/cli/compiler/graph_rules.ts +251 -0
  35. package/src/cli/compiler/ir.ts +233 -0
  36. package/src/cli/compiler/loader.ts +132 -0
  37. package/src/cli/compiler/permissions.ts +227 -0
  38. package/src/cli/index.ts +164 -0
  39. package/src/client/index.ts +1 -0
  40. package/src/client/ws.ts +286 -0
  41. package/src/create/.env.example +8 -0
  42. package/src/create/README.md +101 -0
  43. package/src/create/compose.yml +14 -0
  44. package/src/create/domain.ts +153 -0
  45. package/src/create/package.json +24 -0
  46. package/src/create/server.ts +18 -0
  47. package/src/create/setup.sh +11 -0
  48. package/src/create/tsconfig.json +15 -0
  49. package/src/runtime/auth.ts +39 -0
  50. package/src/runtime/db.ts +33 -0
  51. package/src/runtime/errors.ts +51 -0
  52. package/src/runtime/index.ts +98 -0
  53. package/src/runtime/js_functions.ts +63 -0
  54. package/src/runtime/manifest_loader.ts +29 -0
  55. package/src/runtime/namespace.ts +483 -0
  56. package/src/runtime/server.ts +87 -0
  57. package/src/runtime/ws.ts +197 -0
  58. package/src/shared/ir.ts +197 -0
  59. package/tests/client.test.ts +38 -0
  60. package/tests/codegen.test.ts +71 -0
  61. package/tests/compiler.test.ts +45 -0
  62. package/tests/graph_rules.test.ts +173 -0
  63. package/tests/integration/db.test.ts +174 -0
  64. package/tests/integration/e2e.test.ts +65 -0
  65. package/tests/integration/features.test.ts +922 -0
  66. package/tests/integration/full_stack.test.ts +262 -0
  67. package/tests/integration/setup.ts +45 -0
  68. package/tests/ir.test.ts +32 -0
  69. package/tests/namespace.test.ts +395 -0
  70. package/tests/permissions.test.ts +55 -0
  71. package/tests/pinia.test.ts +48 -0
  72. package/tests/realtime.test.ts +22 -0
  73. package/tests/runtime.test.ts +80 -0
  74. package/tests/subscribable_gen.test.ts +72 -0
  75. package/tests/subscribable_reactivity.test.ts +258 -0
  76. package/tests/venues_gen.test.ts +25 -0
  77. package/tsconfig.json +20 -0
  78. package/tsconfig.tsbuildinfo +1 -0
  79. package/README.md +0 -90
  80. package/bin/cli.js +0 -727
  81. package/docs/compiler/ADVANCED_FILTERS.md +0 -183
  82. package/docs/compiler/CODING_STANDARDS.md +0 -415
  83. package/docs/compiler/COMPARISON.md +0 -673
  84. package/docs/compiler/QUICKSTART.md +0 -326
  85. package/docs/compiler/README.md +0 -134
  86. package/docs/examples/README.md +0 -38
  87. package/docs/examples/blog.sql +0 -160
  88. package/docs/examples/venue-detail-simple.sql +0 -8
  89. package/docs/examples/venue-detail-subscribable.sql +0 -45
  90. package/docs/for-ai/claude-guide.md +0 -1210
  91. package/docs/getting-started/quickstart.md +0 -125
  92. package/docs/getting-started/subscriptions-quick-start.md +0 -203
  93. package/docs/getting-started/tutorial.md +0 -1104
  94. package/docs/guides/atomic-updates.md +0 -299
  95. package/docs/guides/client-stores.md +0 -730
  96. package/docs/guides/composite-primary-keys.md +0 -158
  97. package/docs/guides/custom-functions.md +0 -362
  98. package/docs/guides/drop-semantics.md +0 -554
  99. package/docs/guides/field-defaults.md +0 -240
  100. package/docs/guides/interpreter-vs-compiler.md +0 -237
  101. package/docs/guides/many-to-many.md +0 -929
  102. package/docs/guides/subscriptions.md +0 -537
  103. package/docs/reference/api.md +0 -1373
  104. package/docs/reference/client.md +0 -224
  105. package/src/client/stores/index.js +0 -8
  106. package/src/client/stores/useAppStore.js +0 -285
  107. package/src/client/stores/useWsStore.js +0 -289
  108. package/src/client/ws.js +0 -762
  109. package/src/compiler/cli/compile-example.js +0 -33
  110. package/src/compiler/cli/compile-subscribable.js +0 -43
  111. package/src/compiler/cli/debug-compile.js +0 -44
  112. package/src/compiler/cli/debug-parse.js +0 -26
  113. package/src/compiler/cli/debug-path-parser.js +0 -18
  114. package/src/compiler/cli/debug-subscribable-parser.js +0 -21
  115. package/src/compiler/cli/index.js +0 -174
  116. package/src/compiler/codegen/auth-codegen.js +0 -153
  117. package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
  118. package/src/compiler/codegen/graph-rules-codegen.js +0 -450
  119. package/src/compiler/codegen/notification-codegen.js +0 -232
  120. package/src/compiler/codegen/operation-codegen.js +0 -1382
  121. package/src/compiler/codegen/permission-codegen.js +0 -318
  122. package/src/compiler/codegen/subscribable-codegen.js +0 -827
  123. package/src/compiler/compiler.js +0 -371
  124. package/src/compiler/index.js +0 -11
  125. package/src/compiler/parser/entity-parser.js +0 -440
  126. package/src/compiler/parser/path-parser.js +0 -290
  127. package/src/compiler/parser/subscribable-parser.js +0 -244
  128. package/src/database/dzql-core.sql +0 -161
  129. package/src/database/migrations/001_schema.sql +0 -60
  130. package/src/database/migrations/002_functions.sql +0 -890
  131. package/src/database/migrations/003_operations.sql +0 -1135
  132. package/src/database/migrations/004_search.sql +0 -581
  133. package/src/database/migrations/005_entities.sql +0 -730
  134. package/src/database/migrations/006_auth.sql +0 -94
  135. package/src/database/migrations/007_events.sql +0 -133
  136. package/src/database/migrations/008_hello.sql +0 -18
  137. package/src/database/migrations/008a_meta.sql +0 -172
  138. package/src/database/migrations/009_subscriptions.sql +0 -240
  139. package/src/database/migrations/010_atomic_updates.sql +0 -157
  140. package/src/database/migrations/010_fix_m2m_events.sql +0 -94
  141. package/src/index.js +0 -40
  142. package/src/server/api.js +0 -9
  143. package/src/server/db.js +0 -442
  144. package/src/server/index.js +0 -317
  145. package/src/server/logger.js +0 -259
  146. package/src/server/mcp.js +0 -594
  147. package/src/server/meta-route.js +0 -251
  148. package/src/server/namespace.js +0 -292
  149. package/src/server/subscriptions.js +0 -351
  150. 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
- }