dzql 0.6.1 → 0.6.2
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/docs/README.md +1 -1
- package/docs/feature-requests/applyPatch-bug-report.md +2 -2
- package/docs/feature-requests/connection-ready-profile.md +1 -1
- package/docs/feature-requests/hidden-bug-report.md +4 -4
- package/docs/feature-requests/todo.md +1 -1
- package/docs/for_ai.md +17 -17
- package/docs/project-setup.md +37 -37
- package/package.json +5 -1
- package/src/cli/codegen/client.ts +5 -5
- package/src/cli/codegen/pinia.ts +1 -1
- package/src/cli/codegen/subscribable_store.ts +1 -1
- package/src/client/ws.ts +14 -14
- package/src/runtime/index.ts +1 -1
- package/src/runtime/namespace.ts +47 -47
- package/dist/client/index.ts +0 -1
- package/dist/client/stores/useMyProfileStore.ts +0 -114
- package/dist/client/stores/useOrgDashboardStore.ts +0 -131
- package/dist/client/stores/useVenueDetailStore.ts +0 -117
- package/dist/client/ws.ts +0 -716
- package/dist/db/migrations/000_core.sql +0 -92
- package/dist/db/migrations/20251229T212912022Z_schema.sql +0 -3020
- package/dist/db/migrations/20251229T212912022Z_subscribables.sql +0 -371
- package/dist/runtime/manifest.json +0 -1562
package/src/runtime/namespace.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* DZQL Namespace for invokej integration
|
|
3
3
|
*
|
|
4
|
-
* Provides CLI-style access to
|
|
4
|
+
* Provides CLI-style access to DZQL operations via the compiled manifest.
|
|
5
5
|
* Each method outputs JSON to console and closes the connection before returning.
|
|
6
6
|
*
|
|
7
7
|
* Setup - add to your tasks.js:
|
|
8
8
|
* ```js
|
|
9
|
-
* import {
|
|
9
|
+
* import { DzqlNamespace } from 'dzql/namespace';
|
|
10
10
|
*
|
|
11
11
|
* export class Tasks {
|
|
12
12
|
* constructor() {
|
|
13
|
-
* this.
|
|
13
|
+
* this.dzql = new DzqlNamespace();
|
|
14
14
|
* }
|
|
15
15
|
* }
|
|
16
16
|
* ```
|
|
@@ -18,32 +18,32 @@
|
|
|
18
18
|
* Available Commands:
|
|
19
19
|
*
|
|
20
20
|
* Discovery:
|
|
21
|
-
* invj
|
|
22
|
-
* invj
|
|
23
|
-
* invj
|
|
21
|
+
* invj dzql:entities # List all entities
|
|
22
|
+
* invj dzql:subscribables # List all subscribables
|
|
23
|
+
* invj dzql:functions # List all manifest functions
|
|
24
24
|
*
|
|
25
25
|
* Entity CRUD:
|
|
26
|
-
* invj
|
|
27
|
-
* invj
|
|
28
|
-
* invj
|
|
29
|
-
* invj
|
|
30
|
-
* invj
|
|
31
|
-
* invj
|
|
26
|
+
* invj dzql:search venues '{"query": "test"}' # Search with filters
|
|
27
|
+
* invj dzql:get venues '{"id": 1}' # Get by primary key
|
|
28
|
+
* invj dzql:save venues '{"name": "New", "org_id": 1}' # Create (no id)
|
|
29
|
+
* invj dzql:save venues '{"id": 1, "name": "Updated"}' # Update (with id)
|
|
30
|
+
* invj dzql:delete venues '{"id": 1}' # Delete by primary key
|
|
31
|
+
* invj dzql:lookup venues '{"query": "test"}' # Lookup for dropdowns
|
|
32
32
|
*
|
|
33
33
|
* Subscribables:
|
|
34
|
-
* invj
|
|
34
|
+
* invj dzql:subscribe venue_detail '{"venue_id": 1}' # Get snapshot
|
|
35
35
|
*
|
|
36
36
|
* Ad-hoc Function Calls:
|
|
37
|
-
* invj
|
|
38
|
-
* invj
|
|
39
|
-
* invj
|
|
40
|
-
* invj
|
|
37
|
+
* invj dzql:call login_user '{"email": "x", "password": "y"}'
|
|
38
|
+
* invj dzql:call register_user '{"email": "x", "password": "y"}'
|
|
39
|
+
* invj dzql:call get_venue_detail '{"venue_id": 1}'
|
|
40
|
+
* invj dzql:call save_venues '{"name": "Test", "org_id": 1}'
|
|
41
41
|
*
|
|
42
42
|
* Environment:
|
|
43
43
|
* DATABASE_URL - PostgreSQL connection string (default: postgres://localhost:5432/dzql)
|
|
44
44
|
*
|
|
45
45
|
* Requirements:
|
|
46
|
-
* - Run '
|
|
46
|
+
* - Run 'dzql compile' first to generate dist/runtime/manifest.json
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
import postgres from "postgres";
|
|
@@ -74,7 +74,7 @@ function loadManifestFromDisk(): Manifest {
|
|
|
74
74
|
// Fall back to default paths
|
|
75
75
|
const paths = [
|
|
76
76
|
join(process.cwd(), "dist/runtime/manifest.json"),
|
|
77
|
-
join(process.cwd(), "
|
|
77
|
+
join(process.cwd(), "generated/runtime/manifest.json"),
|
|
78
78
|
];
|
|
79
79
|
|
|
80
80
|
for (const path of paths) {
|
|
@@ -85,7 +85,7 @@ function loadManifestFromDisk(): Manifest {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
throw new Error(
|
|
88
|
-
"Manifest not found. Set MANIFEST_PATH env var or run '
|
|
88
|
+
"Manifest not found. Set MANIFEST_PATH env var or run 'dzql compile' to generate dist/runtime/manifest.json"
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -122,9 +122,9 @@ function discoverSubscribables(manifest: Manifest): Record<string, { params: Rec
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
|
-
*
|
|
125
|
+
* DZQL operations namespace for invokej
|
|
126
126
|
*/
|
|
127
|
-
export class
|
|
127
|
+
export class DzqlNamespace {
|
|
128
128
|
private userId: number;
|
|
129
129
|
private sql: postgres.Sql | null = null;
|
|
130
130
|
private manifest: Manifest | null = null;
|
|
@@ -225,13 +225,13 @@ export class TzqlNamespace {
|
|
|
225
225
|
|
|
226
226
|
/**
|
|
227
227
|
* Search an entity
|
|
228
|
-
* @example invj
|
|
228
|
+
* @example invj dzql:search venues '{"query": "test"}'
|
|
229
229
|
*/
|
|
230
230
|
async search(_context: any, entity?: string, argsJson: string = "{}"): Promise<void> {
|
|
231
231
|
if (!entity) {
|
|
232
232
|
console.error("Error: entity name required");
|
|
233
|
-
console.error("Usage: invj
|
|
234
|
-
console.error('Example: invj
|
|
233
|
+
console.error("Usage: invj dzql:search <entity> '<json_args>'");
|
|
234
|
+
console.error('Example: invj dzql:search venues \'{"query": "test"}\'');
|
|
235
235
|
await this.cleanup();
|
|
236
236
|
process.exit(1);
|
|
237
237
|
}
|
|
@@ -258,13 +258,13 @@ export class TzqlNamespace {
|
|
|
258
258
|
|
|
259
259
|
/**
|
|
260
260
|
* Get entity by ID
|
|
261
|
-
* @example invj
|
|
261
|
+
* @example invj dzql:get venues '{"id": 1}'
|
|
262
262
|
*/
|
|
263
263
|
async get(_context: any, entity?: string, argsJson: string = "{}"): Promise<void> {
|
|
264
264
|
if (!entity) {
|
|
265
265
|
console.error("Error: entity name required");
|
|
266
|
-
console.error("Usage: invj
|
|
267
|
-
console.error('Example: invj
|
|
266
|
+
console.error("Usage: invj dzql:get <entity> '<json_args>'");
|
|
267
|
+
console.error('Example: invj dzql:get venues \'{"id": 1}\'');
|
|
268
268
|
await this.cleanup();
|
|
269
269
|
process.exit(1);
|
|
270
270
|
}
|
|
@@ -291,13 +291,13 @@ export class TzqlNamespace {
|
|
|
291
291
|
|
|
292
292
|
/**
|
|
293
293
|
* Save (create or update) entity
|
|
294
|
-
* @example invj
|
|
294
|
+
* @example invj dzql:save venues '{"name": "New Venue", "org_id": 1}'
|
|
295
295
|
*/
|
|
296
296
|
async save(_context: any, entity?: string, argsJson: string = "{}"): Promise<void> {
|
|
297
297
|
if (!entity) {
|
|
298
298
|
console.error("Error: entity name required");
|
|
299
|
-
console.error("Usage: invj
|
|
300
|
-
console.error('Example: invj
|
|
299
|
+
console.error("Usage: invj dzql:save <entity> '<json_args>'");
|
|
300
|
+
console.error('Example: invj dzql:save venues \'{"name": "Test Venue", "org_id": 1}\'');
|
|
301
301
|
await this.cleanup();
|
|
302
302
|
process.exit(1);
|
|
303
303
|
}
|
|
@@ -324,13 +324,13 @@ export class TzqlNamespace {
|
|
|
324
324
|
|
|
325
325
|
/**
|
|
326
326
|
* Delete entity by ID
|
|
327
|
-
* @example invj
|
|
327
|
+
* @example invj dzql:delete venues '{"id": 1}'
|
|
328
328
|
*/
|
|
329
329
|
async delete(_context: any, entity?: string, argsJson: string = "{}"): Promise<void> {
|
|
330
330
|
if (!entity) {
|
|
331
331
|
console.error("Error: entity name required");
|
|
332
|
-
console.error("Usage: invj
|
|
333
|
-
console.error('Example: invj
|
|
332
|
+
console.error("Usage: invj dzql:delete <entity> '<json_args>'");
|
|
333
|
+
console.error('Example: invj dzql:delete venues \'{"id": 1}\'');
|
|
334
334
|
await this.cleanup();
|
|
335
335
|
process.exit(1);
|
|
336
336
|
}
|
|
@@ -357,13 +357,13 @@ export class TzqlNamespace {
|
|
|
357
357
|
|
|
358
358
|
/**
|
|
359
359
|
* Lookup entity (for dropdowns/autocomplete)
|
|
360
|
-
* @example invj
|
|
360
|
+
* @example invj dzql:lookup organisations '{"query": "acme"}'
|
|
361
361
|
*/
|
|
362
362
|
async lookup(_context: any, entity?: string, argsJson: string = "{}"): Promise<void> {
|
|
363
363
|
if (!entity) {
|
|
364
364
|
console.error("Error: entity name required");
|
|
365
|
-
console.error("Usage: invj
|
|
366
|
-
console.error('Example: invj
|
|
365
|
+
console.error("Usage: invj dzql:lookup <entity> '<json_args>'");
|
|
366
|
+
console.error('Example: invj dzql:lookup organisations \'{"query": "acme"}\'');
|
|
367
367
|
await this.cleanup();
|
|
368
368
|
process.exit(1);
|
|
369
369
|
}
|
|
@@ -390,13 +390,13 @@ export class TzqlNamespace {
|
|
|
390
390
|
|
|
391
391
|
/**
|
|
392
392
|
* Get subscribable snapshot
|
|
393
|
-
* @example invj
|
|
393
|
+
* @example invj dzql:subscribe venue_detail '{"venue_id": 1}'
|
|
394
394
|
*/
|
|
395
395
|
async subscribe(_context: any, name?: string, argsJson: string = "{}"): Promise<void> {
|
|
396
396
|
if (!name) {
|
|
397
397
|
console.error("Error: subscribable name required");
|
|
398
|
-
console.error("Usage: invj
|
|
399
|
-
console.error('Example: invj
|
|
398
|
+
console.error("Usage: invj dzql:subscribe <name> '<json_args>'");
|
|
399
|
+
console.error('Example: invj dzql:subscribe venue_detail \'{"venue_id": 1}\'');
|
|
400
400
|
await this.cleanup();
|
|
401
401
|
process.exit(1);
|
|
402
402
|
}
|
|
@@ -423,15 +423,15 @@ export class TzqlNamespace {
|
|
|
423
423
|
|
|
424
424
|
/**
|
|
425
425
|
* Call any function in the manifest by name
|
|
426
|
-
* @example invj
|
|
427
|
-
* @example invj
|
|
426
|
+
* @example invj dzql:call login_user '{"email": "test@example.com", "password": "secret"}'
|
|
427
|
+
* @example invj dzql:call get_venue_detail '{"venue_id": 1}'
|
|
428
428
|
*/
|
|
429
429
|
async call(_context: any, funcName?: string, argsJson: string = "{}"): Promise<void> {
|
|
430
430
|
if (!funcName) {
|
|
431
431
|
console.error("Error: function name required");
|
|
432
|
-
console.error("Usage: invj
|
|
433
|
-
console.error('Example: invj
|
|
434
|
-
console.error('Example: invj
|
|
432
|
+
console.error("Usage: invj dzql:call <function_name> '<json_args>'");
|
|
433
|
+
console.error('Example: invj dzql:call login_user \'{"email": "test@example.com", "password": "secret"}\'');
|
|
434
|
+
console.error('Example: invj dzql:call get_venue_detail \'{"venue_id": 1}\'');
|
|
435
435
|
await this.cleanup();
|
|
436
436
|
process.exit(1);
|
|
437
437
|
}
|
|
@@ -458,7 +458,7 @@ export class TzqlNamespace {
|
|
|
458
458
|
|
|
459
459
|
/**
|
|
460
460
|
* List all available functions in the manifest
|
|
461
|
-
* @example invj
|
|
461
|
+
* @example invj dzql:functions
|
|
462
462
|
*/
|
|
463
463
|
async functions(_context?: any): Promise<void> {
|
|
464
464
|
try {
|
package/dist/client/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './ws.js';
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// Generated by TZQL Compiler v2.0.0
|
|
2
|
-
// Do not edit this file directly.
|
|
3
|
-
|
|
4
|
-
import { defineStore } from 'pinia';
|
|
5
|
-
import { ref, type Ref } from 'vue';
|
|
6
|
-
import { ws } from '../index.js';
|
|
7
|
-
|
|
8
|
-
/** Parameters for my_profile subscription */
|
|
9
|
-
export interface MyProfileParams {
|
|
10
|
-
[key: string]: unknown;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Event from server for patching */
|
|
14
|
-
export interface PatchEvent {
|
|
15
|
-
table: string;
|
|
16
|
-
op: 'insert' | 'update' | 'delete';
|
|
17
|
-
pk: { id: number };
|
|
18
|
-
data: Record<string, unknown> | null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Document wrapper with loading state */
|
|
22
|
-
export interface DocumentWrapper<T> {
|
|
23
|
-
data: T;
|
|
24
|
-
loading: boolean;
|
|
25
|
-
ready: Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const useMyProfileStore = defineStore('sub-my_profile', () => {
|
|
29
|
-
const documents: Ref<Record<string, DocumentWrapper<Record<string, unknown>>>> = ref({});
|
|
30
|
-
const unsubscribers = new Map<string, () => void>();
|
|
31
|
-
|
|
32
|
-
async function bind(params: MyProfileParams): Promise<DocumentWrapper<Record<string, unknown>>> {
|
|
33
|
-
const key = JSON.stringify(params);
|
|
34
|
-
|
|
35
|
-
if (documents.value[key]) {
|
|
36
|
-
const existing = documents.value[key];
|
|
37
|
-
if (existing.loading) {
|
|
38
|
-
await existing.ready;
|
|
39
|
-
}
|
|
40
|
-
return existing;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let resolveReady!: () => void;
|
|
44
|
-
const ready = new Promise<void>((resolve) => { resolveReady = resolve; });
|
|
45
|
-
|
|
46
|
-
documents.value[key] = { data: {}, loading: true, ready };
|
|
47
|
-
let isFirst = true;
|
|
48
|
-
|
|
49
|
-
const unsubscribe = await ws.api.subscribe_my_profile(params, (eventData: unknown) => {
|
|
50
|
-
if (isFirst) {
|
|
51
|
-
// Initial data - merge into existing object to preserve reactivity
|
|
52
|
-
Object.assign(documents.value[key].data, eventData as Record<string, unknown>);
|
|
53
|
-
documents.value[key].loading = false;
|
|
54
|
-
isFirst = false;
|
|
55
|
-
resolveReady();
|
|
56
|
-
} else {
|
|
57
|
-
// Patch event
|
|
58
|
-
applyPatch(documents.value[key].data, eventData as PatchEvent);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
unsubscribers.set(key, unsubscribe as () => void);
|
|
63
|
-
await ready;
|
|
64
|
-
return documents.value[key];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function unbind(params: MyProfileParams): void {
|
|
68
|
-
const key = JSON.stringify(params);
|
|
69
|
-
const unsubscribe = unsubscribers.get(key);
|
|
70
|
-
if (unsubscribe) {
|
|
71
|
-
unsubscribe();
|
|
72
|
-
unsubscribers.delete(key);
|
|
73
|
-
delete documents.value[key];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function applyPatch(doc: Record<string, unknown>, event: PatchEvent): void {
|
|
78
|
-
if (!doc) return;
|
|
79
|
-
switch (event.table) {
|
|
80
|
-
case 'users':
|
|
81
|
-
if (event.op === 'update' && doc.users) {
|
|
82
|
-
Object.assign(doc.users, event.data);
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
case 'acts_for':
|
|
86
|
-
handleArrayPatch(doc.memberships, event);
|
|
87
|
-
break;
|
|
88
|
-
case 'organisations':
|
|
89
|
-
if (event.data && event.data.membership_id) {
|
|
90
|
-
const parent = doc.memberships?.find((p: { id: number }) => p.id === event.data.membership_id);
|
|
91
|
-
if (parent && parent.org) {
|
|
92
|
-
handleArrayPatch(parent.org, event);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function handleArrayPatch(arr: unknown[] | undefined, event: PatchEvent): void {
|
|
100
|
-
if (!arr || !Array.isArray(arr)) return;
|
|
101
|
-
const pkValue = event.pk?.id;
|
|
102
|
-
const idx = arr.findIndex((i: unknown) => (i as { id: number }).id === pkValue);
|
|
103
|
-
|
|
104
|
-
if (event.op === 'insert') {
|
|
105
|
-
if (idx === -1 && event.data) arr.push(event.data);
|
|
106
|
-
} else if (event.op === 'update') {
|
|
107
|
-
if (idx !== -1 && event.data) Object.assign(arr[idx] as object, event.data);
|
|
108
|
-
} else if (event.op === 'delete') {
|
|
109
|
-
if (idx !== -1) arr.splice(idx, 1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return { bind, unbind, documents };
|
|
114
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
// Generated by TZQL Compiler v2.0.0
|
|
2
|
-
// Do not edit this file directly.
|
|
3
|
-
|
|
4
|
-
import { defineStore } from 'pinia';
|
|
5
|
-
import { ref, type Ref } from 'vue';
|
|
6
|
-
import { ws } from '../index.js';
|
|
7
|
-
|
|
8
|
-
/** Parameters for org_dashboard subscription */
|
|
9
|
-
export interface OrgDashboardParams {
|
|
10
|
-
org_id: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Event from server for patching */
|
|
14
|
-
export interface PatchEvent {
|
|
15
|
-
table: string;
|
|
16
|
-
op: 'insert' | 'update' | 'delete';
|
|
17
|
-
pk: { id: number };
|
|
18
|
-
data: Record<string, unknown> | null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Document wrapper with loading state */
|
|
22
|
-
export interface DocumentWrapper<T> {
|
|
23
|
-
data: T;
|
|
24
|
-
loading: boolean;
|
|
25
|
-
ready: Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const useOrgDashboardStore = defineStore('sub-org_dashboard', () => {
|
|
29
|
-
const documents: Ref<Record<string, DocumentWrapper<Record<string, unknown>>>> = ref({});
|
|
30
|
-
const unsubscribers = new Map<string, () => void>();
|
|
31
|
-
|
|
32
|
-
async function bind(params: OrgDashboardParams): Promise<DocumentWrapper<Record<string, unknown>>> {
|
|
33
|
-
const key = JSON.stringify(params);
|
|
34
|
-
|
|
35
|
-
if (documents.value[key]) {
|
|
36
|
-
const existing = documents.value[key];
|
|
37
|
-
if (existing.loading) {
|
|
38
|
-
await existing.ready;
|
|
39
|
-
}
|
|
40
|
-
return existing;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let resolveReady!: () => void;
|
|
44
|
-
const ready = new Promise<void>((resolve) => { resolveReady = resolve; });
|
|
45
|
-
|
|
46
|
-
documents.value[key] = { data: {}, loading: true, ready };
|
|
47
|
-
let isFirst = true;
|
|
48
|
-
|
|
49
|
-
const unsubscribe = await ws.api.subscribe_org_dashboard(params, (eventData: unknown) => {
|
|
50
|
-
if (isFirst) {
|
|
51
|
-
// Initial data - merge into existing object to preserve reactivity
|
|
52
|
-
Object.assign(documents.value[key].data, eventData as Record<string, unknown>);
|
|
53
|
-
documents.value[key].loading = false;
|
|
54
|
-
isFirst = false;
|
|
55
|
-
resolveReady();
|
|
56
|
-
} else {
|
|
57
|
-
// Patch event
|
|
58
|
-
applyPatch(documents.value[key].data, eventData as PatchEvent);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
unsubscribers.set(key, unsubscribe as () => void);
|
|
63
|
-
await ready;
|
|
64
|
-
return documents.value[key];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function unbind(params: OrgDashboardParams): void {
|
|
68
|
-
const key = JSON.stringify(params);
|
|
69
|
-
const unsubscribe = unsubscribers.get(key);
|
|
70
|
-
if (unsubscribe) {
|
|
71
|
-
unsubscribe();
|
|
72
|
-
unsubscribers.delete(key);
|
|
73
|
-
delete documents.value[key];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function applyPatch(doc: Record<string, unknown>, event: PatchEvent): void {
|
|
78
|
-
if (!doc) return;
|
|
79
|
-
switch (event.table) {
|
|
80
|
-
case 'organisations':
|
|
81
|
-
if (event.op === 'update' && doc.organisations) {
|
|
82
|
-
Object.assign(doc.organisations, event.data);
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
case 'venues':
|
|
86
|
-
handleArrayPatch(doc.venues, event);
|
|
87
|
-
break;
|
|
88
|
-
case 'sites':
|
|
89
|
-
if (event.data && event.data.venue_id) {
|
|
90
|
-
const parent = doc.venues?.find((p: { id: number }) => p.id === event.data.venue_id);
|
|
91
|
-
if (parent && parent.sites) {
|
|
92
|
-
handleArrayPatch(parent.sites, event);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
break;
|
|
96
|
-
case 'products':
|
|
97
|
-
handleArrayPatch(doc.products, event);
|
|
98
|
-
break;
|
|
99
|
-
case 'packages':
|
|
100
|
-
handleArrayPatch(doc.packages, event);
|
|
101
|
-
break;
|
|
102
|
-
case 'brands':
|
|
103
|
-
handleArrayPatch(doc.brands, event);
|
|
104
|
-
break;
|
|
105
|
-
case 'artwork':
|
|
106
|
-
if (event.data && event.data.brand_id) {
|
|
107
|
-
const parent = doc.brands?.find((p: { id: number }) => p.id === event.data.brand_id);
|
|
108
|
-
if (parent && parent.artwork) {
|
|
109
|
-
handleArrayPatch(parent.artwork, event);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function handleArrayPatch(arr: unknown[] | undefined, event: PatchEvent): void {
|
|
117
|
-
if (!arr || !Array.isArray(arr)) return;
|
|
118
|
-
const pkValue = event.pk?.id;
|
|
119
|
-
const idx = arr.findIndex((i: unknown) => (i as { id: number }).id === pkValue);
|
|
120
|
-
|
|
121
|
-
if (event.op === 'insert') {
|
|
122
|
-
if (idx === -1 && event.data) arr.push(event.data);
|
|
123
|
-
} else if (event.op === 'update') {
|
|
124
|
-
if (idx !== -1 && event.data) Object.assign(arr[idx] as object, event.data);
|
|
125
|
-
} else if (event.op === 'delete') {
|
|
126
|
-
if (idx !== -1) arr.splice(idx, 1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return { bind, unbind, documents };
|
|
131
|
-
});
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// Generated by TZQL Compiler v2.0.0
|
|
2
|
-
// Do not edit this file directly.
|
|
3
|
-
|
|
4
|
-
import { defineStore } from 'pinia';
|
|
5
|
-
import { ref, type Ref } from 'vue';
|
|
6
|
-
import { ws } from '../index.js';
|
|
7
|
-
|
|
8
|
-
/** Parameters for venue_detail subscription */
|
|
9
|
-
export interface VenueDetailParams {
|
|
10
|
-
venue_id: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Event from server for patching */
|
|
14
|
-
export interface PatchEvent {
|
|
15
|
-
table: string;
|
|
16
|
-
op: 'insert' | 'update' | 'delete';
|
|
17
|
-
pk: { id: number };
|
|
18
|
-
data: Record<string, unknown> | null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Document wrapper with loading state */
|
|
22
|
-
export interface DocumentWrapper<T> {
|
|
23
|
-
data: T;
|
|
24
|
-
loading: boolean;
|
|
25
|
-
ready: Promise<void>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const useVenueDetailStore = defineStore('sub-venue_detail', () => {
|
|
29
|
-
const documents: Ref<Record<string, DocumentWrapper<Record<string, unknown>>>> = ref({});
|
|
30
|
-
const unsubscribers = new Map<string, () => void>();
|
|
31
|
-
|
|
32
|
-
async function bind(params: VenueDetailParams): Promise<DocumentWrapper<Record<string, unknown>>> {
|
|
33
|
-
const key = JSON.stringify(params);
|
|
34
|
-
|
|
35
|
-
if (documents.value[key]) {
|
|
36
|
-
const existing = documents.value[key];
|
|
37
|
-
if (existing.loading) {
|
|
38
|
-
await existing.ready;
|
|
39
|
-
}
|
|
40
|
-
return existing;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let resolveReady!: () => void;
|
|
44
|
-
const ready = new Promise<void>((resolve) => { resolveReady = resolve; });
|
|
45
|
-
|
|
46
|
-
documents.value[key] = { data: {}, loading: true, ready };
|
|
47
|
-
let isFirst = true;
|
|
48
|
-
|
|
49
|
-
const unsubscribe = await ws.api.subscribe_venue_detail(params, (eventData: unknown) => {
|
|
50
|
-
if (isFirst) {
|
|
51
|
-
// Initial data - merge into existing object to preserve reactivity
|
|
52
|
-
Object.assign(documents.value[key].data, eventData as Record<string, unknown>);
|
|
53
|
-
documents.value[key].loading = false;
|
|
54
|
-
isFirst = false;
|
|
55
|
-
resolveReady();
|
|
56
|
-
} else {
|
|
57
|
-
// Patch event
|
|
58
|
-
applyPatch(documents.value[key].data, eventData as PatchEvent);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
unsubscribers.set(key, unsubscribe as () => void);
|
|
63
|
-
await ready;
|
|
64
|
-
return documents.value[key];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function unbind(params: VenueDetailParams): void {
|
|
68
|
-
const key = JSON.stringify(params);
|
|
69
|
-
const unsubscribe = unsubscribers.get(key);
|
|
70
|
-
if (unsubscribe) {
|
|
71
|
-
unsubscribe();
|
|
72
|
-
unsubscribers.delete(key);
|
|
73
|
-
delete documents.value[key];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function applyPatch(doc: Record<string, unknown>, event: PatchEvent): void {
|
|
78
|
-
if (!doc) return;
|
|
79
|
-
switch (event.table) {
|
|
80
|
-
case 'venues':
|
|
81
|
-
if (event.op === 'update' && doc.venues) {
|
|
82
|
-
Object.assign(doc.venues, event.data);
|
|
83
|
-
}
|
|
84
|
-
break;
|
|
85
|
-
case 'organisations':
|
|
86
|
-
handleArrayPatch(doc.org, event);
|
|
87
|
-
break;
|
|
88
|
-
case 'sites':
|
|
89
|
-
handleArrayPatch(doc.sites, event);
|
|
90
|
-
break;
|
|
91
|
-
case 'allocations':
|
|
92
|
-
if (event.data && event.data.site_id) {
|
|
93
|
-
const parent = doc.sites?.find((p: { id: number }) => p.id === event.data.site_id);
|
|
94
|
-
if (parent && parent.allocations) {
|
|
95
|
-
handleArrayPatch(parent.allocations, event);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function handleArrayPatch(arr: unknown[] | undefined, event: PatchEvent): void {
|
|
103
|
-
if (!arr || !Array.isArray(arr)) return;
|
|
104
|
-
const pkValue = event.pk?.id;
|
|
105
|
-
const idx = arr.findIndex((i: unknown) => (i as { id: number }).id === pkValue);
|
|
106
|
-
|
|
107
|
-
if (event.op === 'insert') {
|
|
108
|
-
if (idx === -1 && event.data) arr.push(event.data);
|
|
109
|
-
} else if (event.op === 'update') {
|
|
110
|
-
if (idx !== -1 && event.data) Object.assign(arr[idx] as object, event.data);
|
|
111
|
-
} else if (event.op === 'delete') {
|
|
112
|
-
if (idx !== -1) arr.splice(idx, 1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return { bind, unbind, documents };
|
|
117
|
-
});
|