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.
- 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 +293 -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 +641 -0
- package/docs/project-setup.md +432 -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 +164 -0
- package/src/client/index.ts +1 -0
- package/src/client/ws.ts +286 -0
- package/src/create/.env.example +8 -0
- package/src/create/README.md +101 -0
- package/src/create/compose.yml +14 -0
- package/src/create/domain.ts +153 -0
- package/src/create/package.json +24 -0
- package/src/create/server.ts +18 -0
- package/src/create/setup.sh +11 -0
- package/src/create/tsconfig.json +15 -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/src/server/meta-route.js
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import { sql } from './db.js';
|
|
2
|
-
|
|
3
|
-
export function metaRoute() {
|
|
4
|
-
return async (req) => {
|
|
5
|
-
try {
|
|
6
|
-
// Get entity metadata from dzql.entities
|
|
7
|
-
const entities = await sql`
|
|
8
|
-
SELECT table_name, label_field, searchable_fields,
|
|
9
|
-
fk_includes, notification_paths, permission_paths
|
|
10
|
-
FROM dzql.entities
|
|
11
|
-
ORDER BY table_name
|
|
12
|
-
`;
|
|
13
|
-
|
|
14
|
-
// Analyze foreign key relationships to determine relationship types
|
|
15
|
-
const foreignKeys = await sql`
|
|
16
|
-
SELECT
|
|
17
|
-
tc.table_name,
|
|
18
|
-
kcu.column_name,
|
|
19
|
-
ccu.table_name AS foreign_table_name,
|
|
20
|
-
ccu.column_name AS foreign_column_name
|
|
21
|
-
FROM
|
|
22
|
-
information_schema.table_constraints AS tc
|
|
23
|
-
JOIN information_schema.key_column_usage AS kcu
|
|
24
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
25
|
-
AND tc.table_schema = kcu.table_schema
|
|
26
|
-
JOIN information_schema.constraint_column_usage AS ccu
|
|
27
|
-
ON ccu.constraint_name = tc.constraint_name
|
|
28
|
-
AND ccu.table_schema = tc.table_schema
|
|
29
|
-
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
30
|
-
AND tc.table_schema = 'public'
|
|
31
|
-
ORDER BY tc.table_name, kcu.column_name
|
|
32
|
-
`;
|
|
33
|
-
|
|
34
|
-
// Get complete table schema information
|
|
35
|
-
const schemaInfo = await sql`
|
|
36
|
-
SELECT
|
|
37
|
-
table_name,
|
|
38
|
-
column_name,
|
|
39
|
-
data_type,
|
|
40
|
-
is_nullable,
|
|
41
|
-
column_default,
|
|
42
|
-
character_maximum_length,
|
|
43
|
-
numeric_precision,
|
|
44
|
-
numeric_scale,
|
|
45
|
-
ordinal_position
|
|
46
|
-
FROM information_schema.columns
|
|
47
|
-
WHERE table_schema = 'public'
|
|
48
|
-
AND table_name = ANY(${entities.map(e => e.table_name)})
|
|
49
|
-
ORDER BY table_name, ordinal_position
|
|
50
|
-
`;
|
|
51
|
-
|
|
52
|
-
// Find junction tables for many-to-many relationships
|
|
53
|
-
const junctionTables = new Set();
|
|
54
|
-
const entityNames = new Set(entities.map(e => e.table_name));
|
|
55
|
-
|
|
56
|
-
// Group foreign keys by table to identify junction tables
|
|
57
|
-
const fksByTable = {};
|
|
58
|
-
foreignKeys.forEach(fk => {
|
|
59
|
-
if (!entityNames.has(fk.table_name)) return;
|
|
60
|
-
if (!fksByTable[fk.table_name]) fksByTable[fk.table_name] = [];
|
|
61
|
-
fksByTable[fk.table_name].push(fk);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Identify junction tables (2+ foreign keys, minimal other fields)
|
|
65
|
-
Object.keys(fksByTable).forEach(tableName => {
|
|
66
|
-
const fks = fksByTable[tableName];
|
|
67
|
-
if (fks.length >= 2) {
|
|
68
|
-
const entity = entities.find(e => e.table_name === tableName);
|
|
69
|
-
const searchableFields = entity?.searchable_fields || [];
|
|
70
|
-
const nonFkFields = searchableFields.filter(field =>
|
|
71
|
-
!fks.some(fk => fk.column_name === field)
|
|
72
|
-
);
|
|
73
|
-
if (nonFkFields.length <= 1) {
|
|
74
|
-
junctionTables.add(tableName);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Build relations array
|
|
80
|
-
const relations = [];
|
|
81
|
-
|
|
82
|
-
// Add foreign key relationships (both directions)
|
|
83
|
-
foreignKeys.forEach(fk => {
|
|
84
|
-
if (!entityNames.has(fk.table_name) || !entityNames.has(fk.foreign_table_name)) return;
|
|
85
|
-
|
|
86
|
-
// Skip junction tables - they'll be handled as many-to-many
|
|
87
|
-
if (junctionTables.has(fk.table_name)) return;
|
|
88
|
-
|
|
89
|
-
// Many-to-one: child.foreign_key → parent.primary_key
|
|
90
|
-
relations.push({
|
|
91
|
-
type: 'many_to_one',
|
|
92
|
-
from: `${fk.table_name}.${fk.column_name}`,
|
|
93
|
-
to: `${fk.foreign_table_name}.${fk.foreign_column_name}`
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// One-to-many: parent.primary_key ← child.foreign_key
|
|
97
|
-
relations.push({
|
|
98
|
-
type: 'one_to_many',
|
|
99
|
-
from: `${fk.foreign_table_name}.${fk.foreign_column_name}`,
|
|
100
|
-
to: `${fk.table_name}.${fk.column_name}`
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Add many-to-many relationships through junction tables
|
|
105
|
-
junctionTables.forEach(tableName => {
|
|
106
|
-
const fks = fksByTable[tableName];
|
|
107
|
-
// For each pair of foreign keys in the junction table
|
|
108
|
-
for (let i = 0; i < fks.length; i++) {
|
|
109
|
-
for (let j = i + 1; j < fks.length; j++) {
|
|
110
|
-
const fk1 = fks[i];
|
|
111
|
-
const fk2 = fks[j];
|
|
112
|
-
|
|
113
|
-
// Both directions of many-to-many
|
|
114
|
-
relations.push({
|
|
115
|
-
type: 'many_to_many',
|
|
116
|
-
from: `${fk1.foreign_table_name}.${fk1.foreign_column_name}`,
|
|
117
|
-
to: `${fk2.foreign_table_name}.${fk2.foreign_column_name}`,
|
|
118
|
-
via: `${tableName}.${fk1.column_name}.${fk2.column_name}`
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
relations.push({
|
|
122
|
-
type: 'many_to_many',
|
|
123
|
-
from: `${fk2.foreign_table_name}.${fk2.foreign_column_name}`,
|
|
124
|
-
to: `${fk1.foreign_table_name}.${fk1.foreign_column_name}`,
|
|
125
|
-
via: `${tableName}.${fk2.column_name}.${fk1.column_name}`
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Build schema object grouped by table
|
|
132
|
-
const schema = {};
|
|
133
|
-
schemaInfo.forEach(col => {
|
|
134
|
-
if (!schema[col.table_name]) {
|
|
135
|
-
schema[col.table_name] = [];
|
|
136
|
-
}
|
|
137
|
-
schema[col.table_name].push({
|
|
138
|
-
column_name: col.column_name,
|
|
139
|
-
data_type: col.data_type,
|
|
140
|
-
is_nullable: col.is_nullable === 'YES',
|
|
141
|
-
column_default: col.column_default,
|
|
142
|
-
character_maximum_length: col.character_maximum_length,
|
|
143
|
-
numeric_precision: col.numeric_precision,
|
|
144
|
-
numeric_scale: col.numeric_scale,
|
|
145
|
-
ordinal_position: col.ordinal_position
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Build navigation graph from user starting point
|
|
150
|
-
const navigationGraph = buildNavigationGraph(entities, relations, schema);
|
|
151
|
-
|
|
152
|
-
const metadata = {
|
|
153
|
-
entities: entities,
|
|
154
|
-
relations: relations,
|
|
155
|
-
schema: schema,
|
|
156
|
-
navigationGraph: navigationGraph,
|
|
157
|
-
operations: ['get', 'save', 'delete', 'lookup', 'search'],
|
|
158
|
-
timestamp: new Date().toISOString()
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
return new Response(JSON.stringify(metadata, null, 2), {
|
|
162
|
-
headers: { 'Content-Type': 'application/json' }
|
|
163
|
-
});
|
|
164
|
-
} catch (error) {
|
|
165
|
-
return new Response(JSON.stringify({ error: error.message }), {
|
|
166
|
-
status: 500,
|
|
167
|
-
headers: { 'Content-Type': 'application/json' }
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function buildNavigationGraph(entities, relations, schema) {
|
|
174
|
-
const graph = {};
|
|
175
|
-
|
|
176
|
-
// Helper to detect UI patterns from schema
|
|
177
|
-
function getUiHints(entityName) {
|
|
178
|
-
const entitySchema = schema[entityName] || [];
|
|
179
|
-
const hints = { primary_view: 'table', alternate_view: 'form', temporal_fields: [], geo_fields: [] };
|
|
180
|
-
|
|
181
|
-
entitySchema.forEach(col => {
|
|
182
|
-
if (col.data_type.includes('date') || col.data_type.includes('time')) {
|
|
183
|
-
hints.temporal_fields.push(col.column_name);
|
|
184
|
-
}
|
|
185
|
-
if (col.column_name.toLowerCase().includes('address') ||
|
|
186
|
-
col.column_name.toLowerCase().includes('location')) {
|
|
187
|
-
hints.geo_fields.push(col.column_name);
|
|
188
|
-
hints.primary_view = 'map';
|
|
189
|
-
}
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
if (hints.temporal_fields.length > 0) {
|
|
193
|
-
hints.alternate_view = 'calendar';
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return hints;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Build navigation paths starting from user
|
|
200
|
-
function buildPathsFrom(currentEntity, currentPath, visited, maxDepth) {
|
|
201
|
-
if (maxDepth <= 0 || visited.has(currentEntity)) return;
|
|
202
|
-
|
|
203
|
-
visited.add(currentEntity);
|
|
204
|
-
const pathKey = currentPath.join('→');
|
|
205
|
-
|
|
206
|
-
if (!graph[pathKey]) {
|
|
207
|
-
const entity = entities.find(e => e.table_name === currentEntity);
|
|
208
|
-
graph[pathKey] = {
|
|
209
|
-
path: pathKey,
|
|
210
|
-
current_entity: currentEntity,
|
|
211
|
-
available_actions: entity ? Object.keys(entity.permission_paths) : [],
|
|
212
|
-
navigation_options: [],
|
|
213
|
-
ui_hints: getUiHints(currentEntity),
|
|
214
|
-
breadcrumb: currentPath.map(p => p.split('.')[0])
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Find all outgoing relations from current entity
|
|
219
|
-
relations.forEach(rel => {
|
|
220
|
-
const fromEntity = rel.from.split('.')[0];
|
|
221
|
-
const toEntity = rel.to.split('.')[0];
|
|
222
|
-
|
|
223
|
-
if (fromEntity === currentEntity && !visited.has(toEntity)) {
|
|
224
|
-
graph[pathKey].navigation_options.push({
|
|
225
|
-
to: toEntity,
|
|
226
|
-
via: `${rel.from}→${rel.to}`,
|
|
227
|
-
relationship: rel.type
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// Recursively build paths
|
|
231
|
-
const newPath = [...currentPath, rel.to];
|
|
232
|
-
buildPathsFrom(toEntity, newPath, new Set(visited), maxDepth - 1);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
visited.delete(currentEntity);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Start building from user
|
|
240
|
-
const userRelations = relations.filter(rel => rel.from.startsWith('users.') || rel.to.startsWith('users.'));
|
|
241
|
-
if (userRelations.length === 0) {
|
|
242
|
-
// If no direct user relations, start from all entities
|
|
243
|
-
entities.forEach(entity => {
|
|
244
|
-
buildPathsFrom(entity.table_name, [entity.table_name + '.id'], new Set(), 3);
|
|
245
|
-
});
|
|
246
|
-
} else {
|
|
247
|
-
buildPathsFrom('users', ['users.id'], new Set(), 4);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return graph;
|
|
251
|
-
}
|
package/src/server/namespace.js
DELETED
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
// Suppress logger output for CLI usage - MUST be set before any imports
|
|
2
|
-
if (!process.env.NODE_ENV) {
|
|
3
|
-
process.env.NODE_ENV = 'production';
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
import { sql, db } from "./db.js";
|
|
7
|
-
|
|
8
|
-
// Default user for CLI operations
|
|
9
|
-
const DEFAULT_USER_ID = 1;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Discover available entities from dzql.entities table or compiled functions
|
|
13
|
-
* @returns {Promise<Object>} Map of entity name to {label, searchable, description}
|
|
14
|
-
*/
|
|
15
|
-
async function discoverEntities() {
|
|
16
|
-
// First try dzql.entities table (runtime mode)
|
|
17
|
-
const result = await sql`
|
|
18
|
-
SELECT table_name, label_field, searchable_fields
|
|
19
|
-
FROM dzql.entities
|
|
20
|
-
ORDER BY table_name
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
const entities = {};
|
|
24
|
-
|
|
25
|
-
if (result.length > 0) {
|
|
26
|
-
// Runtime mode - use dzql.entities table
|
|
27
|
-
for (const row of result) {
|
|
28
|
-
const searchFields = row.searchable_fields?.join(", ") || "none";
|
|
29
|
-
entities[row.table_name] = {
|
|
30
|
-
label: row.label_field,
|
|
31
|
-
searchable: row.searchable_fields || [],
|
|
32
|
-
description: `Entity: ${row.table_name} (label: ${row.label_field}, searchable: ${searchFields})`,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
} else {
|
|
36
|
-
// Compiled mode - discover from function names
|
|
37
|
-
const functions = await sql`
|
|
38
|
-
SELECT DISTINCT substring(proname from 'search_(.+)') as entity_name
|
|
39
|
-
FROM pg_proc
|
|
40
|
-
WHERE pronamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
|
|
41
|
-
AND proname LIKE 'search_%'
|
|
42
|
-
AND substring(proname from 'search_(.+)') IS NOT NULL
|
|
43
|
-
ORDER BY entity_name
|
|
44
|
-
`;
|
|
45
|
-
|
|
46
|
-
for (const row of functions) {
|
|
47
|
-
const entityName = row.entity_name;
|
|
48
|
-
entities[entityName] = {
|
|
49
|
-
label: 'id', // Default, since we can't know from functions alone
|
|
50
|
-
searchable: [],
|
|
51
|
-
description: `Entity: ${entityName} (compiled mode)`,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return entities;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* DZQL operations namespace - provides CLI-style access to DZQL operations
|
|
61
|
-
*
|
|
62
|
-
* Each method outputs JSON to console and calls sql.end() before returning,
|
|
63
|
-
* making instances single-use. On error, methods call process.exit(1).
|
|
64
|
-
*
|
|
65
|
-
* Usage in tasks.js:
|
|
66
|
-
* ```js
|
|
67
|
-
* import { DzqlNamespace } from 'dzql/namespace';
|
|
68
|
-
*
|
|
69
|
-
* export class Tasks {
|
|
70
|
-
* constructor() {
|
|
71
|
-
* this.dzql = new DzqlNamespace();
|
|
72
|
-
* }
|
|
73
|
-
* }
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
export class DzqlNamespace {
|
|
77
|
-
/**
|
|
78
|
-
* @param {number} [userId=1] - User ID for permission checks
|
|
79
|
-
*/
|
|
80
|
-
constructor(userId = DEFAULT_USER_ID) {
|
|
81
|
-
this.userId = userId;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* List all available entities
|
|
86
|
-
* @returns {Promise<void>} Outputs JSON to console
|
|
87
|
-
*/
|
|
88
|
-
async entities(c) {
|
|
89
|
-
try {
|
|
90
|
-
const entities = await discoverEntities();
|
|
91
|
-
console.log(JSON.stringify({ success: true, entities }, null, 2));
|
|
92
|
-
await sql.end();
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error(
|
|
95
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
96
|
-
);
|
|
97
|
-
await sql.end();
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Search an entity
|
|
104
|
-
* @example invj dzql:search venues '{"query": "test"}'
|
|
105
|
-
* @param {string} entity - Entity/table name to search
|
|
106
|
-
* @param {string} [argsJson] - JSON string with search args (query, limit, offset, filters)
|
|
107
|
-
*/
|
|
108
|
-
async search(c, entity, argsJson = "{}") {
|
|
109
|
-
if (!entity) {
|
|
110
|
-
console.error("Error: entity name required");
|
|
111
|
-
console.error("Usage: invokej dzql.search <entity> '<json_args>'");
|
|
112
|
-
console.error(
|
|
113
|
-
'Example: invokej dzql.search organisations \'{"query": "test"}\'',
|
|
114
|
-
);
|
|
115
|
-
await sql.end();
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let args;
|
|
120
|
-
try {
|
|
121
|
-
args = JSON.parse(argsJson);
|
|
122
|
-
} catch (e) {
|
|
123
|
-
console.error("Error: arguments must be valid JSON");
|
|
124
|
-
await sql.end();
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const result = await db.api.search[entity](args, this.userId);
|
|
130
|
-
console.log(JSON.stringify({ success: true, result }, null, 2));
|
|
131
|
-
await sql.end();
|
|
132
|
-
} catch (error) {
|
|
133
|
-
console.error(
|
|
134
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
135
|
-
);
|
|
136
|
-
await sql.end();
|
|
137
|
-
process.exit(1);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Get entity by ID
|
|
143
|
-
* @example invj dzql:get venues '{"id": 1}'
|
|
144
|
-
* @param {string} entity - Entity/table name
|
|
145
|
-
* @param {string} [argsJson] - JSON string with {id: number}
|
|
146
|
-
*/
|
|
147
|
-
async get(c, entity, argsJson = "{}") {
|
|
148
|
-
if (!entity) {
|
|
149
|
-
console.error("Error: entity name required");
|
|
150
|
-
console.error("Usage: invokej dzql.get <entity> '<json_args>'");
|
|
151
|
-
console.error("Example: invokej dzql.get venues '{\"id\": 1}'");
|
|
152
|
-
await sql.end();
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let args;
|
|
157
|
-
try {
|
|
158
|
-
args = JSON.parse(argsJson);
|
|
159
|
-
} catch (e) {
|
|
160
|
-
console.error("Error: arguments must be valid JSON");
|
|
161
|
-
await sql.end();
|
|
162
|
-
process.exit(1);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
try {
|
|
166
|
-
const result = await db.api.get[entity](args, this.userId);
|
|
167
|
-
console.log(JSON.stringify({ success: true, result }, null, 2));
|
|
168
|
-
await sql.end();
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.error(
|
|
171
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
172
|
-
);
|
|
173
|
-
await sql.end();
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Save (create or update) entity
|
|
180
|
-
* @example invj dzql:save venues '{"name": "New Venue", "org_id": 1}'
|
|
181
|
-
* @param {string} entity - Entity/table name
|
|
182
|
-
* @param {string} [argsJson] - JSON string with entity data (include id to update, omit to create)
|
|
183
|
-
*/
|
|
184
|
-
async save(c, entity, argsJson = "{}") {
|
|
185
|
-
if (!entity) {
|
|
186
|
-
console.error("Error: entity name required");
|
|
187
|
-
console.error("Usage: invokej dzql.save <entity> '<json_args>'");
|
|
188
|
-
console.error(
|
|
189
|
-
'Example: invokej dzql.save venues \'{"name": "Test Venue", "org_id": 1}\'',
|
|
190
|
-
);
|
|
191
|
-
await sql.end();
|
|
192
|
-
process.exit(1);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let args;
|
|
196
|
-
try {
|
|
197
|
-
args = JSON.parse(argsJson);
|
|
198
|
-
} catch (e) {
|
|
199
|
-
console.error("Error: arguments must be valid JSON");
|
|
200
|
-
await sql.end();
|
|
201
|
-
process.exit(1);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const result = await db.api.save[entity](args, this.userId);
|
|
206
|
-
console.log(JSON.stringify({ success: true, result }, null, 2));
|
|
207
|
-
await sql.end();
|
|
208
|
-
} catch (error) {
|
|
209
|
-
console.error(
|
|
210
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
211
|
-
);
|
|
212
|
-
await sql.end();
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Delete entity by ID
|
|
219
|
-
* @example invj dzql:delete venues '{"id": 1}'
|
|
220
|
-
* @param {string} entity - Entity/table name
|
|
221
|
-
* @param {string} [argsJson] - JSON string with {id: number}
|
|
222
|
-
*/
|
|
223
|
-
async delete(c, entity, argsJson = "{}") {
|
|
224
|
-
if (!entity) {
|
|
225
|
-
console.error("Error: entity name required");
|
|
226
|
-
console.error("Usage: invokej dzql.delete <entity> '<json_args>'");
|
|
227
|
-
console.error("Example: invokej dzql.delete venues '{\"id\": 1}'");
|
|
228
|
-
await sql.end();
|
|
229
|
-
process.exit(1);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
let args;
|
|
233
|
-
try {
|
|
234
|
-
args = JSON.parse(argsJson);
|
|
235
|
-
} catch (e) {
|
|
236
|
-
console.error("Error: arguments must be valid JSON");
|
|
237
|
-
await sql.end();
|
|
238
|
-
process.exit(1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
try {
|
|
242
|
-
const result = await db.api.delete[entity](args, this.userId);
|
|
243
|
-
console.log(JSON.stringify({ success: true, result }, null, 2));
|
|
244
|
-
await sql.end();
|
|
245
|
-
} catch (error) {
|
|
246
|
-
console.error(
|
|
247
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
248
|
-
);
|
|
249
|
-
await sql.end();
|
|
250
|
-
process.exit(1);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Lookup entity (for dropdowns/autocomplete)
|
|
256
|
-
* @example invj dzql:lookup organisations '{"query": "acme"}'
|
|
257
|
-
* @param {string} entity - Entity/table name
|
|
258
|
-
* @param {string} [argsJson] - JSON string with {query: string, limit?: number}
|
|
259
|
-
*/
|
|
260
|
-
async lookup(c, entity, argsJson = "{}") {
|
|
261
|
-
if (!entity) {
|
|
262
|
-
console.error("Error: entity name required");
|
|
263
|
-
console.error("Usage: invokej dzql.lookup <entity> '<json_args>'");
|
|
264
|
-
console.error(
|
|
265
|
-
'Example: invokej dzql.lookup organisations \'{"query": "acme"}\'',
|
|
266
|
-
);
|
|
267
|
-
await sql.end();
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
let args;
|
|
272
|
-
try {
|
|
273
|
-
args = JSON.parse(argsJson);
|
|
274
|
-
} catch (e) {
|
|
275
|
-
console.error("Error: arguments must be valid JSON");
|
|
276
|
-
await sql.end();
|
|
277
|
-
process.exit(1);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
try {
|
|
281
|
-
const result = await db.api.lookup[entity](args, this.userId);
|
|
282
|
-
console.log(JSON.stringify({ success: true, result }, null, 2));
|
|
283
|
-
await sql.end();
|
|
284
|
-
} catch (error) {
|
|
285
|
-
console.error(
|
|
286
|
-
JSON.stringify({ success: false, error: error.message }, null, 2),
|
|
287
|
-
);
|
|
288
|
-
await sql.end();
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|