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,730 +0,0 @@
1
- # DZQL Client Stores Guide
2
-
3
- Complete guide to using the canonical DZQL Pinia stores for Vue.js applications.
4
-
5
- ## Overview
6
-
7
- DZQL provides two canonical Pinia stores that handle the complete application lifecycle:
8
-
9
- 1. **`useWsStore`** - WebSocket connection and authentication management
10
- 2. **`useAppStore`** - Application state and navigation
11
-
12
- These stores implement the **three-phase lifecycle** that DZQL applications follow:
13
-
14
- ### Three-Phase Lifecycle
15
-
16
- ```
17
- ┌─────────────┐
18
- │ CONNECTING │ Initial WebSocket connection
19
- └──────┬──────┘
20
-
21
- ├─ Connected without profile
22
-
23
- v
24
- ┌─────────────┐
25
- │ LOGIN │ Show login form, wait for authentication
26
- └──────┬──────┘
27
-
28
- ├─ User authenticates
29
-
30
- v
31
- ┌─────────────┐
32
- │ READY │ App is ready to use
33
- └─────────────┘
34
- ```
35
-
36
- ## Quick Start
37
-
38
- ### 1. Install Dependencies
39
-
40
- ```bash
41
- npm install pinia vue-router
42
- ```
43
-
44
- ### 2. Basic Setup
45
-
46
- **main.js:**
47
- ```javascript
48
- import { createApp } from 'vue'
49
- import { createPinia } from 'pinia'
50
- import { useAppStore } from 'dzql/client/stores'
51
- import App from './App.vue'
52
-
53
- const pinia = createPinia()
54
- const app = createApp(App)
55
-
56
- app.use(pinia)
57
-
58
- // Initialize app store
59
- const appStore = useAppStore()
60
- await appStore.initialize({
61
- title: 'My DZQL App'
62
- })
63
-
64
- app.mount('#app')
65
- ```
66
-
67
- **App.vue:**
68
- ```vue
69
- <script setup>
70
- import { computed } from 'vue'
71
- import { useWsStore } from 'dzql/client/stores'
72
- import LoginView from './components/LoginView.vue'
73
- import MainLayout from './components/MainLayout.vue'
74
-
75
- const wsStore = useWsStore()
76
- const state = computed(() => wsStore.appState)
77
- </script>
78
-
79
- <template>
80
- <!-- Phase 1: CONNECTING -->
81
- <div v-if="state === 'connecting'">
82
- <span class="loading loading-spinner"></span>
83
- Connecting...
84
- </div>
85
-
86
- <!-- Phase 2: LOGIN -->
87
- <LoginView v-else-if="state === 'login'" />
88
-
89
- <!-- Phase 3: READY -->
90
- <MainLayout v-else-if="state === 'ready'" />
91
- </template>
92
- ```
93
-
94
- ## Store API Reference
95
-
96
- ### useWsStore
97
-
98
- Manages WebSocket connection and user authentication.
99
-
100
- #### State
101
-
102
- ```javascript
103
- const wsStore = useWsStore()
104
-
105
- wsStore.connectionState // 'disconnected' | 'connecting' | 'connected' | 'error'
106
- wsStore.appState // 'connecting' | 'login' | 'ready'
107
- wsStore.profile // User profile object or null
108
- wsStore.error // Last error message or null
109
- ```
110
-
111
- #### Computed
112
-
113
- ```javascript
114
- wsStore.isConnected // boolean - WebSocket is connected
115
- wsStore.isAuthenticated // boolean - User is logged in
116
- wsStore.isReady // boolean - App is ready (connected + authenticated)
117
- wsStore.needsLogin // boolean - Needs to show login form
118
- wsStore.isConnecting // boolean - Currently connecting
119
- ```
120
-
121
- #### Actions
122
-
123
- **connect(url, timeout)**
124
-
125
- Connect to WebSocket server. URL is auto-detected if not provided.
126
-
127
- ```javascript
128
- // Auto-detect URL
129
- await wsStore.connect()
130
-
131
- // Custom URL (development)
132
- await wsStore.connect('ws://localhost:3000/ws')
133
-
134
- // Custom timeout
135
- await wsStore.connect(null, 10000) // 10 seconds
136
- ```
137
-
138
- **login({ email, password })**
139
-
140
- Login with email and password.
141
-
142
- ```javascript
143
- try {
144
- const result = await wsStore.login({
145
- email: 'user@example.com',
146
- password: 'password123'
147
- })
148
-
149
- console.log('Logged in:', result.profile)
150
- } catch (err) {
151
- console.error('Login failed:', err.message)
152
- }
153
- ```
154
-
155
- **register({ email, password })**
156
-
157
- Register a new user.
158
-
159
- ```javascript
160
- try {
161
- const result = await wsStore.register({
162
- email: 'newuser@example.com',
163
- password: 'securepass123'
164
- })
165
-
166
- console.log('Registered:', result.profile)
167
- } catch (err) {
168
- console.error('Registration failed:', err.message)
169
- }
170
- ```
171
-
172
- **logout()**
173
-
174
- Logout current user and clear session.
175
-
176
- ```javascript
177
- await wsStore.logout()
178
- ```
179
-
180
- **disconnect()**
181
-
182
- Disconnect from WebSocket.
183
-
184
- ```javascript
185
- wsStore.disconnect()
186
- ```
187
-
188
- **getWs()**
189
-
190
- Get the WebSocket manager instance for direct API calls.
191
-
192
- ```javascript
193
- const ws = wsStore.getWs()
194
-
195
- // Use DZQL API
196
- const venue = await ws.api.get.venues({ id: 1 })
197
- const created = await ws.api.save.venues({ name: 'New Venue' })
198
-
199
- // Call custom functions
200
- const result = await ws.api.myCustomFunction({ param: 'value' })
201
- ```
202
-
203
- ---
204
-
205
- ### useAppStore
206
-
207
- Manages application-level state and navigation.
208
-
209
- #### State
210
-
211
- ```javascript
212
- const appStore = useAppStore()
213
-
214
- appStore.title // App title
215
- appStore.currentEntity // Current entity name or null
216
- appStore.currentId // Current record ID or null
217
- appStore.entityMetadata // Entity metadata cache object
218
- appStore.isLoadingMetadata // boolean - Fetching metadata
219
- appStore.sidebarOpen // boolean - Sidebar visibility
220
- appStore.propertiesPanelOpen // boolean - Properties panel visibility
221
- ```
222
-
223
- #### Computed
224
-
225
- ```javascript
226
- appStore.hasMetadata // boolean - Metadata loaded
227
- appStore.entityList // string[] - Sorted list of entity names
228
- appStore.currentEntityMeta // object - Metadata for current entity
229
- ```
230
-
231
- #### Actions
232
-
233
- **initialize(options)**
234
-
235
- Initialize the app (connect to WebSocket and set up lifecycle).
236
-
237
- ```javascript
238
- await appStore.initialize({
239
- wsUrl: 'ws://localhost:3000/ws', // Optional
240
- title: 'My DZQL App' // Optional
241
- })
242
- ```
243
-
244
- **fetchMetadata()**
245
-
246
- Fetch entity metadata from server. Called automatically after authentication.
247
-
248
- ```javascript
249
- await appStore.fetchMetadata()
250
- ```
251
-
252
- **setRouter(router)**
253
-
254
- Set Vue Router instance to enable programmatic navigation.
255
-
256
- ```javascript
257
- import { createRouter } from 'vue-router'
258
-
259
- const router = createRouter({ ... })
260
- appStore.setRouter(router)
261
- ```
262
-
263
- **Navigation Methods:**
264
-
265
- ```javascript
266
- // Navigate to entity list
267
- appStore.navigateToEntity('venues')
268
-
269
- // Navigate to entity detail
270
- appStore.navigateToEntityDetail('venues', 123)
271
- appStore.navigateToEntityDetail('venues', 'new')
272
-
273
- // Navigate to home
274
- appStore.navigateToHome()
275
-
276
- // Set context manually (without navigation)
277
- appStore.setContext('venues', 123)
278
- ```
279
-
280
- **UI Toggles:**
281
-
282
- ```javascript
283
- appStore.toggleSidebar()
284
- appStore.togglePropertiesPanel()
285
- ```
286
-
287
- ---
288
-
289
- ## Complete Examples
290
-
291
- ### Example 1: Basic App with Router
292
-
293
- **main.js:**
294
- ```javascript
295
- import { createApp } from 'vue'
296
- import { createRouter, createWebHashHistory } from 'vue-router'
297
- import { createPinia } from 'pinia'
298
- import { useAppStore } from 'dzql/client/stores'
299
- import App from './App.vue'
300
-
301
- // Create Pinia
302
- const pinia = createPinia()
303
-
304
- // Create Router
305
- const router = createRouter({
306
- history: createWebHashHistory(),
307
- routes: [
308
- { path: '/', name: 'home', component: () => import('./views/Home.vue') },
309
- { path: '/:entity', name: 'entity-list', component: () => import('./views/EntityList.vue') },
310
- { path: '/:entity/:id', name: 'entity-detail', component: () => import('./views/EntityDetail.vue') }
311
- ]
312
- })
313
-
314
- // Create App
315
- const app = createApp(App)
316
- app.use(pinia)
317
- app.use(router)
318
-
319
- // Initialize
320
- const appStore = useAppStore()
321
- appStore.setRouter(router)
322
- await appStore.initialize()
323
-
324
- app.mount('#app')
325
- ```
326
-
327
- **App.vue:**
328
- ```vue
329
- <script setup>
330
- import { computed } from 'vue'
331
- import { useWsStore, useAppStore } from 'dzql/client/stores'
332
- import LoginView from './components/LoginView.vue'
333
-
334
- const wsStore = useWsStore()
335
- const appStore = useAppStore()
336
-
337
- const state = computed(() => wsStore.appState)
338
- const profile = computed(() => wsStore.profile)
339
-
340
- async function handleLogout() {
341
- await wsStore.logout()
342
- }
343
- </script>
344
-
345
- <template>
346
- <div class="app">
347
- <!-- CONNECTING -->
348
- <div v-if="state === 'connecting'" class="loading-screen">
349
- <div class="spinner"></div>
350
- <p>Connecting...</p>
351
- </div>
352
-
353
- <!-- LOGIN -->
354
- <LoginView v-else-if="state === 'login'" />
355
-
356
- <!-- READY -->
357
- <div v-else-if="state === 'ready'">
358
- <nav>
359
- <h1>{{ appStore.title }}</h1>
360
- <div class="user-menu">
361
- <span>{{ profile.email }}</span>
362
- <button @click="handleLogout">Logout</button>
363
- </div>
364
- </nav>
365
-
366
- <main>
367
- <router-view />
368
- </main>
369
- </div>
370
- </div>
371
- </template>
372
- ```
373
-
374
- **LoginView.vue:**
375
- ```vue
376
- <script setup>
377
- import { ref } from 'vue'
378
- import { useWsStore } from 'dzql/client/stores'
379
-
380
- const wsStore = useWsStore()
381
- const email = ref('')
382
- const password = ref('')
383
- const error = ref(null)
384
-
385
- async function handleLogin() {
386
- try {
387
- error.value = null
388
- await wsStore.login({
389
- email: email.value,
390
- password: password.value
391
- })
392
- } catch (err) {
393
- error.value = err.message
394
- }
395
- }
396
- </script>
397
-
398
- <template>
399
- <div class="login-form">
400
- <h2>Login</h2>
401
-
402
- <form @submit.prevent="handleLogin">
403
- <input v-model="email" type="email" placeholder="Email" required />
404
- <input v-model="password" type="password" placeholder="Password" required />
405
- <button type="submit">Login</button>
406
- </form>
407
-
408
- <p v-if="error" class="error">{{ error }}</p>
409
- </div>
410
- </template>
411
- ```
412
-
413
- ---
414
-
415
- ### Example 2: Using DZQL API
416
-
417
- **EntityList.vue:**
418
- ```vue
419
- <script setup>
420
- import { ref, onMounted, computed } from 'vue'
421
- import { useRoute } from 'vue-router'
422
- import { useWsStore, useAppStore } from 'dzql/client/stores'
423
-
424
- const route = useRoute()
425
- const wsStore = useWsStore()
426
- const appStore = useAppStore()
427
-
428
- const entity = computed(() => route.params.entity)
429
- const records = ref([])
430
- const loading = ref(false)
431
-
432
- async function loadRecords() {
433
- const ws = wsStore.getWs()
434
- loading.value = true
435
-
436
- try {
437
- const result = await ws.api.search[entity.value]({
438
- filters: {},
439
- page: 1,
440
- limit: 50
441
- })
442
-
443
- records.value = result.data
444
- } catch (err) {
445
- console.error('Failed to load records:', err)
446
- } finally {
447
- loading.value = false
448
- }
449
- }
450
-
451
- onMounted(() => {
452
- loadRecords()
453
- })
454
-
455
- function createNew() {
456
- appStore.navigateToEntityDetail(entity.value, 'new')
457
- }
458
-
459
- function editRecord(id) {
460
- appStore.navigateToEntityDetail(entity.value, id)
461
- }
462
- </script>
463
-
464
- <template>
465
- <div>
466
- <div class="header">
467
- <h2>{{ entity }}</h2>
468
- <button @click="createNew">New</button>
469
- </div>
470
-
471
- <div v-if="loading">Loading...</div>
472
-
473
- <table v-else>
474
- <tbody>
475
- <tr v-for="record in records" :key="record.id" @click="editRecord(record.id)">
476
- <td>{{ record.id }}</td>
477
- <td>{{ record.name || record.title || record.email }}</td>
478
- </tr>
479
- </tbody>
480
- </table>
481
- </div>
482
- </template>
483
- ```
484
-
485
- ---
486
-
487
- ## Patterns and Best Practices
488
-
489
- ### Pattern 1: Reactive State in Components
490
-
491
- ```vue
492
- <script setup>
493
- import { computed } from 'vue'
494
- import { useWsStore } from 'dzql/client/stores'
495
-
496
- const wsStore = useWsStore()
497
-
498
- // Use computed for reactive access
499
- const profile = computed(() => wsStore.profile)
500
- const isAuthenticated = computed(() => wsStore.isAuthenticated)
501
-
502
- // Direct access to actions
503
- async function login() {
504
- await wsStore.login({ email: '...', password: '...' })
505
- }
506
- </script>
507
-
508
- <template>
509
- <div v-if="isAuthenticated">
510
- Welcome, {{ profile.email }}!
511
- </div>
512
- </template>
513
- ```
514
-
515
- ### Pattern 2: Listening to WebSocket Broadcasts
516
-
517
- ```javascript
518
- import { onMounted, onUnmounted } from 'vue'
519
- import { useWsStore } from 'dzql/client/stores'
520
-
521
- const wsStore = useWsStore()
522
- const ws = wsStore.getWs()
523
-
524
- onMounted(() => {
525
- // Listen for real-time updates
526
- const cleanup = ws.onBroadcast((method, params) => {
527
- if (method === 'venues:update') {
528
- console.log('Venue updated:', params.after)
529
- // Refresh your data
530
- }
531
- })
532
-
533
- onUnmounted(() => {
534
- cleanup()
535
- })
536
- })
537
- ```
538
-
539
- ### Pattern 3: Global Navigation
540
-
541
- ```javascript
542
- import { useAppStore } from 'dzql/client/stores'
543
-
544
- const appStore = useAppStore()
545
-
546
- // Navigate from anywhere in your app
547
- function goToVenues() {
548
- appStore.navigateToEntity('venues')
549
- }
550
-
551
- function createNewVenue() {
552
- appStore.navigateToEntityDetail('venues', 'new')
553
- }
554
- ```
555
-
556
- ### Pattern 4: Error Handling
557
-
558
- ```javascript
559
- import { useWsStore } from 'dzql/client/stores'
560
-
561
- const wsStore = useWsStore()
562
-
563
- async function saveRecord(data) {
564
- const ws = wsStore.getWs()
565
-
566
- try {
567
- const result = await ws.api.save.venues(data)
568
- return result
569
- } catch (err) {
570
- if (err.message.includes('Permission denied')) {
571
- // Handle permission error
572
- alert('You do not have permission to save this record')
573
- } else if (err.message.includes('not found')) {
574
- // Handle not found
575
- alert('Record not found')
576
- } else {
577
- // Generic error
578
- console.error('Save failed:', err)
579
- alert('Failed to save: ' + err.message)
580
- }
581
- throw err
582
- }
583
- }
584
- ```
585
-
586
- ---
587
-
588
- ## Common Issues and Solutions
589
-
590
- ### Issue: AI Gets the Pattern Wrong
591
-
592
- **Problem:** When asking AI to help, it often mixes patterns or forgets the three-phase lifecycle.
593
-
594
- **Solution:** Always reference this document and the template files:
595
- ```
596
- packages/dzql/src/client/stores/useWsStore.js
597
- packages/dzql/src/client/stores/useAppStore.js
598
- packages/dzql/src/client/templates/App.vue
599
- ```
600
-
601
- Point the AI to these files explicitly:
602
- > "Use the canonical stores from packages/dzql/src/client/stores/ - follow the pattern in useWsStore.js for authentication"
603
-
604
- ### Issue: Router Integration Confusion
605
-
606
- **Problem:** Router state doesn't sync with app state.
607
-
608
- **Solution:** Always call `setRouter()` in main.js:
609
- ```javascript
610
- const appStore = useAppStore()
611
- appStore.setRouter(router)
612
- ```
613
-
614
- ### Issue: WebSocket Not Connecting
615
-
616
- **Problem:** Connection fails or times out.
617
-
618
- **Solution:** Check:
619
- 1. Server is running
620
- 2. URL is correct (auto-detection works in browser, use explicit URL in dev)
621
- 3. CORS settings allow WebSocket connections
622
- 4. Check browser console for errors
623
-
624
- ```javascript
625
- // Use explicit URL during development
626
- await appStore.initialize({
627
- wsUrl: import.meta.env.DEV ? 'ws://localhost:3000/ws' : null
628
- })
629
- ```
630
-
631
- ### Issue: Metadata Not Loading
632
-
633
- **Problem:** `appStore.entityMetadata` is empty.
634
-
635
- **Solution:** Metadata loads after authentication. Make sure:
636
- ```javascript
637
- // Option 1: Happens automatically after login
638
- await wsStore.login({ email, password })
639
- // fetchMetadata() called automatically
640
-
641
- // Option 2: Call manually
642
- await appStore.fetchMetadata()
643
- ```
644
-
645
- ---
646
-
647
- ## Migration Guide
648
-
649
- ### From Old Pattern to New Stores
650
-
651
- **Before (old pattern in packages/client/src/stores/main.js):**
652
- ```javascript
653
- import { useWs } from "dzql/client"
654
-
655
- export const useProfileStore = defineStore('profile', () => {
656
- const ws = useWs()
657
- const profile = ref(null)
658
-
659
- ws.onBroadcast(async (method, params) => {
660
- if (method === "connected") {
661
- profile.value = params.profile || null
662
- }
663
- })
664
-
665
- const connect = async () => {
666
- await ws.connect('ws://localhost:3000/ws')
667
- }
668
-
669
- return { profile, connect }
670
- })
671
- ```
672
-
673
- **After (new canonical pattern):**
674
- ```javascript
675
- import { useWsStore, useAppStore } from 'dzql/client/stores'
676
-
677
- // In main.js
678
- const appStore = useAppStore()
679
- await appStore.initialize()
680
-
681
- // In components
682
- const wsStore = useWsStore()
683
- const profile = computed(() => wsStore.profile)
684
- ```
685
-
686
- ---
687
-
688
- ## TypeScript Support
689
-
690
- The stores are written in JavaScript but work perfectly with TypeScript projects. Type definitions coming soon!
691
-
692
- ---
693
-
694
- ## Testing
695
-
696
- ### Unit Testing Stores
697
-
698
- ```javascript
699
- import { setActivePinia, createPinia } from 'pinia'
700
- import { useWsStore } from 'dzql/client/stores'
701
-
702
- describe('useWsStore', () => {
703
- beforeEach(() => {
704
- setActivePinia(createPinia())
705
- })
706
-
707
- it('initializes with correct state', () => {
708
- const wsStore = useWsStore()
709
-
710
- expect(wsStore.connectionState).toBe('disconnected')
711
- expect(wsStore.appState).toBe('connecting')
712
- expect(wsStore.profile).toBeNull()
713
- })
714
- })
715
- ```
716
-
717
- ---
718
-
719
- ## Summary
720
-
721
- The DZQL canonical stores provide:
722
-
723
- ✅ **Three-phase lifecycle** - connecting → login → ready
724
- ✅ **Automatic auth handling** - Token storage, profile management
725
- ✅ **Router integration** - Programmatic navigation
726
- ✅ **Metadata caching** - Entity information
727
- ✅ **Direct WebSocket access** - For DZQL API calls
728
- ✅ **Clear patterns** - Easier for AI to understand
729
-
730
- Use these stores as the foundation for all DZQL Vue.js applications!