@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.
- package/Command/BackupCommand.js +2 -0
- package/Command/CommandAbstract.d.ts +2 -0
- package/Command/RestoreCommand.js +2 -0
- package/Command/SnapshotsCommand.js +6 -0
- package/Config/PackageRepositoryConfig.d.ts +3 -3
- package/Config/PackageRepositoryConfig.js +2 -2
- package/Config/RepositoryConfig.d.ts +3 -3
- package/Config/RepositoryConfig.js +2 -2
- package/Factory/RepositoryFactory.js +3 -3
- package/JsonSchema/DefinitionEnum.d.ts +2 -2
- package/JsonSchema/DefinitionEnum.js +2 -2
- package/JsonSchema/JsonSchema.js +3 -3
- package/Repository/{LocalRepository.d.ts → DatatruckRepository.d.ts} +11 -9
- package/Repository/{LocalRepository.js → DatatruckRepository.js} +215 -102
- package/Repository/GitRepository.js +3 -0
- package/Repository/RepositoryAbstract.d.ts +4 -1
- package/Repository/RepositoryAbstract.js +1 -0
- package/Repository/ResticRepository.js +25 -1
- package/SessionDriver/ConsoleSessionDriver.d.ts +8 -2
- package/SessionDriver/ConsoleSessionDriver.js +18 -10
- package/SessionDriver/SessionDriverAbstract.d.ts +6 -7
- package/Task/GitTask.js +1 -0
- package/Task/SqlDumpTaskAbstract.js +1 -0
- package/cli.js +2 -0
- package/config.schema.json +10 -6
- package/package.json +3 -1
- package/util/ResticUtil.d.ts +9 -2
- package/util/ResticUtil.js +47 -20
- package/util/datatruck/config-util.d.ts +6 -6
- package/util/fs-util.d.ts +18 -20
- package/util/fs-util.js +86 -101
- package/util/math-util.js +2 -0
- package/util/process-util.d.ts +1 -0
- package/util/process-util.js +23 -5
- package/util/string-util.d.ts +1 -0
- package/util/string-util.js +8 -1
- package/util/zip-util.d.ts +45 -18
- 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.
|
|
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
|
-
|
|
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
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
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
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
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
|
-
|
|
416
|
-
|
|
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;
|
package/util/process-util.d.ts
CHANGED
package/util/process-util.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
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)
|
package/util/string-util.d.ts
CHANGED
|
@@ -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;
|
package/util/string-util.js
CHANGED
|
@@ -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>";
|
package/util/zip-util.d.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
23
|
+
onStream?: (data: UnzipStream) => void;
|
|
38
24
|
}
|
|
39
|
-
export declare
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
-
toExitCode: true,
|
|
84
|
-
},
|
|
131
|
+
onExitCodeError: (data, error) => (data.exitCode > 2 ? error : false),
|
|
85
132
|
stdout: {
|
|
86
|
-
onData: (
|
|
87
|
-
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
171
|
+
parseLines: true,
|
|
172
|
+
onData: (line) => {
|
|
173
|
+
const stream = parseUnzipLine(line);
|
|
174
|
+
if (stream)
|
|
175
|
+
data.onStream(stream);
|
|
130
176
|
},
|
|
131
177
|
}),
|
|
132
178
|
},
|