dzql 0.5.33 → 0.6.1

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 (142) 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 +309 -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 +653 -0
  20. package/docs/project-setup.md +456 -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 +166 -0
  39. package/src/client/index.ts +1 -0
  40. package/src/client/ws.ts +286 -0
  41. package/src/runtime/auth.ts +39 -0
  42. package/src/runtime/db.ts +33 -0
  43. package/src/runtime/errors.ts +51 -0
  44. package/src/runtime/index.ts +98 -0
  45. package/src/runtime/js_functions.ts +63 -0
  46. package/src/runtime/manifest_loader.ts +29 -0
  47. package/src/runtime/namespace.ts +483 -0
  48. package/src/runtime/server.ts +87 -0
  49. package/src/runtime/ws.ts +197 -0
  50. package/src/shared/ir.ts +197 -0
  51. package/tests/client.test.ts +38 -0
  52. package/tests/codegen.test.ts +71 -0
  53. package/tests/compiler.test.ts +45 -0
  54. package/tests/graph_rules.test.ts +173 -0
  55. package/tests/integration/db.test.ts +174 -0
  56. package/tests/integration/e2e.test.ts +65 -0
  57. package/tests/integration/features.test.ts +922 -0
  58. package/tests/integration/full_stack.test.ts +262 -0
  59. package/tests/integration/setup.ts +45 -0
  60. package/tests/ir.test.ts +32 -0
  61. package/tests/namespace.test.ts +395 -0
  62. package/tests/permissions.test.ts +55 -0
  63. package/tests/pinia.test.ts +48 -0
  64. package/tests/realtime.test.ts +22 -0
  65. package/tests/runtime.test.ts +80 -0
  66. package/tests/subscribable_gen.test.ts +72 -0
  67. package/tests/subscribable_reactivity.test.ts +258 -0
  68. package/tests/venues_gen.test.ts +25 -0
  69. package/tsconfig.json +20 -0
  70. package/tsconfig.tsbuildinfo +1 -0
  71. package/README.md +0 -90
  72. package/bin/cli.js +0 -727
  73. package/docs/compiler/ADVANCED_FILTERS.md +0 -183
  74. package/docs/compiler/CODING_STANDARDS.md +0 -415
  75. package/docs/compiler/COMPARISON.md +0 -673
  76. package/docs/compiler/QUICKSTART.md +0 -326
  77. package/docs/compiler/README.md +0 -134
  78. package/docs/examples/README.md +0 -38
  79. package/docs/examples/blog.sql +0 -160
  80. package/docs/examples/venue-detail-simple.sql +0 -8
  81. package/docs/examples/venue-detail-subscribable.sql +0 -45
  82. package/docs/for-ai/claude-guide.md +0 -1210
  83. package/docs/getting-started/quickstart.md +0 -125
  84. package/docs/getting-started/subscriptions-quick-start.md +0 -203
  85. package/docs/getting-started/tutorial.md +0 -1104
  86. package/docs/guides/atomic-updates.md +0 -299
  87. package/docs/guides/client-stores.md +0 -730
  88. package/docs/guides/composite-primary-keys.md +0 -158
  89. package/docs/guides/custom-functions.md +0 -362
  90. package/docs/guides/drop-semantics.md +0 -554
  91. package/docs/guides/field-defaults.md +0 -240
  92. package/docs/guides/interpreter-vs-compiler.md +0 -237
  93. package/docs/guides/many-to-many.md +0 -929
  94. package/docs/guides/subscriptions.md +0 -537
  95. package/docs/reference/api.md +0 -1373
  96. package/docs/reference/client.md +0 -224
  97. package/src/client/stores/index.js +0 -8
  98. package/src/client/stores/useAppStore.js +0 -285
  99. package/src/client/stores/useWsStore.js +0 -289
  100. package/src/client/ws.js +0 -762
  101. package/src/compiler/cli/compile-example.js +0 -33
  102. package/src/compiler/cli/compile-subscribable.js +0 -43
  103. package/src/compiler/cli/debug-compile.js +0 -44
  104. package/src/compiler/cli/debug-parse.js +0 -26
  105. package/src/compiler/cli/debug-path-parser.js +0 -18
  106. package/src/compiler/cli/debug-subscribable-parser.js +0 -21
  107. package/src/compiler/cli/index.js +0 -174
  108. package/src/compiler/codegen/auth-codegen.js +0 -153
  109. package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
  110. package/src/compiler/codegen/graph-rules-codegen.js +0 -450
  111. package/src/compiler/codegen/notification-codegen.js +0 -232
  112. package/src/compiler/codegen/operation-codegen.js +0 -1382
  113. package/src/compiler/codegen/permission-codegen.js +0 -318
  114. package/src/compiler/codegen/subscribable-codegen.js +0 -827
  115. package/src/compiler/compiler.js +0 -371
  116. package/src/compiler/index.js +0 -11
  117. package/src/compiler/parser/entity-parser.js +0 -440
  118. package/src/compiler/parser/path-parser.js +0 -290
  119. package/src/compiler/parser/subscribable-parser.js +0 -244
  120. package/src/database/dzql-core.sql +0 -161
  121. package/src/database/migrations/001_schema.sql +0 -60
  122. package/src/database/migrations/002_functions.sql +0 -890
  123. package/src/database/migrations/003_operations.sql +0 -1135
  124. package/src/database/migrations/004_search.sql +0 -581
  125. package/src/database/migrations/005_entities.sql +0 -730
  126. package/src/database/migrations/006_auth.sql +0 -94
  127. package/src/database/migrations/007_events.sql +0 -133
  128. package/src/database/migrations/008_hello.sql +0 -18
  129. package/src/database/migrations/008a_meta.sql +0 -172
  130. package/src/database/migrations/009_subscriptions.sql +0 -240
  131. package/src/database/migrations/010_atomic_updates.sql +0 -157
  132. package/src/database/migrations/010_fix_m2m_events.sql +0 -94
  133. package/src/index.js +0 -40
  134. package/src/server/api.js +0 -9
  135. package/src/server/db.js +0 -442
  136. package/src/server/index.js +0 -317
  137. package/src/server/logger.js +0 -259
  138. package/src/server/mcp.js +0 -594
  139. package/src/server/meta-route.js +0 -251
  140. package/src/server/namespace.js +0 -292
  141. package/src/server/subscriptions.js +0 -351
  142. package/src/server/ws.js +0 -573
