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.
- package/.env.sample +28 -0
- package/compose.yml +28 -0
- package/dist/client/index.ts +1 -0
- package/dist/client/stores/useMyProfileStore.ts +114 -0
- package/dist/client/stores/useOrgDashboardStore.ts +131 -0
- package/dist/client/stores/useVenueDetailStore.ts +117 -0
- package/dist/client/ws.ts +716 -0
- package/dist/db/migrations/000_core.sql +92 -0
- package/dist/db/migrations/20251229T212912022Z_schema.sql +3020 -0
- package/dist/db/migrations/20251229T212912022Z_subscribables.sql +371 -0
- package/dist/runtime/manifest.json +1562 -0
- package/docs/README.md +309 -36
- package/docs/feature-requests/applyPatch-bug-report.md +85 -0
- package/docs/feature-requests/connection-ready-profile.md +57 -0
- package/docs/feature-requests/hidden-bug-report.md +111 -0
- package/docs/feature-requests/hidden-fields-subscribables.md +34 -0
- package/docs/feature-requests/subscribable-param-key-bug.md +38 -0
- package/docs/feature-requests/todo.md +146 -0
- package/docs/for_ai.md +653 -0
- package/docs/project-setup.md +456 -0
- package/examples/blog.ts +50 -0
- package/examples/invalid.ts +18 -0
- package/examples/venues.js +485 -0
- package/package.json +23 -60
- package/src/cli/codegen/client.ts +99 -0
- package/src/cli/codegen/manifest.ts +95 -0
- package/src/cli/codegen/pinia.ts +174 -0
- package/src/cli/codegen/realtime.ts +58 -0
- package/src/cli/codegen/sql.ts +698 -0
- package/src/cli/codegen/subscribable_sql.ts +547 -0
- package/src/cli/codegen/subscribable_store.ts +184 -0
- package/src/cli/codegen/types.ts +142 -0
- package/src/cli/compiler/analyzer.ts +52 -0
- package/src/cli/compiler/graph_rules.ts +251 -0
- package/src/cli/compiler/ir.ts +233 -0
- package/src/cli/compiler/loader.ts +132 -0
- package/src/cli/compiler/permissions.ts +227 -0
- package/src/cli/index.ts +166 -0
- package/src/client/index.ts +1 -0
- package/src/client/ws.ts +286 -0
- package/src/runtime/auth.ts +39 -0
- package/src/runtime/db.ts +33 -0
- package/src/runtime/errors.ts +51 -0
- package/src/runtime/index.ts +98 -0
- package/src/runtime/js_functions.ts +63 -0
- package/src/runtime/manifest_loader.ts +29 -0
- package/src/runtime/namespace.ts +483 -0
- package/src/runtime/server.ts +87 -0
- package/src/runtime/ws.ts +197 -0
- package/src/shared/ir.ts +197 -0
- package/tests/client.test.ts +38 -0
- package/tests/codegen.test.ts +71 -0
- package/tests/compiler.test.ts +45 -0
- package/tests/graph_rules.test.ts +173 -0
- package/tests/integration/db.test.ts +174 -0
- package/tests/integration/e2e.test.ts +65 -0
- package/tests/integration/features.test.ts +922 -0
- package/tests/integration/full_stack.test.ts +262 -0
- package/tests/integration/setup.ts +45 -0
- package/tests/ir.test.ts +32 -0
- package/tests/namespace.test.ts +395 -0
- package/tests/permissions.test.ts +55 -0
- package/tests/pinia.test.ts +48 -0
- package/tests/realtime.test.ts +22 -0
- package/tests/runtime.test.ts +80 -0
- package/tests/subscribable_gen.test.ts +72 -0
- package/tests/subscribable_reactivity.test.ts +258 -0
- package/tests/venues_gen.test.ts +25 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/README.md +0 -90
- package/bin/cli.js +0 -727
- package/docs/compiler/ADVANCED_FILTERS.md +0 -183
- package/docs/compiler/CODING_STANDARDS.md +0 -415
- package/docs/compiler/COMPARISON.md +0 -673
- package/docs/compiler/QUICKSTART.md +0 -326
- package/docs/compiler/README.md +0 -134
- package/docs/examples/README.md +0 -38
- package/docs/examples/blog.sql +0 -160
- package/docs/examples/venue-detail-simple.sql +0 -8
- package/docs/examples/venue-detail-subscribable.sql +0 -45
- package/docs/for-ai/claude-guide.md +0 -1210
- package/docs/getting-started/quickstart.md +0 -125
- package/docs/getting-started/subscriptions-quick-start.md +0 -203
- package/docs/getting-started/tutorial.md +0 -1104
- package/docs/guides/atomic-updates.md +0 -299
- package/docs/guides/client-stores.md +0 -730
- package/docs/guides/composite-primary-keys.md +0 -158
- package/docs/guides/custom-functions.md +0 -362
- package/docs/guides/drop-semantics.md +0 -554
- package/docs/guides/field-defaults.md +0 -240
- package/docs/guides/interpreter-vs-compiler.md +0 -237
- package/docs/guides/many-to-many.md +0 -929
- package/docs/guides/subscriptions.md +0 -537
- package/docs/reference/api.md +0 -1373
- package/docs/reference/client.md +0 -224
- package/src/client/stores/index.js +0 -8
- package/src/client/stores/useAppStore.js +0 -285
- package/src/client/stores/useWsStore.js +0 -289
- package/src/client/ws.js +0 -762
- package/src/compiler/cli/compile-example.js +0 -33
- package/src/compiler/cli/compile-subscribable.js +0 -43
- package/src/compiler/cli/debug-compile.js +0 -44
- package/src/compiler/cli/debug-parse.js +0 -26
- package/src/compiler/cli/debug-path-parser.js +0 -18
- package/src/compiler/cli/debug-subscribable-parser.js +0 -21
- package/src/compiler/cli/index.js +0 -174
- package/src/compiler/codegen/auth-codegen.js +0 -153
- package/src/compiler/codegen/drop-semantics-codegen.js +0 -553
- package/src/compiler/codegen/graph-rules-codegen.js +0 -450
- package/src/compiler/codegen/notification-codegen.js +0 -232
- package/src/compiler/codegen/operation-codegen.js +0 -1382
- package/src/compiler/codegen/permission-codegen.js +0 -318
- package/src/compiler/codegen/subscribable-codegen.js +0 -827
- package/src/compiler/compiler.js +0 -371
- package/src/compiler/index.js +0 -11
- package/src/compiler/parser/entity-parser.js +0 -440
- package/src/compiler/parser/path-parser.js +0 -290
- package/src/compiler/parser/subscribable-parser.js +0 -244
- package/src/database/dzql-core.sql +0 -161
- package/src/database/migrations/001_schema.sql +0 -60
- package/src/database/migrations/002_functions.sql +0 -890
- package/src/database/migrations/003_operations.sql +0 -1135
- package/src/database/migrations/004_search.sql +0 -581
- package/src/database/migrations/005_entities.sql +0 -730
- package/src/database/migrations/006_auth.sql +0 -94
- package/src/database/migrations/007_events.sql +0 -133
- package/src/database/migrations/008_hello.sql +0 -18
- package/src/database/migrations/008a_meta.sql +0 -172
- package/src/database/migrations/009_subscriptions.sql +0 -240
- package/src/database/migrations/010_atomic_updates.sql +0 -157
- package/src/database/migrations/010_fix_m2m_events.sql +0 -94
- package/src/index.js +0 -40
- package/src/server/api.js +0 -9
- package/src/server/db.js +0 -442
- package/src/server/index.js +0 -317
- package/src/server/logger.js +0 -259
- package/src/server/mcp.js +0 -594
- package/src/server/meta-route.js +0 -251
- package/src/server/namespace.js +0 -292
- package/src/server/subscriptions.js +0 -351
- package/src/server/ws.js +0 -573
package/docs/reference/client.md
DELETED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
# DZQL Client Quick Start
|
|
2
|
-
|
|
3
|
-
**TL;DR:** Copy-paste guide to get a DZQL Vue.js app running in minutes.
|
|
4
|
-
|
|
5
|
-
## 1. Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install dzql pinia vue-router
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## 2. Copy Template
|
|
12
|
-
|
|
13
|
-
Copy the canonical App.vue template:
|
|
14
|
-
```bash
|
|
15
|
-
cp node_modules/dzql/src/client/templates/App.vue src/App.vue
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Or create it manually:
|
|
19
|
-
|
|
20
|
-
**src/App.vue:**
|
|
21
|
-
```vue
|
|
22
|
-
<script setup>
|
|
23
|
-
import { computed, onMounted } from 'vue'
|
|
24
|
-
import { useWsStore, useAppStore } from 'dzql/client/stores'
|
|
25
|
-
import LoginView from './components/LoginView.vue'
|
|
26
|
-
|
|
27
|
-
const wsStore = useWsStore()
|
|
28
|
-
const appStore = useAppStore()
|
|
29
|
-
const state = computed(() => wsStore.appState)
|
|
30
|
-
const profile = computed(() => wsStore.profile)
|
|
31
|
-
|
|
32
|
-
onMounted(() => {
|
|
33
|
-
appStore.initialize({ title: 'My App' })
|
|
34
|
-
})
|
|
35
|
-
</script>
|
|
36
|
-
|
|
37
|
-
<template>
|
|
38
|
-
<!-- CONNECTING -->
|
|
39
|
-
<div v-if="state === 'connecting'">Connecting...</div>
|
|
40
|
-
|
|
41
|
-
<!-- LOGIN -->
|
|
42
|
-
<LoginView v-else-if="state === 'login'" />
|
|
43
|
-
|
|
44
|
-
<!-- READY -->
|
|
45
|
-
<div v-else>
|
|
46
|
-
<nav>
|
|
47
|
-
<h1>{{ appStore.title }}</h1>
|
|
48
|
-
<button @click="wsStore.logout()">Logout</button>
|
|
49
|
-
</nav>
|
|
50
|
-
<router-view />
|
|
51
|
-
</div>
|
|
52
|
-
</template>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## 3. Create LoginView
|
|
56
|
-
|
|
57
|
-
**src/components/LoginView.vue:**
|
|
58
|
-
```vue
|
|
59
|
-
<script setup>
|
|
60
|
-
import { ref } from 'vue'
|
|
61
|
-
import { useWsStore } from 'dzql/client/stores'
|
|
62
|
-
|
|
63
|
-
const wsStore = useWsStore()
|
|
64
|
-
const email = ref('')
|
|
65
|
-
const password = ref('')
|
|
66
|
-
|
|
67
|
-
async function login() {
|
|
68
|
-
try {
|
|
69
|
-
await wsStore.login({ email: email.value, password: password.value })
|
|
70
|
-
} catch (err) {
|
|
71
|
-
alert(err.message)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
</script>
|
|
75
|
-
|
|
76
|
-
<template>
|
|
77
|
-
<form @submit.prevent="login">
|
|
78
|
-
<input v-model="email" type="email" placeholder="Email" required />
|
|
79
|
-
<input v-model="password" type="password" placeholder="Password" required />
|
|
80
|
-
<button type="submit">Login</button>
|
|
81
|
-
</form>
|
|
82
|
-
</template>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Registration with options (e.g., organisation name):**
|
|
86
|
-
```vue
|
|
87
|
-
<script setup>
|
|
88
|
-
import { ref } from 'vue'
|
|
89
|
-
import { useWsStore } from 'dzql/client/stores'
|
|
90
|
-
|
|
91
|
-
const wsStore = useWsStore()
|
|
92
|
-
const email = ref('')
|
|
93
|
-
const password = ref('')
|
|
94
|
-
const orgName = ref('')
|
|
95
|
-
|
|
96
|
-
async function register() {
|
|
97
|
-
try {
|
|
98
|
-
await wsStore.register({
|
|
99
|
-
email: email.value,
|
|
100
|
-
password: password.value,
|
|
101
|
-
options: { org_name: orgName.value }
|
|
102
|
-
})
|
|
103
|
-
} catch (err) {
|
|
104
|
-
alert(err.message)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
</script>
|
|
108
|
-
|
|
109
|
-
<template>
|
|
110
|
-
<form @submit.prevent="register">
|
|
111
|
-
<input v-model="email" type="email" placeholder="Email" required />
|
|
112
|
-
<input v-model="password" type="password" placeholder="Password" required />
|
|
113
|
-
<input v-model="orgName" type="text" placeholder="Organisation Name" />
|
|
114
|
-
<button type="submit">Register</button>
|
|
115
|
-
</form>
|
|
116
|
-
</template>
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
The `options` parameter allows passing additional JSONB data to the `register_user` and `login_user` PostgreSQL functions. This is useful for:
|
|
120
|
-
- Organisation name during registration
|
|
121
|
-
- Device ID for login tracking
|
|
122
|
-
- Any custom fields your auth functions support
|
|
123
|
-
|
|
124
|
-
See [API Reference - Authentication](./api.md#authentication) for details on configuring your PostgreSQL functions.
|
|
125
|
-
|
|
126
|
-
## 4. Setup main.js
|
|
127
|
-
|
|
128
|
-
**src/main.js:**
|
|
129
|
-
```javascript
|
|
130
|
-
import { createApp } from 'vue'
|
|
131
|
-
import { createRouter, createWebHashHistory } from 'vue-router'
|
|
132
|
-
import { createPinia } from 'pinia'
|
|
133
|
-
import { useAppStore } from 'dzql/client/stores'
|
|
134
|
-
import App from './App.vue'
|
|
135
|
-
|
|
136
|
-
const pinia = createPinia()
|
|
137
|
-
|
|
138
|
-
const router = createRouter({
|
|
139
|
-
history: createWebHashHistory(),
|
|
140
|
-
routes: [
|
|
141
|
-
{ path: '/', name: 'home', component: () => import('./views/Home.vue') },
|
|
142
|
-
{ path: '/:entity', name: 'entity-list', component: () => import('./views/EntityList.vue') },
|
|
143
|
-
{ path: '/:entity/:id', name: 'entity-detail', component: () => import('./views/EntityDetail.vue') }
|
|
144
|
-
]
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
const app = createApp(App)
|
|
148
|
-
app.use(pinia)
|
|
149
|
-
app.use(router)
|
|
150
|
-
|
|
151
|
-
const appStore = useAppStore()
|
|
152
|
-
appStore.setRouter(router)
|
|
153
|
-
|
|
154
|
-
app.mount('#app')
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## 5. Use DZQL API
|
|
158
|
-
|
|
159
|
-
**Any component:**
|
|
160
|
-
```vue
|
|
161
|
-
<script setup>
|
|
162
|
-
import { ref, onMounted } from 'vue'
|
|
163
|
-
import { useWsStore } from 'dzql/client/stores'
|
|
164
|
-
|
|
165
|
-
const wsStore = useWsStore()
|
|
166
|
-
const ws = wsStore.getWs()
|
|
167
|
-
const venues = ref([])
|
|
168
|
-
|
|
169
|
-
onMounted(async () => {
|
|
170
|
-
const result = await ws.api.search.venues({ limit: 50 })
|
|
171
|
-
venues.value = result.data
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
async function createVenue() {
|
|
175
|
-
await ws.api.save.venues({ name: 'New Venue' })
|
|
176
|
-
}
|
|
177
|
-
</script>
|
|
178
|
-
|
|
179
|
-
<template>
|
|
180
|
-
<div v-for="venue in venues" :key="venue.id">
|
|
181
|
-
{{ venue.name }}
|
|
182
|
-
</div>
|
|
183
|
-
<button @click="createVenue">Create</button>
|
|
184
|
-
</template>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## That's It! 🎉
|
|
188
|
-
|
|
189
|
-
You now have:
|
|
190
|
-
- ✅ WebSocket connection with auto-reconnect
|
|
191
|
-
- ✅ Three-phase lifecycle (connecting → login → ready)
|
|
192
|
-
- ✅ Authentication (login/logout)
|
|
193
|
-
- ✅ Router integration
|
|
194
|
-
- ✅ DZQL API access
|
|
195
|
-
|
|
196
|
-
## Next Steps
|
|
197
|
-
|
|
198
|
-
- Read [Client Stores Guide](../guides/client-stores.md) for complete API reference
|
|
199
|
-
- Customize the App.vue template
|
|
200
|
-
- Add your own components
|
|
201
|
-
- Style with Tailwind/DaisyUI
|
|
202
|
-
|
|
203
|
-
## Common Issues
|
|
204
|
-
|
|
205
|
-
**"Cannot find module 'dzql/client/stores'"**
|
|
206
|
-
- Make sure you're using dzql v0.1.6 or later
|
|
207
|
-
- Run `npm install dzql@latest`
|
|
208
|
-
|
|
209
|
-
**Connection fails**
|
|
210
|
-
- Check server is running
|
|
211
|
-
- For dev, use explicit URL: `appStore.initialize({ wsUrl: 'ws://localhost:3000/ws' })`
|
|
212
|
-
|
|
213
|
-
**Router not working**
|
|
214
|
-
- Make sure you call `appStore.setRouter(router)` in main.js
|
|
215
|
-
|
|
216
|
-
## Example Projects
|
|
217
|
-
|
|
218
|
-
Check `packages/client` for a complete working example.
|
|
219
|
-
|
|
220
|
-
## Help
|
|
221
|
-
|
|
222
|
-
For more help, see:
|
|
223
|
-
- [Client Stores Guide](../guides/client-stores.md) - Complete documentation
|
|
224
|
-
- [GitHub Issues](https://github.com/blueshed/dzql/issues)
|
|
@@ -1,285 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical DZQL App Pinia Store
|
|
3
|
-
*
|
|
4
|
-
* Manages application-level state including:
|
|
5
|
-
* - App initialization
|
|
6
|
-
* - Router integration
|
|
7
|
-
* - Global UI state
|
|
8
|
-
* - Entity metadata caching
|
|
9
|
-
*
|
|
10
|
-
* Works with useWsStore to provide complete app lifecycle management.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* // In main.js
|
|
14
|
-
* import { createApp } from 'vue'
|
|
15
|
-
* import { createPinia } from 'pinia'
|
|
16
|
-
* import { useAppStore } from 'dzql/client/stores'
|
|
17
|
-
* import App from './App.vue'
|
|
18
|
-
*
|
|
19
|
-
* const pinia = createPinia()
|
|
20
|
-
* const app = createApp(App)
|
|
21
|
-
* app.use(pinia)
|
|
22
|
-
*
|
|
23
|
-
* const appStore = useAppStore()
|
|
24
|
-
* await appStore.initialize()
|
|
25
|
-
*
|
|
26
|
-
* app.mount('#app')
|
|
27
|
-
*/
|
|
28
|
-
import { defineStore } from 'pinia'
|
|
29
|
-
import { ref, computed } from 'vue'
|
|
30
|
-
import { useWsStore } from './useWsStore.js'
|
|
31
|
-
|
|
32
|
-
export const useAppStore = defineStore('dzql-app', () => {
|
|
33
|
-
// ===== State =====
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* App title
|
|
37
|
-
*/
|
|
38
|
-
const title = ref('DZQL App')
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Current route/entity context
|
|
42
|
-
*/
|
|
43
|
-
const currentEntity = ref(null)
|
|
44
|
-
const currentId = ref(null)
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Entity metadata cache
|
|
48
|
-
* Maps entity name -> metadata object
|
|
49
|
-
*/
|
|
50
|
-
const entityMetadata = ref({})
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Loading states
|
|
54
|
-
*/
|
|
55
|
-
const isLoadingMetadata = ref(false)
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* UI state
|
|
59
|
-
*/
|
|
60
|
-
const sidebarOpen = ref(true)
|
|
61
|
-
const propertiesPanelOpen = ref(true)
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Router instance (set via setRouter)
|
|
65
|
-
*/
|
|
66
|
-
let routerInstance = null
|
|
67
|
-
|
|
68
|
-
// ===== Computed =====
|
|
69
|
-
|
|
70
|
-
const hasMetadata = computed(() => Object.keys(entityMetadata.value).length > 0)
|
|
71
|
-
|
|
72
|
-
const entityList = computed(() => {
|
|
73
|
-
return Object.keys(entityMetadata.value).sort()
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
const currentEntityMeta = computed(() => {
|
|
77
|
-
if (!currentEntity.value) return null
|
|
78
|
-
return entityMetadata.value[currentEntity.value] || null
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// ===== Actions =====
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Initialize the app
|
|
85
|
-
*
|
|
86
|
-
* Connects to WebSocket and sets up app lifecycle.
|
|
87
|
-
*
|
|
88
|
-
* @param {Object} options
|
|
89
|
-
* @param {string} [options.wsUrl] - WebSocket URL (auto-detected if not provided)
|
|
90
|
-
* @param {string} [options.title] - App title
|
|
91
|
-
* @returns {Promise<void>}
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* await appStore.initialize()
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* await appStore.initialize({
|
|
98
|
-
* wsUrl: 'ws://localhost:3000/ws',
|
|
99
|
-
* title: 'My DZQL App'
|
|
100
|
-
* })
|
|
101
|
-
*/
|
|
102
|
-
async function initialize(options = {}) {
|
|
103
|
-
const wsStore = useWsStore()
|
|
104
|
-
|
|
105
|
-
// Set app title if provided
|
|
106
|
-
if (options.title) {
|
|
107
|
-
title.value = options.title
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Connect to WebSocket
|
|
111
|
-
await wsStore.connect(options.wsUrl)
|
|
112
|
-
|
|
113
|
-
// If authenticated, fetch metadata
|
|
114
|
-
if (wsStore.isAuthenticated) {
|
|
115
|
-
await fetchMetadata()
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
console.log('[AppStore] Initialized')
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Fetch entity metadata from server
|
|
123
|
-
*
|
|
124
|
-
* Called automatically after authentication.
|
|
125
|
-
*
|
|
126
|
-
* @returns {Promise<void>}
|
|
127
|
-
*/
|
|
128
|
-
async function fetchMetadata() {
|
|
129
|
-
const wsStore = useWsStore()
|
|
130
|
-
const ws = wsStore.getWs()
|
|
131
|
-
|
|
132
|
-
if (!wsStore.isConnected) {
|
|
133
|
-
console.warn('[AppStore] Cannot fetch metadata: not connected')
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
try {
|
|
138
|
-
isLoadingMetadata.value = true
|
|
139
|
-
|
|
140
|
-
// Call the meta endpoint
|
|
141
|
-
const result = await ws.call('meta')
|
|
142
|
-
|
|
143
|
-
if (result && result.entities) {
|
|
144
|
-
entityMetadata.value = result.entities
|
|
145
|
-
console.log('[AppStore] Metadata loaded:', Object.keys(result.entities))
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
} catch (err) {
|
|
149
|
-
console.error('[AppStore] Failed to fetch metadata:', err)
|
|
150
|
-
} finally {
|
|
151
|
-
isLoadingMetadata.value = false
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Set the router instance
|
|
157
|
-
*
|
|
158
|
-
* Call this in main.js after creating the router to enable
|
|
159
|
-
* programmatic navigation from the store.
|
|
160
|
-
*
|
|
161
|
-
* @param {Router} router - Vue Router instance
|
|
162
|
-
*
|
|
163
|
-
* @example
|
|
164
|
-
* import { createRouter } from 'vue-router'
|
|
165
|
-
* import { useAppStore } from 'dzql/client/stores'
|
|
166
|
-
*
|
|
167
|
-
* const router = createRouter({ ... })
|
|
168
|
-
* const appStore = useAppStore()
|
|
169
|
-
* appStore.setRouter(router)
|
|
170
|
-
*/
|
|
171
|
-
function setRouter(router) {
|
|
172
|
-
routerInstance = router
|
|
173
|
-
|
|
174
|
-
// Watch route changes to update current context
|
|
175
|
-
if (router) {
|
|
176
|
-
router.afterEach((to) => {
|
|
177
|
-
currentEntity.value = to.params.entity || null
|
|
178
|
-
currentId.value = to.params.id || null
|
|
179
|
-
})
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Navigate to entity list
|
|
185
|
-
*
|
|
186
|
-
* @param {string} entity - Entity name
|
|
187
|
-
*
|
|
188
|
-
* @example
|
|
189
|
-
* appStore.navigateToEntity('venues')
|
|
190
|
-
*/
|
|
191
|
-
function navigateToEntity(entity) {
|
|
192
|
-
if (routerInstance) {
|
|
193
|
-
routerInstance.push({ name: 'entity-list', params: { entity } })
|
|
194
|
-
}
|
|
195
|
-
currentEntity.value = entity
|
|
196
|
-
currentId.value = null
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Navigate to entity detail/edit
|
|
201
|
-
*
|
|
202
|
-
* @param {string} entity - Entity name
|
|
203
|
-
* @param {number|string} id - Record ID or 'new'
|
|
204
|
-
*
|
|
205
|
-
* @example
|
|
206
|
-
* appStore.navigateToEntityDetail('venues', 123)
|
|
207
|
-
* appStore.navigateToEntityDetail('venues', 'new')
|
|
208
|
-
*/
|
|
209
|
-
function navigateToEntityDetail(entity, id) {
|
|
210
|
-
if (routerInstance) {
|
|
211
|
-
const routeName = id === 'new' ? 'entity-create' : 'entity-edit'
|
|
212
|
-
routerInstance.push({ name: routeName, params: { entity, id } })
|
|
213
|
-
}
|
|
214
|
-
currentEntity.value = entity
|
|
215
|
-
currentId.value = id
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* Navigate to home
|
|
220
|
-
*
|
|
221
|
-
* @example
|
|
222
|
-
* appStore.navigateToHome()
|
|
223
|
-
*/
|
|
224
|
-
function navigateToHome() {
|
|
225
|
-
if (routerInstance) {
|
|
226
|
-
routerInstance.push({ name: 'home' })
|
|
227
|
-
}
|
|
228
|
-
currentEntity.value = null
|
|
229
|
-
currentId.value = null
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Toggle sidebar
|
|
234
|
-
*/
|
|
235
|
-
function toggleSidebar() {
|
|
236
|
-
sidebarOpen.value = !sidebarOpen.value
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Toggle properties panel
|
|
241
|
-
*/
|
|
242
|
-
function togglePropertiesPanel() {
|
|
243
|
-
propertiesPanelOpen.value = !propertiesPanelOpen.value
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Set current context (useful for manual navigation)
|
|
248
|
-
*
|
|
249
|
-
* @param {string|null} entity - Entity name
|
|
250
|
-
* @param {number|string|null} id - Record ID
|
|
251
|
-
*/
|
|
252
|
-
function setContext(entity, id = null) {
|
|
253
|
-
currentEntity.value = entity
|
|
254
|
-
currentId.value = id
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ===== Return Public API =====
|
|
258
|
-
|
|
259
|
-
return {
|
|
260
|
-
// State
|
|
261
|
-
title,
|
|
262
|
-
currentEntity,
|
|
263
|
-
currentId,
|
|
264
|
-
entityMetadata,
|
|
265
|
-
isLoadingMetadata,
|
|
266
|
-
sidebarOpen,
|
|
267
|
-
propertiesPanelOpen,
|
|
268
|
-
|
|
269
|
-
// Computed
|
|
270
|
-
hasMetadata,
|
|
271
|
-
entityList,
|
|
272
|
-
currentEntityMeta,
|
|
273
|
-
|
|
274
|
-
// Actions
|
|
275
|
-
initialize,
|
|
276
|
-
fetchMetadata,
|
|
277
|
-
setRouter,
|
|
278
|
-
navigateToEntity,
|
|
279
|
-
navigateToEntityDetail,
|
|
280
|
-
navigateToHome,
|
|
281
|
-
toggleSidebar,
|
|
282
|
-
togglePropertiesPanel,
|
|
283
|
-
setContext
|
|
284
|
-
}
|
|
285
|
-
})
|