@fachkraftfreund/n8n-nodes-supabase 1.3.4 → 1.3.5

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.
@@ -55,7 +55,6 @@ exports.executeDatabaseOperation = executeDatabaseOperation;
55
55
  const BULK_BATCH_SIZE = 500;
56
56
  const MAX_RETRIES = 3;
57
57
  const RETRY_BASE_DELAY_MS = 1000;
58
- const DUPLICATE_ROW_ERROR = 'cannot affect row a second time';
59
58
  function deduplicateByConflictKeys(rows, conflictColumns) {
60
59
  const keys = conflictColumns.split(',').map((k) => k.trim()).filter(Boolean);
61
60
  if (keys.length === 0)
@@ -205,32 +204,27 @@ async function handleBulkUpsert(supabase, itemCount) {
205
204
  const table = this.getNodeParameter('table', 0);
206
205
  const onConflict = this.getNodeParameter('onConflict', 0, '');
207
206
  (0, supabaseClient_1.validateTableName)(table);
208
- const rows = collectRowData(this, itemCount);
207
+ let rows = collectRowData(this, itemCount);
209
208
  const options = {};
210
- if (onConflict)
209
+ if (onConflict) {
211
210
  options.onConflict = onConflict;
211
+ const before = rows.length;
212
+ rows = deduplicateByConflictKeys(rows, onConflict);
213
+ if (rows.length < before) {
214
+ console.log(`[Supabase UPSERT ${table}] deduplicated input: ${before} → ${rows.length} rows by conflict key "${onConflict}"`);
215
+ }
216
+ }
212
217
  const returnData = [];
213
218
  for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
214
- let batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
219
+ const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
215
220
  const batchLabel = `UPSERT ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`;
216
221
  const data = await withRetry(async () => {
217
222
  const { data, error } = await supabase
218
223
  .from(table)
219
224
  .upsert(batch, options)
220
225
  .select();
221
- if (error) {
222
- const msg = (0, supabaseClient_1.formatSupabaseError)(error);
223
- if (msg.toLowerCase().includes(DUPLICATE_ROW_ERROR) && onConflict) {
224
- const before = batch.length;
225
- batch = deduplicateByConflictKeys(batch, onConflict);
226
- console.log(`[Supabase ${batchLabel}] removed ${before - batch.length} duplicate(s) by conflict key "${onConflict}", retrying ${batch.length} rows`);
227
- const retry = await supabase.from(table).upsert(batch, options).select();
228
- if (retry.error)
229
- throw new Error((0, supabaseClient_1.formatSupabaseError)(retry.error));
230
- return retry.data;
231
- }
232
- throw new Error(msg);
233
- }
226
+ if (error)
227
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
234
228
  return data;
235
229
  }, batchLabel);
236
230
  if (Array.isArray(data)) {
@@ -249,35 +243,29 @@ async function handleBulkUpdate(supabase, itemCount) {
249
243
  if (!matchColumn) {
250
244
  throw new Error('Match Column is required for update operations');
251
245
  }
252
- const rows = collectRowData(this, itemCount);
246
+ let rows = collectRowData(this, itemCount);
253
247
  for (let i = 0; i < rows.length; i++) {
254
248
  const row = rows[i];
255
249
  if (!row || row[matchColumn] === undefined) {
256
250
  throw new Error(`Item ${i} is missing the match column "${matchColumn}"`);
257
251
  }
258
252
  }
253
+ const before = rows.length;
254
+ rows = deduplicateByConflictKeys(rows, matchColumn);
255
+ if (rows.length < before) {
256
+ console.log(`[Supabase UPDATE ${table}] deduplicated input: ${before} → ${rows.length} rows by match column "${matchColumn}"`);
257
+ }
259
258
  const returnData = [];
260
259
  for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
261
- let batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
260
+ const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
262
261
  const batchLabel = `UPDATE ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`;
263
262
  const data = await withRetry(async () => {
264
263
  const { data, error } = await supabase
265
264
  .from(table)
266
265
  .upsert(batch, { onConflict: matchColumn })
267
266
  .select();
268
- if (error) {
269
- const msg = (0, supabaseClient_1.formatSupabaseError)(error);
270
- if (msg.toLowerCase().includes(DUPLICATE_ROW_ERROR)) {
271
- const before = batch.length;
272
- batch = deduplicateByConflictKeys(batch, matchColumn);
273
- console.log(`[Supabase ${batchLabel}] removed ${before - batch.length} duplicate(s) by match column "${matchColumn}", retrying ${batch.length} rows`);
274
- const retry = await supabase.from(table).upsert(batch, { onConflict: matchColumn }).select();
275
- if (retry.error)
276
- throw new Error((0, supabaseClient_1.formatSupabaseError)(retry.error));
277
- return retry.data;
278
- }
279
- throw new Error(msg);
280
- }
267
+ if (error)
268
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
281
269
  return data;
282
270
  }, batchLabel);
283
271
  if (Array.isArray(data)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fachkraftfreund/n8n-nodes-supabase",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",