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,318 +0,0 @@
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
- * Supports multi-hop paths like: @product_id->products.organisation_id->acts_for[organisation_id=$].user_id
175
- * @private
176
- */
177
- _generateTraversalCheck(ast) {
178
- const steps = ast.steps;
179
-
180
- // First step should be the source field reference
181
- if (steps.length === 0 || steps[0].type !== 'field_ref') {
182
- return 'false';
183
- }
184
-
185
- const sourceField = steps[0].field;
186
-
187
- // Collect all table_ref steps (these are the hops)
188
- const tableSteps = steps.filter(s => s.type === 'table_ref');
189
-
190
- if (tableSteps.length === 0) {
191
- return 'false';
192
- }
193
-
194
- // Build the value expression that resolves through intermediate tables
195
- // Start with the record's source field
196
- let valueExpr = `(p_record->>'${sourceField}')::int`;
197
-
198
- // Process intermediate hops (all but the last table_ref)
199
- // Each intermediate hop needs a subquery to resolve to the next field
200
- for (let i = 0; i < tableSteps.length - 1; i++) {
201
- const hop = tableSteps[i];
202
- const table = hop.table;
203
- const targetField = hop.targetField;
204
-
205
- if (!targetField) {
206
- // If no target field specified, assume 'id' for the lookup
207
- // This shouldn't normally happen in well-formed paths
208
- continue;
209
- }
210
-
211
- // Build subquery: (SELECT targetField FROM table WHERE id = previousValue)
212
- valueExpr = `(SELECT ${targetField} FROM ${table} WHERE id = ${valueExpr})`;
213
- }
214
-
215
- // The last table_ref is where we do the EXISTS check
216
- const finalStep = tableSteps[tableSteps.length - 1];
217
- const targetTable = finalStep.table;
218
- const targetField = finalStep.targetField;
219
- const filters = finalStep.filter || [];
220
- const temporal = finalStep.temporal || false;
221
-
222
- // Build WHERE conditions for the final EXISTS query
223
- const conditions = [];
224
-
225
- // Add filter conditions
226
- for (const filter of filters) {
227
- if (filter.operator === '=' && filter.value.type === 'param') {
228
- // field=$ means match the resolved value from the path
229
- conditions.push(`${targetTable}.${filter.field} = ${valueExpr}`);
230
- } else if (filter.operator === '=') {
231
- const value = this._formatValue(filter.value);
232
- conditions.push(`${targetTable}.${filter.field} = ${value}`);
233
- }
234
- }
235
-
236
- // Add temporal condition
237
- if (temporal) {
238
- conditions.push(`${targetTable}.valid_from <= NOW()`);
239
- conditions.push(`(${targetTable}.valid_to > NOW() OR ${targetTable}.valid_to IS NULL)`);
240
- }
241
-
242
- // Add user_id check (final target)
243
- if (targetField) {
244
- conditions.push(`${targetTable}.${targetField} = p_user_id`);
245
- }
246
-
247
- // Build EXISTS query
248
- const whereClause = conditions.length > 0
249
- ? 'WHERE ' + conditions.join('\n AND ')
250
- : '';
251
-
252
- return `EXISTS (
253
- SELECT 1 FROM ${targetTable}
254
- ${whereClause}
255
- )`;
256
- }
257
-
258
- /**
259
- * Generate filter condition SQL
260
- * @private
261
- */
262
- _generateFilterCondition(condition, tableAlias) {
263
- const field = `${tableAlias}.${condition.field}`;
264
- const value = this._formatValue(condition.value);
265
-
266
- switch (condition.operator) {
267
- case '=':
268
- if (condition.value.type === 'param') {
269
- // Special case: field=$ means use the record's value
270
- return `${field} = (p_record->>'${condition.field}')::int`;
271
- }
272
- return `${field} = ${value}`;
273
-
274
- default:
275
- return `${field} ${condition.operator} ${value}`;
276
- }
277
- }
278
-
279
- /**
280
- * Format a value for SQL
281
- * @private
282
- */
283
- _formatValue(value) {
284
- switch (value.type) {
285
- case 'literal':
286
- return `'${value.value}'`;
287
- case 'number':
288
- return value.value;
289
- case 'field':
290
- return `(p_record->>'${value.value}')`;
291
- case 'param':
292
- return '?'; // Will be replaced by caller
293
- default:
294
- return 'NULL';
295
- }
296
- }
297
-
298
- /**
299
- * Generate dot path check (less common)
300
- * @private
301
- */
302
- _generateDotPathCheck(ast) {
303
- // For now, treat as a field reference to the last field
304
- const lastField = ast.fields[ast.fields.length - 1];
305
- return `(p_record->>'${lastField}')::int = p_user_id`;
306
- }
307
- }
308
-
309
- /**
310
- * Generate permission check functions for an entity
311
- * @param {string} tableName - Table name
312
- * @param {Object} permissionPaths - Permission paths object
313
- * @returns {string} SQL for permission functions
314
- */
315
- export function generatePermissionFunctions(tableName, permissionPaths) {
316
- const codegen = new PermissionCodegen(tableName, permissionPaths);
317
- return codegen.generate();
318
- }