@fachkraftfreund/n8n-nodes-supabase 1.3.6 → 1.3.8

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.
@@ -203,31 +203,41 @@ async function handleBulkCreate(supabase, itemCount) {
203
203
  async function handleBulkUpsert(supabase, itemCount) {
204
204
  const table = this.getNodeParameter('table', 0);
205
205
  const onConflict = this.getNodeParameter('onConflict', 0, '');
206
- (0, supabaseClient_1.validateTableName)(table);
207
206
  const deduplicate = this.getNodeParameter('deduplicateByConflict', 0, false);
207
+ (0, supabaseClient_1.validateTableName)(table);
208
208
  let rows = collectRowData(this, itemCount);
209
+ console.log(`[Supabase UPSERT ${table}] rows=${rows.length}, onConflict="${onConflict}", deduplicate=${deduplicate}`);
209
210
  const options = {};
210
- if (onConflict) {
211
+ if (onConflict)
211
212
  options.onConflict = onConflict;
212
- if (deduplicate) {
213
- const before = rows.length;
214
- rows = deduplicateByConflictKeys(rows, onConflict);
215
- if (rows.length < before) {
216
- console.log(`[Supabase UPSERT ${table}] deduplicated input: ${before} → ${rows.length} rows by conflict key "${onConflict}"`);
217
- }
218
- }
213
+ if (deduplicate && onConflict) {
214
+ const before = rows.length;
215
+ rows = deduplicateByConflictKeys(rows, onConflict);
216
+ console.log(`[Supabase UPSERT ${table}] dedup by "${onConflict}": ${before} → ${rows.length} rows`);
219
217
  }
220
218
  const returnData = [];
219
+ const duplicateError = 'cannot affect row a second time';
221
220
  for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
222
- const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
221
+ let batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
223
222
  const batchLabel = `UPSERT ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`;
224
223
  const data = await withRetry(async () => {
225
224
  const { data, error } = await supabase
226
225
  .from(table)
227
226
  .upsert(batch, options)
228
227
  .select();
229
- if (error)
230
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
228
+ if (error) {
229
+ const msg = (0, supabaseClient_1.formatSupabaseError)(error);
230
+ if (msg.toLowerCase().includes(duplicateError) && onConflict) {
231
+ const before = batch.length;
232
+ batch = deduplicateByConflictKeys(batch, onConflict);
233
+ console.log(`[Supabase ${batchLabel}] duplicate-row error caught, dedup fallback: ${before} → ${batch.length} rows`);
234
+ const retry = await supabase.from(table).upsert(batch, options).select();
235
+ if (retry.error)
236
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(retry.error));
237
+ return retry.data;
238
+ }
239
+ throw new Error(msg);
240
+ }
231
241
  return data;
232
242
  }, batchLabel);
233
243
  if (Array.isArray(data)) {
@@ -248,6 +258,7 @@ async function handleBulkUpdate(supabase, itemCount) {
248
258
  }
249
259
  const deduplicate = this.getNodeParameter('deduplicateByConflict', 0, false);
250
260
  let rows = collectRowData(this, itemCount);
261
+ console.log(`[Supabase UPDATE ${table}] rows=${rows.length}, matchColumn="${matchColumn}", deduplicate=${deduplicate}`);
251
262
  for (let i = 0; i < rows.length; i++) {
252
263
  const row = rows[i];
253
264
  if (!row || row[matchColumn] === undefined) {
@@ -257,21 +268,31 @@ async function handleBulkUpdate(supabase, itemCount) {
257
268
  if (deduplicate) {
258
269
  const before = rows.length;
259
270
  rows = deduplicateByConflictKeys(rows, matchColumn);
260
- if (rows.length < before) {
261
- console.log(`[Supabase UPDATE ${table}] deduplicated input: ${before} → ${rows.length} rows by match column "${matchColumn}"`);
262
- }
271
+ console.log(`[Supabase UPDATE ${table}] dedup by "${matchColumn}": ${before} → ${rows.length} rows`);
263
272
  }
264
273
  const returnData = [];
274
+ const duplicateError = 'cannot affect row a second time';
265
275
  for (let offset = 0; offset < rows.length; offset += BULK_BATCH_SIZE) {
266
- const batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
276
+ let batch = rows.slice(offset, offset + BULK_BATCH_SIZE);
267
277
  const batchLabel = `UPDATE ${table} batch ${Math.floor(offset / BULK_BATCH_SIZE) + 1}`;
268
278
  const data = await withRetry(async () => {
269
279
  const { data, error } = await supabase
270
280
  .from(table)
271
281
  .upsert(batch, { onConflict: matchColumn })
272
282
  .select();
273
- if (error)
274
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
283
+ if (error) {
284
+ const msg = (0, supabaseClient_1.formatSupabaseError)(error);
285
+ if (msg.toLowerCase().includes(duplicateError)) {
286
+ const before = batch.length;
287
+ batch = deduplicateByConflictKeys(batch, matchColumn);
288
+ console.log(`[Supabase ${batchLabel}] duplicate-row error caught, dedup fallback: ${before} → ${batch.length} rows`);
289
+ const retry = await supabase.from(table).upsert(batch, { onConflict: matchColumn }).select();
290
+ if (retry.error)
291
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(retry.error));
292
+ return retry.data;
293
+ }
294
+ throw new Error(msg);
295
+ }
275
296
  return data;
276
297
  }, batchLabel);
277
298
  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.6",
3
+ "version": "1.3.8",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",