zip-lib 1.1.2 → 1.2.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.
package/lib/cancelable.js CHANGED
@@ -33,7 +33,9 @@ class CancellationToken {
33
33
  return;
34
34
  }
35
35
  this._isCancelled = true;
36
- this._callbacks.forEach((cb) => cb());
36
+ this._callbacks.forEach((cb) => {
37
+ cb();
38
+ });
37
39
  this._callbacks.clear();
38
40
  }
39
41
  }
package/lib/fs.d.ts CHANGED
@@ -5,15 +5,19 @@ export interface FileEntry {
5
5
  mtime: Date;
6
6
  mode: number;
7
7
  }
8
+ export type FolderStat = {
9
+ isDirectory: true;
10
+ isSymbolicLink: false;
11
+ } | {
12
+ isDirectory: false;
13
+ isSymbolicLink: true;
14
+ realpath: string;
15
+ };
8
16
  export type FileType = "file" | "dir";
9
17
  export declare function realpath(target: string): Promise<string>;
10
18
  export declare function readdirp(folder: string): Promise<FileEntry[]>;
11
19
  export declare function getFileEntry(target: string): Promise<FileEntry>;
12
- export declare function ensureFolder(folder: string): Promise<{
13
- isDirectory: boolean;
14
- isSymbolicLink: boolean;
15
- realpath?: string;
16
- }>;
20
+ export declare function ensureFolder(folder: string): Promise<FolderStat>;
17
21
  export declare function pathExists(target: string): Promise<boolean>;
18
22
  export declare function rimraf(target: string): Promise<void>;
19
23
  export declare function isRootPath(target: string): boolean;
package/lib/fs.js CHANGED
@@ -7,10 +7,10 @@ exports.ensureFolder = ensureFolder;
7
7
  exports.pathExists = pathExists;
8
8
  exports.rimraf = rimraf;
9
9
  exports.isRootPath = isRootPath;
10
- const path = require("path");
11
- const fs = require("fs/promises");
10
+ const fsSync = require("node:fs");
11
+ const fs = require("node:fs/promises");
12
+ const path = require("node:path");
12
13
  const util = require("node:util");
13
- const fsSync = require("fs");
14
14
  async function realpath(target) {
15
15
  // fs.promises.realpath has a bug with long path on Windows.
16
16
  // https://github.com/nodejs/node/issues/51031
@@ -59,7 +59,7 @@ async function getFileEntry(target) {
59
59
  isSymbolicLink,
60
60
  type: fileType,
61
61
  mtime: stat.mtime,
62
- mode: stat.mode
62
+ mode: stat.mode,
63
63
  };
64
64
  }
