n8n-nodes-binary-to-url 0.1.38 → 0.2.2
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.
|
@@ -9,11 +9,8 @@ interface StorageStats {
|
|
|
9
9
|
export declare class MemoryStorage {
|
|
10
10
|
private static workflowCaches;
|
|
11
11
|
private static readonly DEFAULT_TTL;
|
|
12
|
-
private static readonly MAX_CACHE_SIZE;
|
|
13
|
-
private static readonly GLOBAL_MAX_CACHE_SIZE;
|
|
14
12
|
private static globalCacheSize;
|
|
15
13
|
private static nextGlobalExpirationTime?;
|
|
16
|
-
private static cleanupInterval?;
|
|
17
14
|
private static readonly MAX_DELETE_PER_CLEANUP;
|
|
18
15
|
private static readonly MIN_FILES_TO_KEEP;
|
|
19
16
|
private static readonly VALIDATION_THRESHOLD;
|
|
@@ -48,17 +45,11 @@ export declare class MemoryStorage {
|
|
|
48
45
|
* @returns The removed entry, or undefined if index is invalid
|
|
49
46
|
*/
|
|
50
47
|
private static heapRemove;
|
|
51
|
-
/**
|
|
52
|
-
* Get the root element of the heap without removing it
|
|
53
|
-
* @returns The oldest file entry, or undefined if heap is empty
|
|
54
|
-
*/
|
|
55
|
-
private static heapPeek;
|
|
56
48
|
/**
|
|
57
49
|
* Remove and return the root element of the heap
|
|
58
50
|
* @returns The oldest file entry, or undefined if heap is empty
|
|
59
51
|
*/
|
|
60
52
|
private static heapPop;
|
|
61
|
-
private static heapContains;
|
|
62
53
|
private static heapGetIndex;
|
|
63
54
|
private static removeFromExpirationQueue;
|
|
64
55
|
private static uploadLocks;
|
|
@@ -86,13 +86,6 @@ class MemoryStorage {
|
|
|
86
86
|
}
|
|
87
87
|
return entry;
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Get the root element of the heap without removing it
|
|
91
|
-
* @returns The oldest file entry, or undefined if heap is empty
|
|
92
|
-
*/
|
|
93
|
-
static heapPeek() {
|
|
94
|
-
return this.globalUploadQueue[0];
|
|
95
|
-
}
|
|
96
89
|
/**
|
|
97
90
|
* Remove and return the root element of the heap
|
|
98
91
|
* @returns The oldest file entry, or undefined if heap is empty
|
|
@@ -100,10 +93,6 @@ class MemoryStorage {
|
|
|
100
93
|
static heapPop() {
|
|
101
94
|
return this.heapRemove(0);
|
|
102
95
|
}
|
|
103
|
-
static heapContains(workflowId, fileKey) {
|
|
104
|
-
const key = this.UPLOAD_QUEUE_INDEX_KEY(workflowId, fileKey);
|
|
105
|
-
return this.globalUploadQueueIndex.has(key);
|
|
106
|
-
}
|
|
107
96
|
static heapGetIndex(workflowId, fileKey) {
|
|
108
97
|
const key = this.UPLOAD_QUEUE_INDEX_KEY(workflowId, fileKey);
|
|
109
98
|
return this.globalUploadQueueIndex.get(key);
|
|
@@ -169,6 +158,8 @@ class MemoryStorage {
|
|
|
169
158
|
catch (error) {
|
|
170
159
|
this.warn(`Upload lock failed for workflow ${workflowId}: ${error}`);
|
|
171
160
|
this.uploadLocks.delete(workflowId);
|
|
161
|
+
// Re-throw to ensure caller is aware of the failure
|
|
162
|
+
throw error;
|
|
172
163
|
}
|
|
173
164
|
}
|
|
174
165
|
// Create new lock for this upload
|
|
@@ -264,9 +255,8 @@ class MemoryStorage {
|
|
|
264
255
|
// Remove from expiration queue with proper index update
|
|
265
256
|
this.removeFromExpirationQueue(workflowCache, fileKey);
|
|
266
257
|
if (workflowCache.cache.size === 0) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
workflowCache.queueIndex.clear();
|
|
258
|
+
// Clean up empty workflow cache to prevent memory leaks
|
|
259
|
+
this.workflowCaches.delete(workflowId);
|
|
270
260
|
}
|
|
271
261
|
else {
|
|
272
262
|
workflowCache.nextExpirationTime = workflowCache.expirationQueue[0]?.expiresAt;
|
|
@@ -323,7 +313,8 @@ class MemoryStorage {
|
|
|
323
313
|
}
|
|
324
314
|
}
|
|
325
315
|
if (workflowCache.cache.size === 0) {
|
|
326
|
-
|
|
316
|
+
// Clean up empty workflow cache to prevent memory leaks
|
|
317
|
+
this.workflowCaches.delete(workflowId);
|
|
327
318
|
}
|
|
328
319
|
else {
|
|
329
320
|
workflowCache.nextExpirationTime = workflowCache.expirationQueue[0]?.expiresAt;
|
|
@@ -588,8 +579,6 @@ class MemoryStorage {
|
|
|
588
579
|
exports.MemoryStorage = MemoryStorage;
|
|
589
580
|
MemoryStorage.workflowCaches = new Map();
|
|
590
581
|
MemoryStorage.DEFAULT_TTL = constants_js_1.TTL.DEFAULT_MS;
|
|
591
|
-
MemoryStorage.MAX_CACHE_SIZE = constants_js_1.CACHE_LIMITS.MAX_CACHE_SIZE;
|
|
592
|
-
MemoryStorage.GLOBAL_MAX_CACHE_SIZE = constants_js_1.CACHE_LIMITS.GLOBAL_MAX_CACHE_SIZE;
|
|
593
582
|
MemoryStorage.globalCacheSize = 0;
|
|
594
583
|
MemoryStorage.MAX_DELETE_PER_CLEANUP = constants_js_1.CLEANUP.MAX_DELETE_PER_CLEANUP;
|
|
595
584
|
MemoryStorage.MIN_FILES_TO_KEEP = constants_js_1.CLEANUP.MIN_FILES_TO_KEEP;
|
|
@@ -5,10 +5,14 @@ const n8n_workflow_1 = require("n8n-workflow");
|
|
|
5
5
|
const crypto_1 = require("crypto");
|
|
6
6
|
const MemoryStorage_js_1 = require("../../drivers/MemoryStorage.js");
|
|
7
7
|
const constants_js_1 = require("../../config/constants.js");
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Debug logging helper function
|
|
10
|
+
* @param logger - n8n logger instance
|
|
11
|
+
* @param message - Log message
|
|
12
|
+
* @param args - Additional arguments to log
|
|
13
|
+
*/
|
|
10
14
|
function debugLog(logger, message, ...args) {
|
|
11
|
-
if (
|
|
15
|
+
if (logger) {
|
|
12
16
|
const formattedMessage = args.length > 0
|
|
13
17
|
? `[BinaryToUrl] ${message} ${args.map(String).join(' ')}`
|
|
14
18
|
: `[BinaryToUrl] ${message}`;
|
|
@@ -29,6 +33,7 @@ class BinaryToUrl {
|
|
|
29
33
|
},
|
|
30
34
|
inputs: ['main'],
|
|
31
35
|
outputs: ['main'],
|
|
36
|
+
usableAsTool: true,
|
|
32
37
|
webhooks: [
|
|
33
38
|
{
|
|
34
39
|
name: 'default',
|
|
@@ -208,11 +213,27 @@ async function handleUpload(context, items) {
|
|
|
208
213
|
const binaryData = context.helpers.assertBinaryData(i, binaryPropertyName);
|
|
209
214
|
buffer = await context.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
|
210
215
|
contentType = binaryData.mimeType || 'application/octet-stream';
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
// Auto-detect file extension:
|
|
217
|
+
// 1. From original filename in binary data
|
|
218
|
+
// 2. From MIME type
|
|
219
|
+
if (binaryData.fileName && typeof binaryData.fileName === 'string') {
|
|
220
|
+
const lastDot = binaryData.fileName.lastIndexOf('.');
|
|
221
|
+
if (lastDot > 0 && lastDot < binaryData.fileName.length - 1) {
|
|
222
|
+
fileExtension = binaryData.fileName.slice(lastDot + 1);
|
|
223
|
+
debugLog(context.logger, `Extracted extension from filename ${binaryData.fileName}: ${fileExtension}`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
227
|
+
debugLog(context.logger, `No extension in filename, using MIME type: ${fileExtension}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
232
|
+
debugLog(context.logger, `No filename available, using MIME type: ${fileExtension}`);
|
|
233
|
+
}
|
|
213
234
|
debugLog(context.logger, `Got binary data from property: size=${buffer.length}, mimeType=${contentType}, extension=${fileExtension}`);
|
|
214
235
|
}
|
|
215
|
-
catch
|
|
236
|
+
catch {
|
|
216
237
|
// If no binary data in property, try to get file ID from JSON
|
|
217
238
|
if (item.json && typeof item.json === 'object' && 'id' in item.json && typeof item.json.id === 'string') {
|
|
218
239
|
const fileId = item.json.id;
|
|
@@ -221,8 +242,24 @@ async function handleUpload(context, items) {
|
|
|
221
242
|
const stream = await context.helpers.getBinaryStream(fileId);
|
|
222
243
|
buffer = await context.helpers.binaryToBuffer(stream);
|
|
223
244
|
contentType = String(item.json?.mimeType || 'application/octet-stream');
|
|
224
|
-
|
|
225
|
-
|
|
245
|
+
// Auto-detect file extension:
|
|
246
|
+
// 1. From original filename in item data
|
|
247
|
+
// 2. From MIME type
|
|
248
|
+
if (item.json?.fileName && typeof item.json.fileName === 'string') {
|
|
249
|
+
const lastDot = item.json.fileName.lastIndexOf('.');
|
|
250
|
+
if (lastDot > 0 && lastDot < item.json.fileName.length - 1) {
|
|
251
|
+
fileExtension = item.json.fileName.slice(lastDot + 1);
|
|
252
|
+
debugLog(context.logger, `Extracted extension from filename ${item.json.fileName}: ${fileExtension}`);
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
256
|
+
debugLog(context.logger, `No extension in filename, using MIME type: ${fileExtension}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
fileExtension = contentType.split('/')[1] || '';
|
|
261
|
+
debugLog(context.logger, `No filename available, using MIME type: ${fileExtension}`);
|
|
262
|
+
}
|
|
226
263
|
debugLog(context.logger, `File fetched via getBinaryStream: size=${buffer.length}, mimeType=${contentType}, extension=${fileExtension}`);
|
|
227
264
|
}
|
|
228
265
|
catch (streamError) {
|
package/package.json
CHANGED