@fachkraftfreund/n8n-nodes-supabase 1.3.10 → 1.3.11

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.
@@ -594,6 +594,36 @@ class Supabase {
594
594
  default: 'left',
595
595
  description: 'Left returns all rows even without a match; Inner only returns rows with a match',
596
596
  },
597
+ {
598
+ displayName: 'Order By',
599
+ name: 'orderBy',
600
+ type: 'string',
601
+ default: '',
602
+ placeholder: 'created_at',
603
+ description: 'Column in the joined table to order by (leave empty for no ordering)',
604
+ },
605
+ {
606
+ displayName: 'Order Ascending',
607
+ name: 'orderAscending',
608
+ type: 'boolean',
609
+ default: false,
610
+ description: 'Whether to sort in ascending order (default descending, useful for "latest first")',
611
+ displayOptions: {
612
+ hide: {
613
+ orderBy: [''],
614
+ },
615
+ },
616
+ },
617
+ {
618
+ displayName: 'Limit',
619
+ name: 'limit',
620
+ type: 'number',
621
+ typeOptions: {
622
+ minValue: 0,
623
+ },
624
+ default: 0,
625
+ description: 'Max rows to return from the joined table per parent row (0 = no limit). Set to 1 to get only the latest match.',
626
+ },
597
627
  ],
598
628
  },
599
629
  ],
@@ -54,7 +54,8 @@ function streamWrite(stream, data) {
54
54
  }
55
55
  });
56
56
  }
