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
@@ -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.0",
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
+ }