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,258 @@
1
+ import { describe, test, expect, beforeEach } from "bun:test";
2
+ import { ref, isRef, isReactive, nextTick, watch } from "vue";
3
+ import { createPinia, setActivePinia, defineStore } from "pinia";
4
+ import { generateSubscribableStore } from "../src/cli/codegen/subscribable_store.js";
5
+ import { generateManifest } from "../src/cli/codegen/manifest.js";
6
+ import { generateIR } from "../src/cli/compiler/ir.js";
7
+ import { entities, subscribables } from "../examples/venues.js";
8
+
9
+ // Mock WebSocket manager
10
+ const mockWs = {
11
+ api: {
12
+ subscribe_venue_detail: (params: any, callback: (data: any) => void) => {
13
+ // Simulate async subscription response
14
+ setTimeout(() => {
15
+ callback({
16
+ id: 1,
17
+ name: "Test Venue",
18
+ sites: [
19
+ { id: 1, name: "Site A", allocations: [] },
20
+ { id: 2, name: "Site B", allocations: [] }
21
+ ]
22
+ });
23
+ }, 10);
24
+ }
25
+ },
26
+ registerStore: (listener: (event: any) => void) => {
27
+ // Store the listener so tests can dispatch events
28
+ mockWs._storeListener = listener;
29
+ return () => {};
30
+ },
31
+ _storeListener: null as ((event: any) => void) | null,
32
+ dispatchEvent: (event: any) => {
33
+ if (mockWs._storeListener) {
34
+ mockWs._storeListener(event);
35
+ }
36
+ }
37
+ };
38
+
39
+ describe("Subscribable Store Reactivity", () => {
40
+ beforeEach(() => {
41
+ setActivePinia(createPinia());
42
+ mockWs._storeListener = null;
43
+ });
44
+
45
+ test("generated store code structure is correct", () => {
46
+ const ir = generateIR({ entities, subscribables });
47
+ const manifest = generateManifest(ir);
48
+ const code = generateSubscribableStore(manifest, "venue_detail");
49
+
50
+ // Should use typed ref for documents
51
+ expect(code).toContain("const documents: Ref<Record<string, DocumentWrapper<Record<string, unknown>>>> = ref({});");
52
+
53
+ // Should add plain object with empty data object to documents.value (preserves reactivity)
54
+ expect(code).toContain("documents.value[key] = { data: {}, loading: true, ready };");
55
+
56
+ // Should merge initial data into existing object via Object.assign (preserves reactivity)
57
+ expect(code).toContain("Object.assign(documents.value[key].data, eventData");
58
+ expect(code).toContain("documents.value[key].loading = false;");
59
+
60
+ // Should have typed unbind function
61
+ expect(code).toContain("function unbind(params: VenueDetailParams)");
62
+
63
+ // Should NOT pre-wrap in ref()
64
+ expect(code).not.toContain("const docState = ref(");
65
+ expect(code).not.toContain("const loading = ref(");
66
+ });
67
+
68
+ test("documents.value entries become reactive when added as plain objects", async () => {
69
+ // Simulate what the generated store does
70
+ const documents = ref<Record<string, any>>({});
71
+
72
+ // Add a plain object - Vue should make it reactive
73
+ documents.value["test-key"] = { data: null, loading: true };
74
+
75
+ // The entry should be reactive
76
+ expect(isReactive(documents.value["test-key"])).toBe(true);
77
+
78
+ // Assign data
79
+ documents.value["test-key"].data = { id: 1, name: "Test" };
80
+ documents.value["test-key"].loading = false;
81
+
82
+ // Data should also be reactive
83
+ expect(isReactive(documents.value["test-key"].data)).toBe(true);
84
+ });
85
+
86
+ test("mutations to document data trigger reactivity", async () => {
87
+ const documents = ref<Record<string, any>>({});
88
+
89
+ // Setup
90
+ documents.value["key1"] = {
91
+ data: { id: 1, name: "Original", items: [{ id: 1, value: "a" }] },
92
+ loading: false
93
+ };
94
+
95
+ let changeCount = 0;
96
+
97
+ // Watch for changes
98
+ watch(
99
+ () => documents.value["key1"]?.data?.name,
100
+ () => { changeCount++; },
101
+ { flush: 'sync' }
102
+ );
103
+
104
+ // Mutate via Object.assign (like applyPatch does)
105
+ Object.assign(documents.value["key1"].data, { name: "Updated" });
106
+
107
+ expect(changeCount).toBe(1);
108
+ expect(documents.value["key1"].data.name).toBe("Updated");
109
+ });
110
+
111
+ test("array mutations trigger reactivity", async () => {
112
+ const documents = ref<Record<string, any>>({});
113
+
114
+ documents.value["key1"] = {
115
+ data: {
116
+ id: 1,
117
+ sites: [
118
+ { id: 1, name: "Site A" },
119
+ { id: 2, name: "Site B" }
120
+ ]
121
+ },
122
+ loading: false
123
+ };
124
+
125
+ let changeCount = 0;
126
+
127
+ watch(
128
+ () => documents.value["key1"]?.data?.sites?.length,
129
+ () => { changeCount++; },
130
+ { flush: 'sync' }
131
+ );
132
+
133
+ // Insert (push)
134
+ documents.value["key1"].data.sites.push({ id: 3, name: "Site C" });
135
+ expect(changeCount).toBe(1);
136
+ expect(documents.value["key1"].data.sites.length).toBe(3);
137
+
138
+ // Delete (splice)
139
+ documents.value["key1"].data.sites.splice(0, 1);
140
+ expect(changeCount).toBe(2);
141
+ expect(documents.value["key1"].data.sites.length).toBe(2);
142
+ });
143
+
144
+ test("Object.assign on array items triggers reactivity", async () => {
145
+ const documents = ref<Record<string, any>>({});
146
+
147
+ documents.value["key1"] = {
148
+ data: {
149
+ sites: [{ id: 1, name: "Original" }]
150
+ },
151
+ loading: false
152
+ };
153
+
154
+ let changeCount = 0;
155
+
156
+ watch(
157
+ () => documents.value["key1"]?.data?.sites?.[0]?.name,
158
+ () => { changeCount++; },
159
+ { flush: 'sync' }
160
+ );
161
+
162
+ // Update via Object.assign (like handleArrayPatch does)
163
+ Object.assign(documents.value["key1"].data.sites[0], { name: "Updated" });
164
+
165
+ expect(changeCount).toBe(1);
166
+ expect(documents.value["key1"].data.sites[0].name).toBe("Updated");
167
+ });
168
+
169
+ test("applyPatch simulation works with reactivity", async () => {
170
+ const documents = ref<Record<string, any>>({});
171
+
172
+ // Simulate bind() adding the document
173
+ documents.value["venue-1"] = {
174
+ data: {
175
+ id: 1,
176
+ name: "Test Venue",
177
+ sites: [
178
+ { id: 1, name: "Site A", allocations: [] }
179
+ ]
180
+ },
181
+ loading: false
182
+ };
183
+
184
+ // Simulate applyPatch for root entity update
185
+ function applyPatch(doc: any, event: any) {
186
+ if (!doc) return;
187
+ switch (event.table) {
188
+ case 'venues':
189
+ if (event.op === 'update') Object.assign(doc, event.data);
190
+ break;
191
+ case 'sites':
192
+ handleArrayPatch(doc.sites, event);
193
+ break;
194
+ }
195
+ }
196
+
197
+ function handleArrayPatch(arr: any[], event: any) {
198
+ if (!arr) return;
199
+ const pkValue = event.pk?.id;
200
+ const idx = arr.findIndex(i => i.id === pkValue);
201
+ if (event.op === 'insert') {
202
+ if (idx === -1) arr.push(event.data);
203
+ } else if (event.op === 'update') {
204
+ if (idx !== -1) Object.assign(arr[idx], event.data);
205
+ } else if (event.op === 'delete') {
206
+ if (idx !== -1) arr.splice(idx, 1);
207
+ }
208
+ }
209
+
210
+ let venueNameChanges = 0;
211
+ let sitesLengthChanges = 0;
212
+
213
+ watch(
214
+ () => documents.value["venue-1"]?.data?.name,
215
+ () => { venueNameChanges++; },
216
+ { flush: 'sync' }
217
+ );
218
+
219
+ watch(
220
+ () => documents.value["venue-1"]?.data?.sites?.length,
221
+ () => { sitesLengthChanges++; },
222
+ { flush: 'sync' }
223
+ );
224
+
225
+ // Test root entity update
226
+ applyPatch(documents.value["venue-1"].data, {
227
+ table: 'venues',
228
+ op: 'update',
229
+ data: { name: "Updated Venue" },
230
+ pk: { id: 1 }
231
+ });
232
+
233
+ expect(venueNameChanges).toBe(1);
234
+ expect(documents.value["venue-1"].data.name).toBe("Updated Venue");
235
+
236
+ // Test insert into nested array
237
+ applyPatch(documents.value["venue-1"].data, {
238
+ table: 'sites',
239
+ op: 'insert',
240
+ data: { id: 2, name: "Site B", allocations: [] },
241
+ pk: { id: 2 }
242
+ });
243
+
244
+ expect(sitesLengthChanges).toBe(1);
245
+ expect(documents.value["venue-1"].data.sites.length).toBe(2);
246
+
247
+ // Test delete from nested array
248
+ applyPatch(documents.value["venue-1"].data, {
249
+ table: 'sites',
250
+ op: 'delete',
251
+ data: { id: 1 },
252
+ pk: { id: 1 }
253
+ });
254
+
255
+ expect(sitesLengthChanges).toBe(2);
256
+ expect(documents.value["venue-1"].data.sites.length).toBe(1);
257
+ });
258
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { generatePiniaStore } from "../src/cli/codegen/pinia.js";
3
+ import { generateManifest } from "../src/cli/codegen/manifest.js";
4
+ import { generateIR } from "../src/cli/compiler/ir.js";
5
+ import { entities, subscribables } from "../examples/venues.js";
6
+
7
+ const venuesDomain = { entities, subscribables };
8
+
9
+ describe("Venues Store Generation", () => {
10
+ test("should generate venues store", () => {
11
+ const ir = generateIR(venuesDomain);
12
+ const manifest = generateManifest(ir);
13
+
14
+ // Generate the store for the 'venues' entity
15
+ const code = generatePiniaStore(manifest, "venues");
16
+
17
+ console.log("--- GENERATED VENUES STORE ---");
18
+ console.log(code);
19
+ console.log("------------------------------");
20
+
21
+ expect(code).toContain("useVenuesStore");
22
+ expect(code).toContain("ws.api.save_venues");
23
+ expect(code).toContain("table_changed");
24
+ });
25
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": ["ESNext"],
4
+ "module": "esnext",
5
+ "target": "esnext",
6
+ "moduleResolution": "bundler",
7
+ "moduleDetection": "force",
8
+ "allowImportingTsExtensions": true,
9
+ "noEmit": true,
10
+ "composite": true,
11
+ "strict": true,
12
+ "downlevelIteration": true,
13
+ "skipLibCheck": true,
14
+ "jsx": "preserve",
15
+ "allowSyntheticDefaultImports": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "allowJs": true,
18
+ "types": ["bun-types"]
19
+ }
20
+ }