@datatruck/cli 0.12.1 → 0.14.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.
Files changed (38) hide show
  1. package/Command/BackupCommand.js +2 -0
  2. package/Command/CommandAbstract.d.ts +2 -0
  3. package/Command/RestoreCommand.js +2 -0
  4. package/Command/SnapshotsCommand.js +6 -0
  5. package/Config/PackageRepositoryConfig.d.ts +3 -3
  6. package/Config/PackageRepositoryConfig.js +2 -2
  7. package/Config/RepositoryConfig.d.ts +3 -3
  8. package/Config/RepositoryConfig.js +2 -2
  9. package/Factory/RepositoryFactory.js +3 -3
  10. package/JsonSchema/DefinitionEnum.d.ts +2 -2
  11. package/JsonSchema/DefinitionEnum.js +2 -2
  12. package/JsonSchema/JsonSchema.js +3 -3
  13. package/Repository/{LocalRepository.d.ts → DatatruckRepository.d.ts} +11 -9
  14. package/Repository/{LocalRepository.js → DatatruckRepository.js} +215 -102
  15. package/Repository/GitRepository.js +3 -0
  16. package/Repository/RepositoryAbstract.d.ts +4 -1
  17. package/Repository/RepositoryAbstract.js +1 -0
  18. package/Repository/ResticRepository.js +25 -1
  19. package/SessionDriver/ConsoleSessionDriver.d.ts +8 -2
  20. package/SessionDriver/ConsoleSessionDriver.js +18 -10
  21. package/SessionDriver/SessionDriverAbstract.d.ts +6 -7
  22. package/Task/GitTask.js +1 -0
  23. package/Task/SqlDumpTaskAbstract.js +1 -0
  24. package/cli.js +2 -0
  25. package/config.schema.json +10 -6
  26. package/package.json +3 -1
  27. package/util/ResticUtil.d.ts +9 -2
  28. package/util/ResticUtil.js +47 -20
  29. package/util/datatruck/config-util.d.ts +6 -6
  30. package/util/fs-util.d.ts +18 -20
  31. package/util/fs-util.js +86 -101
  32. package/util/math-util.js +2 -0
  33. package/util/process-util.d.ts +1 -0
  34. package/util/process-util.js +23 -5
  35. package/util/string-util.d.ts +1 -0
  36. package/util/string-util.js +8 -1
  37. package/util/zip-util.d.ts +45 -18
  38. package/util/zip-util.js +98 -52
package/util/fs-util.js CHANGED
@@ -3,21 +3,59 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.cpy = exports.updateFileStats = exports.copyFileWithStreams = exports.writePathLists = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = exports.isWSLSystem = void 0;
6
+ exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = exports.pathIterator = exports.applyPermissions = exports.isEmptyDir = exports.isWSLSystem = void 0;
7
7
  const globalData_1 = __importDefault(require("../globalData"));
8
8
  const path_util_1 = require("./path-util");
9
9
  const async_1 = require("async");
10
10
  const crypto_1 = require("crypto");
11
+ const fast_folder_size_1 = __importDefault(require("fast-folder-size"));
11
12
  const fast_glob_1 = __importDefault(require("fast-glob"));
12
13
  const fs_1 = require("fs");
13
14
  const fs_2 = require("fs");
14
15
  const promises_1 = require("fs/promises");
15
- const micromatch_1 = require("micromatch");
16
16
  const os_1 = require("os");
17
17
  const path_1 = require("path");
18
18
  const path_2 = require("path");
19
19
  const readline_1 = require("readline");
20
+ const util_1 = require("util");
20
21
  exports.isWSLSystem = (0, os_1.release)().includes("microsoft-standard-WSL");
