@fachkraftfreund/n8n-nodes-supabase 1.2.13 → 1.2.15

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.
@@ -928,7 +928,7 @@ class Supabase {
928
928
  try {
929
929
  let operationResults = [];
930
930
  if (resource === 'database') {
931
- operationResults = await database_1.executeDatabaseOperation.call(this, supabase, operation, itemIndex);
931
+ operationResults = await database_1.executeDatabaseOperation.call(this, supabase, operation, itemIndex, credentials.host);
932
932
  }
933
933
  else if (resource === 'storage') {
934
934
  operationResults = await storage_1.executeStorageOperation.call(this, supabase, operation, itemIndex);
@@ -1,5 +1,5 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
3
3
  import { DatabaseOperation } from '../../types';
4
- export declare function executeDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemIndex: number): Promise<INodeExecutionData[]>;
4
+ export declare function executeDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemIndex: number, hostUrl: string): Promise<INodeExecutionData[]>;
5
5
  export declare function executeBulkDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemCount: number): Promise<INodeExecutionData[]>;
@@ -2,15 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.executeBulkDatabaseOperation = exports.executeDatabaseOperation = void 0;
4
4
  const supabaseClient_1 = require("../../utils/supabaseClient");
5
- async function executeDatabaseOperation(supabase, operation, itemIndex) {
5
+ async function executeDatabaseOperation(supabase, operation, itemIndex, hostUrl) {
6
6
  const returnData = [];
7
7
  try {
8
8
  switch (operation) {
9
9
  case 'read':
10
- returnData.push(...await handleRead.call(this, supabase, itemIndex));
10
+ returnData.push(...await handleRead.call(this, supabase, itemIndex, hostUrl));
11
11
  break;
12
12
  case 'delete':
13
- returnData.push(...await handleDelete.call(this, supabase, itemIndex));
13
+ returnData.push(...await handleDelete.call(this, supabase, itemIndex, hostUrl));
14
14
  break;
15
15
  case 'createTable':
16
16
  returnData.push(...await handleCreateTable.call(this, supabase, itemIndex));
@@ -188,14 +188,17 @@ function buildReadQuery(supabase, table, returnFields, filters, sort, options) {
188
188
  }
189
189
  return query;
190
190
  }
191
- async function handleRead(supabase, itemIndex) {
191
+ async function handleRead(supabase, itemIndex, hostUrl) {
192
192
  const table = this.getNodeParameter('table', itemIndex);
193
193
  (0, supabaseClient_1.validateTableName)(table);
194
194
  const returnFields = this.getNodeParameter('returnFields', itemIndex, '*');
195
195
  const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
196
196
  const filters = getFilters(this, itemIndex);
197
197
  const sort = this.getNodeParameter('sort.sortField', itemIndex, []);
198
- const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters);
198
+ const overhead = (0, supabaseClient_1.estimateUrlOverhead)(hostUrl, table, returnFields, filters, sort);
199
+ const maxInChars = Math.max(500, supabaseClient_1.MAX_SAFE_URL_LENGTH - overhead);
200
+ const maxItems = (0, supabaseClient_1.computeMaxIdsPerChunk)(returnFields);
201
+ const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters, maxInChars, maxItems);
199
202
  const returnData = [];
200
203
  if (returnAll) {
201
204
  for (const chunkFilters of filterChunks) {
@@ -267,14 +270,16 @@ async function handleRead(supabase, itemIndex) {
267
270
  }
268
271
  return returnData;
269
272
  }
270
- async function handleDelete(supabase, itemIndex) {
273
+ async function handleDelete(supabase, itemIndex, hostUrl) {
271
274
  const table = this.getNodeParameter('table', itemIndex);
272
275
  (0, supabaseClient_1.validateTableName)(table);
273
276
  const filters = this.getNodeParameter('filters.filter', itemIndex, []);
274
277
  if (filters.length === 0) {
275
278
  throw new Error('At least one filter is required for delete operations to prevent accidental data loss');
276
279
  }
277
- const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters);
280
+ const overhead = (0, supabaseClient_1.estimateUrlOverhead)(hostUrl, table, undefined, filters);
281
+ const maxInChars = Math.max(500, supabaseClient_1.MAX_SAFE_URL_LENGTH - overhead);
282
+ const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters, maxInChars);
278
283
  const allDeleted = [];
279
284
  for (const chunkFilters of filterChunks) {
280
285
  let query = supabase.from(table).delete();
@@ -1,5 +1,5 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
- import { ISupabaseCredentials, IRowFilter } from '../types';
2
+ import { ISupabaseCredentials, IRowFilter, IRowSort } from '../types';
3
3
  export declare function createSupabaseClient(credentials: ISupabaseCredentials): SupabaseClient;
4
4
  export declare function validateCredentials(credentials: ISupabaseCredentials): void;
5
5
  export declare function getStorageUrl(projectUrl: string): string;
@@ -12,6 +12,8 @@ export declare function validateTableName(tableName: string): void;
12
12
  export declare function validateColumnName(columnName: string): void;
13
13
  export declare function convertFilterOperator(operator: string): string;
14
14
  export declare function normalizeFilterValue(operator: string, value: string | number | boolean | null | unknown[]): string | number | boolean | null;
15
- export declare const IN_FILTER_MAX_CHAR_LENGTH = 4000;
16
- export declare function chunkInFilterValues(values: unknown[]): unknown[][];
17
- export declare function expandChunkedFilters(filters: IRowFilter[]): IRowFilter[][];
15
+ export declare const MAX_SAFE_URL_LENGTH = 7500;
16
+ export declare function computeMaxIdsPerChunk(selectFields?: string): number;
17
+ export declare function estimateUrlOverhead(hostUrl: string, table: string, selectFields?: string, filters?: IRowFilter[], sort?: IRowSort[]): number;
18
+ export declare function chunkInFilterValues(values: unknown[], maxChars: number, maxItems?: number): unknown[][];
19
+ export declare function expandChunkedFilters(filters: IRowFilter[], maxInChars?: number, maxItems?: number): IRowFilter[][];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.expandChunkedFilters = exports.chunkInFilterValues = exports.IN_FILTER_MAX_CHAR_LENGTH = exports.normalizeFilterValue = exports.convertFilterOperator = exports.validateColumnName = exports.validateTableName = exports.sanitizeColumnName = exports.isNetworkError = exports.isAuthError = exports.formatSupabaseError = exports.getDatabaseUrl = exports.getStorageUrl = exports.validateCredentials = exports.createSupabaseClient = void 0;
3
+ exports.expandChunkedFilters = exports.chunkInFilterValues = exports.estimateUrlOverhead = exports.computeMaxIdsPerChunk = exports.MAX_SAFE_URL_LENGTH = exports.normalizeFilterValue = exports.convertFilterOperator = exports.validateColumnName = exports.validateTableName = exports.sanitizeColumnName = exports.isNetworkError = exports.isAuthError = exports.formatSupabaseError = exports.getDatabaseUrl = exports.getStorageUrl = exports.validateCredentials = exports.createSupabaseClient = void 0;
4
4
  const supabase_js_1 = require("@supabase/supabase-js");
5
5
  function createSupabaseClient(credentials) {
6
6
  const client = (0, supabase_js_1.createClient)(credentials.host, credentials.serviceKey, {
@@ -152,15 +152,51 @@ function normalizeFilterValue(operator, value) {
152
152
  return value;
153
153
  }
154
154
  exports.normalizeFilterValue = normalizeFilterValue;
155
- exports.IN_FILTER_MAX_CHAR_LENGTH = 4000;
156
- function chunkInFilterValues(values) {
155
+ exports.MAX_SAFE_URL_LENGTH = 7500;
156
+ const MIN_IN_CHUNK_CHARS = 500;
157
+ function computeMaxIdsPerChunk(selectFields) {
158
+ const BASE_LIMIT = 2000;
159
+ if (!selectFields || selectFields === '*')
160
+ return BASE_LIMIT;
161
+ const joinCount = (selectFields.match(/\(/g) || []).length;
162
+ if (joinCount === 0)
163
+ return BASE_LIMIT;
164
+ return Math.max(100, Math.floor(BASE_LIMIT / (1 + joinCount * 1.5)));
165
+ }
166
+ exports.computeMaxIdsPerChunk = computeMaxIdsPerChunk;
167
+ function estimateUrlOverhead(hostUrl, table, selectFields, filters, sort) {
168
+ let overhead = hostUrl.length + '/rest/v1/'.length + table.length + 1;
169
+ if (selectFields) {
170
+ overhead += 'select='.length + selectFields.length + 1;
171
+ }
172
+ if (filters) {
173
+ for (const f of filters) {
174
+ if (f.operator === 'in') {
175
+ overhead += f.column.length + '=in.()&'.length;
176
+ }
177
+ else {
178
+ const val = normalizeFilterValue(f.operator, f.value);
179
+ overhead += f.column.length + 1 + f.operator.length + 1 + String(val).length + 1;
180
+ }
181
+ }
182
+ }
183
+ if (sort) {
184
+ for (const s of sort) {
185
+ overhead += 'order='.length + s.column.length + 1 + (s.ascending ? 3 : 4) + 1;
186
+ }
187
+ }
188
+ overhead += 230;
189
+ return overhead;
190
+ }
191
+ exports.estimateUrlOverhead = estimateUrlOverhead;
192
+ function chunkInFilterValues(values, maxChars, maxItems = Infinity) {
157
193
  const chunks = [];
158
194
  let currentChunk = [];
159
195
  let currentLength = 0;
160
196
  for (const value of values) {
161
197
  const valueStr = String(value);
162
198
  const addedLength = currentChunk.length === 0 ? valueStr.length : valueStr.length + 1;
163
- if (currentLength + addedLength > exports.IN_FILTER_MAX_CHAR_LENGTH && currentChunk.length > 0) {
199
+ if ((currentLength + addedLength > maxChars || currentChunk.length >= maxItems) && currentChunk.length > 0) {
164
200
  chunks.push(currentChunk);
165
201
  currentChunk = [value];
166
202
  currentLength = valueStr.length;
@@ -176,9 +212,9 @@ function chunkInFilterValues(values) {
176
212
  return chunks;
177
213
  }
178
214
  exports.chunkInFilterValues = chunkInFilterValues;
179
- function expandChunkedFilters(filters) {
215
+ function expandChunkedFilters(filters, maxInChars, maxItems) {
180
216
  const staticFilters = [];
181
- const chunkedEntries = [];
217
+ const inEntries = [];
182
218
  for (const filter of filters) {
183
219
  if (filter.operator === 'in') {
184
220
  let values;
@@ -197,20 +233,37 @@ function expandChunkedFilters(filters) {
197
233
  staticFilters.push(filter);
198
234
  continue;
199
235
  }
200
- const serialized = values.map(String).join(',');
201
- if (serialized.length > exports.IN_FILTER_MAX_CHAR_LENGTH) {
202
- chunkedEntries.push({
203
- filter: { ...filter, value: values },
204
- chunks: chunkInFilterValues(values),
205
- });
206
- continue;
207
- }
236
+ const serializedLength = values.map(String).join(',').length;
237
+ inEntries.push({ filter: { ...filter, value: values }, values, serializedLength });
238
+ continue;
208
239
  }
209
240
  staticFilters.push(filter);
210
241
  }
211
- if (chunkedEntries.length === 0) {
242
+ if (inEntries.length === 0) {
212
243
  return [filters];
213
244
  }
245
+ const defaultBudget = exports.MAX_SAFE_URL_LENGTH - 500;
246
+ const totalBudget = maxInChars !== null && maxInChars !== void 0 ? maxInChars : defaultBudget;
247
+ const perFilterBudget = Math.max(MIN_IN_CHUNK_CHARS, Math.floor(totalBudget / inEntries.length));
248
+ const chunkedEntries = [];
249
+ const itemCap = maxItems !== null && maxItems !== void 0 ? maxItems : Infinity;
250
+ for (const entry of inEntries) {
251
+ if (entry.serializedLength > perFilterBudget || entry.values.length > itemCap) {
252
+ chunkedEntries.push({
253
+ filter: entry.filter,
254
+ chunks: chunkInFilterValues(entry.values, perFilterBudget, itemCap),
255
+ });
256
+ }
257
+ else {
258
+ staticFilters.push(entry.filter);
259
+ }
260
+ }
261
+ if (chunkedEntries.length === 0) {
262
+ return [filters.map(f => {
263
+ const inEntry = inEntries.find(e => e.filter.column === f.column && f.operator === 'in');
264
+ return inEntry ? inEntry.filter : f;
265
+ })];
266
+ }
214
267
  let combinations = [staticFilters];
215
268
  for (const { filter, chunks } of chunkedEntries) {
216
269
  const newCombinations = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fachkraftfreund/n8n-nodes-supabase",
3
- "version": "1.2.13",
3
+ "version": "1.2.15",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",