dzql 0.5.33 → 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,299 +0,0 @@
1
- # Atomic Updates for Subscribables
2
-
3
- Atomic updates enable efficient real-time synchronization by sending only the changes (insert/update/delete) to subscribed clients, instead of re-querying and sending the entire document on every change.
4
-
5
- ## Overview
6
-
7
- ### Problem Solved
8
-
9
- Previously, when any data changed that affected a subscription, the server would:
10
- 1. Re-query the entire document using `get_<subscribable>()`
11
- 2. Send the complete document to the client
12
- 3. Client replaces its entire local state
13
-
14
- This approach has several problems:
15
- - **Network inefficiency**: Sends full product catalogue when one task template duration changes
16
- - **Database load**: Re-executes complex queries on every tiny change
17
- - **Client state loss**: Replaces entire local state, losing UI state (scroll position, expanded rows, etc.)
18
-
19
- ### Solution: Atomic Updates
20
-
21
- With atomic updates, the server:
22
- 1. Forwards the raw event (table, operation, primary key, data) directly to clients
23
- 2. Client applies the patch to their local copy of the document
24
- 3. Only changed data traverses the network
25
-
26
- Benefits:
27
- - **Efficient**: O(change size) instead of O(document size) per update
28
- - **Preserved state**: Client UI state remains intact
29
- - **Reduced database load**: No re-querying on every change
30
-
31
- ## How It Works
32
-
33
- ### 1. Subscribe: Initial Data + Schema
34
-
35
- When a client subscribes, they receive:
36
- - The full initial document (unchanged)
37
- - A **schema** that maps table names to document paths
38
-
39
- ```javascript
40
- const { data, schema, unsubscribe } = await ws.api.subscribe_venue_detail(
41
- { venue_id: 1 },
42
- (updated) => console.log('Updated:', updated)
43
- );
44
-
45
- // schema = {
46
- // root: 'venues',
47
- // paths: {
48
- // 'venues': '.', // Root entity
49
- // 'organisations': 'org', // FK expansion
50
- // 'sites': 'sites', // Child collection
51
- // 'packages': 'packages' // Child collection
52
- // }
53
- // }
54
- ```
55
-
56
- ### 2. On Changes: Atomic Events
57
-
58
- When data changes, instead of re-querying, the server sends a `subscription:event` message:
59
-
60
- ```json
61
- {
62
- "jsonrpc": "2.0",
63
- "method": "subscription:event",
64
- "params": {
65
- "subscription_id": "550e8400-e29b-41d4-a716-446655440000",
66
- "subscribable": "venue_detail",
67
- "event": {
68
- "table": "sites",
69
- "op": "update",
70
- "pk": { "id": 5 },
71
- "data": { "id": 5, "name": "Updated Site Name", "venue_id": 1 },
72
- "before": { "id": 5, "name": "Old Name", "venue_id": 1 }
73
- }
74
- }
75
- }
76
- ```
77
-
78
- ### 3. Client: Apply Patch
79
-
80
- The client uses the schema to locate where the change belongs in the document and applies it:
81
-
82
- - **insert**: Adds new item to the appropriate array
83
- - **update**: Finds item by primary key and merges changes
84
- - **delete**: Finds item by primary key and removes it
85
-
86
- The callback receives the updated local document, preserving any UI state.
87
-
88
- ## Scope Tables
89
-
90
- Each subscribable tracks which tables are "in scope" - tables that can affect the document. This enables an optimization: events from tables not in scope are immediately skipped, avoiding unnecessary `_affected_documents()` calls.
91
-
92
- ### Interpreted Mode (Runtime)
93
-
94
- In interpreted mode, scope tables are stored in the `dzql.subscribables` table:
95
-
96
- ```sql
97
- SELECT scope_tables FROM dzql.subscribables WHERE name = 'venue_detail';
98
- -- Returns: ['venues', 'organisations', 'sites', 'packages']
99
- ```
100
-
101
- Scope tables are automatically extracted when registering a subscribable:
102
-
103
- ```sql
104
- SELECT dzql.register_subscribable(
105
- 'venue_detail',
106
- '{"subscribe": [...]}'::jsonb,
107
- '{"venue_id": "int"}'::jsonb,
108
- 'venues', -- root_entity -> scope includes 'venues'
109
- '{
110
- "org": "organisations", -- scope includes 'organisations'
111
- "sites": {"entity": "sites", ...} -- scope includes 'sites'
112
- }'::jsonb
113
- );
114
-
115
- -- scope_tables automatically set to: ['venues', 'organisations', 'sites']
116
- ```
117
-
118
- ### Compiled Mode (Static SQL)
119
-
120
- In compiled mode, scope tables are embedded directly in the `get_<name>()` function return value. No `dzql.subscribables` table is required.
121
-
122
- The compiled function returns:
123
- ```json
124
- {
125
- "data": { ... },
126
- "schema": {
127
- "root": "organisations",
128
- "paths": {
129
- "organisations": ".",
130
- "products": "products"
131
- },
132
- "scopeTables": ["organisations", "products"]
133
- }
134
- }
135
- ```
136
-
137
- When a client subscribes:
138
- 1. Server calls `get_<name>(params, user_id)`
139
- 2. Server extracts the embedded schema (including `scopeTables`)
140
- 3. Server caches the metadata for event filtering
141
- 4. Client receives `data` and `schema` for client-side patching
142
-
143
- **Important**: The scope tables are only cached after the first subscribe call. This means:
144
- - The `dzql.subscribables` table is not needed for compiled deployments
145
- - Event filtering works correctly once at least one client has subscribed
146
- - If no clients have subscribed, events will still be processed (the scope check is skipped when cache is empty)
147
-
148
- ## Path Mapping
149
-
150
- The path mapping tells the client where each table's data lives in the document structure:
151
-
152
- | Table | Path | Meaning |
153
- |-------|------|---------|
154
- | `venues` | `.` | Root level |
155
- | `organisations` | `org` | `document.org` |
156
- | `sites` | `sites` | `document.sites[]` |
157
- | `allocations` | `packages.allocations` | `document.packages[].allocations[]` |
158
-
159
- ### Nested Relations
160
-
161
- For nested relations, paths are dot-separated:
162
-
163
- ```javascript
164
- relations = {
165
- packages: {
166
- entity: 'packages',
167
- filter: 'venue_id=$venue_id',
168
- include: {
169
- allocations: 'allocations'
170
- }
171
- }
172
- }
173
-
174
- // Results in paths:
175
- // 'packages' -> 'packages'
176
- // 'allocations' -> 'packages.allocations'
177
- ```
178
-
179
- ## Client-Side Patching
180
-
181
- The `WebSocketManager` automatically handles `subscription:event` messages:
182
-
183
- ```javascript
184
- // Internally, when subscription:event is received:
185
- applyAtomicUpdate(sub, event) {
186
- const { table, op, pk, data } = event;
187
- const path = sub.schema.paths[table];
188
-
189
- if (path === '.') {
190
- // Root entity update
191
- Object.assign(sub.localData[sub.schema.root], data);
192
- } else {
193
- // Relation update
194
- const arr = getArrayAtPath(sub.localData, path);
195
- if (op === 'insert') arr.push(data);
196
- if (op === 'update') {
197
- const idx = arr.findIndex(item => pkMatch(item, pk));
198
- if (idx !== -1) Object.assign(arr[idx], data);
199
- }
200
- if (op === 'delete') {
201
- const idx = arr.findIndex(item => pkMatch(item, pk));
202
- if (idx !== -1) arr.splice(idx, 1);
203
- }
204
- }
205
-
206
- // Trigger callback with patched document
207
- sub.callback(sub.localData);
208
- }
209
- ```
210
-
211
- ## Composite Primary Keys
212
-
213
- Atomic updates support composite primary keys:
214
-
215
- ```json
216
- {
217
- "pk": { "product_id": 1, "part_id": 2 }
218
- }
219
- ```
220
-
221
- The client matches all key fields when finding items to update or delete.
222
-
223
- ## Migration from Full Re-queries
224
-
225
- If you have existing subscribables, atomic updates are enabled automatically when:
226
- 1. The `scope_tables` column is populated (happens on `register_subscribable`)
227
- 2. The client receives the `schema` in the subscribe response
228
-
229
- No code changes required - the system is backward compatible.
230
-
231
- ## Debugging
232
-
233
- ### Check Schema (Compiled Mode)
234
-
235
- Subscribe and log the full schema including scopeTables:
236
-
237
- ```javascript
238
- const { data, schema } = await ws.api.subscribe_product_catalogue(
239
- { organisation_id: 1 },
240
- () => {}
241
- );
242
- console.log('Schema:', schema);
243
- // Expected: { root: 'organisations', paths: {...}, scopeTables: [...] }
244
- ```
245
-
246
- If `schema.root` is null or `schema.scopeTables` is missing, ensure you're using dzql >= 0.5.10 and have recompiled your subscribable functions.
247
-
248
- ### Check Scope Tables (Interpreted Mode)
249
-
250
- ```sql
251
- SELECT name, scope_tables
252
- FROM dzql.subscribables
253
- WHERE name = 'your_subscribable';
254
- ```
255
-
256
- ### Verify Path Mapping
257
-
258
- ```javascript
259
- const { schema } = await ws.api.subscribe_venue_detail(
260
- { venue_id: 1 },
261
- () => {}
262
- );
263
- console.log('Path mapping:', schema.paths);
264
- // Should map each table to its document path
265
- ```
266
-
267
- ### Server Logs
268
-
269
- Enable debug logging to see atomic events:
270
-
271
- ```bash
272
- LOG_CATEGORIES="notify:debug" bun run server
273
- ```
274
-
275
- You should see:
276
- ```
277
- Cached compiled metadata for product_catalogue: { scopeTables: ['organisations', 'products'] }
278
- product_catalogue: 1 param set(s) affected by products:update
279
- Sent atomic event to subscription abc123... (products:update)
280
- ```
281
-
282
- ### Common Issues
283
-
284
- 1. **`schema.root` is null**: Update to dzql >= 0.5.10 and recompile your subscribables
285
- 2. **No updates pushed**: Check that scopeTables includes the changed table
286
- 3. **Metadata not cached**: Ensure at least one client has subscribed before events occur
287
-
288
- ## Limitations
289
-
290
- 1. **Deeply nested updates**: For very deep nesting (3+ levels), paths become complex. Consider flattening your subscribable structure.
291
-
292
- 2. **Cascading deletes**: When a parent is deleted, the client may receive the parent delete before child deletes. The client handles missing parents gracefully.
293
-
294
- 3. **Permission changes**: If a user loses access mid-subscription, they may receive one final event before the subscription is terminated.
295
-
296
- ## See Also
297
-
298
- - [Subscriptions Guide](./subscriptions.md) - Full subscribable documentation
299
- - [Getting Started with Subscriptions](../getting-started/subscriptions-quick-start.md)