22
+ async function isEmptyDir(path) {
23
+ const iterator = await (0, promises_1.opendir)(path);
24
+ let done = false;
25
+ try {
26
+ const next = await iterator[Symbol.asyncIterator]().next();
27
+ done = !!next.done;
28
+ return done;
29
+ }
30
+ finally {
31
+ if (!done) {
32
+ await iterator.close();
33
+ }
34
+ }
35
+ }
36
+ exports.isEmptyDir = isEmptyDir;
37
+ async function applyPermissions(baseDir, permissionsPath) {
38
+ const singleReader = (0, readline_1.createInterface)({
39
+ input: (0, fs_1.createReadStream)(permissionsPath),
40
+ });
41
+ for await (const line of singleReader) {
42
+ const [rpath, rawUid, rawGui, rawMode] = line.split(":");
43
+ const path = (0, path_1.join)(baseDir, rpath);
44
+ if (!path.startsWith(baseDir)) {
45
+ throw new Error(`Entry path is out of the base dir: (${path}, ${baseDir})`);
46
+ }
47
+ const uid = Number(rawUid);
48
+ const guid = Number(rawGui);
49
+ await (0, promises_1.chown)(path, uid, guid);
50
+ const mode = Number(rawMode);
51
+ await (0, promises_1.chmod)(path, mode);
52
+ }
53
+ }
54
+ exports.applyPermissions = applyPermissions;
55
+ function pathIterator(stream) {
56
+ return stream;
57
+ }
58
+ exports.pathIterator = pathIterator;
21
59
  function isLocalDir(path) {
22
60
  return /^[\/\.]|([A-Z]:)/i.test(path);
23
61
  }
@@ -57,9 +95,7 @@ async function writeJSONFile(path, json) {
57
95
  }
58
96
  exports.writeJSONFile = writeJSONFile;
59
97
  async function readdirIfExists(path) {
60
- if (!(await existsDir(path)))
61
- return [];
62
- return await readDir(path);
98
+ return await readDir(path, true);
63
99
  }
64
100
  exports.readdirIfExists = readdirIfExists;
65
101
  exports.parseFileExtensions = ["json", "js", "ts", "yaml", "yml"];
@@ -130,6 +166,10 @@ function tmpDir(prefix, id) {
130
166
  return (0, path_1.join)(sessionTmpDir(), `${prefix}-${id}`);
131
167
  }
132
168
  exports.tmpDir = tmpDir;
169
+ async function fastFolderSizeAsync(path) {
170
+ return (await (0, util_1.promisify)(fast_folder_size_1.default)(path)) || 0;
171
+ }
172
+ exports.fastFolderSizeAsync = fastFolderSizeAsync;
133
173
  async function mkTmpDir(prefix, id) {
134
174
  const path = tmpDir(prefix, id);
135
175
  await (0, promises_1.mkdir)(path, { recursive: true });
@@ -181,13 +221,15 @@ async function checkDir(path) {
181
221
  }
182
222
  }
183
223
  exports.checkDir = checkDir;
