dzql 0.5.16 → 0.5.18

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dzql",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "PostgreSQL-powered framework with zero boilerplate CRUD operations and real-time WebSocket synchronization",
5
5
  "type": "module",
6
6
  "main": "src/server/index.js",
@@ -318,7 +318,7 @@ $$ LANGUAGE plpgsql SECURITY DEFINER;`;
318
318
  if (relIncludes) {
319
319
  const nestedFields = Object.entries(relIncludes).map(([nestedName, nestedEntity]) => {
320
320
  return `'${nestedName}', (
321
- SELECT jsonb_agg(row_to_json(nested.*))
321
+ SELECT COALESCE(jsonb_agg(row_to_json(nested.*)), '[]'::jsonb)
322
322
  FROM ${nestedEntity} nested
323
323
  WHERE nested.${relEntity}_id = rel.id
324
324
  )`;
@@ -334,23 +334,23 @@ $$ LANGUAGE plpgsql SECURITY DEFINER;`;
334
334
  if (relVia) {
335
335
  const { joinClause, whereClause } = this._generateViaJoin(relConfig);
336
336
  return `,
337
- '${relName}', (
337
+ '${relName}', COALESCE((
338
338
  SELECT jsonb_agg(${nestedSelect})
339
339
  FROM ${relEntity} rel
340
340
  ${joinClause}
341
341
  WHERE ${whereClause}
342
- )`;
342
+ ), '[]'::jsonb)`;
343
343
  }
344
344
 
345
345
  // Direct relation (no via)
346
346
  let filterSQL = this._generateRelationFilter(relFilter, relEntity, relConfig);
347
347
 
348
348
  return `,
349
- '${relName}', (
349
+ '${relName}', COALESCE((
350
350
  SELECT jsonb_agg(${nestedSelect})
351
351
  FROM ${relEntity} rel
352
352
  WHERE ${filterSQL}
353
- )`;
353
+ ), '[]'::jsonb)`;
354
354
  }).join('');
355
355
 
356
356
  return selects;
@@ -18,6 +18,23 @@ export { createMCPRoute } from "./mcp.js";
18
18
  async function processSubscriptionUpdates(event, broadcast) {
19
19
  const { table, op, pk, data } = event;
20
20
 
21
+ // Map data to p_old/p_new based on operation type
22
+ // INSERT: new data only, UPDATE: both (we only have current), DELETE: old data only
23
+ let p_old = null;
24
+ let p_new = null;
25
+ switch (op.toUpperCase()) {
26
+ case 'INSERT':
27
+ p_new = data;
28
+ break;
29
+ case 'UPDATE':
30
+ p_old = data;
31
+ p_new = data;
32
+ break;
33
+ case 'DELETE':
34
+ p_old = data;
35
+ break;
36
+ }
37
+
21
38
  // Get all active subscriptions grouped by subscribable
22
39
  const subscriptionsByName = getSubscriptionsBySubscribable();
23
40
 
@@ -39,10 +56,9 @@ async function processSubscriptionUpdates(event, broadcast) {
39
56
  }
40
57
 
41
58
  // Ask PostgreSQL which subscription instances are affected
42
- // Pass data for both old/new - COALESCE in the function handles it
43
59
  const result = await sql.unsafe(
44
60
  `SELECT ${subscribableName}_affected_documents($1, $2, $3, $4) as affected`,
45
- [table, op, data, data]
61
+ [table, op, p_old, p_new]
46
62
  );
47
63
 
48
64
  const affectedParamSets = result[0]?.affected;