@datatruck/cli 0.18.0 → 0.20.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 (56) hide show
  1. package/Action/BackupAction.js +4 -2
  2. package/Action/BackupSessionsAction.js +2 -0
  3. package/Action/CleanCacheAction.js +1 -0
  4. package/Action/ConfigAction.js +1 -0
  5. package/Action/InitAction.js +2 -0
  6. package/Action/PruneAction.js +2 -0
  7. package/Action/RestoreAction.js +5 -3
  8. package/Action/RestoreSessionsAction.js +2 -0
  9. package/Action/SnapshotsAction.js +2 -0
  10. package/Command/CommandAbstract.js +2 -0
  11. package/Entity/BackupSessionEntity.js +5 -2
  12. package/Entity/BackupSessionRepositoryEntity.js +5 -2
  13. package/Entity/BackupSessionTaskEntity.js +4 -2
  14. package/Entity/CrudEntityAbstract.js +3 -0
  15. package/Entity/RestoreSessionEntity.js +4 -2
  16. package/Entity/RestoreSessionRepositoryEntity.js +5 -2
  17. package/Entity/RestoreSessionTaskEntity.js +4 -2
  18. package/Entity/StateEntityAbstract.js +5 -0
  19. package/Factory/CommandFactory.js +1 -1
  20. package/JsonSchema/DefinitionEnum.js +1 -1
  21. package/Repository/DatatruckRepository.d.ts +8 -13
  22. package/Repository/DatatruckRepository.js +122 -231
  23. package/Repository/GitRepository.js +1 -1
  24. package/Repository/RepositoryAbstract.js +4 -2
  25. package/Repository/ResticRepository.js +2 -1
  26. package/SessionDriver/ConsoleSessionDriver.js +9 -5
  27. package/SessionDriver/SessionDriverAbstract.js +3 -2
  28. package/SessionDriver/SqliteSessionDriver.js +2 -4
  29. package/SessionManager/BackupSessionManager.js +3 -6
  30. package/SessionManager/RestoreSessionManager.js +3 -6
  31. package/SessionManager/SessionManagerAbstract.js +4 -0
  32. package/Task/GitTask.js +3 -2
  33. package/Task/MariadbTask.js +6 -4
  34. package/Task/MssqlTask.js +2 -1
  35. package/Task/ScriptTask.js +1 -0
  36. package/Task/SqlDumpTaskAbstract.js +2 -1
  37. package/Task/TaskAbstract.js +2 -1
  38. package/config.schema.json +23 -37
  39. package/index.d.ts +1 -0
  40. package/index.js +1 -0
  41. package/package.json +8 -8
  42. package/utils/DataFormat.js +1 -0
  43. package/utils/Git.js +2 -1
  44. package/utils/ObjectVault.js +3 -5
  45. package/utils/Restic.js +1 -0
  46. package/utils/datatruck/paths.js +5 -1
  47. package/utils/fs.d.ts +23 -24
  48. package/utils/fs.js +108 -96
  49. package/utils/process.d.ts +1 -1
  50. package/utils/process.js +17 -8
  51. package/utils/string.d.ts +0 -2
  52. package/utils/string.js +1 -9
  53. package/utils/tar.d.ts +35 -0
  54. package/utils/tar.js +127 -0
  55. package/utils/zip.d.ts +0 -97
  56. package/utils/zip.js +0 -238
package/Task/MssqlTask.js CHANGED
@@ -25,6 +25,8 @@ exports.mssqlTaskDefinition = {
25
25
  },
26
26
  };
27
27
  class MssqlTask extends TaskAbstract_1.TaskAbstract {
28
+ static SUFFIX = ".BAK";
29
+ verbose;
28
30
  get command() {
29
31
  return this.config.command ?? "sqlcmd";
30
32
  }
@@ -106,4 +108,3 @@ class MssqlTask extends TaskAbstract_1.TaskAbstract {
106
108
  }
107
109
  }
108
110
  exports.MssqlTask = MssqlTask;
109
- MssqlTask.SUFFIX = ".BAK";
@@ -92,6 +92,7 @@ exports.scriptTaskDefinition = {
92
92
  },
93
93
  };
