@datatruck/cli 0.6.1 → 0.9.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 (51) hide show
  1. package/Action/BackupAction.d.ts +1 -0
  2. package/Action/BackupAction.js +4 -1
  3. package/Action/RestoreAction.d.ts +1 -0
  4. package/Action/RestoreAction.js +1 -0
  5. package/Action/SnapshotsAction.d.ts +1 -0
  6. package/Command/BackupCommand.d.ts +1 -0
  7. package/Command/BackupCommand.js +8 -2
  8. package/Command/BackupSessionsCommand.js +1 -1
  9. package/Command/ConfigCommand.d.ts +1 -0
  10. package/Command/ConfigCommand.js +7 -1
  11. package/Command/InitCommand.js +1 -1
  12. package/Command/PruneCommand.js +2 -2
  13. package/Command/RestoreCommand.d.ts +1 -0
  14. package/Command/RestoreCommand.js +8 -2
  15. package/Command/RestoreSessionsCommand.js +1 -1
  16. package/Command/SnapshotsCommand.d.ts +1 -0
  17. package/Command/SnapshotsCommand.js +16 -2
  18. package/Repository/GitRepository.js +5 -0
  19. package/Repository/LocalRepository.d.ts +1 -0
  20. package/Repository/LocalRepository.js +5 -0
  21. package/Repository/RepositoryAbstract.d.ts +4 -1
  22. package/Repository/RepositoryAbstract.js +1 -0
  23. package/Repository/ResticRepository.d.ts +2 -2
  24. package/Repository/ResticRepository.js +9 -0
  25. package/SessionDriver/ConsoleSessionDriver.d.ts +6 -2
  26. package/SessionDriver/ConsoleSessionDriver.js +19 -29
  27. package/SessionDriver/SessionDriverAbstract.d.ts +1 -1
  28. package/SessionDriver/SessionDriverAbstract.js +1 -1
  29. package/SessionManager/BackupSessionManager.d.ts +1 -1
  30. package/SessionManager/BackupSessionManager.js +2 -2
  31. package/Task/MysqlDumpTask.d.ts +3 -2
  32. package/Task/MysqlDumpTask.js +35 -3
  33. package/Task/PostgresqlDumpTask.d.ts +3 -2
  34. package/Task/PostgresqlDumpTask.js +6 -5
  35. package/Task/SqlDumpTaskAbstract.d.ts +4 -3
  36. package/Task/SqlDumpTaskAbstract.js +67 -19
  37. package/config.schema.json +3 -0
  38. package/package.json +9 -9
  39. package/util/cli-util.d.ts +1 -0
  40. package/util/cli-util.js +13 -1
  41. package/util/datatruck/config-util.d.ts +1 -0
  42. package/util/datatruck/config-util.js +3 -0
  43. package/util/date-util.d.ts +4 -0
  44. package/util/date-util.js +17 -1
  45. package/util/entity-util.d.ts +1 -1
  46. package/util/fs-util.d.ts +1 -0
  47. package/util/process-util.d.ts +1 -0
  48. package/util/process-util.js +2 -2
  49. package/util/string-util.d.ts +1 -0
  50. package/util/string-util.js +21 -1
  51. package/CHANGELOG.md +0 -119
@@ -17,6 +17,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
17
17
  const password = await this.fetchPassword();
18
18
  return [
19
19
  `--host=${this.config.hostname}`,
20
+ ...(this.config.port ? [`--port=${this.config.port}`] : []),
20
21
  `--user=${this.config.username}`,
21
22
  `--password=${password ?? ""}`,
22
23
  ...(database && this.config.database ? [this.config.database] : []),
@@ -57,17 +58,17 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
57
58
  },
58
59
  });
59
60
  }