184
- async function readDir(path) {
224
+ async function readDir(path, optional) {
185
225
  try {
186
226
  return await (0, promises_1.readdir)(path);
187
227
  }
188
228
  catch (anyError) {
189
229
  const nodeError = anyError;
190
230
  if (nodeError.code === "ENOENT") {
231
+ if (optional)
232
+ return [];
191
233
  const error = new Error(nodeError.message);
192
234
  error.code = nodeError.code;
193
235
  error.errno = nodeError.errno;
@@ -256,93 +298,14 @@ async function writeGitIgnoreList(options) {
256
298
  return path;
257
299
  }
258
300
  exports.writeGitIgnoreList = writeGitIgnoreList;
259
- async function writePathLists(options) {
260
- const tempDir = await mkTmpDir("path-lists");
261
- const includedPaths = [];
262
- const excludedPaths = [];
263
- const included = [];
264
- const excluded = [];
265
- const multipleStats = {};
266
- const total = new Array((options.packs?.length || 0) + 1).fill(0);
267
- await Promise.all([
268
- ...new Array((options.packs?.length || 0) + 1).fill(null).map((_, index) => new Promise((resolve, reject) => {
269
- const path = (0, path_1.join)(tempDir, `${index}-included.txt`);
270
- const stream = (0, fs_2.createWriteStream)(path);
271
- includedPaths.push(path);
272
- included.push(stream);
273
- stream.on("close", resolve);
274
- stream.on("error", reject);
275
- })),
276
- ...new Array(options.packs?.length || 0).fill(null).map((_, index) => new Promise((resolve, reject) => {
277
- const path = (0, path_1.join)(tempDir, `${index}-excluded.txt`);
278
- const stream = (0, fs_2.createWriteStream)(path);
279
- excludedPaths.push(path);
280
- excluded.push(stream);
281
- stream.on("close", resolve);
282
- stream.on("error", reject);
283
- })),
284
- new Promise(async (resolve) => {
285
- const packDirectories = [];
286
- for await (const value of options.paths) {
287
- const entry = value.toString();
288
- const isDir = entry.endsWith("/");
289
- const matchEntry = isDir ? entry.slice(0, -1) : entry;
290
- let packIndex = 1;
291
- let matches = false;
292
- for (const pack of options.packs || []) {
293
- if ((0, micromatch_1.isMatch)(matchEntry, pack.include) &&
294
- (!pack.exclude || !(0, micromatch_1.isMatch)(matchEntry, pack.exclude))) {
295
- if (isDir)
296
- packDirectories.push([packIndex - 1, entry]);
297
- included[packIndex].write(`${entry}\n`);
298
- if (!isDir)
299
- total[packIndex]++;
300
- matches = true;
301
- break;
302
- }
303
- packIndex++;
304
- }
305
- if (!matches) {
306
- const packDir = packDirectories.find(([, p]) => entry.startsWith(p));
307
- if (packDir) {
308
- const [i, v] = packDir;
309
- const multipleExclude = options.packs?.[i].exclude;
310
- if (multipleExclude && (0, micromatch_1.isMatch)(matchEntry, multipleExclude)) {
311
- included[0].write(`${entry}\n`);
312
- excluded[i].write(`${entry}\n`);
313
- }
314
- else {
315
- if (!multipleStats[v])
316
- multipleStats[v] = 0;
317
- multipleStats[v]++;
318
- }
319
- }
320
- else {
321
- included[0].write(`${entry}\n`);
322
- }
323
- if (!isDir)
324
- total[0]++;
325
- }
326
- }
327
- for (const stream of [...included, ...excluded]) {
328
- stream.end();
329
- }
330
- resolve();
331
- }),
332
- ]);
333
- return {
334
- path: includedPaths[0],
335
- includedPackPaths: includedPaths.slice(1),
336
- excludedPackPaths: excludedPaths,
337
- total: {
338
- all: total.reduce((p, v) => p + v, 0),
339
- path: total[0],
340
- packsPaths: total.slice(1),
341
- multipleStats,
342
- },
343
- };
301
+ async function waitForClose(stream) {
302
+ return new Promise(async (resolve, reject) => {
303
+ stream.on("close", resolve);
304
+ stream.on("error", reject);
305
+ return stream;
306
+ });
344
307
  }
345
- exports.writePathLists = writePathLists;
308
+ exports.waitForClose = waitForClose;
346
309
  async function copyFileWithStreams(source, target) {
347
310
  const r = (0, fs_1.createReadStream)(source);
348
311
  const w = (0, fs_2.createWriteStream)(target);
@@ -367,6 +330,10 @@ async function updateFileStats(path, fileInfo) {
367
330
  await (0, promises_1.chown)(path, fileInfo.uid, fileInfo.gid);
368
331
  }
369
332
  exports.updateFileStats = updateFileStats;
333
+ function isNotFoundError(error) {
334
+ return error.code === "ENOENT";
335
+ }
336
+ exports.isNotFoundError = isNotFoundError;
370
337
  async function cpy(options) {
371
338
  const stats = { paths: 0, files: 0, dirs: 0 };
372
339
  const dirs = new Set();
@@ -381,6 +348,7 @@ async function cpy(options) {
381
348
  }
382
349
  };
383
350
  const task = async (rawEntryPath, basePath) => {
351
+ [rawEntryPath] = rawEntryPath.split(":");
384
352
  const isDir = rawEntryPath.endsWith("/");
385
353
  const entryPath = (0, path_1.normalize)(rawEntryPath);
386
354
  const entrySourcePath = (0, path_1.resolve)((0, path_1.join)(basePath, rawEntryPath));
@@ -404,17 +372,34 @@ async function cpy(options) {
404
372
  stats.files++;
405
373
  // https://github.com/nodejs/node/issues/44261
406
374
  if (exports.isWSLSystem) {
407
- const fileInfo = await (0, promises_1.stat)(entrySourcePath);
408
- const isWritable = (fileInfo.mode & 0o200) === 0o200;
409
- if (!isWritable) {
410
- await copyFileWithStreams(entrySourcePath, entryTargetPath);
411
- await updateFileStats(entryTargetPath, fileInfo);
412
- return;
375
+ let fileInfo;
376
+ try {
377
+ fileInfo = await (0, promises_1.stat)(entrySourcePath);
378
+ }
379
+ catch (error) {
380
+ const skipError = options.skipNotFoundError && isNotFoundError(error);
381
+ if (!skipError)
382
+ throw error;
383
+ }
384
+ if (fileInfo) {
385
+ const isWritable = (fileInfo.mode & 0o200) === 0o200;
386
+ if (!isWritable) {
387
+ await copyFileWithStreams(entrySourcePath, entryTargetPath);
388
+ await updateFileStats(entryTargetPath, fileInfo);
389
+ return;
390
+ }
413
391
  }
414
392
  }
415
- await (0, promises_1.cp)(entrySourcePath, entryTargetPath, {
416
- preserveTimestamps: true,
417
- });
393
+ try {
394
+ await (0, promises_1.cp)(entrySourcePath, entryTargetPath, {
395
+ preserveTimestamps: true,
396
+ });
397
+ }
398
+ catch (error) {
399
+ const skipError = options.skipNotFoundError && isNotFoundError(error);
400
+ if (!skipError)
401
+ throw error;
402
+ }
418
403
  }
419
404
  };
420
405
  const { input } = options;
package/util/math-util.js CHANGED
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.progressPercent = void 0;
4
4
  function progressPercent(total, current) {
5
+ if (total === 0 && current === 0)
6
+ return 0;
5
7
  return Number(((current / total) * 100).toFixed(2));
6
8
  }
7
9
  exports.progressPercent = progressPercent;
@@ -24,6 +24,7 @@ export interface ExecSettingsInterface {
24
24
  onSpawn?: (p: ChildProcess) => void;
25
25
  stdout?: {
26
26
  save?: boolean;
27
+ parseLines?: boolean;
27
28
  onData?: (data: string) => void;
28
29
  };
29
30
  stderr?: {
@@ -11,6 +11,7 @@ const chalk_1 = __importDefault(require("chalk"));
11
11
  const child_process_1 = require("child_process");
12
12
  const fs_1 = require("fs");
13
13
  const promises_1 = require("fs/promises");
14
+ const readline_1 = require("readline");
14
15
  function logExecStdout(input) {
15
16
  let text = input.colorize ? chalk_1.default.grey(input.data) : input.data;
16
17
  if (input.lineSalt)
@@ -34,7 +35,9 @@ async function exec(command, argv = [], options = null, settings = {}) {
34
35
  return new Promise(async (resolve, reject) => {
35
36
  if (log.exec) {
36
37
  const logEnv = log.envNames?.reduce((env, key) => {
37
- env[key] = options?.env?.[key] ?? "";
38
+ const value = options?.env?.[key];
39
+ if (typeof value !== "undefined")
40
+ env[key] = value;
38
41
  return env;
39
42
  }, {});
40
43
  (0, cli_util_1.logExec)(command, pipe
@@ -68,7 +71,11 @@ async function exec(command, argv = [], options = null, settings = {}) {
68
71
  stderr: "",
69
72
  exitCode: 0,
70
73
  };
71
- let finishListeners = pipe?.stream instanceof fs_1.WriteStream ? 2 : 1;
74
+ let finishListeners = 1;
75
+ if (pipe?.stream instanceof fs_1.WriteStream)
76
+ finishListeners++;
77
+ if (settings.stdout?.parseLines)
78
+ finishListeners++;
72
79
  let streamError;
73
80
  const tryFinish = () => {
74
81
  if (!--finishListeners)
@@ -127,10 +134,11 @@ async function exec(command, argv = [], options = null, settings = {}) {
127
134
  if (log.stdout || settings.stdout) {
128
135
  if (!p.stdout)
129
136
  throw new Error(`stdout is not defined`);
130
- p.stdout.on("data", (data) => {
137
+ const parseLines = settings.stdout?.parseLines;
138
+ const onData = (data) => {
131
139
  if (log.stdout)
132
140
  logExecStdout({
133
- data: data.toString(),
141
+ data: parseLines ? `${data}\n` : data.toString(),
134
142
  stderr: log.allToStderr,
135
143
  colorize: log.colorize,
136
144
  });
@@ -138,7 +146,17 @@ async function exec(command, argv = [], options = null, settings = {}) {
138
146
  spawnData.stdout += data.toString();
139
147
  if (settings.stdout?.onData)
140
148
  settings.stdout.onData(data.toString());
141
- });
149
+ };
150
+ if (parseLines) {
151
+ const rl = (0, readline_1.createInterface)({
152
+ input: p.stdout,
153
+ });
154
+ rl.on("line", onData);
155
+ rl.on("close", tryFinish);
156
+ }
157
+ else {
158
+ p.stdout.on("data", onData);
159
+ }
142
160
  }
143
161
  if (log.stderr || settings.stderr) {
144
162
  if (!p.stderr)
@@ -15,5 +15,6 @@ export declare type UriType = {
15
15
  export declare function formatUri(input: UriType, hidePassword?: boolean): string;
16
16
  export declare function formatSeconds(seconds: number): string;
17
17
  export declare function makePathPatterns(values: string[] | undefined): string[] | undefined;
18
+ export declare function checkPath(path: string, include: string[], exclude?: string[]): boolean;
18
19
  export declare function checkMatch(subject: string | undefined, patterns: string[]): boolean;
19
20
  export declare function formatDateTime(datetime: string): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatDateTime = exports.checkMatch = exports.makePathPatterns = exports.formatSeconds = exports.formatUri = exports.parseStringList = exports.render = exports.snakeCase = exports.lcfirst = exports.ucfirst = exports.serialize = void 0;
3
+ exports.formatDateTime = exports.checkMatch = exports.checkPath = exports.makePathPatterns = exports.formatSeconds = exports.formatUri = exports.parseStringList = exports.render = exports.snakeCase = exports.lcfirst = exports.ucfirst = exports.serialize = void 0;
4
4
  const AppError_1 = require("../Error/AppError");
5
5
  const micromatch_1 = require("micromatch");
6
6
  function serialize(message, data) {
@@ -99,6 +99,13 @@ function makePathPatterns(values) {
99
99
  });
100
100
  }
101
101
  exports.makePathPatterns = makePathPatterns;
102
+ function checkPath(path, include, exclude) {
103
+ return ((0, micromatch_1.isMatch)(path, include, {
104
+ dot: true,
105
+ }) &&
106
+ (!exclude || !(0, micromatch_1.isMatch)(path, exclude, { dot: true })));
107
+ }
108
+ exports.checkPath = checkPath;
102
109
  function checkMatch(subject, patterns) {
103
110
  if (!subject?.length)
104
111
  subject = "<empty>";
@@ -3,20 +3,6 @@ export interface ZipDataFilterType {
3
3
  exclude?: boolean;
4
4
  patterns: string[];
5
5
  }
6
- export declare type ZipStreamDataType = {
7
- type: "progress";
8
- data: {
9
- progress: number;
10
- files: number;
11
- path: string;
12
- };
13
- } | {
14
- type: "summary";
15
- data: {
16
- folders: number;
17
- files: number;
18
- };
19
- };
20
6
  export interface ZipDataType {
21
7
  command?: string;
22
8
  path: string;
@@ -26,7 +12,7 @@ export interface ZipDataType {
26
12
  includeList?: string;
27
13
  excludeList?: string;
28
14
  verbose?: boolean;
29
- onStream?: (data: ZipStreamDataType) => void;
15
+ onStream?: (data: ZipStream) => void;
30
16
  }
31
17
  export interface UnzipDataType {
32
18
  command?: string;
@@ -34,19 +20,60 @@ export interface UnzipDataType {
34
20
  files?: (ZipDataFilterType | string)[];
35
21
  output: string;
36
22
  verbose?: boolean;
37
- onStream?: (data: UnzipStreamDataType) => void;
23
+ onStream?: (data: UnzipStream) => void;
38
24
  }
39
- export declare type UnzipStreamDataType = {
25
+ export declare function buildArguments(filters: (ZipDataFilterType | string)[]): string[];
26
+ export declare function checkSSEOption(command?: string): Promise<boolean>;
27
+ declare type ListZipStream = {
28
+ Path?: string;
29
+ Folder?: string;
30
+ Size?: string;
31
+ "Packed Size"?: string;
32
+ Modified?: string;
33
+ Created?: string;
34
+ Accessed?: string;
35
+ Attributes?: string;
36
+ Encrypted?: string;
37
+ Comment?: string;
38
+ CRC?: string;
39
+ Method?: string;
40
+ Characteristics?: string;
41
+ "Host OS"?: string;
42
+ Version?: string;
43
+ Volume?: string;
44
+ Offset?: string;
45
+ };
46
+ export declare function listZip(data: {
47
+ command?: string;
48
+ path: string;
49
+ onStream: (item: ListZipStream) => void;
50
+ verbose?: boolean;
51
+ }): Promise<void>;
52
+ export declare type ZipStream = {
40
53
  type: "progress";
41
54
  data: {
42
55
  progress: number;
43
56
  files: number;
44
57
  path: string;
45
58
  };
59
+ } | {
60
+ type: "summary";
61
+ data: {
62
+ folders: number;
63
+ files: number;
64
+ };
46
65
  };
47
- export declare function buildArguments(filters: (ZipDataFilterType | string)[]): string[];
48
66
  export declare function zip(data: ZipDataType): Promise<{
49
67
  folders: number;
50
68
  files: number;
51
69
  }>;
70
+ export declare type UnzipStream = {
71
+ type: "progress";
72
+ data: {
73
+ progress: number;
74
+ files: number;
75
+ path: string;
76
+ };
77
+ };
52
78
  export declare function unzip(data: UnzipDataType): Promise<import("./process-util").ExecResultType>;
79
+ export {};
package/util/zip-util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.unzip = exports.zip = exports.buildArguments = void 0;
3
+ exports.unzip = exports.zip = exports.listZip = exports.checkSSEOption = exports.buildArguments = void 0;
4
4
  const process_util_1 = require("./process-util");
5
5
  const path_1 = require("path");
6
6
  function buildArguments(filters) {
@@ -30,45 +30,94 @@ function buildArguments(filters) {
30
30
  return args;
31
31
  }
32
32
  exports.buildArguments = buildArguments;
33
- function parseZipStream(chunk, buffer, cb) {
34
- const lines = chunk.replaceAll("\b", "").trim().split(/\r?\n/);
35
- for (const line of lines) {
36
- let matches = null;
37
- if ((matches = /^(\d+)% (\d+ )?\+/.exec(line))) {
38
- const path = line.slice(line.indexOf("+") + 1).trim();
39
- const progress = Number(matches[1]);
40
- if (!buffer.currentPaths)
41
- buffer.currentPaths = 0;
42
- if (path !== buffer.lastPath)
43
- buffer.currentPaths++;
44
- buffer.lastPath = path;
45
- cb({
46
- type: "progress",
47
- data: { progress, path, files: buffer.currentPaths },
48
- });
33
+ let checkSSEOptionResult;
34
+ async function checkSSEOption(command = "7z") {
35
+ const result = await (0, process_util_1.exec)(command);
36
+ if (typeof checkSSEOptionResult === "boolean")
37
+ return checkSSEOptionResult;
38
+ return (checkSSEOptionResult = result.stdout.includes(" -sse"));
39
+ }
40
+ exports.checkSSEOption = checkSSEOption;
41
+ const listZipLineEqChar = " = ";
42
+ function parseListZipLine(line, buffer) {
43
+ if (buffer.started) {
44
+ if (line === "") {
45
+ if (buffer.opened) {
46
+ const { stream } = buffer;
47
+ buffer.stream = {};
48
+ buffer.opened = false;
49
+ return stream;
50
+ }
49
51
  }
50
- else if (line.startsWith("Add new data to archive:")) {
51
- const [, folders] = /(\d+) folders?/i.exec(line) || [, 0];
52
- const [, files] = /(\d+) files?/i.exec(line) || [, 0];
53
- cb({
54
- type: "summary",
55
- data: {
56
- folders: Number(folders),
57
- files: Number(files),
58
- },
59
- });
52
+ else {
53
+ const separator = line.indexOf(listZipLineEqChar);
54
+ const key = line.slice(0, separator);
55
+ const value = line.slice(separator + listZipLineEqChar.length);
56
+ buffer.opened = true;
57
+ buffer.stream[key] = value;
60
58
  }
61
59
  }
60
+ else if (line.startsWith("----------")) {
61
+ buffer.started = true;
62
+ buffer.stream = {};
63
+ }
64
+ }
65
+ async function listZip(data) {
66
+ const buffer = {};
67
+ await (0, process_util_1.exec)(data.command ?? "7z", ["l", data.path, "-slt"], {}, {
68
+ log: {
69
+ exec: data.verbose ?? false,
70
+ stderr: data.verbose ?? false,
71
+ stdout: false,
72
+ },
73
+ onExitCodeError: (data, error) => (data.exitCode > 2 ? error : false),
74
+ stdout: {
75
+ parseLines: true,
76
+ onData: (line) => {
77
+ const stream = parseListZipLine(line, buffer);
78
+ if (stream) {
79
+ data.onStream?.(stream);
80
+ }
81
+ },
82
+ },
83
+ });
84
+ }
85
+ exports.listZip = listZip;
86
+ function parseZipLine(line) {
87
+ let matches = null;
88
+ if (!line.trim().length)
89
+ return;
90
+ if ((matches = /^\s*(\d+)% (\d+ )?\+/.exec(line))) {
91
+ const path = line.slice(line.indexOf("+") + 1).trim();
92
+ const progress = Number(matches[1]);
93
+ const files = Number(matches[2]);
94
+ return {
95
+ type: "progress",
96
+ data: { progress, path, files },
97
+ };
98
+ }
99
+ else if (line.startsWith("Add new data to archive:")) {
100
+ const [, folders] = /(\d+) folders?/i.exec(line) || [, 0];
101
+ const [, files] = /(\d+) files?/i.exec(line) || [, 0];
102
+ return {
103
+ type: "summary",
104
+ data: {
105
+ folders: Number(folders),
106
+ files: Number(files),
107
+ },
108
+ };
109
+ }
62
110
  }
63
111
  async function zip(data) {
64
112
  let result = {
65
113
  folders: 0,
66
114
  files: 0,
67
115
  };
68
- let buffer = {};
69
116
  await (0, process_util_1.exec)(data.command ?? "7z", [
70
117
  "a",
71
- "-mmt1",
118
+ // https://sourceforge.net/p/sevenzip/bugs/2099/,
119
+ // https://github.com/mcmilk/7-Zip/commit/87ba6f01ba3c5b2ce3186bddfe3d7d880639193c#diff-779d6b1bfa6196b288478f78ca96c4d4c6d7ac6cf8be15a28a20dabc9137ca36L515
120
+ ...((await checkSSEOption(data.command)) ? [] : ["-mmt1"]),
72
121
  "-bsp1",
73
122
  ...(data.deleteOnZip ? ["-sdel"] : []),
74
123
  (0, path_1.normalize)(data.output),
@@ -79,41 +128,36 @@ async function zip(data) {
79
128
  cwd: data.path,
80
129
  }, {
81
130
  log: data.verbose ?? false,
82
- stderr: {
83
- toExitCode: true,
84
- },
131
+ onExitCodeError: (data, error) => (data.exitCode > 2 ? error : false),
85
132
  stdout: {
86
- onData: (chunk) => {
87
- parseZipStream(chunk, buffer, (stream) => {
133
+ onData: (line) => {
134
+ const stream = parseZipLine(line);
135
+ if (stream) {
88
136
  data.onStream?.(stream);
89
137
  if (stream.type === "summary")
90
138
  result = stream.data;
91
- });
139
+ }
92
140
  },
93
141
  },
94
142
  });
95
143
  return result;
96
144
  }
97
145
  exports.zip = zip;
98
- function parseUnzipStream(chunk, cb) {
99
- const lines = chunk.trim().split(/\r?\n/g);
100
- for (const line of lines) {
101
- let matches = null;
102
- if ((matches = /^(\d+)% (\d+) \-/.exec(line))) {
103
- const progress = Number(matches[1]);
104
- const files = Number(matches[2]);
105
- const path = line.slice(line.indexOf("-") + 1).trim();
106
- cb({
107
- type: "progress",
108
- data: { progress, path, files },
109
- });
110
- }
146
+ function parseUnzipLine(line) {
147
+ let matches = null;
148
+ if ((matches = /^\s*(\d+)% (\d+) \-/.exec(line))) {
149
+ const progress = Number(matches[1]);
150
+ const files = Number(matches[2]);
151
+ const path = line.slice(line.indexOf("-") + 1).trim();
152
+ return {
153
+ type: "progress",
154
+ data: { progress, path, files },
155
+ };
111
156
  }
112
157
  }
113
158
  async function unzip(data) {
114
159
  return await (0, process_util_1.exec)(data.command ?? "7z", [
115
160
  "x",
116
- "-mmt1",
117
161
  "-bsp1",
118
162
  (0, path_1.normalize)(data.input),
119
163
  ...buildArguments(data.files ?? []),
@@ -124,9 +168,11 @@ async function unzip(data) {
124
168
  stderr: { toExitCode: true },
125
169
  stdout: {
126
170
  ...(data.onStream && {
127
- onData: (chunk) => {
128
- if (data.onStream)
129
- parseUnzipStream(chunk, data.onStream);
171
+ parseLines: true,
172
+ onData: (line) => {
173
+ const stream = parseUnzipLine(line);
174
+ if (stream)
175
+ data.onStream(stream);
130
176
  },
131
177
  }),
132
178
  },