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
- workflowCache.nextExpirationTime = undefined;
268
- workflowCache.expirationQueue = [];
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
- workflowCache.nextExpirationTime = undefined;
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
- // Enable debug logging via environment variable
9
- const DEBUG_ENABLED = process.env.BINARY_TO_URL_DEBUG === 'true';
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 (DEBUG_ENABLED && logger) {
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
- const ext = item.json?.fileExtension;
212
- fileExtension = typeof ext === 'string' ? ext : (contentType.split('/')[1] || '');
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 (error) {
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
- const ext = item.json?.fileExtension;
225
- fileExtension = typeof ext === 'string' ? ext : (contentType.split('/')[1] || '');
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-binary-to-url",
3
- "version": "0.1.38",
3
+ "version": "0.2.2",
4
4
  "description": "n8n community node for creating temporary URLs for binary files within workflow execution",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",