@beltar/n8n-nodes-extract-archive 1.1.1 → 1.3.0

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.
@@ -2,9 +2,10 @@ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription
2
2
  export declare class ExtractArchive implements INodeType {
3
3
  description: INodeTypeDescription;
4
4
  private detectArchiveType;
5
- private checkToolAvailable;
6
5
  private extractZip;
7
6
  private extractRar;
7
+ private extractNestedZips;
8
+ private findZipFiles;
8
9
  private getAllFiles;
9
10
  private matchesFilter;
10
11
  private getMimeType;
@@ -32,16 +32,17 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.ExtractArchive = void 0;
37
40
  const n8n_workflow_1 = require("n8n-workflow");
38
- const child_process_1 = require("child_process");
39
41
  const fs = __importStar(require("fs"));
40
42
  const crypto = __importStar(require("crypto"));
41
43
  const path = __importStar(require("path"));
42
- const util_1 = require("util");
43
44
  const promises_1 = require("stream/promises");
44
- const execAsync = (0, util_1.promisify)(child_process_1.exec);
45
+ const adm_zip_1 = __importDefault(require("adm-zip"));
45
46
  class ExtractArchive {
46
47
  constructor() {
47
48
  this.description = {
@@ -195,6 +196,13 @@ class ExtractArchive {
195
196
  },
196
197
  },
197
198
  },
199
+ {
200
+ displayName: 'Recursive Extract',
201
+ name: 'recursiveExtract',
202
+ type: 'boolean',
203
+ default: false,
204
+ description: 'Whether to recursively extract nested ZIP archives found inside the extracted files until no more ZIP files remain',
205
+ },
198
206
  {
199
207
  displayName: 'Include Subdirectories',
200
208
  name: 'includeSubdirectories',
@@ -246,26 +254,9 @@ class ExtractArchive {
246
254
  // Default to zip
247
255
  return 'zip';
248
256
  }
249
- checkToolAvailable(tool) {
250
- try {
251
- (0, child_process_1.execSync)(`which ${tool}`, { encoding: 'utf-8', stdio: 'pipe' });
252
- return true;
253
- }
254
- catch {
255
- return false;
256
- }
257
- }
258
257
  async extractZip(archivePath, outputDir, password) {
259
- if (!this.checkToolAvailable('unzip')) {
260
- throw new Error('unzip is not installed. Please install it: apk add unzip (Alpine) or apt-get install unzip (Debian/Ubuntu)');
261
- }
262
- const args = ['-o', '-q']; // Overwrite, quiet
263
- if (password) {
264
- args.push('-P', password);
265
- }
266
- args.push(archivePath, '-d', outputDir);
267
- const command = `unzip ${args.map(a => `"${a}"`).join(' ')}`;
268
- await execAsync(command);
258
+ const zip = new adm_zip_1.default(archivePath);
259
+ zip.extractAllTo(outputDir, true, undefined, password || undefined);
269
260
  }
270
261
  async extractRar(archivePath, outputDir, password) {
271
262
  const { createExtractorFromFile } = await Promise.resolve().then(() => __importStar(require('node-unrar-js')));
@@ -279,6 +270,34 @@ class ExtractArchive {
279
270
  // Iterating drives extraction; nothing else needed since targetPath handles file writing
280
271
  }
281
272
  }
273
+ async extractNestedZips(dir, password) {
274
+ let foundZip = true;
275
+ while (foundZip) {
276
+ foundZip = false;
277
+ const zipFiles = this.findZipFiles(dir);
278
+ for (const zipPath of zipFiles) {
279
+ foundZip = true;
280
+ const extractTarget = zipPath.replace(/\.zip$/i, '');
281
+ fs.mkdirSync(extractTarget, { recursive: true });
282
+ await this.extractZip(zipPath, extractTarget, password);
283
+ fs.unlinkSync(zipPath);
284
+ }
285
+ }
286
+ }
287
+ findZipFiles(dir) {
288
+ const results = [];
289
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
290
+ for (const entry of entries) {
291
+ const fullPath = path.join(dir, entry.name);
292
+ if (entry.isDirectory()) {
293
+ results.push(...this.findZipFiles(fullPath));
294
+ }
295
+ else if (entry.isFile() && /\.zip$/i.test(entry.name)) {
296
+ results.push(fullPath);
297
+ }
298
+ }
299
+ return results;
300
+ }
282
301
  getAllFiles(dir, includeSubdirs) {
283
302
  const files = [];
284
303
  const readDir = (currentDir, relativePath = '') => {
@@ -419,6 +438,11 @@ class ExtractArchive {
419
438
  else if (archiveType === 'rar') {
420
439
  await extractor.extractRar(archivePath, extractDir, password || undefined);
421
440
  }
441
+ // Recursively extract nested ZIP archives
442
+ const recursiveExtract = this.getNodeParameter('recursiveExtract', itemIndex);
443
+ if (recursiveExtract) {
444
+ await extractor.extractNestedZips(extractDir, password || undefined);
445
+ }
422
446
  // Get all extracted files
423
447
  let files = extractor.getAllFiles(extractDir, includeSubdirectories);
424
448
  // Apply file filter
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beltar/n8n-nodes-extract-archive",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "n8n node to extract ZIP and RAR archive files. Supports binary data or file path input.",
5
5
  "keywords": [
6
6
  "n8n",
@@ -40,6 +40,7 @@
40
40
  ]
41
41
  },
42
42
  "devDependencies": {
43
+ "@types/adm-zip": "^0.5.7",
43
44
  "@types/node": "^20.10.0",
44
45
  "n8n-workflow": "^1.20.0",
45
46
  "shx": "^0.4.0",
@@ -52,6 +53,7 @@
52
53
  "node": ">=18.0.0"
53
54
  },
54
55
  "dependencies": {
56
+ "adm-zip": "^0.5.16",
55
57
  "node-unrar-js": "^2.0.2"
56
58
  }
57
59
  }