60
- async onFetchTableNames() {
61
+ async onFetchTableNames(database) {
61
62
  return await this.fetchValues(`
62
63
  SELECT
63
64
  table_name
64
65
  FROM
65
66
  information_schema.tables
66
67
  WHERE
67
- table_schema = '${this.config.database}'
68
+ table_schema = '${database}'
68
69
  `);
69
70
  }
70
- async onExport(tableNames, output) {
71
+ async onExportTables(tableNames, output) {
71
72
  const stream = (0, fs_1.createWriteStream)(output);
72
73
  await Promise.all([
73
74
  new Promise((resolve, reject) => {
@@ -77,6 +78,7 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
77
78
  await (0, process_util_1.exec)("mysqldump", [
78
79
  ...(await this.buildConnectionArgs()),
79
80
  "--lock-tables=false",
81
+ "--skip-add-drop-table=false",
80
82
  ...tableNames,
81
83
  ], null, {
82
84
  pipe: { stream: stream },
@@ -105,6 +107,36 @@ class MysqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
105
107
  if (!successFooter)
106
108
  throw new AppError_1.AppError("No end line found (incomplete backup)");
107
109
  }
110
+ async onExportStoredPrograms(output) {
111
+ const stream = (0, fs_1.createWriteStream)(output);
112
+ await Promise.all([
113
+ new Promise((resolve, reject) => {
114
+ stream.on("close", resolve);
115
+ stream.on("error", reject);
116
+ }),
117
+ await (0, process_util_1.exec)("mysqldump", [
118
+ ...(await this.buildConnectionArgs()),
119
+ "--lock-tables=false",
120
+ "--routines",
121
+ "--events",
122
+ "--skip-triggers",
123
+ "--no-create-info",
124
+ "--no-data",
125
+ "--no-create-db",
126
+ "--skip-opt",
127
+ ], null, {
128
+ pipe: { stream: stream },
129
+ log: {
130
+ exec: this.verbose,
131
+ stderr: this.verbose,
132
+ allToStderr: true,
133
+ },
134
+ stderr: {
135
+ toExitCode: true,
136
+ },
137
+ }),
138
+ ]);
139
+ }
108
140
  async onImport(path, database) {
109
141
  await (0, process_util_1.exec)("mysql", [...(await this.buildConnectionArgs(false)), database], null, {
110
142
  pipe: {
@@ -8,7 +8,8 @@ export declare class PostgresqlDumpTask extends SqlDumpTaskAbstract<PostgresqlDu
8
8
  onDatabaseIsEmpty(name: string): Promise<boolean>;
9
9
  onCreateDatabase(database: TargetDatabaseType): Promise<void>;
10
10
  onExecQuery(query: string): Promise<import("../util/process-util").ExecResultType>;
11
- onFetchTableNames(): Promise<string[]>;
12
- onExport(tableNames: string[], output: string): Promise<void>;
11
+ onFetchTableNames(database: string): Promise<string[]>;
12
+ onExportTables(tableNames: string[], output: string): Promise<void>;
13
+ onExportStoredPrograms(): Promise<void>;
13
14
  onImport(path: string, database: string): Promise<void>;
14
15
  }
@@ -60,18 +60,18 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
60
60
  },
61
61
  });
62
62
  }
63
- async onFetchTableNames() {
63
+ async onFetchTableNames(database) {
64
64
  return await this.fetchValues(`
65
65
  SELECT
66
66
  CONCAT(table_schema, '.', table_name)
67
67
  FROM
68
68
  information_schema.tables
69
69
  WHERE
70
- table_catalog = '${this.config.database}' AND
70
+ table_catalog = '${database}' AND
71
71
  table_schema NOT IN ('pg_catalog', 'information_schema')
72
72
  `);
73
73
  }
74
- async onExport(tableNames, output) {
74
+ async onExportTables(tableNames, output) {
75
75
  const stream = (0, fs_1.createWriteStream)(output);
76
76
  await Promise.all([
77
77
  new Promise((resolve, reject) => {
@@ -80,8 +80,6 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
80
80
  }),
81
81
  (0, process_util_1.exec)("pg_dump", [
82
82
  ...(await this.buildConnectionArgs()),
83
- "--clean",
84
- "--if-exists",
85
83
  ...(tableNames?.flatMap((v) => ["-t", v]) ?? []),
86
84
  ], null, {
87
85
  pipe: { stream: stream },
@@ -92,6 +90,9 @@ class PostgresqlDumpTask extends SqlDumpTaskAbstract_1.SqlDumpTaskAbstract {
92
90
  }),
93
91
  ]);
94
92
  }
93
+ async onExportStoredPrograms() {
94
+ throw new Error(`Method not implemented: onExportStoredPrograms`);
95
+ }
95
96
  async onImport(path, database) {
96
97
  await (0, process_util_1.exec)("psql", [...(await this.buildConnectionArgs(database)), "-f", (0, path_1.normalize)(path)], undefined, {
97
98
  log: this.verbose,
@@ -14,6 +14,7 @@ export declare type SqlDumpTaskConfigType = {
14
14
  port?: number;
15
15
  database: string;
16
16
  username: string;
17
+ storedPrograms?: boolean;
17
18
  targetDatabase?: TargetDatabaseType;
18
19
  includeTables?: string[];
19
20
  excludeTables?: string[];
@@ -26,11 +27,11 @@ export declare abstract class SqlDumpTaskAbstract<TConfig extends SqlDumpTaskCon
26
27
  fetchValues(query: string): Promise<string[]>;
27
28
  abstract onCreateDatabase(database: TargetDatabaseType): Promise<void>;
28
29
  abstract onDatabaseIsEmpty(databaseName: string): Promise<boolean>;
29
- abstract onFetchTableNames(): Promise<string[]>;
30
+ abstract onFetchTableNames(database: string): Promise<string[]>;
30
31
  abstract onExecQuery(query: string): ReturnType<typeof exec>;
31
- abstract onExport(tableNames: string[], output: string): Promise<void>;
32
+ abstract onExportTables(tableNames: string[], output: string): Promise<void>;
33
+ abstract onExportStoredPrograms(output: string): Promise<void>;
32
34
  abstract onImport(path: string, database: string): Promise<void>;
33
- fetchTableNames(): Promise<string[]>;
34
35
  onBackup(data: BackupDataType): Promise<void>;
35
36
  onRestore(data: RestoreDataType): Promise<void>;
36
37
  }
@@ -44,11 +44,45 @@ exports.sqlDumpTaskDefinition = {
44
44
  collate: { type: "string" },
45
45
  },
46
46
  },
47
+ storedPrograms: { type: "boolean" },
47
48
  includeTables: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
48
49
  excludeTables: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.stringListUtil),
49
50
  oneFileByTable: { type: "boolean" },
50
51
  },
51
52
  };
53
+ function serializeSqlFile(input) {
54
+ if (input.database && input.table) {
55
+ return `${input.database}.${input.table}.table.sql`;
56
+ }
57
+ else if (input.database && !input.table) {
58
+ return `${input.database}.database.sql`;
59
+ }
60
+ else if (!input.database && input.table) {
61
+ return `${input.table}.table.sql`;
62
+ }
63
+ else {
64
+ throw new AppError_1.AppError(`Invalid sql file input: ${JSON.stringify(input)}`);
65
+ }
66
+ }
67
+ function parseSqlFile(fileName) {
68
+ if (!fileName.endsWith(".sql"))
69
+ return;
70
+ const regex = /^(.+)\.(table|database)\.sql$/;
71
+ const matches = regex.exec(fileName);
72
+ if (!matches)
73
+ return { fileName };
74
+ const [, name, type] = matches;
75
+ const lastName = name.split(".").pop();
76
+ if (type === "table") {
77
+ return { fileName, table: lastName };
78
+ }
79
+ else if (type === "database") {
80
+ return { fileName, database: lastName };
81
+ }
82
+ else {
83
+ throw new Error(`Invalid sql file type: ${type}`);
84
+ }
85
+ }
52
86
  class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
53
87
  async fetchPassword() {
54
88
  if (typeof this.config.password === "string")
@@ -66,27 +100,24 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
66
100
  return result;
67
101
  }, []);
68
102
  }
69
- async fetchTableNames() {
70
- const tableNames = await this.onFetchTableNames();
103
+ async onBackup(data) {
104
+ this.verbose = data.options.verbose;
71
105
  const config = this.config;
72
- return tableNames.filter((tableName) => {
106
+ const outputPath = data.package.path;
107
+ const allTableNames = await this.onFetchTableNames(this.config.database);
108
+ const tableNames = allTableNames.filter((tableName) => {
73
109
  if (config.includeTables && !(0, micromatch_1.isMatch)(tableName, config.includeTables))
74
110
  return false;
75
111
  if (config.excludeTables && (0, micromatch_1.isMatch)(tableName, config.excludeTables))
76
112
  return false;
77
113
  return true;
78
114
  });
79
- }
80
- async onBackup(data) {
81
- this.verbose = data.options.verbose;
82
- const outputPath = data.package.path;
83
- const tableNames = await this.fetchTableNames();
84
115
  (0, assert_1.ok)(typeof outputPath === "string");
85
116
  if (!(await (0, fs_util_1.checkDir)(outputPath)))
86
117
  await (0, promises_1.mkdir)(outputPath, { recursive: true });
87
118
  if (!this.config.oneFileByTable) {
88
- const outPath = (0, path_1.join)(outputPath, this.config.database) + ".database.sql";
89
- await this.onExport(tableNames, outPath);
119
+ const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ database: this.config.database }));
120
+ await this.onExportTables(tableNames, outPath);
90
121
  }
91
122
  else {
92
123
  let current = 0;
@@ -98,10 +129,14 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
98
129
  step: tableName,
99
130
  });
100
131
  current++;
101
- const outPath = (0, path_1.join)(outputPath, tableName) + ".table.sql";
102
- await this.onExport([tableName], outPath);
132
+ const outPath = (0, path_1.join)(outputPath, serializeSqlFile({ table: tableName }));
133
+ await this.onExportTables([tableName], outPath);
103
134
  }
104
135
  }
136
+ if (this.config.storedPrograms) {
137
+ const outPath = (0, path_1.join)(outputPath, "stored-programs.sql");
138
+ await this.onExportStoredPrograms(outPath);
139
+ }
105
140
  }
106
141
  async onRestore(data) {
107
142
  const restorePath = data.package.restorePath;
@@ -125,20 +160,33 @@ class SqlDumpTaskAbstract extends TaskAbstract_1.TaskAbstract {
125
160
  database: database.name,
126
161
  });
127
162
  }
128
- if (!(await this.onDatabaseIsEmpty(database.name)))
163
+ const items = (await (0, promises_1.readdir)(restorePath))
164
+ .map(parseSqlFile)
165
+ .filter((v) => !!v);
166
+ // Database check
167
+ const databaseItems = items.filter((v) => v.database);
168
+ if (databaseItems.length && !(await this.onDatabaseIsEmpty(database.name)))
129
169
  throw new AppError_1.AppError(`Target database is not empty: ${database.name}`);
170
+ // Table check
171
+ const restoreTables = items
172
+ .filter((v) => v.table)
173
+ .map((v) => v.table);
174
+ const serverTables = await this.onFetchTableNames(database.name);
175
+ const errorTables = restoreTables.filter((v) => serverTables.includes(v));
176
+ if (errorTables.length) {
177
+ throw new AppError_1.AppError(`Target table already exists: ${errorTables.join(", ")}`);
178
+ }
130
179
  await this.onCreateDatabase(database);
131
180
  if (this.verbose)
132
181
  (0, cli_util_1.logExec)("readdir", [restorePath]);
133
- const files = (await (0, promises_1.readdir)(restorePath)).filter((name) => /\.sql$/i.test(name));
134
182
  let current = 0;
135
- for (const file of files) {
136
- const path = (0, path_1.join)(restorePath, file);
183
+ for (const item of items) {
184
+ const path = (0, path_1.join)(restorePath, item.fileName);
137
185
  data.onProgress({
138
- total: files.length,
186
+ total: items.length,
139
187
  current: current,
140
- percent: (0, math_util_1.progressPercent)(files.length, current),
141
- step: file,
188
+ percent: (0, math_util_1.progressPercent)(items.length, current),
189
+ step: item.fileName,
142
190
  });
143
191
  current++;
144
192
  await this.onImport(path, database.name);
@@ -713,6 +713,9 @@
713
713
  }
714
714
  }
715
715
  },
716
+ "storedPrograms": {
717
+ "type": "boolean"
718
+ },
716
719
  "includeTables": {
717
720
  "$ref": "#/definitions/stringlist-util"
718
721
  },
package/package.json CHANGED
@@ -1,29 +1,25 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.6.1",
3
+ "version": "0.9.0",
4
4
  "dependencies": {
5
5
  "ajv": "^8.11.0",
6
6
  "async": "^3.2.4",
7
7
  "chalk": "^4.1.2",
8
8
  "cli-table3": "^0.6.2",
9
- "commander": "^9.2.0",
10
- "dayjs": "^1.11.2",
9
+ "commander": "^9.3.0",
10
+ "dayjs": "^1.11.3",
11
11
  "fast-glob": "^3.2.11",
12
12
  "micromatch": "^4.0.5",
13
13
  "sqlite": "^4.1.1",
14
14
  "sqlite3": "^5.0.8"
15
15
  },
16
16
  "optionalDependencies": {
17
- "ts-node": "^10.7.0",
18
- "yaml": "^2.1.0"
17
+ "ts-node": "^10.8.2",
18
+ "yaml": "^2.1.1"
19
19
  },
20
20
  "engine": {
21
21
  "node": ">=16.0.0"
22
22
  },
23
- "bin": {
24
- "datatruck": "bin.js",
25
- "dtt": "bin.js"
26
- },
27
23
  "description": "Tool for creating and managing backups",
28
24
  "homepage": "https://github.com/swordev/datatruck#readme",
29
25
  "bugs": {
@@ -38,5 +34,9 @@
38
34
  "name": "Juanra GM",
39
35
  "email": "juanrgm724@gmail.com",
40
36
  "url": "https://github.com/juanrgm"
37
+ },
38
+ "bin": {
39
+ "datatruck": "bin.js",
40
+ "dtt": "bin.js"
41
41
  }
42
42
  }
@@ -6,6 +6,7 @@ export declare const clearCommand = "\r\u001B[K";
6
6
  export declare const hideCursorCommand = "\u001B[?25l";
7
7
  export declare function renderSpinner(counter: number): string;
8
8
  export declare function renderProgressBar(progress: number, size?: number): string;
9
+ export declare function logVars(data: Record<string, any>): void;
9
10
  export declare function logExec(command: string, argv?: string[], env?: NodeJS.ProcessEnv, logToStderr?: boolean): void;
10
11
  export declare function resultColumn(error: Error | null | string, state?: "started" | "ended"): "❌" | " ? " | "✅";
11
12
  export declare function errorColumn(error: Error | null | string, verbose: number): string;
package/util/cli-util.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.confirm = exports.truncate = exports.parseOptions = exports.errorColumn = exports.resultColumn = exports.logExec = exports.renderProgressBar = exports.renderSpinner = exports.hideCursorCommand = exports.clearCommand = exports.showCursorCommand = exports.spinnerChars = exports.clearLastLine = void 0;
6
+ exports.confirm = exports.truncate = exports.parseOptions = exports.errorColumn = exports.resultColumn = exports.logExec = exports.logVars = exports.renderProgressBar = exports.renderSpinner = exports.hideCursorCommand = exports.clearCommand = exports.showCursorCommand = exports.spinnerChars = exports.clearLastLine = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const chalk_2 = require("chalk");
9
9
  const readline_1 = require("readline");
@@ -28,6 +28,18 @@ function renderProgressBar(progress, size = 10) {
28
28
  return completeChar.repeat(completedSize) + incompleteChar.repeat(restSize);
29
29
  }
30
30
  exports.renderProgressBar = renderProgressBar;
31
+ function logVars(data) {
32
+ let first = true;
33
+ for (const key in data) {
34
+ if (first) {
35
+ console.log();
36
+ first = false;
37
+ }
38
+ const value = data[key];
39
+ console.log(`${chalk_1.default.cyan(key)}${chalk_1.default.grey(":")} ${chalk_1.default.white(value ?? "")}`);
40
+ }
41
+ }
42
+ exports.logVars = logVars;
31
43
  function logExec(command, argv = [], env, logToStderr) {
32
44
  const envText = env
33
45
  ? Object.keys(env)
@@ -5,6 +5,7 @@ export declare function findRepositoryOrFail(config: ConfigType, repositoryName:
5
5
  export declare function filterRepository(repository: RepositoryConfigType, action?: RepositoryConfigEnabledActionType): boolean;
6
6
  export declare function filterPackages(config: ConfigType, options: {
7
7
  packageNames?: string[];
8
+ packageTaskNames?: string[];
8
9
  repositoryNames?: string[];
9
10
  repositoryTypes?: string[];
10
11
  sourceAction?: RepositoryConfigEnabledActionType;
@@ -22,6 +22,7 @@ function filterRepository(repository, action) {
22
22
  exports.filterRepository = filterRepository;
23
23
  function filterPackages(config, options) {
24
24
  const packagePatterns = (0, string_util_1.makePathPatterns)(options.packageNames);
25
+ const taskNamePatterns = (0, string_util_1.makePathPatterns)(options.packageTaskNames);
25
26
  return config.packages
26
27
  .map((pkg) => {
27
28
  pkg = Object.assign({}, pkg);
@@ -37,6 +38,8 @@ function filterPackages(config, options) {
37
38
  return pkg;
38
39
  })
39
40
  .filter((pkg) => {
41
+ if (taskNamePatterns && !(0, micromatch_1.isMatch)(pkg.task?.name ?? "", taskNamePatterns))
42
+ return false;
40
43
  return ((typeof pkg.enabled !== "boolean" || pkg.enabled) &&
41
44
  !!pkg.repositoryNames?.length &&
42
45
  (!packagePatterns || (0, micromatch_1.isMatch)(pkg.name, packagePatterns)));
@@ -10,3 +10,7 @@ export declare type FilterByLastOptionsType = {
10
10
  export declare function filterByLast<TItem extends {
11
11
  date: string;
12
12
  }>(items: TItem[], options: FilterByLastOptionsType, reasons?: Record<number, string[]>): TItem[];
13
+ export declare function createChron(): {
14
+ start: () => number;
15
+ elapsed: (formatted?: boolean) => string | number;
16
+ };
package/util/date-util.js CHANGED
@@ -3,7 +3,8 @@ 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.filterByLast = void 0;
6
+ exports.createChron = exports.filterByLast = void 0;
7
+ const string_util_1 = require("./string-util");
7
8
  const dayjs_1 = __importDefault(require("dayjs"));
8
9
  const advancedFormat_1 = __importDefault(require("dayjs/plugin/advancedFormat"));
9
10
  const isoWeek_1 = __importDefault(require("dayjs/plugin/isoWeek"));
@@ -72,3 +73,18 @@ function filterByLast(items, options, reasons) {
72
73
  return items.filter((item) => validItems.includes(item));
73
74
  }
74
75
  exports.filterByLast = filterByLast;
76
+ function createChron() {
77
+ let startTime;
78
+ return {
79
+ start: () => (startTime = Date.now()),
80
+ elapsed: (formatted) => {
81
+ if (!startTime)
82
+ throw new Error(`Chron was not started`);
83
+ const seconds = (Date.now() - startTime) / 1000;
84
+ if (formatted)
85
+ return (0, string_util_1.formatSeconds)(seconds);
86
+ return seconds;
87
+ },
88
+ };
89
+ }
90
+ exports.createChron = createChron;
@@ -1,4 +1,4 @@
1
1
  export declare function makeTableSelector<T>(tableName: string): {
2
- (name?: keyof T | undefined): string;
2
+ (name?: keyof T): string;
3
3
  toString: any;
4
4
  };
package/util/fs-util.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { Interface } from "readline";
3
4
  export declare function isLocalDir(path: string): boolean;
4
5
  export declare function isDirEmpty(path: string): Promise<boolean>;
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { SpawnOptions, ChildProcess } from "child_process";
3
4
  import { ReadStream, WriteStream } from "fs";
4
5
  export declare type ExecLogSettingsType = {
@@ -68,10 +68,10 @@ async function exec(command, argv = [], options = null, settings = {}) {
68
68
  stderr: "",
69
69
  exitCode: 0,
70
70
  };
71
- let finishListens = pipe ? 2 : 1;
71
+ let finishListeners = pipe?.stream instanceof fs_1.WriteStream ? 2 : 1;
72
72
  let streamError;
73
73
  const tryFinish = () => {
74
- if (!--finishListens)
74
+ if (!--finishListeners)
75
75
  finish();
76
76
  };
77
77
  const finish = () => {
@@ -13,5 +13,6 @@ export declare type UriType = {
13
13
  path?: string;
14
14
  };
15
15
  export declare function formatUri(input: UriType, hidePassword?: boolean): string;
16
+ export declare function formatSeconds(seconds: number): string;
16
17
  export declare function makePathPatterns(values: string[] | undefined): string[] | undefined;
17
18
  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.makePathPatterns = exports.formatUri = exports.parseStringList = exports.render = exports.snakeCase = exports.lcfirst = exports.ucfirst = exports.serialize = void 0;
3
+ exports.formatDateTime = 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
  function serialize(message, data) {
6
6
  if (data)
@@ -67,6 +67,26 @@ function formatUri(input, hidePassword) {
67
67
  return uri;
68
68
  }
69
69
  exports.formatUri = formatUri;
70
+ function formatSeconds(seconds) {
71
+ let unit;
72
+ let value;
73
+ if (seconds > 60 * 60) {
74
+ value = seconds / 60 / 60;
75
+ unit = `hour`;
76
+ }
77
+ else if (seconds > 60) {
78
+ value = seconds / 60;
79
+ unit = `minute`;
80
+ }
81
+ else {
82
+ value = seconds;
83
+ unit = `second`;
84
+ }
85
+ if (value !== 1)
86
+ unit += `s`;
87
+ return `${value.toFixed(2)} ${unit}`;
88
+ }
89
+ exports.formatSeconds = formatSeconds;
70
90
  function makePathPatterns(values) {
71
91
  return values?.flatMap((v) => [v, `${v}/**`]);
72
92
  }
package/CHANGELOG.md DELETED
@@ -1,119 +0,0 @@
1
- # @datatruck/cli
2
-
3
- ## 0.6.1
4
-
5
- ### Patch Changes
6
-
7
- - [`0ba6229`](https://github.com/swordev/datatruck/commit/0ba6229348c109a59783e72242ab7c0e61f25e36) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix progress bar in restic repository
8
-
9
- ## 0.6.0
10
-
11
- ### Minor Changes
12
-
13
- - [`0c6877d`](https://github.com/swordev/datatruck/commit/0c6877d189761e75dd434b0a8d72b71621d024de) Thanks [@juanrgm](https://github.com/juanrgm)! - Show more progress stats
14
-
15
- * [`751e1f6`](https://github.com/swordev/datatruck/commit/751e1f6d6b33d3fa96eb40d998fdd140ce0e3875) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `fileCopyConcurrency` option
16
-
17
- - [`05487e6`](https://github.com/swordev/datatruck/commit/05487e6a33f875a3afb7ff0815b16da6f2a41301) Thanks [@juanrgm](https://github.com/juanrgm)! - Parse InnoDB error in `MariadbTask` to avoid infinite wait
18
-
19
- ### Patch Changes
20
-
21
- - [`b62a6f8`](https://github.com/swordev/datatruck/commit/b62a6f8a82409339afd65d4f96476eb57bbfb5a2) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve target/restore path in local repository
22
-
23
- ## 0.5.0
24
-
25
- ### Minor Changes
26
-
27
- - [`5aeb2af`](https://github.com/swordev/datatruck/commit/5aeb2afb96692e00bdba501b58df9cc0e02dceaa) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `enabled` option to repository config
28
-
29
- * [`75de836`](https://github.com/swordev/datatruck/commit/75de8369356cf02ed3fd5c58b1f9bea66432cda8) Thanks [@juanrgm](https://github.com/juanrgm)! - Allow restic password without file
30
-
31
- ## 0.4.0
32
-
33
- ### Minor Changes
34
-
35
- - [`eeb00a6`](https://github.com/swordev/datatruck/commit/eeb00a69d75c91da40711ae79475612b1d5193b6) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `tempDir` config option
36
-
37
- ## 0.3.2
38
-
39
- ### Patch Changes
40
-
41
- - [`8957c3b`](https://github.com/swordev/datatruck/commit/8957c3b5846606db8b825fef357445210f2a3ac3) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix restic progress parser
42
-
43
- * [`2989718`](https://github.com/swordev/datatruck/commit/29897185e3d6659359d51ab2212351005137f86c) Thanks [@juanrgm](https://github.com/juanrgm)! - Show closing reason
44
-
45
- - [`b9e0843`](https://github.com/swordev/datatruck/commit/b9e0843c7970944cfd30a7d2a543f515adfa60e4) Thanks [@juanrgm](https://github.com/juanrgm)! - Show restic progress in megabytes
46
-
47
- ## 0.3.1
48
-
49
- ### Patch Changes
50
-
51
- - [`c3bb4c6`](https://github.com/swordev/datatruck/commit/c3bb4c609887c5525cf35487ea237750addb6e75) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix restic stdout parser
52
-
53
- ## 0.3.0
54
-
55
- ### Minor Changes
56
-
57
- - [`d63fd25`](https://github.com/swordev/datatruck/commit/d63fd25ffa8d2e539d2125dfd6a3f55020086804) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `snapshotDate` param
58
-
59
- * [`486ef4a`](https://github.com/swordev/datatruck/commit/486ef4add27ae1dbfd166b16c257522f43537ecd) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve params in `include` and `exclude`
60
-
61
- - [`617dae2`](https://github.com/swordev/datatruck/commit/617dae2c8ed90e6e65e8109f03cfad0e64bd7c02) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `script` task
62
-
63
- ### Patch Changes
64
-
65
- - [`d1b3ea9`](https://github.com/swordev/datatruck/commit/d1b3ea9c9540d30898c00490963523a4fbc68193) Thanks [@juanrgm](https://github.com/juanrgm)! - Avoid use gitignore if is not necessary in restic repository
66
-
67
- ## 0.2.0
68
-
69
- ### Minor Changes
70
-
71
- - [`120460c`](https://github.com/swordev/datatruck/commit/120460c8824cef4184e43f571a4cc0798b899b66) Thanks [@juanrgm](https://github.com/juanrgm)! - Enable `include` option in restic repository
72
-
73
- ### Patch Changes
74
-
75
- - [`e30ede3`](https://github.com/swordev/datatruck/commit/e30ede371bc7ab3fc1cd47758fdac7a28e8e2705) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve `RESTIC_PASSWORD_FILE` path
76
-
77
- * [`8539d28`](https://github.com/swordev/datatruck/commit/8539d285b2c51d700aa811cd772d573fa0d613eb) Thanks [@juanrgm](https://github.com/juanrgm)! - Allow empty backup in restic repository
78
-
79
- ## 0.1.0
80
-
81
- ### Minor Changes
82
-
83
- - [`88d46cd`](https://github.com/swordev/datatruck/commit/88d46cd56293df4c6fc21a9ad61d6236ac91f325) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `custom` output format
84
-
85
- ### Patch Changes
86
-
87
- - [`24a1e5e`](https://github.com/swordev/datatruck/commit/24a1e5e86336e7a92556287e49548dc542f0e579) Thanks [@juanrgm](https://github.com/juanrgm)! - Update dependencies
88
-
89
- ## 0.0.6
90
-
91
- ### Patch Changes
92
-
93
- - [`8de6e6c`](https://github.com/swordev/datatruck/commit/8de6e6ceddb59635cb4634d884e7690eeaf59bac) Thanks [@juanrgm](https://github.com/juanrgm)! - Publish migrations
94
-
95
- ## 0.0.5
96
-
97
- ### Patch Changes
98
-
99
- - [`78cb0c1`](https://github.com/swordev/datatruck/commit/78cb0c17558543841cd7080dc4c672e6cbfd5634) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix docker image
100
-
101
- ## 0.0.4
102
-
103
- ### Patch Changes
104
-
105
- - [`d9e534b`](https://github.com/swordev/datatruck/commit/d9e534bd968acf9cd1c93f20e6152c004cb1f23b) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix package file read
106
-
107
- * [`b882c58`](https://github.com/swordev/datatruck/commit/b882c58183e9a75abc876645e18d7b67186dd662) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix read of migrations
108
-
109
- ## 0.0.3
110
-
111
- ### Patch Changes
112
-
113
- - [`051a7da`](https://github.com/swordev/datatruck/commit/051a7da225fcfea1c30a4fbfa8aea1b8f5538367) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix dist files
114
-
115
- ## 0.0.2
116
-
117
- ### Patch Changes
118
-
119
- - [`0911351`](https://github.com/swordev/datatruck/commit/09113517e1a77f2d2a1e19e4c3d9af7da1e28415) Thanks [@juanrgm](https://github.com/juanrgm)! - Publish docker image