@datatruck/cli 0.34.4 → 0.34.5

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.
@@ -1,4 +1,49 @@
1
1
  "use strict";
2
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
3
+ if (value !== null && value !== void 0) {
4
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
5
+ var dispose;
6
+ if (async) {
7
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
8
+ dispose = value[Symbol.asyncDispose];
9
+ }
10
+ if (dispose === void 0) {
11
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
12
+ dispose = value[Symbol.dispose];
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ env.stack.push({ value: value, dispose: dispose, async: async });
16
+ }
17
+ else if (async) {
18
+ env.stack.push({ async: true });
19
+ }
20
+ return value;
21
+ };
22
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
23
+ return function (env) {
24
+ function fail(e) {
25
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
26
+ env.hasError = true;
27
+ }
28
+ function next() {
29
+ while (env.stack.length) {
30
+ var rec = env.stack.pop();
31
+ try {
32
+ var result = rec.dispose && rec.dispose.call(rec.value);
33
+ if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
34
+ }
35
+ catch (e) {
36
+ fail(e);
37
+ }
38
+ }
39
+ if (env.hasError) throw env.error;
40
+ }
41
+ return next();
42
+ };
43
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
44
+ var e = new Error(message);
45
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
+ });
2
47
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
48
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
49
  };
@@ -166,6 +211,7 @@ class BackupAction {
166
211
  }