94
94
  class ScriptTask extends TaskAbstract_1.TaskAbstract {
95
+ verbose;
95
96
  async onBeforeBackup() {
96
97
  return {
97
98
  targetPath: await this.mkTmpDir(ScriptTask.name),
@@ -85,6 +85,7 @@ function parseSqlFile(fileName) {
85
85
  }
86
86
  }
87
87
  class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
88
+ verbose;
88
89
  async fetchPassword() {
89
90
  if (typeof this.config.password === "string")
90
91
  return this.config.password;
@@ -114,7 +115,7 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
114
115
  return true;
115
116
  });
116
117
  (0, assert_1.ok)(typeof outputPath === "string");
117
- if (!(await (0, fs_1.checkDir)(outputPath)))
118
+ if (!(await (0, fs_1.existsDir)(outputPath)))
118
119
  await (0, promises_1.mkdir)(outputPath, { recursive: true });
119
120
  if (!this.config.oneFileByTable) {
120
121
  const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ database: this.config.database }));
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TaskAbstract = void 0;
4
4
  const fs_1 = require("../utils/fs");
5
5
  class TaskAbstract {
6
+ config;
7
+ tmpDirs = [];
6
8
  constructor(config) {
7
9
  this.config = config;
8
- this.tmpDirs = [];
9
10
  }
10
11
  async mkTmpDir(prefix, id) {
11
12
  const dir = await (0, fs_1.mkTmpDir)(prefix, id);
@@ -458,45 +458,31 @@
458
458
  "additionalProperties": false,
459
459
  "properties": {
460
460
  "compress": {
461
- "anyOf": [
462
- {
463
- "type": "boolean"
464
- },
465
- {
466
- "type": "object",
467
- "additionalProperties": false,
468
- "properties": {
469
- "packs": {
470
- "type": "array",
471
- "items": {
472
- "type": "object",
473
- "additionalProperties": false,
474
- "required": [
475
- "include"
476
- ],
477
- "properties": {
478
- "name": {
479
- "type": "string"
480
- },
481
- "include": {
482
- "$ref": "#/definitions/stringlist-util"
483
- },
484
- "exclude": {
485
- "$ref": "#/definitions/stringlist-util"
486
- },
487
- "onePackByResult": {
488
- "type": "boolean"
489
- }
490
- }
491
- }
492
- }
461
+ "type": "boolean"
462
+ },
463
+ "packs": {
464
+ "type": "array",
465
+ "items": {
466
+ "type": "object",
467
+ "additionalProperties": false,
468
+ "required": [
469
+ "include"
470
+ ],
471
+ "properties": {
472
+ "name": {
473
+ "type": "string"
474
+ },
475
+ "include": {
476
+ "$ref": "#/definitions/stringlist-util"
477
+ },
478
+ "exclude": {
479
+ "$ref": "#/definitions/stringlist-util"
480
+ },
481
+ "onePackByResult": {
482
+ "type": "boolean"
493
483
  }
494
484
  }
495
- ]
496
- },
497
- "fileCopyConcurrency": {
498
- "type": "integer",
499
- "minimum": 1
485
+ }
500
486
  }
501
487
  }
502
488
  },
package/index.d.ts CHANGED
@@ -0,0 +1 @@
1
+ export {};
package/index.js CHANGED
@@ -1 +1,2 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "dependencies": {
5
5
  "ajv": "^8.12.0",
6
6
  "async": "^3.2.4",
7
7
  "chalk": "^4.1.2",
8
8
  "cli-table3": "^0.6.3",
9
- "commander": "^9.4.1",
10
- "dayjs": "^1.11.7",
11
- "fast-folder-size": "^1.7.1",
12
- "fast-glob": "^3.2.12",
9
+ "commander": "^11.0.0",
10
+ "dayjs": "^1.11.10",
11
+ "fast-folder-size": "^2.2.0",
12
+ "fast-glob": "^3.3.1",
13
13
  "micromatch": "^4.0.5",
14
14
  "pretty-bytes": "^5.6.0",
15
- "sqlite": "^4.1.2",
16
- "sqlite3": "^5.1.4"
15
+ "sqlite": "^5.0.1",
16
+ "sqlite3": "^5.1.6"
17
17
  },
18
18
  "optionalDependencies": {
19
19
  "ts-node": "^10.9.1",
20
- "yaml": "^2.2.1"
20
+ "yaml": "^2.3.2"
21
21
  },
22
22
  "engine": {
23
23
  "node": ">=16.0.0"
@@ -10,6 +10,7 @@ const util_1 = require("util");
10
10
  const customPrefix = "custom=";
11
11
  const tplPrefix = "tpl=";
12
12
  class DataFormat {
13
+ options;
13
14
  constructor(options) {
14
15
  this.options = options;
15
16
  }
package/utils/Git.js CHANGED
@@ -4,6 +4,7 @@ exports.Git = void 0;
4
4
  const fs_1 = require("./fs");
5
5
  const process_1 = require("./process");
6
6
  class Git {
7
+ options;
7
8
  constructor(options) {
8
9
  this.options = options;
9
10
  }
@@ -15,7 +16,7 @@ class Git {
15
16
  }
16
17
  async canBeInit(repo) {
17
18
  return ((0, fs_1.isLocalDir)(repo) &&
18
- (!(await (0, fs_1.checkDir)(repo)) || !(await (0, fs_1.readDir)(repo)).length));
19
+ (!(await (0, fs_1.existsDir)(repo)) || !(await (0, fs_1.readDir)(repo)).length));
19
20
  }
20
21
  async clone(options) {
21
22
  return await this.exec([
@@ -2,11 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ObjectVault = void 0;
4
4
  class ObjectVault {
5
- constructor() {
6
- this.counter = 0;
7
- this.ids = {};
8
- this.objects = {};
9
- }
5
+ counter = 0;
6
+ ids = {};
7
+ objects = {};
10
8
  static serializeKeys(keys) {
11
9
  return JSON.stringify(keys);
12
10
  }
package/utils/Restic.js CHANGED
@@ -7,6 +7,7 @@ const string_1 = require("./string");
7
7
  const promises_1 = require("fs/promises");
8
8
  const path_1 = require("path");
9
9
  class Restic {
10
+ options;
10
11
  constructor(options) {
11
12
  this.options = options;
12
13
  }
@@ -9,7 +9,11 @@ async function parsePaths(values, options) {
9
9
  paths.push(value);
10
10
  }
11
11
  else if (value.type === "spawn") {
12
- const spawnResult = await (0, process_1.exec)(value.command, value.args, { cwd: options.cwd }, { log: options.verbose, stderr: { save: true }, stdout: { save: true } });
12
+ const spawnResult = await (0, process_1.exec)(value.command, value.args, { cwd: options.cwd }, {
13
+ log: options.verbose,
14
+ stderr: { save: true },
15
+ stdout: { save: true },
16
+ });
13
17
  const spawnFiles = [spawnResult.stderr, spawnResult.stdout].flatMap((text) => text
14
18
  .split(/\r?\n/)
15
19
  .map((v) => v.trim())
package/utils/fs.d.ts CHANGED
@@ -1,29 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
- /// <reference types="node" />
5
4
  import { Progress } from "./progress";
6
5
  import { Entry, Options } from "fast-glob";
7
- import { Dirent, ReadStream, Stats } from "fs";
6
+ import { ReadStream, Stats } from "fs";
8
7
  import { WriteStream } from "fs";
9
8
  import { Interface } from "readline";
10
9
  export declare const isWSLSystem: boolean;
11
10
  export declare function isEmptyDir(path: string): Promise<boolean>;
12
- type EntryObject = {
13
- name: string;
14
- path: string;
15
- dirent: Dirent;
16
- stats: Stats;
17
- };
18
- export declare function applyPermissions(baseDir: string, permissionsPath: string): Promise<void>;
19
- export declare function pathIterator(stream: AsyncIterable<string | Buffer>): AsyncIterable<EntryObject>;
20
11
  export declare function isLocalDir(path: string): boolean;
21
- export declare function isDirEmpty(path: string): Promise<boolean>;
22
12
  export declare function mkdirIfNotExists(path: string): Promise<string>;
23
13
  export declare function ensureEmptyDir(path: string): Promise<void>;
14
+ export declare function safeStat(path: string): Promise<Stats | undefined>;
24
15
  export declare function existsDir(path: string): Promise<boolean>;
16
+ export declare function existsFile(path: string): Promise<boolean>;
25
17
  export declare function writeJSONFile<T = any>(path: string, json: T): Promise<void>;
26
- export declare function readdirIfExists(path: string): Promise<string[]>;
27
18
  export declare const parseFileExtensions: string[];
28
19
  export declare function parseFile(path: string, jsKey?: string): Promise<any>;
29
20
  export declare function parsePackageFile(): {
@@ -32,7 +23,6 @@ export declare function parsePackageFile(): {
32
23
  description: string;
33
24
  };
34
25
  export declare function findFile(sourcePath: string, baseName: string, extensions: string[], errorMessage?: string): Promise<string>;
35
- export declare function existsFile(path: string): Promise<boolean>;
36
26
  export declare function parentTmpDir(): string;
37
27
  export declare function sessionTmpDir(): string;
38
28
  export declare function isTmpDir(path: string): boolean;
@@ -41,8 +31,6 @@ export declare function tmpDir(prefix: string, id?: string): string;
41
31
  export declare function fastFolderSizeAsync(path: string): Promise<number>;
42
32
  export declare function mkTmpDir(prefix: string, id?: string): Promise<string>;
43
33
  export declare function readPartialFile(path: string, positions: [number, number?]): Promise<string>;
44
- export declare function checkFile(path: string): Promise<boolean>;
45
- export declare function checkDir(path: string): Promise<boolean>;
46
34
  export declare function readDir(path: string, optional?: boolean): Promise<string[]>;
47
35
  export declare function forEachFile(dirPath: string, cb: (path: string, dir: boolean) => void, includeDir?: boolean): Promise<void>;
48
36
  /**
@@ -104,18 +92,29 @@ export declare function createFileScanner(options: {
104
92
  include: string[];
105
93
  };
106
94
  onProgress: (data: Progress) => Promise<void>;
107
- disableCounting?: boolean;
108
- disableEndProgress?: boolean;
109
95
  }): Promise<{
96
+ disposed: boolean;
110
97
  total: number;
111
98
  current: number;
112
- progress: (description: string, data: {
113
- path?: string;
114
- current: number;
115
- type?: "start" | "end";
116
- percent?: number;
117
- }) => Promise<void>;
118
- updateProgress: (end?: boolean) => Promise<void>;
99
+ progress: (description: string, path?: string) => Promise<void>;
100
+ end: () => Promise<void>;
119
101
  start: (cb?: ((entry: Required<Entry>) => any) | undefined) => Promise<void>;
120
102
  }>;
103
+ type StreamItem = {
104
+ key: string;
105
+ stream: WriteStream;
106
+ finished: boolean;
107
+ error?: Error;
108
+ written?: boolean;
109
+ };
110
+ export declare function createWriteStreamPool(options: {
111
+ path: string;
112
+ onStreamPath?: (key: string) => string;
113
+ }): {
114
+ pool: Record<string, StreamItem>;
115
+ path(key: string | number): string | undefined;
116
+ writeLine(key: string | number, v: string): boolean;
117
+ end(): Promise<void>;
118
+ };
119
+ export declare function countFileLines(path: string): Promise<number>;
121
120
  export {};
package/utils/fs.js CHANGED
@@ -3,7 +3,7 @@ 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.createFileScanner = 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.rmTmpDir = exports.isTmpDir = 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;
6
+ exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.mkTmpDir = exports.fastFolderSizeAsync = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
7
7
  const globalData_1 = __importDefault(require("../globalData"));
8
8
  const math_1 = require("./math");
9
9
  const path_1 = require("./path");
@@ -35,37 +35,13 @@ async function isEmptyDir(path) {
35
35
  }
36
36
  }
37
37
  exports.isEmptyDir = isEmptyDir;
38
- async function applyPermissions(baseDir, permissionsPath) {
39
- const singleReader = (0, readline_1.createInterface)({
40
- input: (0, fs_1.createReadStream)(permissionsPath),
41
- });
42
- for await (const line of singleReader) {
43
- const [rpath, rawUid, rawGui, rawMode] = line.split(":");
44
- const path = (0, path_2.join)(baseDir, rpath);
45
- if (!path.startsWith(baseDir)) {
46
- throw new Error(`Entry path is out of the base dir: (${path}, ${baseDir})`);
47
- }
48
- const uid = Number(rawUid);
49
- const guid = Number(rawGui);
50
- await (0, promises_1.chown)(path, uid, guid);
51
- const mode = Number(rawMode);
52
- await (0, promises_1.chmod)(path, mode);
53
- }
54
- }
55
- exports.applyPermissions = applyPermissions;
56
38
  function pathIterator(stream) {
57
39
  return stream;
58
40
  }
59
- exports.pathIterator = pathIterator;
60
41
  function isLocalDir(path) {
61
42
  return /^[\/\.]|([A-Z]:)/i.test(path);
62
43
  }
63
44
  exports.isLocalDir = isLocalDir;
64
- async function isDirEmpty(path) {
65
- const files = await readDir(path);
66
- return !files.length;
67
- }
68
- exports.isDirEmpty = isDirEmpty;
69
45
  async function mkdirIfNotExists(path) {
70
46
  await (0, promises_1.mkdir)(path, {
71
47
  recursive: true,
@@ -74,28 +50,29 @@ async function mkdirIfNotExists(path) {
74
50
  }
75
51
  exports.mkdirIfNotExists = mkdirIfNotExists;
76
52
  async function ensureEmptyDir(path) {
77
- if (!(await isDirEmpty(path)))
53
+ if (!(await isEmptyDir(path)))
78
54
  throw new Error(`Dir is not empty: ${path}`);
79
55
  }
80
56
  exports.ensureEmptyDir = ensureEmptyDir;
81
- async function existsDir(path) {
57
+ async function safeStat(path) {
82
58
  try {
83
- const info = await (0, promises_1.stat)(path);
84
- return info.isDirectory();
85
- }
86
- catch (e) {
87
- return false;
59
+ return await (0, promises_1.stat)(path);
88
60
  }
61
+ catch (e) { }
62
+ }
63
+ exports.safeStat = safeStat;
64
+ async function existsDir(path) {
65
+ return (await safeStat(path))?.isDirectory() ?? false;
89
66
  }
90
67
  exports.existsDir = existsDir;
68
+ async function existsFile(path) {
69
+ return (await safeStat(path))?.isFile() ?? false;
70
+ }
71
+ exports.existsFile = existsFile;
91
72
  async function writeJSONFile(path, json) {
92
73
  await (0, promises_1.writeFile)(path, JSON.stringify(json));
93
74
  }
94
75
  exports.writeJSONFile = writeJSONFile;
95
- async function readdirIfExists(path) {
96
- return await readDir(path, true);
97
- }
98
- exports.readdirIfExists = readdirIfExists;
99
76
  exports.parseFileExtensions = ["json", "js", "ts", "yaml", "yml"];
100
77
  async function parseFile(path, jsKey) {
101
78
  if (!(0, path_3.isAbsolute)(path))
@@ -140,16 +117,6 @@ async function findFile(sourcePath, baseName, extensions, errorMessage = "Path n
140
117
  return path;
141
118
  }
142
119
  exports.findFile = findFile;
143
- async function existsFile(path) {
144
- try {
145
- const info = await (0, promises_1.stat)(path);
146
- return info.isFile();
147
- }
148
- catch (e) {
149
- return false;
150
- }
151
- }
152
- exports.existsFile = existsFile;
153
120
  function parentTmpDir() {
154
121
  return (0, path_2.join)(globalData_1.default.tempDir, "datatruck-temp");
155
122
  }
@@ -219,24 +186,6 @@ async function readPartialFile(path, positions) {
219
186
  });
220
187
  }
221
188
  exports.readPartialFile = readPartialFile;
222
- async function checkFile(path) {
223
- try {
224
- return (await (0, promises_1.stat)(path)).isFile();
225
- }
226
- catch (e) {
227
- return false;
228
- }
229
- }
230
- exports.checkFile = checkFile;
231
- async function checkDir(path) {
232
- try {
233
- return (await (0, promises_1.stat)(path)).isDirectory();
234
- }
235
- catch (e) {
236
- return false;
237
- }
238
- }
239
- exports.checkDir = checkDir;
240
189
  async function readDir(path, optional) {
241
190
  try {
242
191
  return await (0, promises_1.readdir)(path);
@@ -458,62 +407,125 @@ async function cpy(options) {
458
407
  exports.cpy = cpy;
459
408
  async function createFileScanner(options) {
460
409
  const object = {
410
+ disposed: false,
461
411
  total: 0,
462
412
  current: 0,
463
- progress: async (description, data) => {
413
+ progress: async (description, path) => {
414
+ if (object.disposed)
415
+ return;
416
+ if (path)
417
+ object.current++;
464
418
  await options.onProgress({
465
419
  relative: {
466
420
  description,
467
- payload: data.path,
468
- percent: data.percent,
421
+ payload: path,
469
422
  },
470
423
  absolute: {
471
424
  total: object.total,
472
- current: object.current + data.current,
473
- percent: (0, math_1.progressPercent)(object.total, object.current + data.current),
425
+ current: object.current,
426
+ percent: (0, math_1.progressPercent)(object.total, object.current),
474
427
  },
475
428
  });
476
- if (data.type === "end") {
477
- object.current += data.current;
478
- }
479
429
  },
480
- updateProgress: async (end) => {
481
- const currentTime = performance.now();
482
- const diff = currentTime - lastTime;
483
- if (end || diff > 1000) {
484
- await options.onProgress({
485
- relative: {
486
- description: end ? "Scanned files" : "Scanning files",
487
- payload: object.total.toString(),
488
- },
489
- });
490
- lastTime = currentTime;
491
- }
430
+ end: async () => {
431
+ if (!object.disposed)
432
+ await object.progress("Finished");
433
+ object.disposed = true;
492
434
  },
493
435
  start: async (cb) => {
436
+ let lastTime = performance.now();
437
+ await object.progress("Scanning files");
494
438
  for await (const entry of pathIterator(stream)) {
495
- if (!options.disableCounting)
439
+ if (cb) {
440
+ if (await cb(entry))
441
+ object.total++;
442
+ }
443
+ else {
496
444
  object.total++;
497
- await object.updateProgress();
498
- if (cb)
499
- await cb(entry);
445
+ }
446
+ if (lastTime - performance.now() > 500)
447
+ await object.progress("Scanning files");
500
448
  }
501
- if (!options.disableEndProgress)
502
- await object.updateProgress(true);
449
+ await object.progress("Scanned files");
503
450
  },
504
451
  };
505
- await options.onProgress({
506
- relative: {
507
- description: "Scanning files",
508
- },
509
- });
510
452
  const stream = fast_glob_1.default.stream(options.glob.include, {
511
453
  dot: true,
512
454
  markDirectories: true,
513
455
  stats: true,
514
456
  ...options.glob,
515
457
  });
516
- let lastTime = performance.now();
517
458
  return object;
518
459
  }
519
460
  exports.createFileScanner = createFileScanner;
461
+ function createWriteStreamPool(options) {
462
+ const pool = {};
463
+ const create = (key) => {
464
+ const item = {
465
+ key,
466
+ stream: (0, fs_2.createWriteStream)((0, path_2.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
467
+ finished: false,
468
+ };
469
+ item.stream
470
+ .once("error", (error) => {
471
+ item.finished = true;
472
+ item.error = error;
473
+ })
474
+ .once("close", () => (item.finished = true));
475
+ return (pool[item.key] = item);
476
+ };
477
+ return {
478
+ pool,
479
+ path(key) {
480
+ const item = pool[key];
481
+ if (!item)
482
+ return;
483
+ if (typeof item.stream.path !== "string")
484
+ throw new Error(`Stream path is not defined: ${key}`);
485
+ return item.stream.path;
486
+ },
487
+ writeLine(key, v) {
488
+ const item = pool[key] || create(key.toString());
489
+ if (item.finished) {
490
+ return false;
491
+ }
492
+ else if (item.written) {
493
+ return item.stream.write(`\n${v}`);
494
+ }
495
+ else {
496
+ item.written = true;
497
+ return item.stream.write(`${v}`);
498
+ }
499
+ },
500
+ async end() {
501
+ const items = Object.values(pool);
502
+ for (const item of items)
503
+ if (!item.finished)
504
+ item.stream.end();
505
+ const itemWithErrors = items.filter((v) => v.error);
506
+ if (itemWithErrors.length) {
507
+ const keys = itemWithErrors.map((item) => item.key);
508
+ throw new AggregateError(itemWithErrors.map((item) => item.error), `Streams faileds: ${keys.join(", ")}`);
509
+ }
510
+ await Promise.all(items
511
+ .filter((item) => !item.finished)
512
+ .map((item) => waitForClose(item.stream)));
513
+ },
514
+ };
515
+ }
516
+ exports.createWriteStreamPool = createWriteStreamPool;
517
+ function countFileLines(path) {
518
+ let lines = 0;
519
+ const rl = (0, readline_1.createInterface)({
520
+ input: (0, fs_1.createReadStream)(path),
521
+ });
522
+ return new Promise((resolve, reject) => {
523
+ rl.on("line", (line) => {
524
+ if (!line.length)
525
+ lines++;
526
+ });
527
+ rl.on("close", () => resolve(lines));
528
+ rl.on("error", reject);
529
+ });
530
+ }
531
+ exports.countFileLines = countFileLines;
@@ -33,7 +33,7 @@ export interface ExecSettingsInterface {
33
33
  onSpawn?: (p: ChildProcess) => any;
34
34
  stdout?: {
35
35
  save?: boolean;
36
- parseLines?: boolean;
36
+ parseLines?: boolean | "skip-empty";
37
37
  onData?: (data: string) => void;
38
38
  };
39
39
  stderr?: {