@fachkraftfreund/n8n-nodes-supabase 1.2.8 → 1.2.10

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.
@@ -891,6 +891,12 @@ class Supabase {
891
891
  async execute() {
892
892
  const items = this.getInputData();
893
893
  const returnData = [];
894
+ const firstItem = items[0];
895
+ if (items.length === 1 &&
896
+ (firstItem === null || firstItem === void 0 ? void 0 : firstItem.json) &&
897
+ Object.keys(firstItem.json).length === 0) {
898
+ return [[{ json: {} }]];
899
+ }
894
900
  const credentials = await this.getCredentials('supabaseExtendedApi');
895
901
  try {
896
902
  (0, supabaseClient_1.validateCredentials)(credentials);
@@ -0,0 +1,11 @@
1
+ import { ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription, IPollFunctions } from 'n8n-workflow';
2
+ export declare class SupabaseTrigger implements INodeType {
3
+ description: INodeTypeDescription;
4
+ poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
5
+ methods: {
6
+ loadOptions: {
7
+ getTables(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
+ getColumns(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
9
+ };
10
+ };
11
+ }
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SupabaseTrigger = void 0;
4
+ const supabaseClient_1 = require("./utils/supabaseClient");
5
+ class SupabaseTrigger {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'Supabase Trigger',
9
+ name: 'supabaseExtendedTrigger',
10
+ icon: 'file:icons/supabase.svg',
11
+ group: ['trigger'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["event"]}}',
14
+ description: 'Triggers when rows are created or updated in a Supabase table',
15
+ polling: true,
16
+ defaults: {
17
+ name: 'Supabase Trigger',
18
+ },
19
+ inputs: [],
20
+ outputs: ['main'],
21
+ credentials: [
22
+ {
23
+ name: 'supabaseExtendedApi',
24
+ required: true,
25
+ },
26
+ ],
27
+ properties: [
28
+ {
29
+ displayName: 'Table',
30
+ name: 'table',
31
+ type: 'options',
32
+ typeOptions: {
33
+ loadOptionsMethod: 'getTables',
34
+ },
35
+ required: true,
36
+ default: '',
37
+ description: 'Table to watch for changes',
38
+ },
39
+ {
40
+ displayName: 'Event',
41
+ name: 'event',
42
+ type: 'options',
43
+ options: [
44
+ {
45
+ name: 'Row Created',
46
+ value: 'rowCreated',
47
+ description: 'Trigger when new rows are inserted',
48
+ },
49
+ {
50
+ name: 'Row Updated',
51
+ value: 'rowUpdated',
52
+ description: 'Trigger when existing rows are updated',
53
+ },
54
+ {
55
+ name: 'Row Created or Updated',
56
+ value: 'rowCreatedOrUpdated',
57
+ description: 'Trigger on both inserts and updates',
58
+ },
59
+ ],
60
+ default: 'rowCreated',
61
+ },
62
+ {
63
+ displayName: 'Timestamp Column',
64
+ name: 'timestampColumn',
65
+ type: 'options',
66
+ typeOptions: {
67
+ loadOptionsMethod: 'getColumns',
68
+ },
69
+ required: true,
70
+ default: '',
71
+ description: 'Column that tracks when the row was created or last updated',
72
+ displayOptions: {
73
+ show: {
74
+ event: ['rowCreated', 'rowUpdated'],
75
+ },
76
+ },
77
+ },
78
+ {
79
+ displayName: 'Created At Column',
80
+ name: 'createdAtColumn',
81
+ type: 'options',
82
+ typeOptions: {
83
+ loadOptionsMethod: 'getColumns',
84
+ },
85
+ required: true,
86
+ default: '',
87
+ description: 'Column that stores row creation time',
88
+ displayOptions: {
89
+ show: {
90
+ event: ['rowCreatedOrUpdated'],
91
+ },
92
+ },
93
+ },
94
+ {
95
+ displayName: 'Updated At Column',
96
+ name: 'updatedAtColumn',
97
+ type: 'options',
98
+ typeOptions: {
99
+ loadOptionsMethod: 'getColumns',
100
+ },
101
+ required: true,
102
+ default: '',
103
+ description: 'Column that stores row last-update time',
104
+ displayOptions: {
105
+ show: {
106
+ event: ['rowCreatedOrUpdated'],
107
+ },
108
+ },
109
+ },
110
+ {
111
+ displayName: 'Key Column',
112
+ name: 'keyColumn',
113
+ type: 'options',
114
+ typeOptions: {
115
+ loadOptionsMethod: 'getColumns',
116
+ },
117
+ required: true,
118
+ default: '',
119
+ description: 'Unique identifier column (e.g. id) used for deduplication and field-change tracking',
120
+ },
121
+ {
122
+ displayName: 'Watched Fields',
123
+ name: 'watchedFields',
124
+ type: 'multiOptions',
125
+ typeOptions: {
126
+ loadOptionsMethod: 'getColumns',
127
+ },
128
+ default: [],
129
+ description: 'Only trigger when these specific fields change. Leave empty to trigger on any change.',
130
+ },
131
+ ],
132
+ };
133
+ this.methods = {
134
+ loadOptions: {
135
+ async getTables() {
136
+ const credentials = await this.getCredentials('supabaseExtendedApi');
137
+ (0, supabaseClient_1.validateCredentials)(credentials);
138
+ const host = credentials.host.replace(/\/$/, '');
139
+ try {
140
+ const response = await this.helpers.request({
141
+ method: 'GET',
142
+ url: `${host}/rest/v1/`,
143
+ headers: {
144
+ apikey: credentials.serviceKey,
145
+ Authorization: `Bearer ${credentials.serviceKey}`,
146
+ },
147
+ json: true,
148
+ });
149
+ const definitions = response.definitions || {};
150
+ const tables = Object.keys(definitions).sort();
151
+ if (tables.length === 0) {
152
+ return [{ name: 'No tables found', value: '', description: 'No tables are exposed via the REST API' }];
153
+ }
154
+ return tables.map((t) => ({ name: t, value: t }));
155
+ }
156
+ catch (error) {
157
+ const msg = error instanceof Error ? error.message : 'Unknown error';
158
+ return [{ name: `Error: ${msg}`, value: '', description: 'Failed to load tables. Check your credentials.' }];
159
+ }
160
+ },
161
+ async getColumns() {
162
+ const credentials = await this.getCredentials('supabaseExtendedApi');
163
+ (0, supabaseClient_1.validateCredentials)(credentials);
164
+ const table = this.getCurrentNodeParameter('table');
165
+ if (!table) {
166
+ return [{ name: 'Select a table first', value: '', description: 'Choose a table to load its columns' }];
167
+ }
168
+ const host = credentials.host.replace(/\/$/, '');
169
+ try {
170
+ const response = await this.helpers.request({
171
+ method: 'GET',
172
+ url: `${host}/rest/v1/`,
173
+ headers: {
174
+ apikey: credentials.serviceKey,
175
+ Authorization: `Bearer ${credentials.serviceKey}`,
176
+ },
177
+ json: true,
178
+ });
179
+ const definitions = response.definitions || {};
180
+ const tableSchema = definitions[table];
181
+ if (!(tableSchema === null || tableSchema === void 0 ? void 0 : tableSchema.properties)) {
182
+ return [{ name: 'No columns found', value: '', description: `Table "${table}" not found or has no columns` }];
183
+ }
184
+ return Object.keys(tableSchema.properties).sort().map((col) => {
185
+ const colDef = tableSchema.properties[col];
186
+ const typeLabel = colDef.format ? `${colDef.type} (${colDef.format})` : colDef.type;
187
+ return { name: col, value: col, description: typeLabel };
188
+ });
189
+ }
190
+ catch (error) {
191
+ const msg = error instanceof Error ? error.message : 'Unknown error';
192
+ return [{ name: `Error: ${msg}`, value: '', description: 'Failed to load columns. Check your credentials.' }];
193
+ }
194
+ },
195
+ },
196
+ };
197
+ }
198
+ async poll() {
199
+ const credentials = await this.getCredentials('supabaseExtendedApi');
200
+ (0, supabaseClient_1.validateCredentials)(credentials);
201
+ const supabase = (0, supabaseClient_1.createSupabaseClient)(credentials);
202
+ const table = this.getNodeParameter('table');
203
+ const event = this.getNodeParameter('event');
204
+ const keyColumn = this.getNodeParameter('keyColumn');
205
+ const watchedFields = this.getNodeParameter('watchedFields', []);
206
+ const staticData = this.getWorkflowStaticData('node');
207
+ if (!staticData.lastTimestamp) {
208
+ if (this.getMode() === 'manual') {
209
+ const lookback = new Date();
210
+ lookback.setHours(lookback.getHours() - 24);
211
+ staticData.lastTimestamp = lookback.toISOString();
212
+ }
213
+ else {
214
+ staticData.lastTimestamp = new Date().toISOString();
215
+ staticData.fieldStates = {};
216
+ return null;
217
+ }
218
+ }
219
+ const lastTimestamp = staticData.lastTimestamp;
220
+ let query = supabase.from(table).select('*');
221
+ if (event === 'rowCreatedOrUpdated') {
222
+ const createdAtCol = this.getNodeParameter('createdAtColumn');
223
+ const updatedAtCol = this.getNodeParameter('updatedAtColumn');
224
+ query = query.or(`${createdAtCol}.gt.${lastTimestamp},${updatedAtCol}.gt.${lastTimestamp}`);
225
+ }
226
+ else {
227
+ const timestampCol = this.getNodeParameter('timestampColumn');
228
+ query = query.gt(timestampCol, lastTimestamp);
229
+ }
230
+ const orderCol = event === 'rowCreatedOrUpdated'
231
+ ? this.getNodeParameter('updatedAtColumn')
232
+ : this.getNodeParameter('timestampColumn');
233
+ query = query.order(orderCol, { ascending: true }).limit(1000);
234
+ const { data, error } = await query;
235
+ if (error) {
236
+ throw new Error(`Supabase trigger error: ${error.message}`);
237
+ }
238
+ if (!data || data.length === 0) {
239
+ return null;
240
+ }
241
+ const seen = new Set();
242
+ const uniqueRows = [];
243
+ for (const row of data) {
244
+ const key = String(row[keyColumn]);
245
+ if (!seen.has(key)) {
246
+ seen.add(key);
247
+ uniqueRows.push(row);
248
+ }
249
+ }
250
+ let maxTimestamp = lastTimestamp;
251
+ for (const row of uniqueRows) {
252
+ if (event === 'rowCreatedOrUpdated') {
253
+ const createdAtCol = this.getNodeParameter('createdAtColumn');
254
+ const updatedAtCol = this.getNodeParameter('updatedAtColumn');
255
+ const c = row[createdAtCol] || '';
256
+ const u = row[updatedAtCol] || '';
257
+ if (c > maxTimestamp)
258
+ maxTimestamp = c;
259
+ if (u > maxTimestamp)
260
+ maxTimestamp = u;
261
+ }
262
+ else {
263
+ const timestampCol = this.getNodeParameter('timestampColumn');
264
+ const ts = row[timestampCol] || '';
265
+ if (ts > maxTimestamp)
266
+ maxTimestamp = ts;
267
+ }
268
+ }
269
+ staticData.lastTimestamp = maxTimestamp;
270
+ let resultRows = uniqueRows;
271
+ if (watchedFields.length > 0) {
272
+ const fieldStates = (staticData.fieldStates || {});
273
+ const changedRows = [];
274
+ for (const row of uniqueRows) {
275
+ const rowKey = String(row[keyColumn]);
276
+ const prevState = fieldStates[rowKey];
277
+ const currentState = {};
278
+ for (const field of watchedFields) {
279
+ currentState[field] = row[field];
280
+ }
281
+ if (!prevState) {
282
+ const changed = { ...row, _changedFields: watchedFields };
283
+ changedRows.push(changed);
284
+ }
285
+ else {
286
+ const changed = watchedFields.filter((f) => JSON.stringify(prevState[f]) !== JSON.stringify(currentState[f]));
287
+ if (changed.length > 0) {
288
+ changedRows.push({ ...row, _changedFields: changed });
289
+ }
290
+ }
291
+ fieldStates[rowKey] = currentState;
292
+ }
293
+ const keys = Object.keys(fieldStates);
294
+ if (keys.length > 10000) {
295
+ const excess = keys.length - 10000;
296
+ for (let i = 0; i < excess; i++) {
297
+ const key = keys[i];
298
+ if (key !== undefined) {
299
+ delete fieldStates[key];
300
+ }
301
+ }
302
+ }
303
+ staticData.fieldStates = fieldStates;
304
+ resultRows = changedRows;
305
+ }
306
+ if (resultRows.length === 0) {
307
+ return null;
308
+ }
309
+ return [resultRows.map((row) => ({ json: row }))];
310
+ }
311
+ }
312
+ exports.SupabaseTrigger = SupabaseTrigger;
@@ -150,38 +150,39 @@ async function handleBulkUpdate(supabase, itemCount) {
150
150
  }
151
151
  return [{ json: { data, operation: 'update', table } }];
152
152
  }
153
- function buildReadQuery(context, supabase, table, returnFields, itemIndex, options) {
154
- const selectFields = returnFields && returnFields !== '*' ? returnFields : '*';
155
- let query = supabase.from(table).select(selectFields, options);
153
+ function getFilters(context, itemIndex) {
156
154
  const uiMode = context.getNodeParameter('uiMode', itemIndex, 'simple');
157
155
  if (uiMode === 'simple') {
158
- const filters = context.getNodeParameter('filters.filter', itemIndex, []);
159
- for (const filter of filters) {
160
- const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
161
- query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
162
- }
156
+ return context.getNodeParameter('filters.filter', itemIndex, []);
163
157
  }
164
- else {
165
- const advancedFilters = context.getNodeParameter('advancedFilters', itemIndex, '');
166
- if (advancedFilters) {
167
- try {
168
- const filters = JSON.parse(advancedFilters);
169
- for (const [column, condition] of Object.entries(filters)) {
170
- if (typeof condition === 'object' && condition !== null) {
171
- const [operator, value] = Object.entries(condition)[0];
172
- query = query.filter(column, (0, supabaseClient_1.convertFilterOperator)(operator), (0, supabaseClient_1.normalizeFilterValue)(operator, value));
173
- }
174
- else {
175
- query = query.eq(column, condition);
176
- }
177
- }
158
+ const advancedFilters = context.getNodeParameter('advancedFilters', itemIndex, '');
159
+ if (!advancedFilters)
160
+ return [];
161
+ try {
162
+ const parsed = JSON.parse(advancedFilters);
163
+ const filters = [];
164
+ for (const [column, condition] of Object.entries(parsed)) {
165
+ if (typeof condition === 'object' && condition !== null) {
166
+ const [operator, value] = Object.entries(condition)[0];
167
+ filters.push({ column, operator: operator, value });
178
168
  }
179
- catch {
180
- throw new Error('Invalid advanced filters JSON');
169
+ else {
170
+ filters.push({ column, operator: 'eq', value: condition });
181
171
  }
182
172
  }
173
+ return filters;
174
+ }
175
+ catch {
176
+ throw new Error('Invalid advanced filters JSON');
177
+ }
178
+ }
179
+ function buildReadQuery(supabase, table, returnFields, filters, sort, options) {
180
+ const selectFields = returnFields && returnFields !== '*' ? returnFields : '*';
181
+ let query = supabase.from(table).select(selectFields, options);
182
+ for (const filter of filters) {
183
+ const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
184
+ query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
183
185
  }
184
- const sort = context.getNodeParameter('sort.sortField', itemIndex, []);
185
186
  for (const sortField of sort) {
186
187
  query = query.order(sortField.column, { ascending: sortField.ascending });
187
188
  }
@@ -192,46 +193,51 @@ async function handleRead(supabase, itemIndex) {
192
193
  (0, supabaseClient_1.validateTableName)(table);
193
194
  const returnFields = this.getNodeParameter('returnFields', itemIndex, '*');
194
195
  const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
196
+ const filters = getFilters(this, itemIndex);
197
+ const sort = this.getNodeParameter('sort.sortField', itemIndex, []);
198
+ const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters);
195
199
  const returnData = [];
196
- if (returnAll) {
197
- const batchSize = 1000;
198
- let offset = 0;
199
- let hasMore = true;
200
- while (hasMore) {
201
- const batchQuery = buildReadQuery(this, supabase, table, returnFields, itemIndex, { count: 'exact' });
202
- const { data: batchData, error: batchError } = await batchQuery.range(offset, offset + batchSize - 1);
203
- if (batchError) {
204
- throw new Error((0, supabaseClient_1.formatSupabaseError)(batchError));
205
- }
206
- if (Array.isArray(batchData)) {
207
- for (const row of batchData) {
208
- returnData.push({ json: row });
200
+ for (const chunkFilters of filterChunks) {
201
+ if (returnAll) {
202
+ const batchSize = 1000;
203
+ let offset = 0;
204
+ let hasMore = true;
205
+ while (hasMore) {
206
+ const batchQuery = buildReadQuery(supabase, table, returnFields, chunkFilters, sort, { count: 'exact' });
207
+ const { data: batchData, error: batchError } = await batchQuery.range(offset, offset + batchSize - 1);
208
+ if (batchError) {
209
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(batchError));
209
210
  }
210
- hasMore = batchData.length === batchSize;
211
- }
212
- else {
213
- hasMore = false;
211
+ if (Array.isArray(batchData)) {
212
+ for (const row of batchData) {
213
+ returnData.push({ json: row });
214
+ }
215
+ hasMore = batchData.length === batchSize;
216
+ }
217
+ else {
218
+ hasMore = false;
219
+ }
220
+ offset += batchSize;
214
221
  }
215
- offset += batchSize;
216
- }
217
- }
218
- else {
219
- const limit = this.getNodeParameter('limit', itemIndex, undefined);
220
- const offset = this.getNodeParameter('offset', itemIndex, undefined);
221
- let query = buildReadQuery(this, supabase, table, returnFields, itemIndex);
222
- if (limit !== undefined) {
223
- query = query.limit(limit);
224
- }
225
- if (offset !== undefined) {
226
- query = query.range(offset, offset + (limit || 1000) - 1);
227
- }
228
- const { data, error } = await query;
229
- if (error) {
230
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
231
222
  }
232
- if (Array.isArray(data)) {
233
- for (const row of data) {
234
- returnData.push({ json: row });
223
+ else {
224
+ const limit = this.getNodeParameter('limit', itemIndex, undefined);
225
+ const offset = this.getNodeParameter('offset', itemIndex, undefined);
226
+ let query = buildReadQuery(supabase, table, returnFields, chunkFilters, sort);
227
+ if (limit !== undefined) {
228
+ query = query.limit(limit);
229
+ }
230
+ if (offset !== undefined) {
231
+ query = query.range(offset, offset + (limit || 1000) - 1);
232
+ }
233
+ const { data, error } = await query;
234
+ if (error) {
235
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
236
+ }
237
+ if (Array.isArray(data)) {
238
+ for (const row of data) {
239
+ returnData.push({ json: row });
240
+ }
235
241
  }
236
242
  }
237
243
  }
@@ -251,20 +257,27 @@ async function handleRead(supabase, itemIndex) {
251
257
  async function handleDelete(supabase, itemIndex) {
252
258
  const table = this.getNodeParameter('table', itemIndex);
253
259
  (0, supabaseClient_1.validateTableName)(table);
254
- let query = supabase.from(table).delete();
255
260
  const filters = this.getNodeParameter('filters.filter', itemIndex, []);
256
261
  if (filters.length === 0) {
257
262
  throw new Error('At least one filter is required for delete operations to prevent accidental data loss');
258
263
  }
259
- for (const filter of filters) {
260
- const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
261
- query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
262
- }
263
- const { data, error } = await query.select();
264
- if (error) {
265
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
264
+ const filterChunks = (0, supabaseClient_1.expandChunkedFilters)(filters);
265
+ const allDeleted = [];
266
+ for (const chunkFilters of filterChunks) {
267
+ let query = supabase.from(table).delete();
268
+ for (const filter of chunkFilters) {
269
+ const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
270
+ query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
271
+ }
272
+ const { data, error } = await query.select();
273
+ if (error) {
274
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
275
+ }
276
+ if (Array.isArray(data)) {
277
+ allDeleted.push(...data);
278
+ }
266
279
  }
267
- return [{ json: { data, operation: 'delete', table, deleted: (data === null || data === void 0 ? void 0 : data.length) || 0 } }];
280
+ return [{ json: { data: allDeleted, operation: 'delete', table, deleted: allDeleted.length } }];
268
281
  }
269
282
  async function handleCreateTable(supabase, itemIndex) {
270
283
  const tableName = this.getNodeParameter('tableName', itemIndex);
@@ -8,7 +8,7 @@ export interface ISupabaseCredentials {
8
8
  export interface IRowFilter {
9
9
  column: string;
10
10
  operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'is' | 'in' | 'cs' | 'cd';
11
- value: string | number | boolean | null;
11
+ value: string | number | boolean | null | unknown[];
12
12
  }
13
13
  export interface IRowSort {
14
14
  column: string;
@@ -1,5 +1,5 @@
1
1
  import { SupabaseClient } from '@supabase/supabase-js';
2
- import { ISupabaseCredentials } from '../types';
2
+ import { ISupabaseCredentials, IRowFilter } 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;
@@ -11,4 +11,7 @@ export declare function sanitizeColumnName(columnName: string): string;
11
11
  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
- export declare function normalizeFilterValue(operator: string, value: string | number | boolean | null): string | number | boolean | null;
14
+ export declare function normalizeFilterValue(operator: string, value: string | number | boolean | null | unknown[]): string | number | boolean | null;
15
+ export declare const IN_FILTER_CHUNK_SIZE = 200;
16
+ export declare function chunkArray<T>(arr: T[], size: number): T[][];
17
+ export declare function expandChunkedFilters(filters: IRowFilter[]): IRowFilter[][];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.chunkArray = exports.IN_FILTER_CHUNK_SIZE = 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, {
@@ -137,13 +137,60 @@ function convertFilterOperator(operator) {
137
137
  }
138
138
  exports.convertFilterOperator = convertFilterOperator;
139
139
  function normalizeFilterValue(operator, value) {
140
- if (operator === 'in' && typeof value === 'string') {
141
- const trimmed = value.trim();
142
- if (!trimmed.startsWith('(')) {
143
- return `(${trimmed})`;
140
+ if (operator === 'in') {
141
+ if (Array.isArray(value)) {
142
+ return `(${value.join(',')})`;
143
+ }
144
+ if (typeof value === 'string') {
145
+ const trimmed = value.trim();
146
+ if (!trimmed.startsWith('(')) {
147
+ return `(${trimmed})`;
148
+ }
149
+ return trimmed;
144
150
  }
145
- return trimmed;
146
151
  }
147
152
  return value;
148
153
  }
149
154
  exports.normalizeFilterValue = normalizeFilterValue;
155
+ exports.IN_FILTER_CHUNK_SIZE = 200;
156
+ function chunkArray(arr, size) {
157
+ const chunks = [];
158
+ for (let i = 0; i < arr.length; i += size) {
159
+ chunks.push(arr.slice(i, i + size));
160
+ }
161
+ return chunks;
162
+ }
163
+ exports.chunkArray = chunkArray;
164
+ function expandChunkedFilters(filters) {
165
+ const staticFilters = [];
166
+ const chunkedEntries = [];
167
+ for (const filter of filters) {
168
+ if (filter.operator === 'in' && Array.isArray(filter.value) && filter.value.length > exports.IN_FILTER_CHUNK_SIZE) {
169
+ chunkedEntries.push({
170
+ filter,
171
+ chunks: chunkArray(filter.value, exports.IN_FILTER_CHUNK_SIZE),
172
+ });
173
+ }
174
+ else {
175
+ staticFilters.push(filter);
176
+ }
177
+ }
178
+ if (chunkedEntries.length === 0) {
179
+ return [filters];
180
+ }
181
+ let combinations = [staticFilters];
182
+ for (const { filter, chunks } of chunkedEntries) {
183
+ const newCombinations = [];
184
+ for (const existing of combinations) {
185
+ for (const chunk of chunks) {
186
+ newCombinations.push([
187
+ ...existing,
188
+ { ...filter, value: chunk },
189
+ ]);
190
+ }
191
+ }
192
+ combinations = newCombinations;
193
+ }
194
+ return combinations;
195
+ }
196
+ exports.expandChunkedFilters = expandChunkedFilters;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fachkraftfreund/n8n-nodes-supabase",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",
@@ -37,7 +37,8 @@
37
37
  "dist/credentials/SupabaseExtendedApi.credentials.js"
38
38
  ],
39
39
  "nodes": [
40
- "dist/nodes/Supabase/Supabase.node.js"
40
+ "dist/nodes/Supabase/Supabase.node.js",
41
+ "dist/nodes/Supabase/SupabaseTrigger.node.js"
41
42
  ]
42
43
  },
43
44
  "devDependencies": {