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,351 +0,0 @@
1
- /**
2
- * Subscription Manager for Live Query Subscriptions
3
- * Manages in-memory subscription registry and matching
4
- */
5
-
6
- import crypto from 'crypto';
7
- import { wsLogger } from './logger.js';
8
-
9
- /**
10
- * In-memory subscription registry
11
- * Structure: subscription_id -> { subscribable, user_id, connection_id, params }
12
- */
13
- const subscriptions = new Map();
14
-
15
- /**
16
- * Track subscriptions by connection for cleanup
17
- * Structure: connection_id -> Set<subscription_id>
18
- */
19
- const connectionSubscriptions = new Map();
20
-
21
- /**
22
- * Cache for subscribable metadata (scope tables, path mappings)
23
- * Structure: subscribable_name -> { scopeTables, pathMapping, rootEntity, relations }
24
- */
25
- const subscribableMetadataCache = new Map();
26
-
27
- /**
28
- * Register a new subscription
29
- * @param {string} subscribableName - Name of the subscribable
30
- * @param {number} userId - User ID
31
- * @param {string} connectionId - WebSocket connection ID
32
- * @param {object} params - Subscription parameters
33
- * @returns {string} - Subscription ID
34
- */
35
- export function registerSubscription(subscribableName, userId, connectionId, params) {
36
- const subscriptionId = crypto.randomUUID();
37
-
38
- // Store subscription
39
- subscriptions.set(subscriptionId, {
40
- subscribable: subscribableName,
41
- user_id: userId,
42
- connection_id: connectionId,
43
- params,
44
- created_at: new Date()
45
- });
46
-
47
- // Track by connection
48
- if (!connectionSubscriptions.has(connectionId)) {
49
- connectionSubscriptions.set(connectionId, new Set());
50
- }
51
- connectionSubscriptions.get(connectionId).add(subscriptionId);
52
-
53
- wsLogger.debug(`Subscription registered: ${subscriptionId.slice(0, 8)}... (${subscribableName})`, {
54
- user_id: userId,
55
- params
56
- });
57
-
58
- return subscriptionId;
59
- }
60
-
61
- /**
62
- * Unregister a subscription
63
- * @param {string} subscriptionId - Subscription ID to remove
64
- * @returns {boolean} - True if subscription was found and removed
65
- */
66
- export function unregisterSubscription(subscriptionId) {
67
- const sub = subscriptions.get(subscriptionId);
68
-
69
- if (!sub) {
70
- wsLogger.debug(`Subscription not found: ${subscriptionId}`);
71
- return false;
72
- }
73
-
74
- // Remove from connection tracking
75
- const connSubs = connectionSubscriptions.get(sub.connection_id);
76
- if (connSubs) {
77
- connSubs.delete(subscriptionId);
78
- if (connSubs.size === 0) {
79
- connectionSubscriptions.delete(sub.connection_id);
80
- }
81
- }
82
-
83
- // Remove subscription
84
- subscriptions.delete(subscriptionId);
85
-
86
- wsLogger.debug(`Subscription removed: ${subscriptionId.slice(0, 8)}...`);
87
- return true;
88
- }
89
-
90
- /**
91
- * Unregister subscription by params
92
- * Useful for unsubscribe_* methods that specify params instead of subscription ID
93
- * @param {string} subscribableName - Name of the subscribable
94
- * @param {string} connectionId - WebSocket connection ID
95
- * @param {object} params - Subscription parameters to match
96
- * @returns {boolean} - True if subscription was found and removed
97
- */
98
- export function unregisterSubscriptionByParams(subscribableName, connectionId, params) {
99
- const paramsStr = JSON.stringify(params);
100
-
101
- // Find matching subscription
102
- for (const [subId, sub] of subscriptions.entries()) {
103
- if (
104
- sub.subscribable === subscribableName &&
105
- sub.connection_id === connectionId &&
106
- JSON.stringify(sub.params) === paramsStr
107
- ) {
108
- return unregisterSubscription(subId);
109
- }
110
- }
111
-
112
- wsLogger.debug(`No matching subscription found for ${subscribableName} with params`, params);
113
- return false;
114
- }
115
-
116
- /**
117
- * Remove all subscriptions for a connection
118
- * Called when WebSocket connection closes
119
- * @param {string} connectionId - Connection ID
120
- * @returns {number} - Number of subscriptions removed
121
- */
122
- export function removeConnectionSubscriptions(connectionId) {
123
- const subIds = connectionSubscriptions.get(connectionId);
124
-
125
- if (!subIds) {
126
- return 0;
127
- }
128
-
129
- let count = 0;
130
- for (const subId of subIds) {
131
- if (subscriptions.delete(subId)) {
132
- count++;
133
- }
134
- }
135
-
136
- connectionSubscriptions.delete(connectionId);
137
-
138
- if (count > 0) {
139
- wsLogger.info(`Removed ${count} subscription(s) for connection ${connectionId.slice(0, 8)}...`);
140
- }
141
-
142
- return count;
143
- }
144
-
145
- /**
146
- * Get all subscriptions for a specific subscribable
147
- * @param {string} subscribableName - Name of the subscribable
148
- * @returns {Array} - Array of {subscriptionId, subscription} objects
149
- */
150
- export function getSubscriptionsByName(subscribableName) {
151
- const result = [];
152
-
153
- for (const [subId, sub] of subscriptions.entries()) {
154
- if (sub.subscribable === subscribableName) {
155
- result.push({ subscriptionId: subId, ...sub });
156
- }
157
- }
158
-
159
- return result;
160
- }
161
-
162
- /**
163
- * Get all subscriptions grouped by subscribable name
164
- * @returns {Map<string, Array>} - Map of subscribable name to array of subscriptions
165
- */
166
- export function getSubscriptionsBySubscribable() {
167
- const grouped = new Map();
168
-
169
- for (const [subId, sub] of subscriptions.entries()) {
170
- if (!grouped.has(sub.subscribable)) {
171
- grouped.set(sub.subscribable, []);
172
- }
173
- grouped.get(sub.subscribable).push({ subscriptionId: subId, ...sub });
174
- }
175
-
176
- return grouped;
177
- }
178
-
179
- /**
180
- * Check if params match (deep equality)
181
- * @param {object} a - First params object
182
- * @param {object} b - Second params object
183
- * @returns {boolean} - True if params match
184
- */
185
- export function paramsMatch(a, b) {
186
- return JSON.stringify(a) === JSON.stringify(b);
187
- }
188
-
189
- /**
190
- * Get subscription statistics
191
- * @returns {object} - Stats object
192
- */
193
- export function getStats() {
194
- return {
195
- total_subscriptions: subscriptions.size,
196
- active_connections: connectionSubscriptions.size,
197
- subscriptions_by_subscribable: Array.from(getSubscriptionsBySubscribable().entries()).map(
198
- ([name, subs]) => ({
199
- name,
200
- count: subs.length
201
- })
202
- )
203
- };
204
- }
205
-
206
- /**
207
- * Get all active subscriptions (for debugging)
208
- * @returns {Array} - Array of all subscriptions
209
- */
210
- export function getAllSubscriptions() {
211
- return Array.from(subscriptions.entries()).map(([id, sub]) => ({
212
- id,
213
- ...sub
214
- }));
215
- }
216
-
217
- /**
218
- * Get subscribable metadata (scope tables, path mapping) with caching
219
- * @param {string} subscribableName - Name of the subscribable
220
- * @param {function} sql - Database query function
221
- * @returns {Promise<{scopeTables: string[], pathMapping: object, rootEntity: string, relations: object}>}
222
- */
223
- export async function getSubscribableMetadata(subscribableName, sql) {
224
- // Check cache first (compiled mode populates this from embedded schema)
225
- if (subscribableMetadataCache.has(subscribableName)) {
226
- return subscribableMetadataCache.get(subscribableName);
227
- }
228
-
229
- // Interpreted mode: fetch from database table
230
- try {
231
- const result = await sql`
232
- SELECT scope_tables, root_entity, relations
233
- FROM dzql.subscribables
234
- WHERE name = ${subscribableName}
235
- `;
236
-
237
- if (result && result.length > 0) {
238
- const { scope_tables, root_entity, relations } = result[0];
239
-
240
- // Build path mapping from relations
241
- const pathMapping = buildPathMappingFromRelations(root_entity, relations);
242
-
243
- const metadata = {
244
- scopeTables: scope_tables || [],
245
- pathMapping,
246
- rootEntity: root_entity,
247
- relations: relations || {}
248
- };
249
-
250
- // Cache for future use
251
- subscribableMetadataCache.set(subscribableName, metadata);
252
-
253
- wsLogger.debug(`Cached metadata for subscribable ${subscribableName}:`, {
254
- scopeTables: metadata.scopeTables,
255
- pathMapping: metadata.pathMapping
256
- });
257
-
258
- return metadata;
259
- }
260
- } catch (e) {
261
- // Table doesn't exist or query failed
262
- wsLogger.debug(`Failed to fetch metadata for ${subscribableName}: ${e.message}`);
263
- }
264
-
265
- // Return empty metadata if nothing found (compiled mode without prior subscribe)
266
- const emptyMetadata = {
267
- scopeTables: [],
268
- pathMapping: {},
269
- rootEntity: null,
270
- relations: {}
271
- };
272
- return emptyMetadata;
273
- }
274
-
275
- /**
276
- * Build path mapping from relations configuration
277
- * Maps table names to their document path for client-side patching
278
- * @param {string} rootEntity - Root table name
279
- * @param {object} relations - Relations configuration
280
- * @returns {object} - Map of table name -> document path
281
- */
282
- function buildPathMappingFromRelations(rootEntity, relations) {
283
- const paths = {};
284
-
285
- // Root entity maps to top level
286
- if (rootEntity) {
287
- paths[rootEntity] = '.';
288
- }
289
-
290
- const buildPaths = (rels, parentPath = '') => {
291
- for (const [relName, relConfig] of Object.entries(rels || {})) {
292
- const entity = typeof relConfig === 'string' ? relConfig : relConfig?.entity;
293
- const currentPath = parentPath ? `${parentPath}.${relName}` : relName;
294
-
295
- if (entity) {
296
- paths[entity] = currentPath;
297
- }
298
-
299
- // Handle nested relations
300
- if (typeof relConfig === 'object' && relConfig !== null) {
301
- if (relConfig.include) {
302
- buildPaths(relConfig.include, currentPath);
303
- }
304
- if (relConfig.relations) {
305
- buildPaths(relConfig.relations, currentPath);
306
- }
307
- }
308
- }
309
- };
310
-
311
- buildPaths(relations);
312
- return paths;
313
- }
314
-
315
- /**
316
- * Clear the subscribable metadata cache
317
- * Called when subscribables are reregistered or updated
318
- * @param {string} [subscribableName] - Optional: clear specific subscribable, or all if not provided
319
- */
320
- export function clearSubscribableMetadataCache(subscribableName = null) {
321
- if (subscribableName) {
322
- subscribableMetadataCache.delete(subscribableName);
323
- wsLogger.debug(`Cleared metadata cache for ${subscribableName}`);
324
- } else {
325
- subscribableMetadataCache.clear();
326
- wsLogger.debug('Cleared all subscribable metadata cache');
327
- }
328
- }
329
-
330
- /**
331
- * Cache subscribable metadata (used by compiled mode to store embedded schema)
332
- * @param {string} subscribableName - Name of the subscribable
333
- * @param {object} metadata - Metadata to cache
334
- */
335
- export function cacheSubscribableMetadata(subscribableName, metadata) {
336
- subscribableMetadataCache.set(subscribableName, metadata);
337
- wsLogger.debug(`Cached compiled metadata for ${subscribableName}:`, {
338
- scopeTables: metadata.scopeTables
339
- });
340
- }
341
-
342
- /**
343
- * Get scope tables for a subscribable (convenience function)
344
- * @param {string} subscribableName - Name of the subscribable
345
- * @param {function} sql - Database query function
346
- * @returns {Promise<string[]>} - Array of table names in scope
347
- */
348
- export async function getSubscribableScopeTables(subscribableName, sql) {
349
- const metadata = await getSubscribableMetadata(subscribableName, sql);
350
- return metadata.scopeTables;
351
- }