@datatruck/cli 0.36.4 → 0.36.6

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 +1,2 @@
1
1
  export declare function calcFileHash(path: string, algorithm: string): Promise<string>;
2
+ export declare function assertFileChecksum(path: string, checksum: string, algorithm: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.calcFileHash = void 0;
3
+ exports.assertFileChecksum = exports.calcFileHash = void 0;
4
4
  const crypto_1 = require("crypto");
5
5
  const fs_1 = require("fs");
6
6
  function calcFileHash(path, algorithm) {
@@ -13,3 +13,9 @@ function calcFileHash(path, algorithm) {
13
13
  });
14
14
  }
15
15
  exports.calcFileHash = calcFileHash;
16
+ async function assertFileChecksum(path, checksum, algorithm) {
17
+ const fileChecksum = await calcFileHash(path, algorithm);
18
+ if (fileChecksum !== checksum)
19
+ throw new Error(`Invalid checksum file: ${checksum} != ${fileChecksum}`);
20
+ }
21
+ exports.assertFileChecksum = assertFileChecksum;
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { DiskStats } from "../fs";
2
3
  import { BasicProgress } from "../progress";
3
4
  import { AbstractFs, FsOptions } from "../virtual-fs";
@@ -12,12 +13,12 @@ export declare class RemoteFs extends AbstractFs {
12
13
  });
13
14
  isLocal(): boolean;
14
15
  protected fetchJson(name: string, params: any[]): Promise<any>;
15
- protected post(name: string, params: any[], data: string): Promise<void>;
16
- existsDir(path: string): Promise<any>;
17
- rename(source: string, target: string): Promise<any>;
18
- mkdir(path: string): Promise<any>;
19
- readFile(path: string): Promise<any>;
20
- readdir(path: string): Promise<any>;
16
+ protected post(name: string, params: any[], data: string): Promise<Response>;
17
+ existsDir(path: string): Promise<boolean>;
18
+ rename(source: string, target: string): Promise<void>;
19
+ mkdir(path: string): Promise<void>;
20
+ readFile(path: string): Promise<string>;
21
+ readdir(path: string): Promise<string[]>;
21
22
  readFileIfExists(path: string): Promise<string | undefined>;
22
23
  ensureEmptyDir(path: string): Promise<void>;
23
24
  writeFile(path: string, contents: string): Promise<void>;
@@ -27,19 +27,19 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
27
27
  return false;
28
28
  }
