@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.
- package/dist/nodes/Supabase/Supabase.node.js +1 -1
- package/dist/nodes/Supabase/operations/database/index.d.ts +1 -1
- package/dist/nodes/Supabase/operations/database/index.js +12 -7
- package/dist/nodes/Supabase/utils/supabaseClient.d.ts +6 -4
- package/dist/nodes/Supabase/utils/supabaseClient.js +68 -15
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
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
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function
|
|
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.
|
|
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.
|
|
156
|
-
|
|
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 >
|
|
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
|
|
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
|
|
201
|
-
|
|
202
|
-
|
|
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 (
|
|
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