@@ -1,537 +0,0 @@
1
- # Live Query Subscriptions
2
-
3
- Live Query Subscriptions (Pattern 1 from vision.md) enable clients to subscribe to denormalized documents and receive real-time updates when the underlying data changes.
4
-
5
- ## Overview
6
-
7
- ### Architecture Principles
8
-
9
- - **PostgreSQL-First**: All matching logic is compiled to PostgreSQL functions, not JavaScript
10
- - **In-Memory Registry**: Server holds active subscriptions in memory for performance
11
- - **Zero Runtime Interpretation**: All logic pre-compiled during deployment
12
- - **Denormalized Documents**: Subscribables combine data from multiple tables into client-friendly views
13
-
14
- ### How It Works
15
-
16
- 1. **Define Subscribable**: Register a subscribable with permissions, parameters, and relations
17
- 2. **Compile**: Generate three PostgreSQL functions:
18
- - `<name>_can_subscribe(user_id, params)` - Permission check
19
- - `get_<name>(params, user_id)` - Query function
20
- - `<name>_affected_documents(table, op, old, new)` - Change detection
21
- 3. **Subscribe**: Client calls `ws.api.subscribe_<name>(params, callback)`
22
- 4. **Update**: Database changes trigger NOTIFY → server forwards atomic events → client applies patches locally
23
-
24
- > **Note**: DZQL uses [Atomic Updates](./atomic-updates.md) for efficient real-time sync. Instead of re-querying the full document on every change, the server forwards the raw event and the client patches its local copy. This reduces network traffic and preserves client-side UI state.
25
-
26
- ## Quick Start
27
-
28
- ### 1. Define a Subscribable
29
-
30
- Create a SQL file with your subscribable definition:
31
-
32
- ```sql
33
- -- examples/venue-detail-subscribable.sql
34
- SELECT dzql.register_subscribable(
35
- 'venue_detail',
36
- '{"subscribe": ["@org_id->acts_for[org_id=$]{active}.user_id"]}'::jsonb,
37
- '{"venue_id": "int"}'::jsonb,
38
- 'venues',
39
- '{
40
- "org": "organisations",
41
- "sites": {
42
- "entity": "sites",
43
- "filter": "venue_id=$venue_id"
44
- }
45
- }'::jsonb
46
- );
47
- ```
48
-
49
- **Parameters:**
50
- - `name`: Identifier used in API calls (e.g., `venue_detail`)
51
- - `permission_paths`: Access control using path DSL
52
- - `param_schema`: Parameters required to subscribe (subscription key)
53
- - `root_entity`: Primary table
54
- - `relations`: Related entities to include in the document
55
-
56
- ### 2. Compile and Deploy
57
-
58
- ```bash
59
- # Compile subscribable to SQL functions
60
- bun packages/dzql/src/compiler/cli/compile-subscribable.js \
61
- examples/venue-detail-subscribable.sql \
62
- > /tmp/venue_detail.sql
63
-
64
- # Deploy to database
65
- psql $DATABASE_URL < /tmp/venue_detail.sql
66
- ```
67
-
68
- This generates three functions:
69
- - `venue_detail_can_subscribe(user_id, params)`
70
- - `get_venue_detail(params, user_id)`
71
- - `venue_detail_affected_documents(table, op, old, new)`
72
-
73
- ### 3. Client Usage
74
-
75
- ```javascript
76
- import { WebSocketManager } from '@dzql/client';
77
-
78
- const ws = new WebSocketManager('ws://localhost:3000/ws');
79
- await ws.connect();
80
-
81
- // Subscribe to venue updates
82
- const { data, subscription_id, unsubscribe } = await ws.api.subscribe_venue_detail(
83
- { venue_id: 123 },
84
- (updatedData) => {
85
- console.log('Venue updated:', updatedData);
86
- // updatedData = { id: 123, name: 'Venue Name', org: {...}, sites: [...] }
87
- }
88
- );
89
-
90
- // Initial data is returned immediately
91
- console.log('Initial venue data:', data);
92
-
93
- // Later: unsubscribe when done
94
- await unsubscribe();
95
- ```
96
-
97
- ## Subscribable Definition
98
-
99
- ### Permission Paths
100
-
101
- Control who can subscribe using the path DSL:
102
-
103
- ```javascript
104
- {
105
- "subscribe": [
106
- "@org_id->acts_for[org_id=$]{active}.user_id"
107
- ]
108
- }
109
- ```
110
-
111
- This allows users who:
112
- - Have an active `acts_for` relationship
113
- - Where `org_id` matches the venue's `org_id`
114
-
115
- Multiple paths can be provided for OR logic:
116
-
117
- ```javascript
118
- {
119
- "subscribe": [
120
- "@owner_id", // Direct owner
121
- "@org_id->acts_for[org_id=$]{active}.user_id" // OR org member
122
- ]
123
- }
124
- ```
125
-
126
- ### Parameter Schema
127
-
128
- Define the subscription key (what makes each subscription unique):
129
-
130
- ```javascript
131
- {
132
- "venue_id": "int"
133
- }
134
- ```
135
-
136
- Clients must provide these parameters when subscribing.
137
-
138
- ### Relations
139
-
140
- Include related data in the denormalized document:
141
-
142
- ```javascript
143
- {
144
- // Simple relation - include entire related record
145
- "org": "organisations",
146
-
147
- // Filtered relation - include sites filtered by venue_id
148
- "sites": {
149
- "entity": "sites",
150
- "filter": "venue_id=$venue_id"
151
- },
152
-
153
- // Nested relations
154
- "org": {
155
- "entity": "organisations",
156
- "relations": {
157
- "users": {
158
- "entity": "acts_for",
159
- "filter": "org_id=$org_id AND valid_to IS NULL"
160
- }
161
- }
162
- }
163
- }
164
- ```
165
-
166
- ## Generated Functions
167
-
168
- ### 1. Permission Check: `<name>_can_subscribe`
169
-
170
- ```sql
171
- CREATE FUNCTION venue_detail_can_subscribe(
172
- p_user_id INT,
173
- p_params JSONB
174
- ) RETURNS BOOLEAN;
175
- ```
176
-
177
- Returns `true` if the user can subscribe with the given parameters.
178
-
179
- Called automatically when client subscribes.
180
-
181
- ### 2. Query Function: `get_<name>`
182
-
183
- ```sql
184
- CREATE FUNCTION get_venue_detail(
185
- p_params JSONB,
186
- p_user_id INT
187
- ) RETURNS JSONB;
188
- ```
189
-
190
- Builds the denormalized document from the database.
191
-
192
- Called:
193
- - Initially when client subscribes (returns first data)
194
- - After each change that affects the subscription (returns updated data)
195
-
196
- ### 3. Change Detection: `<name>_affected_documents`
197
-
198
- ```sql
199
- CREATE FUNCTION venue_detail_affected_documents(
200
- p_table TEXT,
201
- p_op TEXT,
202
- p_old JSONB,
203
- p_new JSONB
204
- ) RETURNS JSONB[];
205
- ```
206
-
207
- Determines which subscription instances are affected by a database change.
208
-
209
- Returns array of parameter sets (subscription keys) that need updates.
210
-
211
- Example:
212
- ```sql
213
- -- When venue 123 is updated
214
- SELECT venue_detail_affected_documents(
215
- 'venues',
216
- 'update',
217
- '{"id": 123, "name": "Old"}'::jsonb,
218
- '{"id": 123, "name": "New"}'::jsonb
219
- );
220
- -- Returns: [{"venue_id": 123}]
221
- ```
222
-
223
- ## Server Integration
224
-
225
- The server automatically:
226
- 1. Handles `subscribe_<name>` and `unsubscribe_<name>` RPC calls
227
- 2. Maintains in-memory subscription registry
228
- 3. Listens to database NOTIFY events
229
- 4. Calls `_affected_documents()` to find affected subscriptions
230
- 5. Re-executes `get_<name>()` to get fresh data
231
- 6. Sends updates to subscribed clients
232
-
233
- No server code changes needed when adding new subscribables!
234
-
235
- ## WebSocket Protocol
236
-
237
- ### Subscribe
238
-
239
- ```json
240
- {
241
- "jsonrpc": "2.0",
242
- "id": 1,
243
- "method": "subscribe_venue_detail",
244
- "params": {
245
- "venue_id": 123
246
- }
247
- }
248
- ```
249
-
250
- **Response:**
251
- ```json
252
- {
253
- "jsonrpc": "2.0",
254
- "id": 1,
255
- "result": {
256
- "subscription_id": "550e8400-e29b-41d4-a716-446655440000",
257
- "data": {
258
- "id": 123,
259
- "name": "Venue Name",
260
- "org": { "id": 1, "name": "Organization" },
261
- "sites": [...]
262
- }
263
- }
264
- }
265
- ```
266
-
267
- ### Updates
268
-
269
- When data changes, server sends:
270
-
271
- ```json
272
- {
273
- "jsonrpc": "2.0",
274
- "method": "subscription:update",
275
- "params": {
276
- "subscription_id": "550e8400-e29b-41d4-a716-446655440000",
277
- "subscribable": "venue_detail",
278
- "data": {
279
- "id": 123,
280
- "name": "Updated Venue Name",
281
- "org": { "id": 1, "name": "Organization" },
282
- "sites": [...]
283
- }
284
- }
285
- }
286
- ```
287
-
288
- Client's callback is invoked automatically with the new data.
289
-
290
- ### Unsubscribe
291
-
292
- ```json
293
- {
294
- "jsonrpc": "2.0",
295
- "id": 2,
296
- "method": "unsubscribe_venue_detail",
297
- "params": {
298
- "venue_id": 123
299
- }
300
- }
301
- ```
302
-
303
- Or call the returned `unsubscribe()` function:
304
-
305
- ```javascript
306
- const { unsubscribe } = await ws.api.subscribe_venue_detail(...);
307
- await unsubscribe();
308
- ```
309
-
310
- ## Advanced Examples
311
-
312
- ### User Profile with Nested Relations
313
-
314
- ```sql
315
- SELECT dzql.register_subscribable(
316
- 'user_profile',
317
- '{"subscribe": ["@id"]}'::jsonb,
318
- '{"user_id": "int"}'::jsonb,
319
- 'users',
320
- '{
321
- "organisations": {
322
- "entity": "acts_for",
323
- "filter": "user_id=$user_id AND valid_to IS NULL",
324
- "relations": {
325
- "org": "organisations"
326
- }
327
- },
328
- "permissions": {
329
- "entity": "user_permissions",
330
- "filter": "user_id=$user_id"
331
- }
332
- }'::jsonb
333
- );
334
- ```
335
-
336
- ### Multi-Parameter Subscription
337
-
338
- ```sql
339
- SELECT dzql.register_subscribable(
340
- 'booking_detail',
341
- '{"subscribe": ["@user_id", "@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id"]}'::jsonb,
342
- '{"booking_id": "int", "venue_id": "int"}'::jsonb,
343
- 'bookings',
344
- '{
345
- "venue": "venues",
346
- "customer": "users",
347
- "items": {
348
- "entity": "booking_items",
349
- "filter": "booking_id=$booking_id"
350
- }
351
- }'::jsonb
352
- );
353
- ```
354
-
355
- ## Performance Considerations
356
-
357
- ### In-Memory Registry
358
-
359
- Active subscriptions are stored in-memory on the server:
360
- - Fast lookup without database queries
361
- - Automatically cleaned up when WebSocket closes
362
- - Scale by adding more server instances (subscriptions are connection-local)
363
-
364
- ### Change Detection
365
-
366
- The `_affected_documents()` function runs for every database change:
367
- - Keep logic simple and indexed
368
- - Return only truly affected subscriptions
369
- - Use early returns for unrelated tables
370
-
371
- Example optimization:
372
-
373
- ```sql
374
- CREATE FUNCTION my_subscribable_affected_documents(...)
375
- RETURNS JSONB[] AS $$
376
- BEGIN
377
- -- Early return for unrelated tables
378
- IF p_table NOT IN ('venues', 'sites') THEN
379
- RETURN ARRAY[]::JSONB[];
380
- END IF;
381
-
382
- -- Use indexed fields
383
- IF p_table = 'venues' THEN
384
- RETURN ARRAY[jsonb_build_object('venue_id', (p_new->>'id')::int)];
385
- END IF;
386
-
387
- -- ... more logic
388
- END;
389
- $$ LANGUAGE plpgsql STABLE;
390
- ```
391
-
392
- ### Query Efficiency
393
-
394
- The `get_<name>()` function runs on every update:
395
- - Use JOINs and indexes appropriately
396
- - Consider materialized views for complex aggregations
397
- - Limit relation depth to avoid N+1 queries
398
-
399
- ## Debugging
400
-
401
- ### Check Active Subscriptions
402
-
403
- ```javascript
404
- // Server-side (in development)
405
- import { getAllSubscriptions, getStats } from './server/subscriptions.js';
406
-
407
- console.log('Active subscriptions:', getAllSubscriptions());
408
- console.log('Stats:', getStats());
409
- ```
410
-
411
- ### Test Functions Manually
412
-
413
- ```sql
414
- -- Test permission check
415
- SELECT venue_detail_can_subscribe(1, '{"venue_id": 123}'::jsonb);
416
-
417
- -- Test query
418
- SELECT get_venue_detail('{"venue_id": 123}'::jsonb, 1);
419
-
420
- -- Test change detection
421
- SELECT venue_detail_affected_documents(
422
- 'venues',
423
- 'update',
424
- '{"id": 123}'::jsonb,
425
- '{"id": 123, "name": "New Name"}'::jsonb
426
- );
427
- ```
428
-
429
- ### Enable Debug Logging
430
-
431
- ```javascript
432
- // Server logs subscription events
433
- import { wsLogger } from './server/logger.js';
434
-
435
- wsLogger.level = 'debug'; // See all subscription operations
436
- ```
437
-
438
- ## Migration Guide
439
-
440
- ### From Polling
441
-
442
- Before:
443
- ```javascript
444
- // Poll every 5 seconds
445
- setInterval(async () => {
446
- const venue = await fetch(`/api/venues/${venueId}`).then(r => r.json());
447
- updateUI(venue);
448
- }, 5000);
449
- ```
450
-
451
- After:
452
- ```javascript
453
- // Real-time updates
454
- const { data, unsubscribe } = await ws.api.subscribe_venue_detail(
455
- { venue_id: venueId },
456
- (venue) => updateUI(venue)
457
- );
458
-
459
- updateUI(data); // Initial data
460
- ```
461
-
462
- ### From Pattern 2 (Need to Know)
463
-
464
- Pattern 2 notifications tell you "something changed":
465
- ```javascript
466
- ws.onBroadcast('venues:updated', (params) => {
467
- // Manually fetch updated data
468
- refetchVenue(params.id);
469
- });
470
- ```
471
-
472
- Pattern 1 subscriptions give you the data:
473
- ```javascript
474
- ws.api.subscribe_venue_detail(
475
- { venue_id: 123 },
476
- (venue) => {
477
- // Fresh data automatically provided
478
- updateUI(venue);
479
- }
480
- );
481
- ```
482
-
483
- ## Best Practices
484
-
485
- 1. **One subscribable per use case**: Create focused subscribables for specific UI needs
486
- 2. **Minimize relations**: Only include data the client actually needs
487
- 3. **Use specific parameters**: Subscription keys should be precise (e.g., `venue_id`, not `org_id`)
488
- 4. **Clean up subscriptions**: Always unsubscribe when component unmounts
489
- 5. **Handle reconnection**: Client automatically re-authenticates, but may need to re-subscribe
490
- 6. **Test permissions thoroughly**: Use path DSL carefully to prevent unauthorized access
491
-
492
- ## Troubleshooting
493
-
494
- ### Subscription Not Receiving Updates
495
-
496
- 1. Check that `_affected_documents()` returns correct parameter sets:
497
- ```sql
498
- SELECT my_subscribable_affected_documents('table', 'update', old, new);
499
- ```
500
-
501
- 2. Verify subscription is registered:
502
- ```javascript
503
- console.log('Subscriptions:', ws.subscriptions.size);
504
- ```
505
-
506
- 3. Confirm WebSocket is connected:
507
- ```javascript
508
- console.log('Connected:', ws.socket?.readyState === 1);
509
- ```
510
-
511
- ### Permission Denied
512
-
513
- 1. Test permission function directly:
514
- ```sql
515
- SELECT my_subscribable_can_subscribe(user_id, params);
516
- ```
517
-
518
- 2. Check path DSL syntax in subscribable definition
519
-
520
- 3. Verify user has required relationships (e.g., `acts_for` records)
521
-
522
- ### Compilation Errors
523
-
524
- 1. Validate JSON syntax in subscribable definition
525
- 2. Check that all referenced tables exist
526
- 3. Ensure parameter names match between schema and filters
527
- 4. Test parser separately:
528
- ```bash
529
- bun packages/dzql/tests/subscriptions/test-subscribable-parse.js
530
- ```
531
-
532
- ## See Also
533
-
534
- - [Vision Document](../../../../vision.md) - Architecture overview and patterns
535
- - [Permission Paths](../../../../docs/architecture/PERMISSIONS.md) - Permission path DSL syntax
536
- - [Subscription Architecture](../../../../docs/architecture/SUBSCRIPTIONS_STRATEGY.md) - Design decisions
537
- - [Compiler Documentation](../compiler/) - Code generation and compilation guide