29
29
  async fetchJson(name, params) {
30
- return await (0, http_1.fetchJson)(`${this.url}/${name}`, {
30
+ const url = (0, http_1.createHref)(`${this.url}/${name}`, {
31
+ params: JSON.stringify(params),
32
+ });
33
+ return await (0, http_1.fetchJson)(url, {
31
34
  headers: this.headers,
32
- query: {
33
- params: JSON.stringify(params),
34
- },
35
35
  });
36
36
  }
37
37
  async post(name, params, data) {
38
- return await (0, http_1.post)(`${this.url}/${name}`, data, {
38
+ const url = (0, http_1.createHref)(`${this.url}/${name}`, {
39
+ params: JSON.stringify(params),
40
+ });
41
+ return await (0, http_1.post)(url, data, {
39
42
  headers: this.headers,
40
- query: {
41
- params: JSON.stringify(params),
42
- },
43
43
  });
44
44
  }
45
45
  async existsDir(path) {
@@ -67,7 +67,7 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
67
67
  await this.post("writeFile", [path], contents);
68
68
  }
69
69
  async rmAll(path) {
70
- await this.fetchJson("rmAll", [path]);
70
+ return await this.fetchJson("rmAll", [path]);
71
71
  }
72
72
  async fetchDiskStats(path) {
73
73
  if (this.options.verbose)
@@ -77,20 +77,23 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
77
77
  async upload(source, target) {
78
78
  if (this.options.verbose)
79
79
  (0, cli_1.logExec)("fs.upload", [source, target]);
80
- await (0, http_1.uploadFile)(`${this.url}/upload`, source, {
80
+ const url = (0, http_1.createHref)(`${this.url}/upload`, {
81
+ params: JSON.stringify([target]),
82
+ });
83
+ return await (0, http_1.uploadFile)(url, source, {
81
84
  headers: this.headers,
82
- query: {
83
- params: JSON.stringify([target]),
84
- },
85
+ checksum: true,
85
86
  });
86
87
  }
87
88
  async download(source, target, options = {}) {
88
89
  if (this.options.verbose)
89
90
  (0, cli_1.logExec)("fs.download", [source, target]);
90
- return await (0, http_1.downloadFile)(`${this.url}/download`, target, {
91
+ const url = (0, http_1.createHref)(`${this.url}/download`, {
92
+ params: JSON.stringify([source]),
93
+ });
94
+ return await (0, http_1.downloadFile)(url, target, {
91
95
  ...options,
92
96
  headers: this.headers,
93
- query: { params: JSON.stringify([source]) },
94
97
  });
95
98
  }
96
99
  }
@@ -4,9 +4,8 @@ exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
4
4
  const ConfigAction_1 = require("../../actions/ConfigAction");
5
5
  const cli_1 = require("../cli");
6
6
  const http_1 = require("../http");
7
+ const math_1 = require("../math");
7
8
  const virtual_fs_1 = require("../virtual-fs");
8
- const fs_1 = require("fs");
9
- const promises_1 = require("fs/promises");
10
9
  const http_2 = require("http");
11
10
  exports.headerKey = {
12
11
  user: "x-dtt-user",
@@ -58,60 +57,43 @@ const getRemoteAddress = (req, options) => {
58
57
  : undefined) ?? req.socket.remoteAddress);
59
58
  };
60
59
  function createDatatruckRepositoryServer(inOptions, config = {}) {
60
+ const counter = new math_1.Counter();
61
61
  return (0, http_2.createServer)(async (req, res) => {
62
+ const url = req.url || "";
63
+ if (url === "/" || url === "/favicon.ico")
64
+ return res.end();
65
+ const id = counter.next();
66
+ let requestError;
67
+ let responseError;
68
+ req.on("error", (error) => (requestError = error));
69
+ res.on("error", (error) => (responseError = error));
62
70
  try {
63
- if (req.url === "/" || req.url === "/favicon.ico")
64
- return res.end();
65
- const { repository, action, params } = parseUrl(req.url);
66
- if (!repository || !action) {
67
- res.statusCode = 404;
68
- return res.end();
69
- }
71
+ const { repository, action, params } = parseUrl(url);
72
+ if (!repository || !action)
73
+ return res.writeHead(404);
70
74
  const fileOptions = config.configPath
71
75
  ? (await ConfigAction_1.ConfigAction.findAndParseFile(config.configPath)).server
72
76
  ?.repository
73
77
  : undefined;
74
78
  const options = fileOptions ?? inOptions;
75
79
  const backend = findRepositoryBackend(req, repository, options);
76
- if (!backend) {
77
- res.statusCode = 401;
78
- return res.end();
79
- }
80
+ if (!backend)
81
+ return res.writeHead(401);
80
82
  if (config.log)
81
- (0, cli_1.logJson)("repository-server", "request", {
82
- repository,
83
- url: req.url,
84
- });
85
- const fs = new virtual_fs_1.LocalFs({
86
- backend: backend.path,
87
- });
83
+ (0, cli_1.logJson)("repository-server", "request", { id, repository, url });
84
+ const fs = new virtual_fs_1.LocalFs({ backend: backend.path });
88
85
  if (action === "comcheck") {
89
86
  res.write(JSON.stringify({ success: true }));
90
87
  }
91
88
  else if (action === "upload") {
92
89
  const [target] = params;
93
90
  const path = fs.resolvePath(target);
94
- const file = (0, fs_1.createWriteStream)(path);
95
- req.pipe(file);
96
- await new Promise((resolve, reject) => {
97
- req.on("error", reject);
98
- file.on("error", reject);
99
- file.on("close", resolve);
100
- });
91
+ await (0, http_1.recvFile)(req, res, path);
101
92
  }
102
93
  else if (action === "download") {
103
94
  const [target] = params;
104
95
  const path = fs.resolvePath(target);
105
- const file = (0, fs_1.createReadStream)(path);
106
- const fileStat = await (0, promises_1.stat)(path);
107
- res.setHeader("Content-Length", fileStat.size);
108
- file.pipe(res);
109
- await new Promise((resolve, reject) => {
110
- req.on("error", reject);
111
- file.on("error", reject);
112
- res.on("error", reject);
113
- res.on("close", resolve);
114
- });
96
+ await (0, http_1.sendFile)(req, res, path);
115
97
  }
116
98
  else if (action === "writeFile") {
117
99
  const data = await (0, http_1.readRequestData)(req);
@@ -127,21 +109,27 @@ function createDatatruckRepositoryServer(inOptions, config = {}) {
127
109
  res.write(JSON.stringify(json));
128
110
  }
129
111
  if (config.log)
130
- (0, cli_1.logJson)("repository-server", "request finished", {
131
- url: req.url,
132
- });
133
- res.end();
112
+ (0, cli_1.logJson)("repository-server", "request finished", { id });
134
113
  }
135
114
  catch (error) {
136
115
  if (config.log) {
137
- (0, cli_1.logJson)("repository-server", "request failed", {
138
- url: req.url,
139
- });
116
+ (0, cli_1.logJson)("repository-server", "request failed", { id });
140
117
  console.error(error);
141
118
  }
142
- res.statusCode = 500;
143
- res.statusMessage = error.message;
144
- res.end();
119
+ if (!res.writableEnded && !res.headersSent)
120
+ res.writeHead(500, error.message);
121
+ }
122
+ finally {
123
+ if (requestError) {
124
+ (0, cli_1.logJson)("repository-server", "request error", { id });
125
+ console.error(requestError);
126
+ }
127
+ if (responseError) {
128
+ (0, cli_1.logJson)("repository-server", "response error", { id });
129
+ console.error(responseError);
130
+ }
131
+ if (!res.writableEnded)
132
+ res.end();
145
133
  }
146
134
  });
147
135
  }
@@ -1,31 +1,27 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  import { BasicProgress } from "./progress";
3
- import { IncomingMessage, Server } from "http";
4
+ import { IncomingMessage, Server, ServerResponse } from "http";
5
+ export declare function createHref(inUrl: string, query?: Record<string, string>): string;
4
6
  export declare function closeServer(server: Server): Promise<void>;
5
7
  export declare function readRequestData(req: IncomingMessage): Promise<string | undefined>;
6
- export type FetchOptions = {
7
- headers?: Record<string, string>;
8
- query?: Record<string, string>;
9
- statusError?: boolean;
10
- };
11
- export declare function fetch(url: string, options?: FetchOptions): Promise<{
12
- data: string | undefined;
13
- status: number | undefined;
14
- }>;
15
- export declare function fetchJson<T = any>(url: string, options?: FetchOptions): Promise<T | undefined>;
16
- export declare function post(url: string, data: string, options?: {
17
- headers?: Record<string, string>;
18
- query?: Record<string, string>;
8
+ export declare const safeFetch: typeof fetch;
9
+ export declare function fetchJson<T = any>(url: string, options?: RequestInit): Promise<T | undefined>;
10
+ export declare function post(url: string, data: string, options?: Omit<RequestInit, "method" | "body">): Promise<Response>;
11
+ export declare function parseContentLength(value: string | undefined): number;
12
+ export declare function sendFile(req: IncomingMessage, res: ServerResponse, path: string, options?: {
13
+ end?: boolean;
14
+ checksum?: boolean;
15
+ }): Promise<void>;
16
+ export declare function recvFile(req: IncomingMessage, res: ServerResponse, path: string, options?: {
17
+ end?: boolean;
19
18
  }): Promise<void>;
20
- export declare function downloadFile(url: string, output: string, options?: {
21
- headers?: Record<string, string>;
22
- query?: Record<string, string>;
19
+ export declare function downloadFile(url: string, output: string, options?: Omit<RequestInit, "signal"> & {
23
20
  timeout?: number;
24
21
  onProgress?: (progress: BasicProgress) => void;
25
22
  }): Promise<{
26
23
  bytes: number;
27
24
  }>;
28
- export declare function uploadFile(url: string, path: string, options?: {
29
- headers?: Record<string, string>;
30
- query?: Record<string, string>;
25
+ export declare function uploadFile(url: string, path: string, options?: Omit<RequestInit, "method" | "body"> & {
26
+ checksum?: boolean;
31
27
  }): Promise<void>;
package/lib/utils/http.js CHANGED
@@ -1,22 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.uploadFile = exports.downloadFile = exports.post = exports.fetchJson = exports.fetch = exports.readRequestData = exports.closeServer = void 0;
3
+ exports.uploadFile = exports.downloadFile = exports.recvFile = exports.sendFile = exports.parseContentLength = exports.post = exports.fetchJson = exports.safeFetch = exports.readRequestData = exports.closeServer = exports.createHref = void 0;
4
+ const crypto_1 = require("./crypto");
4
5
  const math_1 = require("./math");
5
6
  const fs_1 = require("fs");
6
7
  const promises_1 = require("fs/promises");
7
- const http_1 = require("http");
8
- const https_1 = require("https");
9
- const request = (url, options, callback) => {
10
- return url.startsWith("https://")
11
- ? (0, https_1.request)(url, options, callback)
12
- : (0, http_1.request)(url, options, callback);
13
- };
14
- function href(inUrl, query) {
8
+ const stream_1 = require("stream");
9
+ const promises_2 = require("stream/promises");
10
+ function createHref(inUrl, query) {
15
11
  const url = new URL(inUrl);
16
12
  for (const key in query || {})
17
13
  url.searchParams.set(key, query[key]);
18
14
  return url.href;
19
15
  }
16
+ exports.createHref = createHref;
20
17
  async function closeServer(server) {
21
18
  await new Promise((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())));
22
19
  }
@@ -37,140 +34,150 @@ function readRequestData(req) {
37
34
  });
38
35
  }
39
36
  exports.readRequestData = readRequestData;
40
- async function fetch(url, options = {}) {
41
- const throwStatusCodeError = options.statusError ?? true;
42
- return new Promise((resolve, reject) => {
43
- let data;
44
- request(href(url, options.query), {
45
- method: "GET",
46
- headers: options.headers,
47
- }, (res) => {
48
- if (throwStatusCodeError && res.statusCode !== 200)
49
- return reject(new Error(`GET failed: ${res.statusCode} ${res.statusMessage}`));
50
- res
51
- .on("data", (chunk) => {
52
- if (data === undefined)
53
- data = "";
54
- data += chunk;
55
- })
56
- .on("error", reject)
57
- .on("close", () => {
58
- resolve({ data, status: res.statusCode });
59
- });
60
- })
61
- .on("error", reject)
62
- .end();
63
- });
64
- }
65
- exports.fetch = fetch;
37
+ const safeFetch = async (...args) => {
38
+ const res = await fetch(...args);
39
+ if (res.status !== 200)
40
+ throw new Error(`Fetch request failed: ${res.status} ${res.statusText}`);
41
+ return res;
42
+ };
43
+ exports.safeFetch = safeFetch;
66
44
  async function fetchJson(url, options = {}) {
67
- const res = await fetch(url, options);
68
- if (res.data === undefined)
69
- return;
70
- return JSON.parse(res.data);
45
+ const res = await (0, exports.safeFetch)(url, options);
46
+ const data = await res.text();
47
+ return data.length ? JSON.parse(data) : undefined;
71
48
  }
72
49
  exports.fetchJson = fetchJson;
73
50
  async function post(url, data, options = {}) {
74
- await new Promise((resolve, reject) => {
75
- const req = request(href(url, options.query), { method: "POST", headers: options.headers }, (res) => {
51
+ return await (0, exports.safeFetch)(url, { ...options, method: "POST", body: data });
52
+ }
53
+ exports.post = post;
54
+ function parseContentLength(value) {
55
+ if (!value || !/^\d+$/.test(value))
56
+ throw new Error(`Invalid 'content-length': ${value}`);
57
+ return Number(value);
58
+ }
59
+ exports.parseContentLength = parseContentLength;
60
+ async function sendFile(req, res, path, options = {}) {
61
+ let file;
62
+ try {
63
+ file = (0, fs_1.createReadStream)(path);
64
+ const fileStat = await (0, promises_1.stat)(path);
65
+ res.setHeader("Content-Length", fileStat.size);
66
+ if (options.checksum)
67
+ res.setHeader("x-checksum", await (0, crypto_1.calcFileHash)(path, "sha1"));
68
+ file.pipe(res);
69
+ await new Promise((resolve, reject) => {
70
+ file.on("error", reject);
71
+ req.on("error", reject);
72
+ res.on("error", reject).on("close", resolve);
73
+ });
74
+ }
75
+ finally {
76
+ file?.close();
77
+ if (options.end)
78
+ res.end();
79
+ }
80
+ }
81
+ exports.sendFile = sendFile;
82
+ async function recvFile(req, res, path, options = {}) {
83
+ let file;
84
+ try {
85
+ file = (0, fs_1.createWriteStream)(path);
86
+ req.pipe(file);
87
+ await new Promise((resolve, reject) => {
88
+ file.on("error", reject).on("close", resolve);
89
+ req.on("error", reject);
76
90
  res.on("error", reject);
77
- if (res.statusCode !== 200) {
78
- reject(new Error(`Post failed: ${res.statusCode} ${res.statusMessage}`));
79
- }
80
- else {
81
- resolve();
82
- }
83
91
  });
84
- req.on("error", reject);
85
- req.write(data);
86
- req.end();
87
- });
92
+ const checksum = res.getHeader("x-checksum");
93
+ if (typeof checksum === "string")
94
+ await (0, crypto_1.assertFileChecksum)(path, checksum, "sha1");
95
+ }
96
+ finally {
97
+ file?.close();
98
+ if (options.end)
99
+ res.end();
100
+ }
88
101
  }
89
- exports.post = post;
102
+ exports.recvFile = recvFile;
90
103
  async function downloadFile(url, output, options = {}) {
91
- const timeout = options.timeout ?? 3600 * 1000; // 60m
104
+ const { timeout, onProgress, ...fetchOptions } = options;
92
105
  const file = (0, fs_1.createWriteStream)(output);
93
- let total = 0;
94
- await new Promise((resolve, reject) => {
95
- const req = request(href(url, options.query), {
96
- headers: options.headers,
97
- }, (res) => {
98
- const contentLength = res.headers["content-length"] ?? "";
99
- if (!/^\d+$/.test(contentLength))
100
- return reject(new Error(`Invalid 'content-length': ${contentLength}`));
101
- total = Number(contentLength);
102
- let current = 0;
103
- if (res.statusCode === 200) {
104
- if (options.onProgress) {
105
- res.on("data", (chunk) => {
106
- current += chunk.byteLength;
107
- options.onProgress({
108
- percent: (0, math_1.progressPercent)(total, current),
109
- current,
110
- total,
111
- });
112
- });
113
- }
114
- res
115
- .on("error", async (error) => {
106
+ let checksum;
107
+ const length = { total: 0, current: 0 };
108
+ let requestError;
109
+ try {
110
+ const res = await (0, exports.safeFetch)(url, {
111
+ ...fetchOptions,
112
+ signal: AbortSignal.timeout(timeout ?? 3600 * 1000), // 60m
113
+ });
114
+ length.total = parseContentLength(res.headers.get("content-length") ?? undefined);
115
+ checksum = res.headers.get("x-checksum") ?? undefined;
116
+ const body = stream_1.Readable.fromWeb(res.body);
117
+ const progress = onProgress &&
118
+ new stream_1.Transform({
119
+ transform(chunk, encoding, callback) {
120
+ let error;
116
121
  try {
117
- file.destroy();
122
+ length.current += chunk.byteLength;
123
+ onProgress({
124
+ percent: (0, math_1.progressPercent)(length.total, length.current),
125
+ current: length.current,
126
+ total: length.total,
127
+ });
118
128
  }
119
- catch (_) { }
120
- try {
121
- await (0, promises_1.unlink)(output);
129
+ catch (progressError) {
130
+ error = progressError;
122
131
  }
123
- catch (_) { }
124
- reject(error);
125
- })
126
- .pipe(file);
127
- file.on("finish", () => {
128
- file.close((error) => {
129
- error ? reject(error) : resolve();
130
- });
131
- });
132
- }
133
- else {
134
- reject(new Error(`Download failed: ${res.statusCode} ${res.statusMessage}`));
135
- }
136
- }).on("error", async (error) => {
137
- try {
138
- await (0, promises_1.unlink)(output);
139
- }
140
- catch (_) { }
141
- reject(error);
142
- });
143
- req.setTimeout(timeout, () => {
144
- req.destroy();
145
- reject(new Error(`Request timeout after ${timeout / 1000}s`));
146
- });
147
- req.end();
148
- });
149
- const { size: bytes } = await (0, promises_1.stat)(output);
150
- if (total !== bytes)
151
- throw new Error(`Invalid download size: ${total} != ${bytes}`);
152
- return { bytes };
132
+ callback(error, chunk);
133
+ },
134
+ });
135
+ if (progress) {
136
+ await (0, promises_2.pipeline)(body, progress, file);
137
+ }
138
+ else {
139
+ await (0, promises_2.pipeline)(body, file);
140
+ }
141
+ const { size: fileLength } = await (0, promises_1.stat)(output);
142
+ if (length.total !== fileLength)
143
+ throw new Error(`Invalid download size: ${length.total} != ${fileLength}`);
144
+ }
145
+ catch (error) {
146
+ try {
147
+ await (0, promises_1.unlink)(output);
148
+ }
149
+ catch (_) { }
150
+ throw error;
151
+ }
152
+ if (checksum)
153
+ await (0, crypto_1.assertFileChecksum)(output, checksum, "sha1");
154
+ if (requestError)
155
+ throw requestError;
156
+ return { bytes: length.total };
153
157
  }
154
158
  exports.downloadFile = downloadFile;
155
159
  async function uploadFile(url, path, options = {}) {
156
160
  const { size } = await (0, promises_1.stat)(path);
157
- const readStream = (0, fs_1.createReadStream)(path);
158
- await new Promise((resolve, reject) => {
159
- const req = request(href(url, options.query), {
161
+ const file = (0, fs_1.createReadStream)(path);
162
+ try {
163
+ const res = await fetch(url, {
164
+ ...options,
160
165
  method: "POST",
166
+ duplex: "half",
161
167
  headers: {
162
168
  ...options.headers,
163
- "Content-length": size,
169
+ "Content-Length": size.toString(),
170
+ ...(options.checksum && {
171
+ "x-checksum": await (0, crypto_1.calcFileHash)(path, "sha1"),
172
+ }),
164
173
  },
165
- }, (res) => {
166
- if (res.statusCode !== 200) {
167
- reject(new Error(`Upload failed: ${res.statusCode} ${res.statusMessage}`));
168
- }
169
- else {
170
- resolve();
171
- }
172
- }).on("error", reject);
173
- readStream.on("error", reject).pipe(req);
174
- });
174
+ body: file,
175
+ });
176
+ if (res.status !== 200)
177
+ new Error(`Upload failed: ${res.status} ${res.statusText}`);
178
+ }
179
+ finally {
180
+ file.close();
181
+ }
175
182
  }
176
183
  exports.uploadFile = uploadFile;
@@ -1 +1,7 @@
1
1
  export declare function progressPercent(total: number, current: number): number;
2
+ export declare class Counter {
3
+ protected maxValue: number;
4
+ protected value: number;
5
+ constructor(maxValue?: number);
6
+ next(): number;
7
+ }
package/lib/utils/math.js CHANGED
@@ -1,9 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.progressPercent = void 0;
3
+ exports.Counter = exports.progressPercent = void 0;
4
4
  function progressPercent(total, current) {
5
5
  if (total === 0 && current === 0)
6
6
  return 0;
7
7
  return Number(((current / total) * 100).toFixed(2));
8
8
  }
9
9
  exports.progressPercent = progressPercent;
10
+ class Counter {
11
+ maxValue;
12
+ value = 0;
13
+ constructor(maxValue = 4294967295) {
14
+ this.maxValue = maxValue;
15
+ }
16
+ next() {
17
+ if (this.maxValue && this.value >= this.maxValue)
18
+ this.value = 0;
19
+ return ++this.value;
20
+ }
21
+ }
22
+ exports.Counter = Counter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.36.4",
3
+ "version": "0.36.6",
4
4
  "description": "Tool for creating and managing backups",
5
5
  "homepage": "https://github.com/swordev/datatruck#readme",
6
6
  "bugs": {