dzql 0.5.8 → 0.5.10
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/package.json
CHANGED
|
@@ -238,13 +238,22 @@ $$ LANGUAGE plpgsql STABLE SECURITY DEFINER;`;
|
|
|
238
238
|
// Build relation subqueries
|
|
239
239
|
const relationSelects = this._generateRelationSelects();
|
|
240
240
|
|
|
241
|
+
// Build schema with path mapping and scope tables (baked in at compile time)
|
|
242
|
+
const pathMapping = this.buildPathMapping();
|
|
243
|
+
const scopeTables = this.extractScopeTables();
|
|
244
|
+
const schemaJson = JSON.stringify({
|
|
245
|
+
root: this.rootEntity,
|
|
246
|
+
paths: pathMapping,
|
|
247
|
+
scopeTables: scopeTables
|
|
248
|
+
});
|
|
249
|
+
|
|
241
250
|
return `CREATE OR REPLACE FUNCTION get_${this.name}(
|
|
242
251
|
p_params JSONB,
|
|
243
252
|
p_user_id INT
|
|
244
253
|
) RETURNS JSONB AS $$
|
|
245
254
|
DECLARE
|
|
246
255
|
${paramDeclarations}
|
|
247
|
-
|
|
256
|
+
v_data JSONB;
|
|
248
257
|
BEGIN
|
|
249
258
|
-- Extract parameters
|
|
250
259
|
${paramExtractions}
|
|
@@ -258,11 +267,15 @@ ${paramExtractions}
|
|
|
258
267
|
SELECT jsonb_build_object(
|
|
259
268
|
'${this.rootEntity}', row_to_json(root.*)${relationSelects}
|
|
260
269
|
)
|
|
261
|
-
INTO
|
|
270
|
+
INTO v_data
|
|
262
271
|
FROM ${this.rootEntity} root
|
|
263
272
|
WHERE ${rootFilter};
|
|
264
273
|
|
|
265
|
-
|
|
274
|
+
-- Return data with embedded schema for atomic updates
|
|
275
|
+
RETURN jsonb_build_object(
|
|
276
|
+
'data', v_data,
|
|
277
|
+
'schema', '${schemaJson}'::jsonb
|
|
278
|
+
);
|
|
266
279
|
END;
|
|
267
280
|
$$ LANGUAGE plpgsql SECURITY DEFINER;`;
|
|
268
281
|
}
|
|
@@ -221,50 +221,55 @@ export function getAllSubscriptions() {
|
|
|
221
221
|
* @returns {Promise<{scopeTables: string[], pathMapping: object, rootEntity: string, relations: object}>}
|
|
222
222
|
*/
|
|
223
223
|
export async function getSubscribableMetadata(subscribableName, sql) {
|
|
224
|
-
// Check cache first
|
|
224
|
+
// Check cache first (compiled mode populates this from embedded schema)
|
|
225
225
|
if (subscribableMetadataCache.has(subscribableName)) {
|
|
226
226
|
return subscribableMetadataCache.get(subscribableName);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if (!result || result.length === 0) {
|
|
237
|
-
// Return empty metadata if subscribable not found
|
|
238
|
-
const emptyMetadata = {
|
|
239
|
-
scopeTables: [],
|
|
240
|
-
pathMapping: {},
|
|
241
|
-
rootEntity: null,
|
|
242
|
-
relations: {}
|
|
243
|
-
};
|
|
244
|
-
return emptyMetadata;
|
|
245
|
-
}
|
|
229
|
+
// Interpreted mode: fetch from database table
|
|
230
|
+
try {
|
|
231
|
+
const result = await sql`
|
|
232
|
+
SELECT scope_tables, root_entity, relations
|
|
233
|
+
FROM dzql.subscribables
|
|
234
|
+
WHERE name = ${subscribableName}
|
|
235
|
+
`;
|
|
246
236
|
|
|
247
|
-
|
|
237
|
+
if (result && result.length > 0) {
|
|
238
|
+
const { scope_tables, root_entity, relations } = result[0];
|
|
248
239
|
|
|
249
|
-
|
|
250
|
-
|
|
240
|
+
// Build path mapping from relations
|
|
241
|
+
const pathMapping = buildPathMappingFromRelations(root_entity, relations);
|
|
251
242
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
243
|
+
const metadata = {
|
|
244
|
+
scopeTables: scope_tables || [],
|
|
245
|
+
pathMapping,
|
|
246
|
+
rootEntity: root_entity,
|
|
247
|
+
relations: relations || {}
|
|
248
|
+
};
|
|
258
249
|
|
|
259
|
-
|
|
260
|
-
|
|
250
|
+
// Cache for future use
|
|
251
|
+
subscribableMetadataCache.set(subscribableName, metadata);
|
|
261
252
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
253
|
+
wsLogger.debug(`Cached metadata for subscribable ${subscribableName}:`, {
|
|
254
|
+
scopeTables: metadata.scopeTables,
|
|
255
|
+
pathMapping: metadata.pathMapping
|
|
256
|
+
});
|
|
266
257
|
|
|
267
|
-
|
|
258
|
+
return metadata;
|
|
259
|
+
}
|
|
260
|
+
} catch (e) {
|
|
261
|
+
// Table doesn't exist or query failed
|
|
262
|
+
wsLogger.debug(`Failed to fetch metadata for ${subscribableName}: ${e.message}`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Return empty metadata if nothing found (compiled mode without prior subscribe)
|
|
266
|
+
const emptyMetadata = {
|
|
267
|
+
scopeTables: [],
|
|
268
|
+
pathMapping: {},
|
|
269
|
+
rootEntity: null,
|
|
270
|
+
relations: {}
|
|
271
|
+
};
|
|
272
|
+
return emptyMetadata;
|
|
268
273
|
}
|
|
269
274
|
|
|
270
275
|
/**
|
|
@@ -322,6 +327,18 @@ export function clearSubscribableMetadataCache(subscribableName = null) {
|
|
|
322
327
|
}
|
|
323
328
|
}
|
|
324
329
|
|
|
330
|
+
/**
|
|
331
|
+
* Cache subscribable metadata (used by compiled mode to store embedded schema)
|
|
332
|
+
* @param {string} subscribableName - Name of the subscribable
|
|
333
|
+
* @param {object} metadata - Metadata to cache
|
|
334
|
+
*/
|
|
335
|
+
export function cacheSubscribableMetadata(subscribableName, metadata) {
|
|
336
|
+
subscribableMetadataCache.set(subscribableName, metadata);
|
|
337
|
+
wsLogger.debug(`Cached compiled metadata for ${subscribableName}:`, {
|
|
338
|
+
scopeTables: metadata.scopeTables
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
325
342
|
/**
|
|
326
343
|
* Get scope tables for a subscribable (convenience function)
|
|
327
344
|
* @param {string} subscribableName - Name of the subscribable
|
package/src/server/ws.js
CHANGED
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
unregisterSubscription,
|
|
13
13
|
unregisterSubscriptionByParams,
|
|
14
14
|
removeConnectionSubscriptions,
|
|
15
|
-
getSubscribableMetadata
|
|
15
|
+
getSubscribableMetadata,
|
|
16
|
+
cacheSubscribableMetadata
|
|
16
17
|
} from "./subscriptions.js";
|
|
17
18
|
|
|
18
19
|
// Environment configuration
|
|
@@ -322,14 +323,37 @@ export function createRPCHandler(customHandlers = {}) {
|
|
|
322
323
|
try {
|
|
323
324
|
// Execute initial query (this also checks permissions)
|
|
324
325
|
const queryResult = await sql.unsafe(
|
|
325
|
-
`SELECT get_${subscribableName}($1, $2) as
|
|
326
|
+
`SELECT get_${subscribableName}($1, $2) as result`,
|
|
326
327
|
[params, ws.data.user_id]
|
|
327
328
|
);
|
|
328
329
|
|
|
329
|
-
const
|
|
330
|
-
|
|
331
|
-
//
|
|
332
|
-
|
|
330
|
+
const queryData = queryResult[0]?.result;
|
|
331
|
+
|
|
332
|
+
// Check if compiled function returned embedded schema
|
|
333
|
+
// Compiled: { data, schema } | Interpreted: just the data object
|
|
334
|
+
let data, schema;
|
|
335
|
+
if (queryData && queryData.schema && queryData.data !== undefined) {
|
|
336
|
+
// Compiled mode - schema is embedded (includes scopeTables)
|
|
337
|
+
data = queryData.data;
|
|
338
|
+
schema = queryData.schema;
|
|
339
|
+
// Cache scopeTables for event filtering
|
|
340
|
+
if (schema.scopeTables) {
|
|
341
|
+
cacheSubscribableMetadata(subscribableName, {
|
|
342
|
+
scopeTables: schema.scopeTables,
|
|
343
|
+
pathMapping: schema.paths,
|
|
344
|
+
rootEntity: schema.root,
|
|
345
|
+
relations: {}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
} else {
|
|
349
|
+
// Interpreted mode - fetch schema from metadata table
|
|
350
|
+
data = queryData;
|
|
351
|
+
const metadata = await getSubscribableMetadata(subscribableName, sql);
|
|
352
|
+
schema = {
|
|
353
|
+
root: metadata.rootEntity,
|
|
354
|
+
paths: metadata.pathMapping
|
|
355
|
+
};
|
|
356
|
+
}
|
|
333
357
|
|
|
334
358
|
// Register subscription in memory
|
|
335
359
|
const subscriptionId = registerSubscription(
|
|
@@ -343,11 +367,7 @@ export function createRPCHandler(customHandlers = {}) {
|
|
|
343
367
|
const result = {
|
|
344
368
|
subscription_id: subscriptionId,
|
|
345
369
|
data,
|
|
346
|
-
|
|
347
|
-
schema: {
|
|
348
|
-
root: metadata.rootEntity,
|
|
349
|
-
paths: metadata.pathMapping
|
|
350
|
-
}
|
|
370
|
+
schema
|
|
351
371
|
};
|
|
352
372
|
|
|
353
373
|
wsLogger.response(method, result, Date.now() - startTime);
|