57
- function buildSelectQuery(supabase, table, selectFields, filters, sort) {
57
+ function buildSelectQuery(supabase, table, selectFields, filters, sort, joins) {
58
+ var _a;
58
59
  let query = supabase.from(table).select(selectFields);
59
60
  for (const filter of filters) {
60
61
  const operator = (0, supabaseClient_2.convertFilterOperator)(filter.operator);
@@ -63,6 +64,21 @@ function buildSelectQuery(supabase, table, selectFields, filters, sort) {
63
64
  for (const s of sort) {
64
65
  query = query.order(s.column, { ascending: s.ascending });
65
66
  }
67
+ if (joins) {
68
+ for (const j of joins) {
69
+ if (!j.table)
70
+ continue;
71
+ if (j.orderBy) {
72
+ query = query.order(j.orderBy, {
73
+ ascending: (_a = j.orderAscending) !== null && _a !== void 0 ? _a : false,
74
+ referencedTable: j.table,
75
+ });
76
+ }
77
+ if (j.limit && j.limit > 0) {
78
+ query = query.limit(j.limit, { referencedTable: j.table });
79
+ }
80
+ }
81
+ }
66
82
  return query;
67
83
  }
68
84
  function parseFilters(context, itemIndex) {
@@ -92,7 +108,7 @@ function parseFilters(context, itemIndex) {
92
108
  }
93
109
  }
94
110
  const BATCH_SIZE = 1000;
95
- async function* fetchBatches(supabase, table, selectFields, filters, sort, hostUrl, returnAll, limit) {
111
+ async function* fetchBatches(supabase, table, selectFields, filters, sort, hostUrl, returnAll, limit, joins) {
96
112
  const overhead = (0, supabaseClient_2.estimateUrlOverhead)(hostUrl, table, selectFields, filters, sort);
97
113
  const maxInChars = Math.max(500, supabaseClient_2.MAX_SAFE_URL_LENGTH - overhead);
98
114
  const maxItems = (0, supabaseClient_2.computeMaxIdsPerChunk)(selectFields);
@@ -112,7 +128,7 @@ async function* fetchBatches(supabase, table, selectFields, filters, sort, hostU
112
128
  if (hasIdColumn) {
113
129
  let lastId = null;
114
130
  while (hasMore) {
115
- let query = buildSelectQuery(supabase, table, selectFields, chunkFilters, []);
131
+ let query = buildSelectQuery(supabase, table, selectFields, chunkFilters, [], joins);
116
132
  if (lastId !== null)
117
133
  query = query.gt('id', lastId);
118
134
  query = query.order('id', { ascending: true }).limit(BATCH_SIZE);
@@ -138,7 +154,7 @@ async function* fetchBatches(supabase, table, selectFields, filters, sort, hostU
138
154
  else {
139
155
  let offset = 0;
140
156
  while (hasMore) {
141
- const query = buildSelectQuery(supabase, table, selectFields, chunkFilters, sort);
157
+ const query = buildSelectQuery(supabase, table, selectFields, chunkFilters, sort, joins);
142
158
  const { data, error } = await query.range(offset, offset + BATCH_SIZE - 1);
143
159
  if (error)
144
160
  throw new Error((0, supabaseClient_2.formatSupabaseError)(error));
@@ -163,7 +179,7 @@ async function* fetchBatches(supabase, table, selectFields, filters, sort, hostU
163
179
  const remaining = maxRows - totalYielded;
164
180
  if (remaining <= 0)
165
181
  break;
166
- const query = buildSelectQuery(supabase, table, selectFields, chunkFilters, sort);
182
+ const query = buildSelectQuery(supabase, table, selectFields, chunkFilters, sort, joins);
167
183
  const { data, error } = await query.limit(remaining);
168
184
  if (error)
169
185
  throw new Error((0, supabaseClient_2.formatSupabaseError)(error));
@@ -256,6 +272,36 @@ class SupabaseCsvExport {
256
272
  ],
257
273
  default: 'left',
258
274
  },
275
+ {
276
+ displayName: 'Order By',
277
+ name: 'orderBy',
278
+ type: 'string',
279
+ default: '',
280
+ placeholder: 'created_at',
281
+ description: 'Column in the joined table to order by (leave empty for no ordering)',
282
+ },
283
+ {
284
+ displayName: 'Order Ascending',
285
+ name: 'orderAscending',
286
+ type: 'boolean',
287
+ default: false,
288
+ description: 'Whether to sort in ascending order (default descending, useful for "latest first")',
289
+ displayOptions: {
290
+ hide: {
291
+ orderBy: [''],
292
+ },
293
+ },
294
+ },
295
+ {
296
+ displayName: 'Limit',
297
+ name: 'limit',
298
+ type: 'number',
299
+ typeOptions: {
300
+ minValue: 0,
301
+ },
302
+ default: 0,
303
+ description: 'Max rows from the joined table per parent row (0 = no limit, 1 = latest only)',
304
+ },
259
305
  ],
260
306
  },
261
307
  ],
@@ -619,7 +665,7 @@ class SupabaseCsvExport {
619
665
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Transform code syntax error: ${msg}`);
620
666
  }
621
667
  }
622
- for await (const batch of fetchBatches(supabase, table, selectWithJoins, filters, sort, credentials.host, returnAll, limit)) {
668
+ for await (const batch of fetchBatches(supabase, table, selectWithJoins, filters, sort, credentials.host, returnAll, limit, joins)) {
623
669
  let rows = batch;
624
670
  if (transformFn) {
625
671
  try {
@@ -315,7 +315,8 @@ function getFilters(context, itemIndex) {
315
315
  throw new Error('Invalid advanced filters JSON');
316
316
  }
317
317
  }
318
- function buildReadQuery(supabase, table, returnFields, filters, sort, options) {
318
+ function buildReadQuery(supabase, table, returnFields, filters, sort, options, joins) {
319
+ var _a;
319
320
  const selectFields = returnFields && returnFields !== '*' ? returnFields : '*';
320
321
  let query = supabase.from(table).select(selectFields, options);
321
322
  for (const filter of filters) {
@@ -325,6 +326,21 @@ function buildReadQuery(supabase, table, returnFields, filters, sort, options) {
325
326
  for (const sortField of sort) {
326
327
  query = query.order(sortField.column, { ascending: sortField.ascending });
327
328
  }
329
+ if (joins) {
330
+ for (const j of joins) {
331
+ if (!j.table)
332
+ continue;
333
+ if (j.orderBy) {
334
+ query = query.order(j.orderBy, {
335
+ ascending: (_a = j.orderAscending) !== null && _a !== void 0 ? _a : false,
336
+ referencedTable: j.table,
337
+ });
338
+ }
339
+ if (j.limit && j.limit > 0) {
340
+ query = query.limit(j.limit, { referencedTable: j.table });
341
+ }
342
+ }
343
+ }
328
344
  return query;
329
345
  }
330
346
  async function handleRead(supabase, itemIndex, hostUrl) {
@@ -370,7 +386,7 @@ async function handleRead(supabase, itemIndex, hostUrl) {
370
386
  if (hasIdColumn) {
371
387
  let lastId = null;
372
388
  while (hasMore) {
373
- let query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, []);
389
+ let query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, [], undefined, joins);
374
390
  if (lastId !== null) {
375
391
  query = query.gt('id', lastId);
376
392
  }
@@ -398,7 +414,7 @@ async function handleRead(supabase, itemIndex, hostUrl) {
398
414
  else {
399
415
  let batchOffset = 0;
400
416
  while (hasMore) {
401
- const query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, sort);
417
+ const query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, sort, undefined, joins);
402
418
  const { data, error } = await query.range(batchOffset, batchOffset + batchSize - 1);
403
419
  if (error) {
404
420
  console.log(`[Supabase READ] chunk ${ci + 1} batch ${batchCount + 1} FAILED after ${Date.now() - chunkStart}ms: ${(0, supabaseClient_1.formatSupabaseError)(error)}`);
@@ -447,7 +463,7 @@ async function handleRead(supabase, itemIndex, hostUrl) {
447
463
  const userOffset = this.getNodeParameter('offset', itemIndex, 0);
448
464
  const isMultiChunk = filterChunks.length > 1;
449
465
  for (const chunkFilters of filterChunks) {
450
- let query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, sort);
466
+ let query = buildReadQuery(supabase, table, selectWithJoins, chunkFilters, sort, undefined, joins);
451
467
  if (isMultiChunk) {
452
468
  query = query.limit(userOffset + limit);
453
469
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fachkraftfreund/n8n-nodes-supabase",
3
- "version": "1.3.10",
3
+ "version": "1.3.11",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",