167
212
  async exec() {
168
213
  const { options } = this;
214
+ const gc = new temp_1.GargabeCollector();
169
215
  const pm = new progress_1.ProgressManager({
170
216
  verbose: options.verbose,
171
217
  tty: options.tty,
@@ -174,6 +220,7 @@ class BackupAction {
174
220
  const l = new list_1.Listr3({
175
221
  streams: this.options.streams,
176
222
  progressManager: pm,
223
+ gargabeCollector: gc,
177
224
  });
178
225
  return l
179
226
  .add([
@@ -197,11 +244,11 @@ class BackupAction {
197
244
  return [
198
245
  ...packages.flatMap((pkg) => {
199
246
  let taskResult = {};
200
- const gc = new temp_1.GargabeCollector();
201
247
  const repositories = this.getRepositoryNames(pkg.repositoryNames ?? []);
202
248
  const mirrorRepositories = repositories
203
249
  .filter((r) => r.mirrors.length)
204
250
  .flatMap(({ name, mirrors }) => mirrors.map((mirror) => ({ name, mirror })));
251
+ const taskGc = gc.create();
205
252
  return l.$tasks(!!pkg.task &&
206
253
  l.$task({
207
254
  key: "task",
@@ -214,13 +261,25 @@ class BackupAction {
214
261
  completed: `Task executed: ${pkg.name} (${pkg.task.name})`,
215
262
  },
216
263
  exitOnError: false,
217
- runWrapper: gc.cleanupIfFail.bind(gc),
218
264
  run: async (task) => {
219
- taskResult = await (0, task_1.createTask)(pkg.task).backup({
220
- options,
221
- package: pkg,
222
- snapshot,
223
- onProgress: (p) => pm.update(p, (t) => (task.output = t)),
265
+ await taskGc.disposeIfFail(async () => {
266
+ const env_1 = { stack: [], error: void 0, hasError: false };
267
+ try {
268
+ const progress = __addDisposableResource(env_1, pm.create(task), false);
269
+ taskResult = await (0, task_1.createTask)(pkg.task).backup({
270
+ options,
271
+ package: pkg,
272
+ snapshot,
273
+ onProgress: progress.update,
274
+ });
275
+ }
276
+ catch (e_1) {
277
+ env_1.error = e_1;
278
+ env_1.hasError = true;
279
+ }
280
+ finally {
281
+ __disposeResources(env_1);
282
+ }
224
283
  });
225
284
  },
226
285
  }), ...repositories.map(({ name: repositoryName }) => l.$task({
@@ -238,21 +297,34 @@ class BackupAction {
238
297
  failed: `Backup create failed: ${pkg.name} (${repositoryName})`,
239
298
  },
240
299
  exitOnError: false,
241
- runWrapper: gc.cleanupOnFinish.bind(gc),
242
300
  run: async (task, data) => {
243
- const taskSummary = pkg.task
244
- ? l.result("task", pkg.name)
245
- : undefined;
246
- if (taskSummary?.error)
247
- throw new error_1.AppError(`Task failed`);
248
- const backup = await this.backup({
249
- pkg,
250
- repositoryName,
251
- snapshot,
252
- snapshotPath: taskResult?.snapshotPath,
253
- onProgress: (p) => pm.update(p, (t) => (task.output = t)),
254
- });
255
- data.bytes = backup.bytes;
301
+ const env_2 = { stack: [], error: void 0, hasError: false };
302
+ try {
303
+ const _ = __addDisposableResource(env_2, gc.create().disposeOnFinish(), true);
304
+ const taskSummary = pkg.task
305
+ ? l.result("task", pkg.name)
306
+ : undefined;
307
+ if (taskSummary?.error)
308
+ throw new error_1.AppError(`Task failed`);
309
+ const progress = __addDisposableResource(env_2, pm.create(task), false);
310
+ const backup = await this.backup({
311
+ pkg,
312
+ repositoryName,
313
+ snapshot,
314
+ snapshotPath: taskResult?.snapshotPath,
315
+ onProgress: progress.update,
316
+ });
317
+ data.bytes = backup.bytes;
318
+ }
319
+ catch (e_2) {
320
+ env_2.error = e_2;
321
+ env_2.hasError = true;
322
+ }
323
+ finally {
324
+ const result_1 = __disposeResources(env_2);
325
+ if (result_1)
326
+ await result_1;
327
+ }
256
328
  },
257
329
  })), l.$task({
258
330
  key: "cleanup",
@@ -265,8 +337,8 @@ class BackupAction {
265
337
  failed: "Task files clean failed",
266
338
  },
267
339
  exitOnError: false,
268
- enabled: gc.pending,
269
- run: () => gc.cleanup(),
340
+ enabled: taskGc.pending(),
341
+ run: () => taskGc.dispose(),
270
342
  }), ...mirrorRepositories.map(({ name, mirror }) => l.$task({
271
343
  key: "copy",
272
344
  keyIndex: [pkg.name, mirror],
@@ -283,22 +355,35 @@ class BackupAction {
283
355
  failed: `Snapshot copy failed: ${pkg.name} (${mirror})`,
284
356
  },
285
357
  exitOnError: false,
286
- runWrapper: gc.cleanup.bind(gc),
287
358
  run: async (task, data) => {
288
- const backupSummary = l.result("backup", [
289
- pkg.name,
290
- name,
291
- ]);
292
- if (backupSummary.error)
293
- throw new error_1.AppError(`Backup failed`);
294
- const copy = await this.copy({
295
- repositoryName: name,
296
- mirrorRepositoryName: mirror,
297
- pkg,
298
- snapshot,
299
- onProgress: (p) => pm.update(p, (t) => (task.output = t)),
300
- });
301
- data.bytes = copy.bytes;
359
+ const env_3 = { stack: [], error: void 0, hasError: false };
360
+ try {
361
+ const _ = __addDisposableResource(env_3, gc.create().disposeOnFinish(), true);
362
+ const backupSummary = l.result("backup", [
363
+ pkg.name,
364
+ name,
365
+ ]);
366
+ if (backupSummary.error)
367
+ throw new error_1.AppError(`Backup failed`);
368
+ const progress = __addDisposableResource(env_3, pm.create(task), false);
369
+ const copy = await this.copy({
370
+ repositoryName: name,
371
+ mirrorRepositoryName: mirror,
372
+ pkg,
373
+ snapshot,
374
+ onProgress: progress.update,
375
+ });
376
+ data.bytes = copy.bytes;
377
+ }
378
+ catch (e_3) {
379
+ env_3.error = e_3;
380
+ env_3.hasError = true;
381
+ }
382
+ finally {
383
+ const result_2 = __disposeResources(env_3);
384
+ if (result_2)
385
+ await result_2;
386
+ }
302
387
  },
303
388
  })), !!this.options.prune &&
304
389
  l.$task({
@@ -269,26 +269,48 @@ class CopyAction {
269
269
  ? sourceRepo.get()
270
270
  : undefined;
271
271
  if ($sourceRepo) {
272
- const copy = await $sourceRepo.copy({
273
- mirrorRepositoryConfig: mirrorConfig.config,
274
- options: { verbose: this.options.verbose },
275
- package: { name: snapshot.packageName },
276
- snapshot,
277
- onProgress: (p) => pm.update(p, (d) => (task.output = d)),
278
- });
279
- data.bytes = copy.bytes;
272
+ const env_2 = { stack: [], error: void 0, hasError: false };
273
+ try {
274
+ const progress = __addDisposableResource(env_2, pm.create(task), false);
275
+ const copy = await $sourceRepo.copy({
276
+ mirrorRepositoryConfig: mirrorConfig.config,
277
+ options: { verbose: this.options.verbose },
278
+ package: { name: snapshot.packageName },
279
+ snapshot,
280
+ onProgress: progress.update,
281
+ });
282
+ data.bytes = copy.bytes;
283
+ }
284
+ catch (e_2) {
285
+ env_2.error = e_2;
286
+ env_2.hasError = true;
287
+ }
288
+ finally {
289
+ __disposeResources(env_2);
290
+ }
280
291
  }
281
292
  else {
282
- const copy = await this.copyCrossRepository({
283
- mirrorConfig,
284
- mirrorRepo,
285
- repo,
286
- repoConfig,
287
- snapshot,
288
- onProgress: (p) => pm.update(p, (d) => (task.output = d)),
289
- });
290
- data.bytes = copy.bytes;
291
- sourceRepo.set(mirrorRepo);
293
+ const env_3 = { stack: [], error: void 0, hasError: false };
294
+ try {
295
+ const progress = __addDisposableResource(env_3, pm.create(task), false);
296
+ const copy = await this.copyCrossRepository({
297
+ mirrorConfig,
298
+ mirrorRepo,
299
+ repo,
300
+ repoConfig,
301
+ snapshot,
302
+ onProgress: progress.update,
303
+ });
304
+ data.bytes = copy.bytes;
305
+ sourceRepo.set(mirrorRepo);
306
+ }
307
+ catch (e_3) {
308
+ env_3.error = e_3;
309
+ env_3.hasError = true;
310
+ }
311
+ finally {
312
+ __disposeResources(env_3);
313
+ }
292
314
  }
293
315
  },
294
316
  });
@@ -21,7 +21,7 @@ class InitAction {
21
21
  if (this.options.repositoryTypes &&
22
22
  !this.options.repositoryTypes.includes(repoConfig.type))
23
23
  continue;
24
- const repo = (0, repository_1.createRepo)(repoConfig);
24
+ const repo = (0, repository_1.createRepo)(repoConfig, this.options.verbose);
25
25
  let initError = null;
26
26
  try {
27
27
  await repo.init({
@@ -5,7 +5,6 @@ import type { Config, PackageConfig } from "../utils/datatruck/config-type";
5
5
  import { Listr3TaskResultEnd } from "../utils/list";
6
6
  import { Progress, ProgressMode } from "../utils/progress";
7
7
  import { StdStreams } from "../utils/stream";
8
- import { GargabeCollector } from "../utils/temp";
9
8
  import { IfRequireKeys } from "../utils/ts";
10
9
  export type RestoreActionOptions = {
11
10
  snapshotId: string;
@@ -47,7 +46,6 @@ export declare class RestoreAction<TRequired extends boolean = true> {
47
46
  pkg: PackageConfig;
48
47
  task: TaskAbstract | undefined;
49
48
  snapshot: RestoreSnapshot;
50
- gc: GargabeCollector;
51
49
  onProgress: (progress: Progress) => void;
52
50
  }): Promise<{
53
51
  snapshotPath: string | undefined;
@@ -1,4 +1,49 @@
1
1
  "use strict";
2
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
3
+ if (value !== null && value !== void 0) {
4
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
5
+ var dispose;
6
+ if (async) {
7
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
8
+ dispose = value[Symbol.asyncDispose];
9
+ }
10
+ if (dispose === void 0) {
11
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
12
+ dispose = value[Symbol.dispose];
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ env.stack.push({ value: value, dispose: dispose, async: async });
16
+ }
17
+ else if (async) {
18
+ env.stack.push({ async: true });
19
+ }
20
+ return value;
21
+ };
22
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
23
+ return function (env) {
24
+ function fail(e) {
25
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
26
+ env.hasError = true;
27
+ }
28
+ function next() {
29
+ while (env.stack.length) {
30
+ var rec = env.stack.pop();
31
+ try {
32
+ var result = rec.dispose && rec.dispose.call(rec.value);
33
+ if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
34
+ }
35
+ catch (e) {
36
+ fail(e);
37
+ }
38
+ }
39
+ if (env.hasError) throw env.error;
40
+ }
41
+ return next();
42
+ };
43
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
44
+ var e = new Error(message);
45
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
46
+ });
2
47
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
48
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
49
  };
@@ -71,27 +116,25 @@ class RestoreAction {
71
116
  const repoConfig = (0, config_1.findRepositoryOrFail)(this.config, snapshot.repositoryName);
72
117
  const repo = await (0, repository_1.createAndInitRepo)(repoConfig, this.options.verbose);
73
118
  let snapshotPath = pkg.restorePath ?? pkg.path;
74
- await data.gc.cleanupIfFail(async () => {
75
- if (task) {
76
- const taskResult = await task.prepareRestore({
77
- options: this.options,
78
- package: pkg,
79
- snapshot,
80
- });
81
- snapshotPath = taskResult?.snapshotPath;
82
- }
83
- await (0, fs_1.initEmptyDir)(snapshotPath);
84
- if (this.config.minFreeDiskSpace)
85
- await (0, fs_1.ensureFreeDiskSpace)([snapshotPath], this.config.minFreeDiskSpace);
86
- await repo.restore({
119
+ if (task) {
120
+ const taskResult = await task.prepareRestore({
87
121
  options: this.options,
88
- snapshot: data.snapshot,
89
122
  package: pkg,
90
- snapshotPath: snapshotPath,
91
- packageConfig: pkg.repositoryConfigs?.find((config) => config.type === repoConfig.type &&
92
- (!config.names || config.names.includes(repoConfig.name)))?.config,
93
- onProgress: data.onProgress,
123
+ snapshot,
94
124
  });
125
+ snapshotPath = taskResult?.snapshotPath;
126
+ }
127
+ await (0, fs_1.initEmptyDir)(snapshotPath);
128
+ if (this.config.minFreeDiskSpace)
129
+ await (0, fs_1.ensureFreeDiskSpace)([snapshotPath], this.config.minFreeDiskSpace);
130
+ await repo.restore({
131
+ options: this.options,
132
+ snapshot: data.snapshot,
133
+ package: pkg,
134
+ snapshotPath: snapshotPath,
135
+ packageConfig: pkg.repositoryConfigs?.find((config) => config.type === repoConfig.type &&
136
+ (!config.names || config.names.includes(repoConfig.name)))?.config,
137
+ onProgress: data.onProgress,
95
138
  });
96
139
  return { snapshotPath };
97
140
  }
@@ -139,6 +182,7 @@ class RestoreAction {
139
182
  }
140
183
  async exec() {
141
184
  const { options } = this;
185
+ const gc = new temp_1.GargabeCollector();
142
186
  const pm = new progress_1.ProgressManager({
143
187
  verbose: options.verbose,
144
188
  tty: options.tty,
@@ -147,6 +191,7 @@ class RestoreAction {
147
191
  const l = new list_1.Listr3({
148
192
  streams: options.streams,
149
193
  progressManager: pm,
194
+ gargabeCollector: gc,
150
195
  });
151
196
  return l
152
197
  .add(l.$task({
@@ -184,48 +229,71 @@ class RestoreAction {
184
229
  },
185
230
  exitOnError: false,
186
231
  run: async (listTask) => {
187
- let pkg = (0, config_1.resolvePackage)((0, config_1.findPackageOrFail)(this.config, snapshot.packageName), {
188
- snapshotId: options.snapshotId,
189
- snapshotDate: snapshot.date,
190
- action: "restore",
191
- });
192
- if (this.options.initial)
193
- pkg = { ...pkg, restorePath: pkg.path };
194
- const gc = new temp_1.GargabeCollector();
195
- const task = pkg.task ? (0, task_1.createTask)(pkg.task) : undefined;
196
- const restore = await this.restore({
197
- gc,
198
- pkg,
199
- task,
200
- snapshot: snapshot,
201
- onProgress: (p) => pm.update(p, (t) => (listTask.output = t)),
202
- });
203
- if (!task)
204
- return await gc.cleanup();
205
- return l.$tasks({
206
- key: "task",
207
- keyIndex: pkg.name,
208
- data: { taskName: pkg.task.name, packageName: pkg.name },
209
- title: {
210
- initial: `Execute task: ${pkg.name} (${pkg.task.name})`,
211
- started: `Executing task: ${pkg.name} (${pkg.task.name})`,
212
- completed: `Task executed: ${pkg.name} (${pkg.task.name})`,
213
- failed: `Task execute failed: ${pkg.name} (${pkg.task.name})`,
214
- },
215
- exitOnError: false,
216
- runWrapper: gc.cleanup.bind(gc),
217
- run: async (listTask) => {
218
- const { snapshotPath } = restore;
219
- (0, assert_1.ok)(snapshotPath);
220
- await task.restore({
221
- package: pkg,
222
- options,
223
- snapshot,
224
- snapshotPath,
225
- onProgress: (p) => pm.update(p, (t) => (listTask.output = t)),
226
- });
227
- },
228
- });
232
+ const env_1 = { stack: [], error: void 0, hasError: false };
233
+ try {
234
+ let pkg = (0, config_1.resolvePackage)((0, config_1.findPackageOrFail)(this.config, snapshot.packageName), {
235
+ snapshotId: options.snapshotId,
236
+ snapshotDate: snapshot.date,
237
+ action: "restore",
238
+ });
239
+ if (this.options.initial)
240
+ pkg = { ...pkg, restorePath: pkg.path };
241
+ const task = pkg.task ? (0, task_1.createTask)(pkg.task) : undefined;
242
+ const progress = __addDisposableResource(env_1, pm.create(listTask), false);
243
+ const restoreGc = gc.create();
244
+ const restore = await restoreGc.disposeIfFail(() => this.restore({
245
+ pkg,
246
+ task,
247
+ snapshot: snapshot,
248
+ onProgress: progress.update,
249
+ }));
250
+ if (!task)
251
+ return await restoreGc.dispose();
252
+ return l.$tasks({
253
+ key: "task",
254
+ keyIndex: pkg.name,
255
+ data: { taskName: pkg.task.name, packageName: pkg.name },
256
+ title: {
257
+ initial: `Execute task: ${pkg.name} (${pkg.task.name})`,
258
+ started: `Executing task: ${pkg.name} (${pkg.task.name})`,
259
+ completed: `Task executed: ${pkg.name} (${pkg.task.name})`,
260
+ failed: `Task execute failed: ${pkg.name} (${pkg.task.name})`,
261
+ },
262
+ exitOnError: false,
263
+ run: async (listTask) => {
264
+ const env_2 = { stack: [], error: void 0, hasError: false };
265
+ try {
266
+ const _ = __addDisposableResource(env_2, restoreGc.disposeOnFinish(), true);
267
+ const { snapshotPath } = restore;
268
+ (0, assert_1.ok)(snapshotPath);
269
+ const progress = __addDisposableResource(env_2, pm.create(listTask), false);
270
+ await task.restore({
271
+ package: pkg,
272
+ options,
273
+ snapshot,
274
+ snapshotPath,
275
+ onProgress: progress.update,
276
+ });
277
+ }
278
+ catch (e_2) {
279
+ env_2.error = e_2;
280
+ env_2.hasError = true;
281
+ }
282
+ finally {
283
+ const result_1 = __disposeResources(env_2);
284
+ if (result_1)
285
+ await result_1;
286
+ }
287
+ },
288
+ });
289
+ }
290
+ catch (e_1) {
291
+ env_1.error = e_1;
292
+ env_1.hasError = true;
293
+ }
294
+ finally {
295
+ __disposeResources(env_1);
296
+ }
229
297
  },
230
298
  }));
231
299
  },
@@ -12,7 +12,7 @@ export type MetaData = {
12
12
  files: number;
13
13
  size: number;
14
14
  checksum: string;
15
- }>;
15
+ } | undefined>;
16
16
  };
17
17
  export type DatatruckRepositoryConfig = {
18
18
  backend: string;
@@ -45,15 +45,15 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
45
45
  return this.config.backend;
46
46
  }
47
47
  fetchDiskStats(config) {
48
- const fs = (0, client_1.createFs)(config.backend);
48
+ const fs = (0, client_1.createFs)(config.backend, this.verbose);
49
49
  return fs.fetchDiskStats(".");
50
50
  }
51
51
  async init(data) {
52
- const fs = (0, client_1.createFs)(this.config.backend);
52
+ const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
53
53
  await fs.mkdir(".");
54
54
  }
55
55
  async prune(data) {
56
- const fs = (0, client_1.createFs)(this.config.backend);
56
+ const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
57
57
  const snapshotName = DatatruckRepository.buildSnapshotName(data.snapshot, {
58
58
  name: data.snapshot.packageName,
59
59
  });
@@ -63,7 +63,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
63
63
  await fs.rmAll(snapshotName);
64
64
  }
65
65
  async fetchSnapshots(data) {
66
- const fs = (0, client_1.createFs)(this.config.backend);
66
+ const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
67
67
  if (!(await fs.existsDir(".")))
68
68
  throw new error_1.AppError(`Repository (${this.repository.name}) out path does not exist: ${fs.resolvePath(".")}`);
69
69
  const snapshots = [];
@@ -105,7 +105,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
105
105
  return snapshots;
106
106
  }
107
107
  async backup(data) {
108
- const fs = (0, client_1.createFs)(this.config.backend);
108
+ const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
109
109
  const snapshotName = DatatruckRepository.buildSnapshotName(data.snapshot, data.package);
110
110
  const outPath = fs.isLocal()
111
111
  ? fs.resolvePath(snapshotName)
@@ -177,11 +177,11 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
177
177
  .join("-") + (pack.compress ? `.tar.gz` : `.tar`);
178
178
  const includeList = stream.path(packIndex);
179
179
  if (includeList) {
180
- tarStats[packBasename] = {
180
+ const stats = (tarStats[packBasename] = {
181
181
  files: stream.lines(packIndex),
182
182
  size: 0,
183
183
  checksum: "",
184
- };
184
+ });
185
185
  const tarPath = (0, path_1.join)(outPath, packBasename);
186
186
  await (0, tar_1.createTar)({
187
187
  compress: pack.compress,
@@ -192,8 +192,8 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
192
192
  onEntry: async (data) => scanner.progress(pack.compress ? "Compressing" : "Packing", data.path),
193
193
  });
194
194
  scanner.progress("Fetching tar stats", (0, path_1.basename)(tarPath), false);
195
- tarStats[packBasename].checksum = await (0, crypto_1.calcFileHash)(tarPath, "sha1");
196
- tarStats[packBasename].size = (await (0, promises_1.stat)(tarPath)).size;
195
+ stats.checksum = await (0, crypto_1.calcFileHash)(tarPath, "sha1");
196
+ stats.size = (await (0, promises_1.stat)(tarPath)).size;
197
197
  if (!fs.isLocal()) {
198
198
  scanner.progress("Uploading tar", (0, path_1.basename)(tarPath), false);
199
199
  await fs.upload(tarPath, `${snapshotName}/${packBasename}`);
@@ -204,7 +204,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
204
204
  }
205
205
  scanner.end();
206
206
  // Meta
207
- const size = Object.values(tarStats).reduce((total, { size }) => total + size, 0);
207
+ const size = Object.values(tarStats).reduce((total, stat) => total + stat.size, 0);
208
208
  const metaPath = `${snapshotName}/meta.json`;
209
209
  const nodePkg = (0, fs_1.parsePackageFile)();
210
210
  const meta = {
@@ -225,8 +225,8 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
225
225
  };
226
226
  }
227
227
  async copy(data) {
228
- const sourceFs = (0, client_1.createFs)(this.config.backend);
229
- const targetFs = (0, client_1.createFs)(data.mirrorRepositoryConfig.backend);
228
+ const sourceFs = (0, client_1.createFs)(this.config.backend, this.verbose);
229
+ const targetFs = (0, client_1.createFs)(data.mirrorRepositoryConfig.backend, this.verbose);
230
230
  const snapshotName = DatatruckRepository.buildSnapshotName(data.snapshot, data.package);
231
231
  if (data.options.verbose)
232
232
  (0, cli_1.logExec)(`Copying backup files to ${data.mirrorRepositoryConfig.backend}`);
@@ -296,7 +296,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
296
296
  return { bytes };
297
297
  }
298
298
  async restore(data) {
299
- const fs = (0, client_1.createFs)(this.config.backend);
299
+ const fs = (0, client_1.createFs)(this.config.backend, this.verbose);
300
300
  const relRestorePath = data.snapshotPath;
301
301
  (0, assert_1.ok)(relRestorePath);
302
302
  const restorePath = (0, path_1.resolve)(relRestorePath);
@@ -325,10 +325,20 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
325
325
  if (!fs.isLocal()) {
326
326
  const tempDir = await (0, temp_1.mkTmpDir)(exports.datatruckRepositoryName, "repo", "restore", "remote-fs", entry);
327
327
  tempEntry = `${tempDir}/${entry}`;
328
- await fs.download(sourceEntry, tempEntry);
328
+ await fs.download(sourceEntry, tempEntry, {
329
+ onProgress: (stats) => {
330
+ progress.updateRelative("Downloading", entry, {
331
+ ...stats,
332
+ format: "size",
333
+ });
334
+ },
335
+ });
329
336
  }
337
+ const stats = tarStats[entry];
338
+ if (data.options.verbose)
339
+ (0, cli_1.logExec)(`Stats of '${entry}' is not available`);
330
340
  await (0, tar_1.extractTar)({
331
- total: tarStats[entry].files,
341
+ total: stats?.files,
332
342
  input: tempEntry ?? fs.resolvePath(sourceEntry),
333
343
  output: restorePath,
334
344
  decompress: entry.endsWith(".tar.gz"),
@@ -68,7 +68,11 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
68
68
  branchName,
69
69
  orphan: true,
70
70
  });
71
- await git.exec(["commit", "-m", "Initial commit", "--allow-empty"]);
71
+ await git.commit("Initial commit", {
72
+ allowEmpty: true,
73
+ userName: "datatruck",
74
+ userEmail: "datatruck@localhost",
75
+ });
72
76
  await git.push({ branchName });
73
77
  }
74
78
  }
@@ -184,8 +188,12 @@ class GitRepository extends RepositoryAbstract_1.RepositoryAbstract {
184
188
  if (data.options.verbose)
185
189
  console.info(`Copied ${files} files`);
186
190
  await git.exec(["add", "--verbose", "."]);
187
- if (await git.haveChanges())
188
- await git.exec(["commit", "-m", data.snapshot.id]);
191
+ if (await git.haveChanges()) {
192
+ await git.commit(data.snapshot.id, {
193
+ userName: "datatruck",
194
+ userEmail: "datatruck@localhost",
195
+ });
196
+ }
189
197
  const nodePkg = (0, fs_1.parsePackageFile)();
190
198
  const size = (await (0, fs_1.fastFolderSizeAsync)(tmpPath)) -
191
199
  (await (0, fs_1.fastFolderSizeAsync)((0, path_1.join)(tmpPath, ".git")));
@@ -75,8 +75,9 @@ export type SnapshotTagObject = {
75
75
  };
76
76
  export declare abstract class RepositoryAbstract<TConfig> {
77
77
  readonly repository: RepositoryConfig;
78
+ readonly verbose: boolean;
78
79
  readonly config: TConfig;
79
- constructor(repository: RepositoryConfig);
80
+ constructor(repository: RepositoryConfig, verbose: boolean);
80
81
  abstract getSource(): string;
81
82
  abstract fetchDiskStats(config: TConfig): Promise<DiskStats | undefined>;
82
83
  ensureFreeDiskSpace(config: TConfig, minFreeDiskSpace: number | string): Promise<void>;
@@ -15,9 +15,11 @@ var SnapshotTagEnum;
15
15
  })(SnapshotTagEnum || (exports.SnapshotTagEnum = SnapshotTagEnum = {}));
16
16
  class RepositoryAbstract {
17
17
  repository;
18
+ verbose;
18
19
  config;
19
- constructor(repository) {
20
+ constructor(repository, verbose) {
20
21
  this.repository = repository;
22
+ this.verbose = verbose;
21
23
  this.config = repository.config;
22
24
  }
23
25
  async ensureFreeDiskSpace(config, minFreeDiskSpace) {
@@ -2,10 +2,14 @@ import { DiskStats } from "../fs";
2
2
  import { BasicProgress } from "../progress";
3
3
  import { AbstractFs, FsOptions } from "../virtual-fs";
4
4
  export declare class RemoteFs extends AbstractFs {
5
- readonly options: FsOptions;
5
+ readonly options: FsOptions & {
6
+ verbose?: boolean;
7
+ };
6
8
  protected url: string;
7
9
  protected headers: Record<string, string>;
8
- constructor(options: FsOptions);
10
+ constructor(options: FsOptions & {
11
+ verbose?: boolean;
12
+ });
9
13
  isLocal(): boolean;
10
14
  protected fetchJson(name: string, params: any[]): Promise<any>;
11
15
  protected post(name: string, params: any[], data: string): Promise<void>;
@@ -28,4 +32,4 @@ export declare class RemoteFs extends AbstractFs {
28
32
  }>;
29
33
  }
30
34
  export declare function isRemoteBackend(backend: string): boolean;
31
- export declare function createFs(backend: string): AbstractFs;
35
+ export declare function createFs(backend: string, verbose: boolean | undefined): AbstractFs;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createFs = exports.isRemoteBackend = exports.RemoteFs = void 0;
4
+ const cli_1 = require("../cli");
4
5
  const http_1 = require("../http");
5
6
  const virtual_fs_1 = require("../virtual-fs");
6
7
  const repository_server_1 = require("./repository-server");
@@ -69,9 +70,13 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
69
70
  await this.fetchJson("rmAll", [path]);
70
71
  }
71
72
  async fetchDiskStats(path) {
73
+ if (this.options.verbose)
74
+ (0, cli_1.logExec)("fs.fetchDiskStats", [path]);
72
75
  return await this.fetchJson("fetchDiskStats", [path]);
73
76
  }
74
77
  async upload(source, target) {
78
+ if (this.options.verbose)
79
+ (0, cli_1.logExec)("fs.upload", [source, target]);
75
80
  await (0, http_1.uploadFile)(`${this.url}/upload`, source, {
76
81
  headers: this.headers,
77
82
  query: {
@@ -80,6 +85,8 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
80
85
  });
81
86
  }
82
87
  async download(source, target, options = {}) {
88
+ if (this.options.verbose)
89
+ (0, cli_1.logExec)("fs.download", [source, target]);
83
90
  return await (0, http_1.downloadFile)(`${this.url}/download`, target, {
84
91
  ...options,
85
92
  headers: this.headers,
@@ -92,9 +99,9 @@ function isRemoteBackend(backend) {
92
99
  return backend.startsWith("http:") || backend.startsWith("https:");
93
100
  }
94
101
  exports.isRemoteBackend = isRemoteBackend;
95
- function createFs(backend) {
102
+ function createFs(backend, verbose) {
96
103
  return isRemoteBackend(backend)
97
- ? new RemoteFs({ backend })
104
+ ? new RemoteFs({ backend, verbose })
98
105
  : new virtual_fs_1.LocalFs({ backend });
99
106
  }
100
107
  exports.createFs = createFs;
@@ -8,14 +8,14 @@ const micromatch_1 = require("micromatch");
8
8
  function findRepositoryOrFail(config, repositoryName) {
9
9
  const repo = config.repositories.find((v) => v.name === repositoryName);
10
10
  if (!repo)
11
- throw new error_1.AppError(`Repository '${repositoryName}' not found`);
11
+ throw new error_1.AppError(`Repository '${repositoryName}' not found in the config`);
12
12
  return repo;
13
13
  }
14
14
  exports.findRepositoryOrFail = findRepositoryOrFail;
15
15
  function findPackageOrFail(config, packageName) {
16
16
  const pkg = config.packages.find((v) => v.name === packageName);
17
17
  if (!pkg)
18
- throw new error_1.AppError(`Package '${packageName}' not found`);
18
+ throw new error_1.AppError(`Package '${packageName}' not found in the config`);
19
19
  return pkg;
20
20
  }
21
21
  exports.findPackageOrFail = findPackageOrFail;
@@ -9,8 +9,8 @@ declare const repoMap: {
9
9
  datatruck: typeof DatatruckRepository;
10
10
  };
11
11
  export declare function getRepoConstructor(type: keyof typeof repoMap): {
12
- new (config: RepositoryConfig): RepositoryAbstract<any>;
12
+ new (config: RepositoryConfig, verbose?: boolean): RepositoryAbstract<any>;
13
13
  };
14
- export declare function createRepo(repository: RepositoryConfig): RepositoryAbstract<any>;
15
- export declare function createAndInitRepo(repository: RepositoryConfig, verbose?: boolean): Promise<RepositoryAbstract<any>>;
14
+ export declare function createRepo(repository: RepositoryConfig, verbose: boolean | undefined): RepositoryAbstract<any>;
15
+ export declare function createAndInitRepo(repository: RepositoryConfig, verbose: boolean | undefined): Promise<RepositoryAbstract<any>>;
16
16
  export {};
@@ -17,13 +17,13 @@ function getRepoConstructor(type) {
17
17
  return Constructor;
18
18
  }
19
19
  exports.getRepoConstructor = getRepoConstructor;
20
- function createRepo(repository) {
20
+ function createRepo(repository, verbose) {
21
21
  const Constructor = getRepoConstructor(repository.type);
22
- return new Constructor(repository);
22
+ return new Constructor(repository, verbose);
23
23
  }
24
24
  exports.createRepo = createRepo;
25
25
  async function createAndInitRepo(repository, verbose) {
26
- const repo = createRepo(repository);
26
+ const repo = createRepo(repository, verbose);
27
27
  await repo.init({ options: { verbose } });
28
28
  return repo;
29
29
  }
package/lib/utils/fs.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  /// <reference types="node" />
3
3
  /// <reference types="node" />
4
- import { Progress } from "./progress";
4
+ import { Progress, ProgressStats } from "./progress";
5
5
  import { Entry, Options } from "fast-glob";
6
6
  import { Stats } from "fs";
7
7
  import { WriteStream } from "fs";
@@ -90,6 +90,7 @@ type ProgressObject = {
90
90
  total: number;
91
91
  current: number;
92
92
  update: (description: string, path?: string, increment?: boolean) => void;
93
+ updateRelative: (description: string, path: string, stats: ProgressStats) => void;
93
94
  };
94
95
  export declare function createProgress(options: {
95
96
  onProgress: (data: Progress) => void;
package/lib/utils/fs.js CHANGED
@@ -400,11 +400,29 @@ function createProgress(options) {
400
400
  disposed: false,
401
401
  total: 0,
402
402
  current: 0,
403
+ updateRelative(description, path, stats) {
404
+ if (progress.disposed)
405
+ return;
406
+ options.onProgress({
407
+ relative: {
408
+ ...stats,
409
+ description,
410
+ payload: path,
411
+ },
412
+ absolute: {
413
+ total: progress.total,
414
+ current: progress.current,
415
+ percent: (0, math_1.progressPercent)(progress.total, progress.current),
416
+ },
417
+ });
418
+ },
403
419
  update: (description, path, increment = true) => {
404
420
  if (progress.disposed)
405
421
  return;
406
422
  if (path && increment)
407
423
  progress.current++;
424
+ if (path === "." || path === "./")
425
+ return;
408
426
  options.onProgress({
409
427
  relative: {
410
428
  description,
@@ -28,6 +28,11 @@ export declare class Git {
28
28
  removeAll(): Promise<void>;
29
29
  haveChanges(): Promise<boolean>;
30
30
  fetchCommitId(tag: string): Promise<string>;
31
+ commit(description: string, options?: {
32
+ allowEmpty?: boolean;
33
+ userName?: string;
34
+ userEmail?: string;
35
+ }): Promise<void>;
31
36
  getTags(names?: string[]): Promise<{
32
37
  name: string;
33
38
  message?: string | undefined;
package/lib/utils/git.js CHANGED
@@ -70,6 +70,16 @@ class Git {
70
70
  async fetchCommitId(tag) {
71
71
  return (await this.stdout(["rev-list", "-n", "1", tag])).trim();
72
72
  }
73
+ async commit(description, options = {}) {
74
+ await this.exec([
75
+ ...(options.userName ? ["-c", `user.name='${options.userName}'`] : []),
76
+ ...(options.userEmail ? ["-c", `user.email='${options.userEmail}'`] : []),
77
+ "commit",
78
+ "-m",
79
+ description,
80
+ ...(options.allowEmpty ? ["--allow-empty"] : []),
81
+ ]);
82
+ }
73
83
  async getTags(names) {
74
84
  const stdout = await this.stdout(["tag", "-n", ...(names ?? [])]);
75
85
  return stdout.split(/\r?\n/).reduce((result, value) => {
@@ -1,6 +1,7 @@
1
1
  import { Timer } from "./date";
2
2
  import { ProgressManager } from "./progress";
3
3
  import { StdStreams } from "./stream";
4
+ import { GargabeCollector } from "./temp";
4
5
  import { Listr, ListrGetRendererClassFromValue, ListrLogger, ListrTask, ListrTaskWrapper } from "listr2";
5
6
  export declare class List3Logger<Levels extends string = string> extends ListrLogger<Levels> {
6
7
  constructor(options?: {
@@ -19,7 +20,6 @@ type Listr3Task<T extends Listr3Context, K extends keyof T> = {
19
20
  failed?: string;
20
21
  completed?: string;
21
22
  };
22
- runWrapper?: (cb: () => any) => any;
23
23
  run: (task: ListrTaskWrapper<any, any, any>, data: T[K]) => Promise<void | ListrTask[] | Listr | undefined> | void | undefined | ListrTask[] | Listr;
24
24
  exitOnError?: boolean;
25
25
  enabled?: boolean;
@@ -43,6 +43,7 @@ export declare class Listr3<T extends Listr3Context> extends Listr<void, "defaul
43
43
  readonly $options: {
44
44
  streams?: StdStreams;
45
45
  progressManager?: ProgressManager;
46
+ gargabeCollector?: GargabeCollector;
46
47
  };
47
48
  readonly resultMap: Record<string, Listr3TaskResult<T>>;
48
49
  readonly resultList: Listr3TaskResult<T>[];
@@ -51,6 +52,7 @@ export declare class Listr3<T extends Listr3Context> extends Listr<void, "defaul
51
52
  constructor($options: {
52
53
  streams?: StdStreams;
53
54
  progressManager?: ProgressManager;
55
+ gargabeCollector?: GargabeCollector;
54
56
  });
55
57
  private serializeKeyIndex;
56
58
  private createResultIndex;
package/lib/utils/list.js CHANGED
@@ -89,9 +89,7 @@ class Listr3 extends listr2_1.Listr {
89
89
  const timer = (0, date_1.createTimer)();
90
90
  if (title)
91
91
  try {
92
- const runResult = item.runWrapper
93
- ? await item.runWrapper(async () => await item.run(task, result.data))
94
- : await item.run(task, result.data);
92
+ const runResult = await item.run(task, result.data);
95
93
  if (title.completed)
96
94
  task.title = title.completed;
97
95
  return Array.isArray(runResult)
@@ -153,6 +151,7 @@ class Listr3 extends listr2_1.Listr {
153
151
  throw error;
154
152
  }
155
153
  finally {
154
+ await this.$options.gargabeCollector?.dispose();
156
155
  dispose();
157
156
  }
158
157
  }
@@ -1,4 +1,5 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { Timer } from "./date";
3
4
  export type BasicProgress = {
4
5
  percent: number;
@@ -35,6 +36,7 @@ export declare class ProgressManager {
35
36
  protected interval: Timer | undefined;
36
37
  protected intervalMs: number;
37
38
  protected keydownListener: ((data: Buffer | undefined) => void) | undefined;
39
+ protected pendingProgress: Progress | undefined;
38
40
  readonly tty: Exclude<ProgressTty, "auto">;
39
41
  readonly mode: Exclude<ProgressMode, "auto" | `interval:${number}`>;
40
42
  constructor(options: {
@@ -51,7 +53,12 @@ export declare class ProgressManager {
51
53
  elapsed(): number;
52
54
  start(): void;
53
55
  dispose(): void;
54
- update(progress: Progress, cb: (text: string) => void): void;
56
+ create(input: ((text: string) => void) | {
57
+ output: string;
58
+ }, delay?: number): Disposable & {
59
+ update: (progress: Progress) => void;
60
+ };
61
+ renderProgress(progress: Progress, force?: boolean): string | undefined;
55
62
  }
56
63
  export declare function renderProgress(progress: Progress, bar?: boolean): string[];
57
64
  export declare function renderProgressStats(stats: ProgressStats, progressBar?: boolean): string;
@@ -13,6 +13,7 @@ class ProgressManager {
13
13
  interval = (0, date_1.createTimer)();
14
14
  intervalMs;
15
15
  keydownListener;
16
+ pendingProgress;
16
17
  tty;
17
18
  mode;
18
19
  constructor(options) {
@@ -66,19 +67,51 @@ class ProgressManager {
66
67
  this.keydownListener = undefined;
67
68
  }
68
69
  }
69
- update(progress, cb) {
70
- if (!this.mode)
70
+ create(input, delay = 1000) {
71
+ const update = (progress, force) => {
72
+ const text = this.renderProgress(progress, force);
73
+ if (typeof text === "string") {
74
+ if (typeof input === "function") {
75
+ input(text);
76
+ }
77
+ else {
78
+ input.output = text;
79
+ }
80
+ }
81
+ };
82
+ const updatePending = () => {
83
+ const pendingProgress = this.pendingProgress;
84
+ if (pendingProgress) {
85
+ this.pendingProgress = undefined;
86
+ update(pendingProgress, true);
87
+ }
88
+ };
89
+ const interval = setInterval(updatePending, delay);
90
+ return {
91
+ update,
92
+ [Symbol.dispose]: () => {
93
+ clearInterval(interval);
94
+ updatePending();
95
+ },
96
+ };
97
+ }
98
+ renderProgress(progress, force = false) {
99
+ if (!this.mode) {
71
100
  return;
72
- if (this.mode === "interval") {
101
+ }
102
+ else if (this.mode === "interval") {
73
103
  if (this.interval) {
74
- if (!this.interval.reset(this.intervalMs))
104
+ if (!this.interval.reset(this.intervalMs) && !force) {
105
+ this.pendingProgress = progress;
75
106
  return;
107
+ }
76
108
  }
77
109
  else {
78
110
  this.interval = (0, date_1.createTimer)();
79
111
  }
80
112
  }
81
- cb(renderProgress(progress, this.tty).join("\n"));
113
+ this.pendingProgress = undefined;
114
+ return renderProgress(progress, this.tty).join("\n");
82
115
  }
83
116
  }
84
117
  exports.ProgressManager = ProgressManager;
@@ -5,6 +5,7 @@ export declare function ensureFreeDiskTempSpace(size: number | string): Promise<
5
5
  export declare function isTmpDir(path: string): boolean;
6
6
  export declare function rmTmpDir(input: string | string[]): Promise<void>;
7
7
  export declare function tmpDir(...keys: [string, ...string[]]): string;
8
+ export declare const collectors: Set<GargabeCollector>;
8
9
  export declare function mkTmpDir(...keys: [string, ...string[]]): Promise<string>;
9
10
  export declare function useTempDir(...keys: [string, ...string[]]): Promise<AsyncDisposable & {
10
11
  path: string;
@@ -12,15 +13,17 @@ export declare function useTempDir(...keys: [string, ...string[]]): Promise<Asyn
12
13
  export declare function useTempFile(path: string): AsyncDisposable & {
13
14
  path: string;
14
15
  };
15
- export declare class CleanupListener {
16
- readonly paths: string[];
17
- stop(): void;
18
- dispose(): Promise<void>;
19
- }
20
16
  export declare class GargabeCollector {
21
- protected listeners: Set<CleanupListener>;
22
- get pending(): boolean;
23
- cleanup(cb?: () => any): Promise<void>;
24
- cleanupOnFinish(cb: () => any): Promise<void>;
25
- cleanupIfFail(cb: () => any): Promise<CleanupListener>;
17
+ protected parent?: GargabeCollector | undefined;
18
+ readonly paths: Set<string>;
19
+ readonly children: Set<GargabeCollector>;
20
+ constructor(parent?: GargabeCollector | undefined);
21
+ pending(): boolean;
22
+ cleanup(): Promise<void>;
23
+ dispose(): Promise<void>;
24
+ disposeIfFail<T>(cb: () => Promise<T>): Promise<T>;
25
+ disposeOnFinish(): {
26
+ [Symbol.asyncDispose]: () => Promise<void>;
27
+ };
28
+ create(): GargabeCollector;
26
29
  }
package/lib/utils/temp.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.GargabeCollector = exports.CleanupListener = exports.useTempFile = exports.useTempDir = exports.mkTmpDir = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.ensureFreeDiskTempSpace = exports.sessionTmpDir = exports.parentTmpDir = void 0;
6
+ exports.GargabeCollector = exports.useTempFile = exports.useTempDir = exports.mkTmpDir = exports.collectors = exports.tmpDir = exports.rmTmpDir = exports.isTmpDir = exports.ensureFreeDiskTempSpace = exports.sessionTmpDir = exports.parentTmpDir = void 0;
7
7
  const globalData_1 = __importDefault(require("../globalData"));
8
8
  const fs_1 = require("./fs");
9
9
  const crypto_1 = require("crypto");
@@ -44,12 +44,15 @@ exports.rmTmpDir = rmTmpDir;
44
44
  function tmpDir(...keys) {
45
45
  const id = (0, crypto_1.randomUUID)().slice(0, 8);
46
46
  const path = (0, path_1.join)(sessionTmpDir(), [...keys, id].map(encodeURIComponent).join("-"));
47
- for (const listener of listeners)
48
- listener.paths.push(path);
47
+ if (exports.collectors.size) {
48
+ const lastListener = [...exports.collectors.values()].at(exports.collectors.size - 1);
49
+ if (lastListener)
50
+ lastListener.paths.add(path);
51
+ }
49
52
  return path;
50
53
  }
51
54
  exports.tmpDir = tmpDir;
52
- const listeners = new Set();
55
+ exports.collectors = new Set();
53
56
  async function mkTmpDir(...keys) {
54
57
  const path = tmpDir(...keys);
55
58
  await (0, promises_1.mkdir)(path, { recursive: true });
@@ -81,53 +84,57 @@ function useTempFile(path) {
81
84
  };
82
85
  }
83
86
  exports.useTempFile = useTempFile;
84
- class CleanupListener {
85
- paths = [];
86
- stop() {
87
- listeners.delete(this);
88
- }
89
- async dispose() {
90
- this.stop();
91
- await rmTmpDir(this.paths);
92
- }
93
- }
94
- exports.CleanupListener = CleanupListener;
95
87
  class GargabeCollector {
96
- listeners = new Set();
97
- get pending() {
98
- return this.listeners.size > 0;
88
+ parent;
89
+ paths = new Set();
90
+ children = new Set();
91
+ constructor(parent) {
92
+ this.parent = parent;
93
+ exports.collectors.add(this);
99
94
  }
100
- async cleanup(cb) {
101
- try {
102
- await cb?.();
103
- }
104
- finally {
105
- for (const listener of this.listeners) {
106
- this.listeners.delete(listener);
107
- await listener.dispose();
95
+ pending() {
96
+ if (this.paths.size)
97
+ return true;
98
+ for (const child of this.children)
99
+ if (child.pending())
100
+ return true;
101
+ return false;
102
+ }
103
+ async cleanup() {
104
+ for (const path of this.paths) {
105
+ try {
106
+ await rmTmpDir(path);
107
+ this.paths.delete(path);
108
108
  }
109
+ catch (_) { }
109
110
  }
111
+ for (const child of this.children)
112
+ await child.cleanup();
110
113
  }
111
- async cleanupOnFinish(cb) {
112
- const cleanup = new CleanupListener();
113
- try {
114
- await cb();
115
- }
116
- finally {
117
- cleanup.dispose();
118
- }
114
+ async dispose() {
115
+ await this.cleanup();
116
+ exports.collectors.delete(this);
119
117
  }
120
- async cleanupIfFail(cb) {
121
- const cleanup = new CleanupListener();
118
+ async disposeIfFail(cb) {
122
119
  try {
123
- await cb();
120
+ return await cb();
124
121
  }
125
122
  catch (error) {
126
- await cleanup.dispose();
123
+ await this.dispose();
127
124
  throw error;
128
125
  }
129
- this.listeners.add(cleanup);
130
- return cleanup;
126
+ }
127
+ disposeOnFinish() {
128
+ return {
129
+ [Symbol.asyncDispose]: async () => {
130
+ return this.dispose();
131
+ },
132
+ };
133
+ }
134
+ create() {
135
+ const gc = new GargabeCollector();
136
+ this.children.add(gc);
137
+ return gc;
131
138
  }
132
139
  }
133
140
  exports.GargabeCollector = GargabeCollector;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.34.4",
3
+ "version": "0.34.5",
4
4
  "description": "Tool for creating and managing backups",
5
5
  "homepage": "https://github.com/swordev/datatruck#readme",
6
6
  "bugs": {