@fachkraftfreund/n8n-nodes-supabase 1.2.7 → 1.2.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.
@@ -335,6 +335,23 @@ class Supabase {
335
335
  },
336
336
  },
337
337
  },
338
+ {
339
+ displayName: 'Match Column',
340
+ name: 'matchColumn',
341
+ type: 'options',
342
+ typeOptions: {
343
+ loadOptionsMethod: 'getColumns',
344
+ },
345
+ required: true,
346
+ default: '',
347
+ description: 'Column used to match rows for updating (typically the primary key). Each input item must include this column in its data. Uses upsert internally, so rows will be created if no match is found.',
348
+ displayOptions: {
349
+ show: {
350
+ resource: ['database'],
351
+ operation: ['update'],
352
+ },
353
+ },
354
+ },
338
355
  {
339
356
  displayName: 'Match Columns',
340
357
  name: 'matchColumns',
@@ -431,7 +448,7 @@ class Supabase {
431
448
  displayOptions: {
432
449
  show: {
433
450
  resource: ['database'],
434
- operation: ['read', 'update', 'delete'],
451
+ operation: ['read', 'delete'],
435
452
  uiMode: ['simple'],
436
453
  },
437
454
  },
@@ -491,7 +508,7 @@ class Supabase {
491
508
  displayOptions: {
492
509
  show: {
493
510
  resource: ['database'],
494
- operation: ['read', 'update', 'delete'],
511
+ operation: ['read', 'delete'],
495
512
  uiMode: ['advanced'],
496
513
  },
497
514
  },
@@ -883,34 +900,51 @@ class Supabase {
883
900
  throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Invalid credentials: ${errorMessage}`);
884
901
  }
885
902
  const supabase = (0, supabaseClient_1.createSupabaseClient)(credentials);
886
- for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
903
+ const resource = this.getNodeParameter('resource', 0);
904
+ const operation = this.getNodeParameter('operation', 0);
905
+ if (resource === 'database' && ['create', 'upsert', 'update'].includes(operation)) {
887
906
  try {
888
- const resource = this.getNodeParameter('resource', itemIndex);
889
- const operation = this.getNodeParameter('operation', itemIndex);
890
- let operationResults = [];
891
- if (resource === 'database') {
892
- operationResults = await database_1.executeDatabaseOperation.call(this, supabase, operation, itemIndex);
893
- }
894
- else if (resource === 'storage') {
895
- operationResults = await storage_1.executeStorageOperation.call(this, supabase, operation, itemIndex);
896
- }
897
- else {
898
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown resource: ${resource}`);
899
- }
900
- returnData.push(...operationResults);
907
+ const results = await database_1.executeBulkDatabaseOperation.call(this, supabase, operation, items.length);
908
+ returnData.push(...results);
901
909
  }
902
910
  catch (error) {
903
911
  const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
904
912
  if (this.continueOnFail()) {
905
- returnData.push({
906
- json: {
907
- error: errorMessage,
908
- itemIndex,
909
- },
910
- });
911
- continue;
913
+ returnData.push({ json: { error: errorMessage } });
914
+ }
915
+ else {
916
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage);
917
+ }
918
+ }
919
+ }
920
+ else {
921
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
922
+ try {
923
+ let operationResults = [];
924
+ if (resource === 'database') {
925
+ operationResults = await database_1.executeDatabaseOperation.call(this, supabase, operation, itemIndex);
926
+ }
927
+ else if (resource === 'storage') {
928
+ operationResults = await storage_1.executeStorageOperation.call(this, supabase, operation, itemIndex);
929
+ }
930
+ else {
931
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Unknown resource: ${resource}`);
932
+ }
933
+ returnData.push(...operationResults);
934
+ }
935
+ catch (error) {
936
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
937
+ if (this.continueOnFail()) {
938
+ returnData.push({
939
+ json: {
940
+ error: errorMessage,
941
+ itemIndex,
942
+ },
943
+ });
944
+ continue;
945
+ }
946
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex });
912
947
  }
913
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex });
914
948
  }
915
949
  }
916
950
  return [returnData];
@@ -2,3 +2,4 @@ import { SupabaseClient } from '@supabase/supabase-js';
2
2
  import { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
3
3
  import { DatabaseOperation } from '../../types';
4
4
  export declare function executeDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemIndex: number): Promise<INodeExecutionData[]>;
5
+ export declare function executeBulkDatabaseOperation(this: IExecuteFunctions, supabase: SupabaseClient, operation: DatabaseOperation, itemCount: number): Promise<INodeExecutionData[]>;
@@ -1,26 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.executeDatabaseOperation = void 0;
3
+ exports.executeBulkDatabaseOperation = exports.executeDatabaseOperation = void 0;
4
4
  const supabaseClient_1 = require("../../utils/supabaseClient");
5
5
  async function executeDatabaseOperation(supabase, operation, itemIndex) {
6
6
  const returnData = [];
7
7
  try {
8
8
  switch (operation) {
9
- case 'create':
10
- returnData.push(...await handleCreate.call(this, supabase, itemIndex));
11
- break;
12
9
  case 'read':
13
10
  returnData.push(...await handleRead.call(this, supabase, itemIndex));
14
11
  break;
15
- case 'update':
16
- returnData.push(...await handleUpdate.call(this, supabase, itemIndex));
17
- break;
18
12
  case 'delete':
19
13
  returnData.push(...await handleDelete.call(this, supabase, itemIndex));
20
14
  break;
21
- case 'upsert':
22
- returnData.push(...await handleUpsert.call(this, supabase, itemIndex));
23
- break;
24
15
  case 'createTable':
25
16
  returnData.push(...await handleCreateTable.call(this, supabase, itemIndex));
26
17
  break;
@@ -55,56 +46,123 @@ async function executeDatabaseOperation(supabase, operation, itemIndex) {
55
46
  return returnData;
56
47
  }
57
48
  exports.executeDatabaseOperation = executeDatabaseOperation;
58
- async function handleCreate(supabase, itemIndex) {
59
- const table = this.getNodeParameter('table', itemIndex);
60
- const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
61
- (0, supabaseClient_1.validateTableName)(table);
62
- let dataToInsert;
63
- if (uiMode === 'advanced') {
64
- const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
65
- try {
66
- dataToInsert = JSON.parse(jsonData);
49
+ function collectRowData(context, itemCount) {
50
+ const rows = [];
51
+ for (let i = 0; i < itemCount; i++) {
52
+ const uiMode = context.getNodeParameter('uiMode', i, 'simple');
53
+ let row;
54
+ if (uiMode === 'advanced') {
55
+ const jsonData = context.getNodeParameter('jsonData', i, '{}');
56
+ try {
57
+ row = JSON.parse(jsonData);
58
+ }
59
+ catch {
60
+ throw new Error(`Invalid JSON data at item ${i}`);
61
+ }
67
62
  }
68
- catch {
69
- throw new Error('Invalid JSON data provided');
63
+ else {
64
+ const columns = context.getNodeParameter('columns.column', i, []);
65
+ row = {};
66
+ for (const column of columns) {
67
+ if (column.name && column.value !== undefined) {
68
+ row[column.name] = column.value;
69
+ }
70
+ }
70
71
  }
72
+ rows.push(row);
71
73
  }
72
- else {
73
- const columns = this.getNodeParameter('columns.column', itemIndex, []);
74
- dataToInsert = {};
75
- for (const column of columns) {
76
- if (column.name && column.value !== undefined) {
77
- dataToInsert[column.name] = column.value;
78
- }
74
+ return rows;
75
+ }
76
+ async function executeBulkDatabaseOperation(supabase, operation, itemCount) {
77
+ try {
78
+ switch (operation) {
79
+ case 'create':
80
+ return await handleBulkCreate.call(this, supabase, itemCount);
81
+ case 'upsert':
82
+ return await handleBulkUpsert.call(this, supabase, itemCount);
83
+ case 'update':
84
+ return await handleBulkUpdate.call(this, supabase, itemCount);
85
+ default:
86
+ throw new Error(`Operation ${operation} does not support bulk mode`);
79
87
  }
80
88
  }
89
+ catch (error) {
90
+ throw new Error(`Database operation failed: ${(0, supabaseClient_1.formatSupabaseError)(error)}`);
91
+ }
92
+ }
93
+ exports.executeBulkDatabaseOperation = executeBulkDatabaseOperation;
94
+ async function handleBulkCreate(supabase, itemCount) {
95
+ const table = this.getNodeParameter('table', 0);
96
+ (0, supabaseClient_1.validateTableName)(table);
97
+ const rows = collectRowData(this, itemCount);
81
98
  const { data, error } = await supabase
82
99
  .from(table)
83
- .insert(dataToInsert)
100
+ .insert(rows)
84
101
  .select();
85
- if (error) {
102
+ if (error)
86
103
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
104
+ if (Array.isArray(data)) {
105
+ return data.map((row) => ({ json: row }));
87
106
  }
88
107
  return [{ json: { data, operation: 'create', table } }];
89
108
  }
90
- async function handleRead(supabase, itemIndex) {
91
- const table = this.getNodeParameter('table', itemIndex);
92
- const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
109
+ async function handleBulkUpsert(supabase, itemCount) {
110
+ const table = this.getNodeParameter('table', 0);
111
+ const onConflict = this.getNodeParameter('onConflict', 0, '');
93
112
  (0, supabaseClient_1.validateTableName)(table);
94
- let query = supabase.from(table).select('*');
95
- const returnFields = this.getNodeParameter('returnFields', itemIndex, '*');
96
- if (returnFields && returnFields !== '*') {
97
- query = supabase.from(table).select(returnFields);
113
+ const rows = collectRowData(this, itemCount);
114
+ const options = {};
115
+ if (onConflict)
116
+ options.onConflict = onConflict;
117
+ const { data, error } = await supabase
118
+ .from(table)
119
+ .upsert(rows, options)
120
+ .select();
121
+ if (error)
122
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
123
+ if (Array.isArray(data)) {
124
+ return data.map((row) => ({ json: row }));
98
125
  }
126
+ return [{ json: { data, operation: 'upsert', table } }];
127
+ }
128
+ async function handleBulkUpdate(supabase, itemCount) {
129
+ const table = this.getNodeParameter('table', 0);
130
+ const matchColumn = this.getNodeParameter('matchColumn', 0);
131
+ (0, supabaseClient_1.validateTableName)(table);
132
+ if (!matchColumn) {
133
+ throw new Error('Match Column is required for update operations');
134
+ }
135
+ const rows = collectRowData(this, itemCount);
136
+ for (let i = 0; i < rows.length; i++) {
137
+ const row = rows[i];
138
+ if (!row || row[matchColumn] === undefined) {
139
+ throw new Error(`Item ${i} is missing the match column "${matchColumn}"`);
140
+ }
141
+ }
142
+ const { data, error } = await supabase
143
+ .from(table)
144
+ .upsert(rows, { onConflict: matchColumn })
145
+ .select();
146
+ if (error)
147
+ throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
148
+ if (Array.isArray(data)) {
149
+ return data.map((row) => ({ json: row }));
150
+ }
151
+ return [{ json: { data, operation: 'update', table } }];
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);
156
+ const uiMode = context.getNodeParameter('uiMode', itemIndex, 'simple');
99
157
  if (uiMode === 'simple') {
100
- const filters = this.getNodeParameter('filters.filter', itemIndex, []);
158
+ const filters = context.getNodeParameter('filters.filter', itemIndex, []);
101
159
  for (const filter of filters) {
102
160
  const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
103
161
  query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
104
162
  }
105
163
  }
106
164
  else {
107
- const advancedFilters = this.getNodeParameter('advancedFilters', itemIndex, '');
165
+ const advancedFilters = context.getNodeParameter('advancedFilters', itemIndex, '');
108
166
  if (advancedFilters) {
109
167
  try {
110
168
  const filters = JSON.parse(advancedFilters);
@@ -123,10 +181,16 @@ async function handleRead(supabase, itemIndex) {
123
181
  }
124
182
  }
125
183
  }
126
- const sort = this.getNodeParameter('sort.sortField', itemIndex, []);
184
+ const sort = context.getNodeParameter('sort.sortField', itemIndex, []);
127
185
  for (const sortField of sort) {
128
186
  query = query.order(sortField.column, { ascending: sortField.ascending });
129
187
  }
188
+ return query;
189
+ }
190
+ async function handleRead(supabase, itemIndex) {
191
+ const table = this.getNodeParameter('table', itemIndex);
192
+ (0, supabaseClient_1.validateTableName)(table);
193
+ const returnFields = this.getNodeParameter('returnFields', itemIndex, '*');
130
194
  const returnAll = this.getNodeParameter('returnAll', itemIndex, false);
131
195
  const returnData = [];
132
196
  if (returnAll) {
@@ -134,42 +198,8 @@ async function handleRead(supabase, itemIndex) {
134
198
  let offset = 0;
135
199
  let hasMore = true;
136
200
  while (hasMore) {
137
- let batchQuery = supabase.from(table).select(returnFields && returnFields !== '*' ? returnFields : '*', { count: 'exact' });
138
- const batchFilterMode = this.getNodeParameter('filterMode', itemIndex, 'simple');
139
- if (batchFilterMode === 'simple') {
140
- const batchFilters = this.getNodeParameter('filters.conditions', itemIndex, []);
141
- for (const filter of batchFilters) {
142
- if (filter.column && filter.value !== undefined) {
143
- batchQuery = batchQuery.eq(filter.column, filter.value);
144
- }
145
- }
146
- }
147
- else {
148
- const batchAdvancedFilters = this.getNodeParameter('advancedFilters', itemIndex, '');
149
- if (batchAdvancedFilters) {
150
- try {
151
- const parsed = JSON.parse(batchAdvancedFilters);
152
- for (const condition of parsed) {
153
- const { column, operator, value } = condition;
154
- if (operator) {
155
- batchQuery = batchQuery.filter(column, (0, supabaseClient_1.convertFilterOperator)(operator), (0, supabaseClient_1.normalizeFilterValue)(operator, value));
156
- }
157
- else {
158
- batchQuery = batchQuery.eq(column, condition);
159
- }
160
- }
161
- }
162
- catch {
163
- throw new Error('Invalid advanced filters JSON');
164
- }
165
- }
166
- }
167
- const batchSort = this.getNodeParameter('sort.sortField', itemIndex, []);
168
- for (const sortField of batchSort) {
169
- batchQuery = batchQuery.order(sortField.column, { ascending: sortField.ascending });
170
- }
171
- batchQuery = batchQuery.range(offset, offset + batchSize - 1);
172
- const { data: batchData, error: batchError } = await batchQuery;
201
+ const batchQuery = buildReadQuery(this, supabase, table, returnFields, itemIndex, { count: 'exact' });
202
+ const { data: batchData, error: batchError } = await batchQuery.range(offset, offset + batchSize - 1);
173
203
  if (batchError) {
174
204
  throw new Error((0, supabaseClient_1.formatSupabaseError)(batchError));
175
205
  }
@@ -188,6 +218,7 @@ async function handleRead(supabase, itemIndex) {
188
218
  else {
189
219
  const limit = this.getNodeParameter('limit', itemIndex, undefined);
190
220
  const offset = this.getNodeParameter('offset', itemIndex, undefined);
221
+ let query = buildReadQuery(this, supabase, table, returnFields, itemIndex);
191
222
  if (limit !== undefined) {
192
223
  query = query.limit(limit);
193
224
  }
@@ -217,41 +248,6 @@ async function handleRead(supabase, itemIndex) {
217
248
  }
218
249
  return returnData;
219
250
  }
220
- async function handleUpdate(supabase, itemIndex) {
221
- const table = this.getNodeParameter('table', itemIndex);
222
- const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
223
- (0, supabaseClient_1.validateTableName)(table);
224
- let dataToUpdate;
225
- if (uiMode === 'advanced') {
226
- const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
227
- try {
228
- dataToUpdate = JSON.parse(jsonData);
229
- }
230
- catch {
231
- throw new Error('Invalid JSON data provided');
232
- }
233
- }
234
- else {
235
- const columns = this.getNodeParameter('columns.column', itemIndex, []);
236
- dataToUpdate = {};
237
- for (const column of columns) {
238
- if (column.name && column.value !== undefined) {
239
- dataToUpdate[column.name] = column.value;
240
- }
241
- }
242
- }
243
- let query = supabase.from(table).update(dataToUpdate);
244
- const filters = this.getNodeParameter('filters.filter', itemIndex, []);
245
- for (const filter of filters) {
246
- const operator = (0, supabaseClient_1.convertFilterOperator)(filter.operator);
247
- query = query.filter(filter.column, operator, (0, supabaseClient_1.normalizeFilterValue)(filter.operator, filter.value));
248
- }
249
- const { data, error } = await query.select();
250
- if (error) {
251
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
252
- }
253
- return [{ json: { data, operation: 'update', table, updated: (data === null || data === void 0 ? void 0 : data.length) || 0 } }];
254
- }
255
251
  async function handleDelete(supabase, itemIndex) {
256
252
  const table = this.getNodeParameter('table', itemIndex);
257
253
  (0, supabaseClient_1.validateTableName)(table);
@@ -270,43 +266,6 @@ async function handleDelete(supabase, itemIndex) {
270
266
  }
271
267
  return [{ json: { data, operation: 'delete', table, deleted: (data === null || data === void 0 ? void 0 : data.length) || 0 } }];
272
268
  }
273
- async function handleUpsert(supabase, itemIndex) {
274
- const table = this.getNodeParameter('table', itemIndex);
275
- const uiMode = this.getNodeParameter('uiMode', itemIndex, 'simple');
276
- const onConflict = this.getNodeParameter('onConflict', itemIndex, '');
277
- (0, supabaseClient_1.validateTableName)(table);
278
- let dataToUpsert;
279
- if (uiMode === 'advanced') {
280
- const jsonData = this.getNodeParameter('jsonData', itemIndex, '{}');
281
- try {
282
- dataToUpsert = JSON.parse(jsonData);
283
- }
284
- catch {
285
- throw new Error('Invalid JSON data provided');
286
- }
287
- }
288
- else {
289
- const columns = this.getNodeParameter('columns.column', itemIndex, []);
290
- dataToUpsert = {};
291
- for (const column of columns) {
292
- if (column.name && column.value !== undefined) {
293
- dataToUpsert[column.name] = column.value;
294
- }
295
- }
296
- }
297
- const options = {};
298
- if (onConflict) {
299
- options.onConflict = onConflict;
300
- }
301
- const { data, error } = await supabase
302
- .from(table)
303
- .upsert(dataToUpsert, options)
304
- .select();
305
- if (error) {
306
- throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
307
- }
308
- return [{ json: { data, operation: 'upsert', table } }];
309
- }
310
269
  async function handleCreateTable(supabase, itemIndex) {
311
270
  const tableName = this.getNodeParameter('tableName', itemIndex);
312
271
  const columns = this.getNodeParameter('columnDefinitions.column', itemIndex, []);
@@ -334,7 +293,7 @@ async function handleCreateTable(supabase, itemIndex) {
334
293
  columnDefs.push(columnDef);
335
294
  }
336
295
  sql += columnDefs.join(', ') + ')';
337
- const { data, error } = await supabase.rpc('exec_sql', { sql });
296
+ const { error } = await supabase.rpc('exec_sql', { sql });
338
297
  if (error) {
339
298
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
340
299
  }
@@ -345,7 +304,7 @@ async function handleDropTable(supabase, itemIndex) {
345
304
  const cascade = this.getNodeParameter('cascade', itemIndex, false);
346
305
  (0, supabaseClient_1.validateTableName)(tableName);
347
306
  const sql = `DROP TABLE "${tableName}"${cascade ? ' CASCADE' : ''}`;
348
- const { data, error } = await supabase.rpc('exec_sql', { sql });
307
+ const { error } = await supabase.rpc('exec_sql', { sql });
349
308
  if (error) {
350
309
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
351
310
  }
@@ -363,7 +322,7 @@ async function handleAddColumn(supabase, itemIndex) {
363
322
  if (columnDefinition.defaultValue) {
364
323
  sql += ` DEFAULT ${columnDefinition.defaultValue}`;
365
324
  }
366
- const { data, error } = await supabase.rpc('exec_sql', { sql });
325
+ const { error } = await supabase.rpc('exec_sql', { sql });
367
326
  if (error) {
368
327
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
369
328
  }
@@ -376,7 +335,7 @@ async function handleDropColumn(supabase, itemIndex) {
376
335
  (0, supabaseClient_1.validateTableName)(tableName);
377
336
  (0, supabaseClient_1.validateColumnName)(columnName);
378
337
  const sql = `ALTER TABLE "${tableName}" DROP COLUMN "${columnName}"${cascade ? ' CASCADE' : ''}`;
379
- const { data, error } = await supabase.rpc('exec_sql', { sql });
338
+ const { error } = await supabase.rpc('exec_sql', { sql });
380
339
  if (error) {
381
340
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
382
341
  }
@@ -393,7 +352,7 @@ async function handleCreateIndex(supabase, itemIndex) {
393
352
  const method = indexDefinition.method ? ` USING ${indexDefinition.method}` : '';
394
353
  const columnList = indexDefinition.columns.map(col => `"${col}"`).join(', ');
395
354
  const sql = `CREATE ${uniqueKeyword}INDEX "${indexDefinition.name}" ON "${tableName}"${method} (${columnList})`;
396
- const { data, error } = await supabase.rpc('exec_sql', { sql });
355
+ const { error } = await supabase.rpc('exec_sql', { sql });
397
356
  if (error) {
398
357
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
399
358
  }
@@ -406,7 +365,7 @@ async function handleDropIndex(supabase, itemIndex) {
406
365
  throw new Error('Index name is required');
407
366
  }
408
367
  const sql = `DROP INDEX "${indexName}"${cascade ? ' CASCADE' : ''}`;
409
- const { data, error } = await supabase.rpc('exec_sql', { sql });
368
+ const { error } = await supabase.rpc('exec_sql', { sql });
410
369
  if (error) {
411
370
  throw new Error((0, supabaseClient_1.formatSupabaseError)(error));
412
371
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fachkraftfreund/n8n-nodes-supabase",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
4
4
  "description": "Comprehensive n8n community node for Supabase with database and storage operations",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",