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
@@ -0,0 +1,485 @@
1
+ // venues-domain.js - v2 format
2
+ // Complete domain definition: all tables, all config
3
+
4
+ export const entities = {
5
+
6
+ // === Users ===
7
+ users: {
8
+ schema: {
9
+ id: 'serial PRIMARY KEY',
10
+ name: 'text NOT NULL',
11
+ email: 'text UNIQUE NOT NULL',
12
+ password_hash: 'text NOT NULL',
13
+ created_at: 'timestamptz DEFAULT now()'
14
+ },
15
+ label: 'name',
16
+ searchable: ['name', 'email'],
17
+ hidden: ['password_hash'],
18
+ permissions: {
19
+ view: [],
20
+ create: [],
21
+ update: ['@id'],
22
+ delete: ['@id']
23
+ }
24
+ },
25
+
26
+ // === Organisations ===
27
+ organisations: {
28
+ schema: {
29
+ id: 'serial PRIMARY KEY',
30
+ name: 'text UNIQUE NOT NULL',
31
+ description: 'text'
32
+ },
33
+ label: 'name',
34
+ searchable: ['name', 'description'],
35
+ permissions: {
36
+ view: [],
37
+ create: [],
38
+ update: ['@id->acts_for[org_id=$]{active}.user_id'],
39
+ delete: ['@id->acts_for[org_id=$]{active}.user_id']
40
+ },
41
+ graphRules: {
42
+ on_create: {
43
+ establish_ownership: {
44
+ description: 'Creator becomes owner',
45
+ actions: [
46
+ {
47
+ type: 'create',
48
+ entity: 'acts_for',
49
+ data: {
50
+ user_id: '@user_id',
51
+ org_id: '@id',
52
+ valid_from: '@today'
53
+ }
54
+ }
55
+ ]
56
+ }
57
+ },
58
+ on_delete: {
59
+ cleanup_relationships: {
60
+ description: 'Cascading delete for related entities',
61
+ actions: [
62
+ { type: 'delete', target: 'acts_for', params: { org_id: '@id' } },
63
+ { type: 'delete', target: 'venues', params: { org_id: '@id' } },
64
+ { type: 'update', target: 'packages', params: { sponsor_org_id: '@id' }, data: { sponsor_org_id: null } } // SET NULL example
65
+ ]
66
+ }
67
+ }
68
+ }
69
+ },
70
+
71
+ // === Acts For (temporal, composite PK) ===
72
+ acts_for: {
73
+ schema: {
74
+ user_id: 'int NOT NULL REFERENCES users(id)',
75
+ org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
76
+ valid_from: 'date NOT NULL DEFAULT current_date',
77
+ valid_to: 'date',
78
+ active: 'boolean DEFAULT true' // Added for permission check compatibility
79
+ },
80
+ primaryKey: ['user_id', 'org_id', 'valid_from'],
81
+ label: 'org_id',
82
+ searchable: ['org_id', 'user_id'],
83
+ includes: {
84
+ user: 'users',
85
+ org: 'organisations'
86
+ },
87
+ temporal: {
88
+ validFrom: 'valid_from',
89
+ validTo: 'valid_to'
90
+ },
91
+ permissions: {}
92
+ },
93
+
94
+ // === Venues ===
95
+ venues: {
96
+ schema: {
97
+ id: 'serial PRIMARY KEY',
98
+ org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
99
+ name: 'text UNIQUE NOT NULL',
100
+ address: 'text NOT NULL',
101
+ description: 'text'
102
+ },
103
+ label: 'name',
104
+ searchable: ['name', 'address', 'description'],
105
+ includes: {
106
+ org: 'organisations',
107
+ sites: 'sites'
108
+ },
109
+ permissions: {
110
+ view: [],
111
+ create: ['@org_id->acts_for[org_id=$]{active}.user_id'],
112
+ update: ['@org_id->acts_for[org_id=$]{active}.user_id'],
113
+ delete: ['@org_id->acts_for[org_id=$]{active}.user_id']
114
+ },
115
+ notifications: {
116
+ ownership: ['@org_id->acts_for[org_id=$]{active}.user_id']
117
+ }
118
+ },
119
+
120
+ // === Sites ===
121
+ sites: {
122
+ schema: {
123
+ id: 'serial PRIMARY KEY',
124
+ venue_id: 'int NOT NULL REFERENCES venues(id)',
125
+ name: 'text NOT NULL',
126
+ description: 'text'
127
+ },
128
+ label: 'name',
129
+ searchable: ['name', 'description'],
130
+ includes: {
131
+ venue: 'venues'
132
+ },
133
+ permissions: {
134
+ view: [],
135
+ create: ['@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id'],
136
+ update: ['@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id'],
137
+ delete: ['@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id']
138
+ },
139
+ notifications: {
140
+ ownership: ['@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id']
141
+ }
142
+ },
143
+
144
+ // === Products (with soft delete and field defaults) ===
145
+ products: {
146
+ schema: {
147
+ id: 'serial PRIMARY KEY',
148
+ org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
149
+ name: 'text UNIQUE NOT NULL',
150
+ description: 'text',
151
+ price: 'decimal(10, 2) NOT NULL DEFAULT 0.00',
152
+ created_by: 'int REFERENCES users(id)',
153
+ created_at: 'timestamptz',
154
+ deleted_at: 'timestamptz' // For soft delete
155
+ },
156
+ label: 'name',
157
+ searchable: ['name', 'description'],
158
+ includes: {
159
+ org: 'organisations'
160
+ },
161
+ // Soft delete - sets deleted_at instead of DELETE
162
+ softDelete: true,
163
+ // Auto-populate on INSERT
164
+ fieldDefaults: {
165
+ created_by: '@user_id',
166
+ created_at: '@now'
167
+ },
168
+ permissions: {
169
+ view: [],
170
+ create: ['@org_id->acts_for[org_id=$]{active}.user_id'],
171
+ update: ['@org_id->acts_for[org_id=$]{active}.user_id'],
172
+ delete: ['@org_id->acts_for[org_id=$]{active}.user_id']
173
+ },
174
+ notifications: {
175
+ ownership: ['@org_id->acts_for[org_id=$]{active}.user_id']
176
+ }
177
+ },
178
+
179
+ // === Packages (with CHECK constraint) ===
180
+ packages: {
181
+ schema: {
182
+ id: 'serial PRIMARY KEY',
183
+ owner_org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
184
+ sponsor_org_id: 'int REFERENCES organisations(id) ON DELETE SET NULL',
185
+ name: 'text NOT NULL',
186
+ price: 'decimal(10, 2) NOT NULL DEFAULT 0.00',
187
+ status: "text NOT NULL DEFAULT 'draft'"
188
+ },
189
+ constraints: [
190
+ "CHECK (status IN ('draft', 'available', 'sold', 'expired'))"
191
+ ],
192
+ label: 'name',
193
+ searchable: ['name'],
194
+ includes: {
195
+ owner_org: 'organisations',
196
+ sponsor_org: 'organisations'
197
+ },
198
+ permissions: {
199
+ view: [
200
+ '@owner_org_id->acts_for[org_id=$]{active}.user_id',
201
+ '@sponsor_org_id->acts_for[org_id=$]{active}.user_id'
202
+ ],
203
+ create: [
204
+ '@owner_org_id->acts_for[org_id=$]{active}.user_id',
205
+ '@sponsor_org_id->acts_for[org_id=$]{active}.user_id'
206
+ ],
207
+ update: [
208
+ '@owner_org_id->acts_for[org_id=$]{active}.user_id',
209
+ '@sponsor_org_id->acts_for[org_id=$]{active}.user_id'
210
+ ],
211
+ delete: ['@owner_org_id->acts_for[org_id=$]{active}.user_id']
212
+ },
213
+ notifications: {
214
+ ownership: ['@owner_org_id->acts_for[org_id=$]{active}.user_id'],
215
+ commercial: ['@sponsor_org_id->acts_for[org_id=$]{active}.user_id']
216
+ }
217
+ },
218
+
219
+ // === Allocations (complex permission paths) ===
220
+ allocations: {
221
+ schema: {
222
+ id: 'serial PRIMARY KEY',
223
+ package_id: 'int NOT NULL REFERENCES packages(id)',
224
+ site_id: 'int NOT NULL REFERENCES sites(id)',
225
+ from_date: 'date NOT NULL',
226
+ to_date: 'date NOT NULL'
227
+ },
228
+ label: 'id',
229
+ searchable: ['site_id'],
230
+ includes: {
231
+ package: 'packages',
232
+ site: 'sites'
233
+ },
234
+ permissions: {
235
+ view: [
236
+ '@site_id->sites.venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id',
237
+ '@package_id->packages.owner_org_id->acts_for[org_id=$]{active}.user_id',
238
+ '@package_id->packages.sponsor_org_id->acts_for[org_id=$]{active}.user_id',
239
+ 'contractor_rights[package_id=@package_id]{active}.contractor_org_id->acts_for[org_id=$]{active}.user_id'
240
+ ],
241
+ create: ['@site_id->sites.venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id'],
242
+ update: [
243
+ '@site_id->sites.venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id',
244
+ '@package_id->packages.owner_org_id->acts_for[org_id=$]{active}.user_id'
245
+ ],
246
+ delete: ['@site_id->sites.venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id']
247
+ },
248
+ notifications: {
249
+ ownership: ['@site_id->sites.venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id'],
250
+ commercial: [
251
+ '@package_id->packages.owner_org_id->acts_for[org_id=$]{active}.user_id',
252
+ '@package_id->packages.sponsor_org_id->acts_for[org_id=$]{active}.user_id'
253
+ ],
254
+ delegated: [
255
+ 'contractor_rights[package_id=@package_id]{active}.contractor_org_id->acts_for[org_id=$]{active}.user_id'
256
+ ]
257
+ }
258
+ },
259
+
260
+ // === Contractor Rights (composite PK, temporal) ===
261
+ contractor_rights: {
262
+ schema: {
263
+ contractor_org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
264
+ sponsor_org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
265
+ package_id: 'int NOT NULL REFERENCES packages(id) ON DELETE CASCADE',
266
+ valid_from: 'date NOT NULL DEFAULT current_date',
267
+ valid_to: 'date'
268
+ },
269
+ primaryKey: ['contractor_org_id', 'package_id', 'valid_from'],
270
+ label: 'contractor_org_id',
271
+ searchable: ['contractor_org_id', 'sponsor_org_id'],
272
+ includes: {
273
+ contractor_org: 'organisations',
274
+ sponsor_org: 'organisations',
275
+ package: 'packages'
276
+ },
277
+ temporal: {
278
+ validFrom: 'valid_from',
279
+ validTo: 'valid_to'
280
+ },
281
+ permissions: {
282
+ view: [],
283
+ create: ['@sponsor_org_id->acts_for[org_id=$]{active}.user_id'],
284
+ update: ['@sponsor_org_id->acts_for[org_id=$]{active}.user_id'],
285
+ delete: ['@sponsor_org_id->acts_for[org_id=$]{active}.user_id']
286
+ },
287
+ notifications: {
288
+ parties: [
289
+ '@contractor_org_id->acts_for[org_id=$]{active}.user_id',
290
+ '@sponsor_org_id->acts_for[org_id=$]{active}.user_id'
291
+ ]
292
+ }
293
+ },
294
+
295
+ // === Brands (with M2M tags) ===
296
+ brands: {
297
+ schema: {
298
+ id: 'serial PRIMARY KEY',
299
+ org_id: 'int NOT NULL REFERENCES organisations(id) ON DELETE CASCADE',
300
+ name: 'text NOT NULL',
301
+ description: 'text'
302
+ },
303
+ constraints: [
304
+ 'UNIQUE(org_id, name)'
305
+ ],
306
+ indexes: [
307
+ 'CREATE INDEX idx_brands_org_id ON brands(org_id)'
308
+ ],
309
+ label: 'name',
310
+ searchable: ['name', 'description'],
311
+ includes: {
312
+ org: 'organisations',
313
+ artwork: 'artwork'
314
+ },
315
+ manyToMany: {
316
+ tags: {
317
+ junctionTable: 'brand_tags',
318
+ localKey: 'brand_id',
319
+ foreignKey: 'tag_id',
320
+ targetEntity: 'tags',
321
+ idField: 'tag_ids',
322
+ expand: false
323
+ }
324
+ },
325
+ permissions: {
326
+ view: [],
327
+ create: ['@org_id->acts_for[org_id=$]{active}.user_id'],
328
+ update: ['@org_id->acts_for[org_id=$]{active}.user_id'],
329
+ delete: ['@org_id->acts_for[org_id=$]{active}.user_id']
330
+ },
331
+ notifications: {
332
+ org_members: ['@org_id->acts_for[org_id=$]{active}.user_id']
333
+ }
334
+ },
335
+
336
+ // === Artwork ===
337
+ artwork: {
338
+ schema: {
339
+ id: 'serial PRIMARY KEY',
340
+ brand_id: 'int NOT NULL REFERENCES brands(id) ON DELETE CASCADE',
341
+ url: 'text NOT NULL',
342
+ ratio: 'decimal(10, 4) NOT NULL'
343
+ },
344
+ constraints: [
345
+ 'CHECK (ratio > 0)'
346
+ ],
347
+ indexes: [
348
+ 'CREATE INDEX idx_artwork_brand_id ON artwork(brand_id)'
349
+ ],
350
+ label: 'url',
351
+ searchable: ['url'],
352
+ includes: {
353
+ brand: 'brands'
354
+ },
355
+ permissions: {
356
+ view: [],
357
+ create: ['@brand_id->brands.org_id->acts_for[org_id=$]{active}.user_id'],
358
+ update: ['@brand_id->brands.org_id->acts_for[org_id=$]{active}.user_id'],
359
+ delete: ['@brand_id->brands.org_id->acts_for[org_id=$]{active}.user_id']
360
+ },
361
+ notifications: {
362
+ brand_org_members: ['@brand_id->brands.org_id->acts_for[org_id=$]{active}.user_id']
363
+ }
364
+ },
365
+
366
+ // === Tags ===
367
+ tags: {
368
+ schema: {
369
+ id: 'serial PRIMARY KEY',
370
+ name: 'text NOT NULL UNIQUE',
371
+ color: 'text',
372
+ description: 'text'
373
+ },
374
+ label: 'name',
375
+ searchable: ['name', 'description'],
376
+ permissions: {
377
+ view: [],
378
+ create: [],
379
+ update: [],
380
+ delete: []
381
+ }
382
+ },
383
+
384
+ // === Brand Tags (junction table) ===
385
+ brand_tags: {
386
+ schema: {
387
+ brand_id: 'int NOT NULL REFERENCES brands(id) ON DELETE CASCADE',
388
+ tag_id: 'int NOT NULL REFERENCES tags(id) ON DELETE CASCADE'
389
+ },
390
+ primaryKey: ['brand_id', 'tag_id'],
391
+ indexes: [
392
+ 'CREATE INDEX idx_brand_tags_tag_id ON brand_tags(tag_id)'
393
+ ],
394
+ // Junction tables typically don't need DZQL CRUD - managed via M2M
395
+ managed: false
396
+ }
397
+
398
+ };
399
+
400
+ // === Subscribables (real-time documents) ===
401
+ export const subscribables = {
402
+
403
+ // Venue detail - venue with org, sites, and allocations
404
+ venue_detail: {
405
+ params: {
406
+ venue_id: 'int'
407
+ },
408
+ root: {
409
+ entity: 'venues',
410
+ key: 'venue_id'
411
+ },
412
+ includes: {
413
+ org: 'organisations',
414
+ sites: {
415
+ entity: 'sites',
416
+ includes: {
417
+ allocations: 'allocations'
418
+ }
419
+ }
420
+ },
421
+ // Tables that affect this document - for subscription routing
422
+ scopeTables: ['venues', 'organisations', 'sites', 'allocations'],
423
+ // Who can subscribe
424
+ canSubscribe: ['@venue_id->venues.org_id->acts_for[org_id=$]{active}.user_id']
425
+ },
426
+
427
+ // Org dashboard - everything an organisation member sees
428
+ org_dashboard: {
429
+ params: {
430
+ org_id: 'int'
431
+ },
432
+ root: {
433
+ entity: 'organisations',
434
+ key: 'org_id'
435
+ },
436
+ includes: {
437
+ venues: {
438
+ entity: 'venues',
439
+ filter: { org_id: '@org_id' },
440
+ includes: {
441
+ sites: 'sites'
442
+ }
443
+ },
444
+ products: {
445
+ entity: 'products',
446
+ filter: { org_id: '@org_id' }
447
+ },
448
+ packages: {
449
+ entity: 'packages',
450
+ filter: { owner_org_id: '@org_id' }
451
+ },
452
+ brands: {
453
+ entity: 'brands',
454
+ filter: { org_id: '@org_id' },
455
+ includes: {
456
+ artwork: 'artwork'
457
+ }
458
+ }
459
+ },
460
+ scopeTables: ['organisations', 'venues', 'sites', 'products', 'packages', 'brands', 'artwork'],
461
+ canSubscribe: ['@org_id->acts_for[org_id=$]{active}.user_id']
462
+ },
463
+
464
+ // User profile - current user's orgs and roles
465
+ my_profile: {
466
+ params: {}, // No params - uses current user
467
+ root: {
468
+ entity: 'users',
469
+ key: '@user_id' // Built-in: current user
470
+ },
471
+ includes: {
472
+ memberships: {
473
+ entity: 'acts_for',
474
+ filter: { user_id: '@user_id' },
475
+ temporal: true, // Only active memberships
476
+ includes: {
477
+ org: 'organisations'
478
+ }
479
+ }
480
+ },
481
+ scopeTables: ['users', 'acts_for', 'organisations'],
482
+ canSubscribe: [] // Anyone authenticated can subscribe to their own profile
483
+ }
484
+
485
+ };
package/package.json CHANGED
@@ -1,71 +1,34 @@
1
1
  {
2
2
  "name": "dzql",
3
- "version": "0.5.33",
4
- "description": "PostgreSQL-powered framework with zero boilerplate CRUD operations and real-time WebSocket synchronization",
3
+ "version": "0.6.1",
4
+ "description": "Database-first real-time framework with TypeScript support",
5
5
  "type": "module",
6
- "main": "src/server/index.js",
7
- "exports": {
8
- ".": "./src/server/index.js",
9
- "./client": "./src/client/ws.js",
10
- "./client/stores": "./src/client/stores/index.js",
11
- "./client/templates": "./src/client/templates/App.vue",
12
- "./server": "./src/server/index.js",
13
- "./db": "./src/server/db.js",
14
- "./compiler": "./src/compiler/index.js",
15
- "./namespace": "./src/server/namespace.js"
6
+ "bin": {
7
+ "dzql": "./src/cli/index.ts"
16
8
  },
17
- "files": [
18
- "bin/**/*.js",
19
- "src/**/*.js",
20
- "src/database/**/*.sql",
21
- "docs/**/*.md",
22
- "docs/examples/*.sql",
23
- "README.md",
24
- "LICENSE"
25
- ],
26
9
  "scripts": {
27
- "test": "bun test ../../tests/core/*.test.js",
28
- "prepublishOnly": "echo '✅ Publishing DZQL...' "
29
- },
30
- "dependencies": {
31
- "jose": "^6.1.0",
32
- "postgres": "^3.4.7"
10
+ "dev": "bun run src/cli/index.ts",
11
+ "test": "bun test",
12
+ "build": "bun build ./src/cli/index.ts --outdir ./dist/cli --target node"
33
13
  },
34
- "keywords": [
35
- "postgresql",
36
- "postgres",
37
- "websocket",
38
- "real-time",
39
- "realtime",
40
- "crud",
41
- "api",
42
- "framework",
43
- "zero-boilerplate",
44
- "database",
45
- "bun",
46
- "graph-rules",
47
- "permissions",
48
- "row-level-security",
49
- "rls",
50
- "notify",
51
- "listen",
52
- "subscriptions"
53
- ],
54
- "author": "Peter Bunyan",
55
- "license": "MIT",
56
- "repository": {
57
- "type": "git",
58
- "url": "https://github.com/blueshed/dzql",
59
- "directory": "packages/dzql"
14
+ "exports": {
15
+ ".": "./src/runtime/index.ts",
16
+ "./client": "./src/client/index.ts",
17
+ "./compiler": "./src/cli/index.ts",
18
+ "./namespace": "./src/runtime/namespace.ts"
60
19
  },
61
- "bugs": {
62
- "url": "https://github.com/blueshed/dzql/issues"
20
+ "dependencies": {
21
+ "postgres": "^3.4.3",
22
+ "dotenv": "^16.4.5",
23
+ "jose": "^5.2.3"
63
24
  },
64
- "homepage": "https://github.com/blueshed/dzql#readme",
65
- "engines": {
66
- "bun": ">=1.0.0"
25
+ "devDependencies": {
26
+ "@types/bun": "latest",
27
+ "pinia": "^3.0.4",
28
+ "typescript": "^5.0.0",
29
+ "vue": "^3.5.26"
67
30
  },
68
- "publishConfig": {
69
- "access": "public"
31
+ "peerDependencies": {
32
+ "typescript": "^5.0.0"
70
33
  }
71
34
  }
@@ -0,0 +1,99 @@
1
+ import { Manifest, FunctionDef } from "./manifest.js";
2
+ import { generateTypeDefinitions } from "./types.js";
3
+
4
+ function toPascalCase(str: string): string {
5
+ return str.replace(/(^|_)([a-z])/g, (g) => g[g.length - 1]!.toUpperCase());
6
+ }
7
+
8
+ /**
9
+ * Generate a TypeScript client SDK
10
+ */
11
+ export function generateClientSDK(manifest: Manifest): string {
12
+ // Separate regular functions from subscriptions
13
+ const regularFunctions = Object.entries(manifest.functions)
14
+ .filter(([_, def]) => !def.isSubscription);
15
+ const subscriptionFunctions = Object.entries(manifest.functions)
16
+ .filter(([_, def]) => def.isSubscription);
17
+
18
+ // Generate entity types
19
+ const typeDefs = generateTypeDefinitions(manifest.entities, manifest.subscribables);
20
+
21
+ // Generate API interface with proper types
22
+ const apiMethods = Object.entries(manifest.functions).map(([funcName, def]) => {
23
+ const isSubscription = def.isSubscription;
24
+ const parts = funcName.split('_');
25
+ const op = parts[0];
26
+ const entity = parts.slice(1).join('_');
27
+ const pascalEntity = toPascalCase(entity);
28
+
29
+ // Check if this entity exists in manifest
30
+ const entityExists = manifest.entities[entity];
31
+
32
+ let paramType = 'Record<string, unknown>';
33
+ let returnType = 'unknown';
34
+
35
+ if (isSubscription) {
36
+ // subscribe_venue_detail -> VenueDetailParams
37
+ paramType = `${pascalEntity}Params`;
38
+ returnType = 'void';
39
+ return ` ${funcName}: (params: ${paramType}, callback: (data: unknown) => void) => Promise<() => ${returnType}>;`;
40
+ } else if (op === 'get' && entityExists) {
41
+ // get_venue_detail (subscribable getter) vs get_venues (entity getter)
42
+ if (manifest.subscribables?.[entity]) {
43
+ paramType = `${pascalEntity}Params`;
44
+ returnType = 'unknown';
45
+ } else {
46
+ paramType = `${pascalEntity}PK`;
47
+ returnType = `${pascalEntity} | null`;
48
+ }
49
+ } else if (op === 'save' && entityExists) {
50
+ paramType = `Save${pascalEntity}Params`;
51
+ returnType = pascalEntity;
52
+ } else if (op === 'delete' && entityExists) {
53
+ paramType = `${pascalEntity}PK`;
54
+ returnType = pascalEntity;
55
+ } else if (op === 'search' && entityExists) {
56
+ paramType = `Search${pascalEntity}Params`;
57
+ returnType = `${pascalEntity}[]`;
58
+ } else if (op === 'lookup' && entityExists) {
59
+ paramType = `Lookup${pascalEntity}Params`;
60
+ returnType = `${pascalEntity}[]`;
61
+ }
62
+ // Custom functions and auth functions stay as Record<string, unknown>
63
+
64
+ return ` ${funcName}: (params: ${paramType}) => Promise<${returnType}>;`;
65
+ }).join('\n');
66
+
67
+ return `// Generated by TZQL Compiler v${manifest.version}
68
+ // Do not edit this file directly.
69
+
70
+ import { WebSocketManager } from 'tzql/client';
71
+
72
+ ${typeDefs}
73
+
74
+ /** API interface with typed methods */
75
+ export interface TzqlAPI {
76
+ ${apiMethods}
77
+ }
78
+
79
+ /** Extended WebSocket manager with typed API */
80
+ export class GeneratedWebSocketManager extends WebSocketManager {
81
+ api: TzqlAPI;
82
+
83
+ constructor(options: { url?: string; reconnect?: boolean } = {}) {
84
+ super(options);
85
+ this.api = {
86
+ ${regularFunctions.map(([funcName]) =>
87
+ ` ${funcName}: (params: Record<string, unknown>) => this.call('${funcName}', params),`
88
+ ).join('\n')}
89
+ ${subscriptionFunctions.map(([funcName]) =>
90
+ ` ${funcName}: (params: Record<string, unknown>, callback: (data: unknown) => void) => this.subscribe('${funcName}', params, callback),`
91
+ ).join('\n')}
92
+ } as TzqlAPI;
93
+ }
94
+ }
95
+
96
+ /** Singleton WebSocket instance */
97
+ export const ws = new GeneratedWebSocketManager();
98
+ `;
99
+ }