65
65
  async function ensureFolder(folder) {
@@ -67,7 +67,7 @@ async function ensureFolder(folder) {
67
67
  if (folder === path.dirname(folder)) {
68
68
  return Promise.resolve({
69
69
  isDirectory: true,
70
- isSymbolicLink: false
70
+ isSymbolicLink: false,
71
71
  });
72
72
  }
73
73
  try {
@@ -90,7 +90,7 @@ async function pathExists(target) {
90
90
  await fs.access(target);
91
91
  return true;
92
92
  }
93
- catch (error) {
93
+ catch (_error) {
94
94
  return false;
95
95
  }
96
96
  }
@@ -105,7 +105,7 @@ async function rimraf(target) {
105
105
  if (stat.isDirectory() && !stat.isSymbolicLink()) {
106
106
  // Children
107
107
  const children = await fs.readdir(target);
108
- await Promise.all(children.map(child => rimraf(path.join(target, child))));
108
+ await Promise.all(children.map((child) => rimraf(path.join(target, child))));
109
109
  // Folder
110
110
  await fs.rmdir(target);
111
111
  }
@@ -113,7 +113,8 @@ async function rimraf(target) {
113
113
  else {
114
114
  // chmod as needed to allow for unlink
115
115
  const mode = stat.mode;
116
- if (!(mode & 128)) { // 128 === 0200
116
+ if (!(mode & 128)) {
117
+ // 128 === 0200
117
118
  await fs.chmod(target, mode | 128);
118
119
  }
119
120
  return fs.unlink(target);
@@ -164,7 +165,7 @@ async function mkdir(folder) {
164
165
  };
165
166
  }
166
167
  }
167
- catch (statError) {
168
+ catch (_statError) {
168
169
  throw error; // rethrow original error
169
170
  }
170
171
  }
@@ -182,9 +183,9 @@ const charColon = 58;
182
183
  // "\"
183
184
  const charWinSep = 92;
184
185
  // "/"
185
- const cahrUnixSep = 47;
186
+ const charUnixSep = 47;
186
187
  function isDriveLetter(char0) {
187
- return char0 >= charA && char0 <= charZ || char0 >= chara && char0 <= charz;
188
+ return (char0 >= charA && char0 <= charZ) || (char0 >= chara && char0 <= charz);
188
189
  }
189
190
  const winSep = "\\";
190
191
  const unixSep = "/";
@@ -192,17 +193,16 @@ function isRootPath(target) {
192
193
  if (!target) {
193
194
  return false;
194
195
  }
195
- if (target === winSep ||
196
- target === unixSep) {
196
+ if (target === winSep || target === unixSep) {
197
197
  return true;
198
198
  }
199
199
  if (process.platform === "win32") {
200
200
  if (target.length > 3) {
201
201
  return false;
202
202
  }
203
- return isDriveLetter(target.charCodeAt(0))
204
- && target.charCodeAt(1) === charColon
205
- && (target.length === 2 || target.charCodeAt(2) === charWinSep || target.charCodeAt(2) === cahrUnixSep);
203
+ return (isDriveLetter(target.charCodeAt(0)) &&
204
+ target.charCodeAt(1) === charColon &&
205
+ (target.length === 2 || target.charCodeAt(2) === charWinSep || target.charCodeAt(2) === charUnixSep));
206
206
  }
207
207
  return false;
208
208
  }
package/lib/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { IZipOptions } from "./zip";
2
- import { IExtractOptions } from "./unzip";
3
- export * from "./zip";
1
+ import { type IExtractOptions } from "./unzip";
2
+ import { type IZipOptions } from "./zip";
4
3
  export * from "./unzip";
4
+ export * from "./zip";
5
5
  /**
6
6
  * Compress a single file to zip.
7
7
  * @param file
package/lib/index.js CHANGED
@@ -17,10 +17,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.archiveFile = archiveFile;
18
18
  exports.archiveFolder = archiveFolder;
19
19
  exports.extract = extract;
20
- const zip_1 = require("./zip");
21
20
  const unzip_1 = require("./unzip");
22
- __exportStar(require("./zip"), exports);
21
+ const zip_1 = require("./zip");
23
22
  __exportStar(require("./unzip"), exports);
23
+ __exportStar(require("./zip"), exports);
24
24
  /**
25
25
  * Compress a single file to zip.
26
26
  * @param file
package/lib/unzip.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Unzip = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const fs = require("node:fs/promises");
6
+ const path = require("node:path");
4
7
  const yauzl = require("yauzl");
5
- const exfs = require("./fs");
6
- const fs_1 = require("fs");
7
- const fs = require("fs/promises");
8
- const path = require("path");
9
8
  const cancelable_1 = require("./cancelable");
9
+ const exfs = require("./fs");
10
10
  class EntryEvent {
11
11
  /**
12
12
  *
@@ -64,8 +64,14 @@ class EntryContext {
64
64
  this._symlinkFolders.push({ folder, realpath });
65
65
  }
66
66
  }
67
+ isOutside(baseDir, targetPath) {
68
+ const absoluteBase = path.resolve(baseDir);
69
+ const absoluteTarget = path.resolve(targetPath);
70
+ const relative = path.relative(absoluteBase, absoluteTarget);
71
+ return relative.startsWith("..") || path.isAbsolute(relative);
72
+ }
67
73
  getFilePath() {
68
- return path.join(this.targetFolder, this.decodeEntryFileName);
74
+ return path.resolve(path.join(this.targetFolder, this.decodeEntryFileName));
69
75
  }
70
76
  async ensureFolder(folder) {
71
77
  if (this._ensuredFolders.includes(folder)) {
@@ -78,12 +84,10 @@ class EntryContext {
78
84
  this._ensuredFolders.push(folder);
79
85
  }
80
86
  async isOutsideTargetFolder(tpath) {
81
- if (this._symlinkFileNames.length === 0 &&
82
- this._symlinkFolders.length === 0) {
87
+ if (this._symlinkFileNames.length === 0 && this._symlinkFolders.length === 0) {
83
88
  return false;
84
89
  }
85
- if (process.platform === "win32" &&
86
- this.symlinkAsFileOnWindows) {
90
+ if (process.platform === "win32" && this.symlinkAsFileOnWindows) {
87
91
  return false;
88
92
  }
89
93
  for (const { folder, realpath } of this._symlinkFolders) {
@@ -96,7 +100,7 @@ class EntryContext {
96
100
  for (const fileName of this._symlinkFileNames) {
97
101
  if (tpath.includes(fileName)) {
98
102
  const realFilePath = await exfs.realpath(tpath);
99
- if (realFilePath.indexOf(this.realTargetFolder) !== 0) {
103
+ if (this.isOutside(this.realTargetFolder, realFilePath)) {
100
104
  return true;
101
105
  }
102
106
  }
@@ -227,7 +231,7 @@ class Unzip extends cancelable_1.Cancelable {
227
231
  yauzl.open(zipFile, {
228
232
  lazyEntries: true,
229
233
  // see https://github.com/thejoshwolfe/yauzl/issues/84
230
- decodeStrings: false
234
+ decodeStrings: false,
231
235
  }, (err, zfile) => {
232
236
  if (err) {
233
237
  e(this.wrapError(err, token.isCancelled));
@@ -285,17 +289,17 @@ class Unzip extends cancelable_1.Cancelable {
285
289
  fileStream.destroy(this.canceledError());
286
290
  }
287
291
  });
288
- return new Promise(async (c, e) => {
292
+ return new Promise((c, e) => {
289
293
  try {
290
294
  const filePath = entryContext.getFilePath();
291
- const mode = this.modeFromEntry(entry);
292
- // see https://unix.stackexchange.com/questions/193465/what-file-mode-is-a-symlink
293
- const isSymlink = ((mode & 0o170000) === 0o120000);
294
295
  readStream.once("error", (err) => {
295
296
  e(this.wrapError(err, token.isCancelled));
296
297
  });
298
+ const mode = this.modeFromEntry(entry);
299
+ // see https://unix.stackexchange.com/questions/193465/what-file-mode-is-a-symlink
300
+ const isSymlink = (mode & 0o170000) === 0o120000;
297
301
  if (isSymlink) {
298
- entryContext.symlinkFileNames.push(entryContext.decodeEntryFileName);
302
+ entryContext.symlinkFileNames.push(path.resolve(path.join(entryContext.targetFolder, entryContext.decodeEntryFileName)));
299
303
  }
300
304
  if (isSymlink && !this.symlinkToFile()) {
301
305
  let linkContent = "";
@@ -312,7 +316,7 @@ class Unzip extends cancelable_1.Cancelable {
312
316
  });
313
317
  }
314
318
  else {
315
- fileStream = (0, fs_1.createWriteStream)(filePath, { mode });
319
+ fileStream = (0, node_fs_1.createWriteStream)(filePath, { mode });
316
320
  fileStream.once("close", () => c());
317
321
  fileStream.once("error", (err) => {
318
322
  e(this.wrapError(err, token.isCancelled));
@@ -328,12 +332,12 @@ class Unzip extends cancelable_1.Cancelable {
328
332
  modeFromEntry(entry) {
329
333
  const attr = entry.externalFileAttributes >> 16 || 33188;
330
334
  return [448 /* S_IRWXU */, 56 /* S_IRWXG */, 7 /* S_IRWXO */]
331
- .map(mask => attr & mask)
335
+ .map((mask) => attr & mask)
332
336
  .reduce((a, b) => a + b, attr & 61440 /* S_IFMT */);
333
337
  }
334
338
  async createSymlink(linkContent, des) {
335
339
  let linkType = "file";
336
- if (process.platform === 'win32') {
340
+ if (process.platform === "win32") {
337
341
  if (/\/$/.test(linkContent)) {
338
342
  linkType = "dir";
339
343
  }
@@ -348,7 +352,7 @@ class Unzip extends cancelable_1.Cancelable {
348
352
  linkType = "dir";
349
353
  }
350
354
  }
351
- catch (error) {
355
+ catch (_error) {
352
356
  // ignore
353
357
  }
354
358
  }
@@ -356,22 +360,22 @@ class Unzip extends cancelable_1.Cancelable {
356
360
  await fs.symlink(linkContent, des, linkType);
357
361
  }
358
362
  isOverwrite() {
359
- if (this.options &&
360
- this.options.overwrite) {
363
+ var _a;
364
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.overwrite) {
361
365
  return true;
362
366
  }
363
367
  return false;
364
368
  }
365
369
  onEntryCallback(event) {
366
- if (this.options && this.options.onEntry) {
370
+ var _a;
371
+ if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.onEntry) {
367
372
  this.options.onEntry(event);
368
373
  }
369
374
  }
370
375
  symlinkToFile() {
371
376
  let symlinkToFile = false;
372
377
  if (process.platform === "win32") {
373
- if (this.options &&
374
- this.options.symlinkAsFileOnWindows === false) {
378
+ if (this.options && this.options.symlinkAsFileOnWindows === false) {
375
379
  symlinkToFile = false;
376
380
  }
377
381
  else {
package/lib/zip.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Zip = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const fs = require("node:fs/promises");
6
+ const path = require("node:path");
4
7
  const yazl = require("yazl");
5
- const fs_1 = require("fs");
6
- const fs = require("fs/promises");
7
- const exfs = require("./fs");
8
- const path = require("path");
9
8
  const cancelable_1 = require("./cancelable");
9
+ const exfs = require("./fs");
10
10
  /**
11
11
  * Compress files or folders to a zip file.
12
12
  */
@@ -34,7 +34,7 @@ class Zip extends cancelable_1.Cancelable {
34
34
  }
35
35
  this.zipFiles.push({
36
36
  path: file,
37
- metadataPath: mPath
37
+ metadataPath: mPath,
38
38
  });
39
39
  }
40
40
  /**
@@ -46,7 +46,7 @@ class Zip extends cancelable_1.Cancelable {
46
46
  addFolder(folder, metadataPath) {
47
47
  this.zipFolders.push({
48
48
  path: folder,
49
- metadataPath
49
+ metadataPath,
50
50
  });
51
51
  }
52
52
  /**
@@ -64,13 +64,13 @@ class Zip extends cancelable_1.Cancelable {
64
64
  // Re-instantiate yazl every time the archive method is called to ensure that files are not added repeatedly.
65
65
  // This will also make the Zip class reusable.
66
66
  this.yazlFile = new yazl.ZipFile();
67
- return new Promise(async (c, e) => {
67
+ return new Promise((c, e) => {
68
68
  this.yazlFile.once("error", (err) => {
69
69
  e(this.wrapError(err, token.isCancelled));
70
70
  });
71
71
  const zip = this.yazlFile;
72
72
  if (!token.isCancelled) {
73
- this.zipStream = (0, fs_1.createWriteStream)(zipFile);
73
+ this.zipStream = (0, node_fs_1.createWriteStream)(zipFile);
74
74
  this.zipStream.once("error", (err) => {
75
75
  e(this.wrapError(err, token.isCancelled));
76
76
  });
@@ -88,21 +88,24 @@ class Zip extends cancelable_1.Cancelable {
88
88
  zip.outputStream.pipe(this.zipStream);
89
89
  this.isPipe = true;
90
90
  }
91
- try {
92
- const files = this.zipFiles;
93
- for (const file of files) {
94
- const entry = await exfs.getFileEntry(file.path);
95
- await this.addEntry(zip, entry, file, token);
91
+ const start = async () => {
92
+ try {
93
+ const files = this.zipFiles;
94
+ for (const file of files) {
95
+ const entry = await exfs.getFileEntry(file.path);
96
+ await this.addEntry(zip, entry, file, token);
97
+ }
98
+ if (this.zipFolders.length > 0) {
99
+ await this.walkDir(this.zipFolders, token);
100
+ }
96
101
  }
97
- if (this.zipFolders.length > 0) {
98
- await this.walkDir(this.zipFolders, token);
102
+ catch (error) {
103
+ e(this.wrapError(error, token.isCancelled));
99
104
  }
100
- }
101
- catch (error) {
102
- e(this.wrapError(error, token.isCancelled));
103
- return;
104
- }
105
- zip.end();
105
+ };
106
+ start().finally(() => {
107
+ zip.end();
108
+ });
106
109
  });
107
110
  }
108
111
  /**
@@ -135,7 +138,7 @@ class Zip extends cancelable_1.Cancelable {
135
138
  if (entry.type === "dir") {
136
139
  zip.addEmptyDirectory(file.metadataPath, {
137
140
  mtime: entry.mtime,
138
- mode: entry.mode
141
+ mode: entry.mode,
139
142
  });
140
143
  }
141
144
  else {
@@ -145,7 +148,7 @@ class Zip extends cancelable_1.Cancelable {
145
148
  }
146
149
  addFileStream(zip, file, metadataPath, token) {
147
150
  return new Promise((c, e) => {
148
- const fileStream = (0, fs_1.createReadStream)(file.path);
151
+ const fileStream = (0, node_fs_1.createReadStream)(file.path);
149
152
  fileStream.once("error", (err) => {
150
153
  const wrappedError = this.wrapError(err, token.isCancelled);
151
154
  this.stopPipe(wrappedError);
@@ -175,7 +178,9 @@ class Zip extends cancelable_1.Cancelable {
175
178
  return;
176
179
  }
177
180
  const relativePath = path.relative(folder.path, entry.path);
178
- const metadataPath = folder.metadataPath ? path.join(folder.metadataPath, relativePath) : relativePath;
181
+ const metadataPath = folder.metadataPath
182
+ ? path.join(folder.metadataPath, relativePath)
183
+ : relativePath;
179
184
  await this.addEntry(this.yazlFile, entry, { path: entry.path, metadataPath }, token);
180
185
  }
181
186
  }
@@ -197,8 +202,7 @@ class Zip extends cancelable_1.Cancelable {
197
202
  }
198
203
  followSymlink() {
199
204
  let followSymlink = false;
200
- if (this.options &&
201
- this.options.followSymlinks === true) {
205
+ if (this.options && this.options.followSymlinks === true) {
202
206
  followSymlink = true;
203
207
  }
204
208
  return followSymlink;
package/package.json CHANGED
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "name": "zip-lib",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "zip and unzip library for node",
5
5
  "main": "lib/index.js",
6
6
  "types": "./lib/index.d.ts",
7
7
  "scripts": {
8
- "compile": "rimraf ./dist && tsc -p ./src/tsconfig.json",
9
- "release": "rimraf ./pkg && npm run tsc-cjs && node ./build.mjs",
8
+ "lint": "biome lint",
10
9
  "tsc-cjs": "tsc -p ./src/tsconfig.production.cjs.json",
11
- "compile-test": "rimraf ./test/out && tsc -p ./test/src/tsconfig.json",
12
- "test": "npm run compile && npm run compile-test && node ./test/src/before.js && mocha ./test/out --timeout 10000"
10
+ "release": "rimraf ./pkg && npm run tsc-cjs && node ./build.mjs",
11
+ "test": "vitest run",
12
+ "test:coverage": "vitest run --coverage"
13
13
  },
14
14
  "repository": {
15
15
  "type": "git",
16
16
  "url": "https://github.com/fpsqdb/zip-lib.git"
17
17
  },
18
18
  "engines": {
19
- "node": ">=18"
19
+ "node": ">=20"
20
20
  },
21
21
  "keywords": [
22
22
  "zip",
@@ -34,12 +34,13 @@
34
34
  "yazl": "^3.3.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@types/mocha": "^10.0.10",
38
- "@types/node": "^18.19.86",
37
+ "@biomejs/biome": "^2.3.14",
38
+ "@types/node": "^20.19.32",
39
39
  "@types/yauzl": "^2.10.3",
40
- "@types/yazl": "^2.4.6",
41
- "mocha": "^11.1.0",
42
- "rimraf": "^6.0.1",
43
- "typescript": "^5.8.3"
40
+ "@types/yazl": "^3.3.0",
41
+ "@vitest/coverage-v8": "^4.0.18",
42
+ "rimraf": "^6.1.2",
43
+ "typescript": "^5.9.3",
44
+ "vitest": "^4.0.18"
44
45
  }
45
46
  }