@dynamicweb/cli 1.0.16 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +256 -222
- package/bin/commands/command.js +164 -89
- package/bin/commands/config.js +54 -54
- package/bin/commands/database.js +65 -71
- package/bin/commands/env.js +174 -155
- package/bin/commands/files.js +502 -377
- package/bin/commands/install.js +64 -63
- package/bin/commands/login.js +228 -218
- package/bin/commands/query.js +172 -102
- package/bin/commands/swift.js +78 -78
- package/bin/downloader.js +70 -67
- package/bin/extractor.js +28 -28
- package/bin/index.js +73 -73
- package/bin/utils.js +112 -125
- package/package.json +51 -42
package/bin/commands/query.js
CHANGED
|
@@ -1,102 +1,172 @@
|
|
|
1
|
-
import fetch from 'node-fetch';
|
|
2
|
-
import { setupEnv, getAgent } from './env.js';
|
|
3
|
-
import { setupUser } from './login.js';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
const exclude = ['_', '$0', 'query', 'list', 'i', 'l', 'interactive']
|
|
7
|
-
|
|
8
|
-
export function queryCommand() {
|
|
9
|
-
return {
|
|
10
|
-
command: 'query [query]',
|
|
11
|
-
describe: 'Runs the given query',
|
|
12
|
-
builder: (yargs) => {
|
|
13
|
-
return yargs
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
1
|
+
import fetch from 'node-fetch';
|
|
2
|
+
import { setupEnv, getAgent } from './env.js';
|
|
3
|
+
import { setupUser } from './login.js';
|
|
4
|
+
import { input } from '@inquirer/prompts';
|
|
5
|
+
|
|
6
|
+
const exclude = ['_', '$0', 'query', 'list', 'i', 'l', 'interactive', 'verbose', 'v', 'host', 'protocol', 'apiKey', 'env', 'output']
|
|
7
|
+
|
|
8
|
+
export function queryCommand() {
|
|
9
|
+
return {
|
|
10
|
+
command: 'query [query]',
|
|
11
|
+
describe: 'Runs the given query',
|
|
12
|
+
builder: (yargs) => {
|
|
13
|
+
return yargs
|
|
14
|
+
.positional('query', {
|
|
15
|
+
describe: 'The query to execute'
|
|
16
|
+
})
|
|
17
|
+
.option('list', {
|
|
18
|
+
alias: 'l',
|
|
19
|
+
describe: 'Lists all the properties for the query'
|
|
20
|
+
})
|
|
21
|
+
.option('interactive', {
|
|
22
|
+
alias: 'i',
|
|
23
|
+
describe: 'Runs in interactive mode to ask for query parameters one by one'
|
|
24
|
+
})
|
|
25
|
+
.option('output', {
|
|
26
|
+
choices: ['json'],
|
|
27
|
+
describe: 'Outputs a single JSON response for automation-friendly parsing'
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
handler: async (argv) => {
|
|
31
|
+
const output = createQueryOutput(argv);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
output.verboseLog(`Running query ${argv.query}`);
|
|
35
|
+
await handleQuery(argv, output);
|
|
36
|
+
output.finish();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
output.fail(err);
|
|
39
|
+
output.finish();
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function handleQuery(argv, output) {
|
|
47
|
+
let env = await setupEnv(argv);
|
|
48
|
+
let user = await setupUser(argv, env);
|
|
49
|
+
if (argv.list) {
|
|
50
|
+
const properties = await getProperties(env, user, argv.query);
|
|
51
|
+
output.addData(properties);
|
|
52
|
+
output.log(properties);
|
|
53
|
+
} else {
|
|
54
|
+
let response = await runQuery(env, user, argv.query, await getQueryParams(argv));
|
|
55
|
+
output.addData(response);
|
|
56
|
+
output.log(response);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function getProperties(env, user, query) {
|
|
61
|
+
let res = await fetch(`${env.protocol}://${env.host}/Admin/Api/QueryByName?name=${query}`, {
|
|
62
|
+
method: 'GET',
|
|
63
|
+
headers: {
|
|
64
|
+
'Authorization': `Bearer ${user.apiKey}`
|
|
65
|
+
},
|
|
66
|
+
agent: getAgent(env.protocol)
|
|
67
|
+
})
|
|
68
|
+
if (res.ok) {
|
|
69
|
+
let body = await res.json()
|
|
70
|
+
if (body.model.properties.groups === undefined) {
|
|
71
|
+
throw createCommandError('Unable to fetch query parameters.', res.status, body);
|
|
72
|
+
}
|
|
73
|
+
return body.model.properties.groups.filter(g => g.name === 'Properties')[0].fields.map(field => `${field.name} (${field.typeName})`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw createCommandError('Unable to fetch query parameters.', res.status, await parseJsonSafe(res));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function getQueryParams(argv) {
|
|
80
|
+
let params = {}
|
|
81
|
+
if (argv.interactive) {
|
|
82
|
+
let env = await setupEnv(argv);
|
|
83
|
+
let user = await setupUser(argv, env);
|
|
84
|
+
let properties = await getProperties(env, user, argv.query);
|
|
85
|
+
console.log('The following properties will be requested:')
|
|
86
|
+
console.log(properties)
|
|
87
|
+
for (const p of properties) {
|
|
88
|
+
const value = await input({ message: p });
|
|
89
|
+
if (value) {
|
|
90
|
+
const fieldName = p.split(' (')[0];
|
|
91
|
+
params[fieldName] = value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
Object.keys(argv).filter(k => !exclude.includes(k)).forEach(k => params[k] = argv[k])
|
|
96
|
+
}
|
|
97
|
+
return params
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function runQuery(env, user, query, params) {
|
|
101
|
+
let res = await fetch(`${env.protocol}://${env.host}/Admin/Api/${query}?` + new URLSearchParams(params), {
|
|
102
|
+
method: 'GET',
|
|
103
|
+
headers: {
|
|
104
|
+
'Authorization': `Bearer ${user.apiKey}`
|
|
105
|
+
},
|
|
106
|
+
agent: getAgent(env.protocol)
|
|
107
|
+
})
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
throw createCommandError(`Error when doing request ${res.url}`, res.status, await parseJsonSafe(res));
|
|
110
|
+
}
|
|
111
|
+
return await res.json()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function createQueryOutput(argv) {
|
|
115
|
+
const response = {
|
|
116
|
+
ok: true,
|
|
117
|
+
command: 'query',
|
|
118
|
+
operation: argv.list ? 'list' : 'run',
|
|
119
|
+
status: 200,
|
|
120
|
+
data: [],
|
|
121
|
+
errors: [],
|
|
122
|
+
meta: {
|
|
123
|
+
query: argv.query
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
json: argv.output === 'json',
|
|
129
|
+
response,
|
|
130
|
+
log(value) {
|
|
131
|
+
if (!this.json) {
|
|
132
|
+
console.log(value);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
verboseLog(...args) {
|
|
136
|
+
if (argv.verbose && !this.json) {
|
|
137
|
+
console.info(...args);
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
addData(entry) {
|
|
141
|
+
response.data.push(entry);
|
|
142
|
+
},
|
|
143
|
+
fail(err) {
|
|
144
|
+
response.ok = false;
|
|
145
|
+
response.status = err?.status || 1;
|
|
146
|
+
response.errors.push({
|
|
147
|
+
message: err?.message || 'Unknown query command error.',
|
|
148
|
+
details: err?.details ?? null
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
finish() {
|
|
152
|
+
if (this.json) {
|
|
153
|
+
console.log(JSON.stringify(response, null, 2));
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function createCommandError(message, status, details = null) {
|
|
160
|
+
const error = new Error(message);
|
|
161
|
+
error.status = status;
|
|
162
|
+
error.details = details;
|
|
163
|
+
return error;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function parseJsonSafe(res) {
|
|
167
|
+
try {
|
|
168
|
+
return await res.json();
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
package/bin/commands/swift.js
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Agent } from 'https';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import fetch from 'node-fetch';
|
|
5
|
-
|
|
6
|
-
const agent = new Agent({
|
|
7
|
-
rejectUnauthorized: false
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
export function swiftCommand() {
|
|
11
|
-
return {
|
|
12
|
-
command: 'swift [outPath]',
|
|
13
|
-
describe: 'Downloads latest swift version to outPath',
|
|
14
|
-
builder: (yargs) => {
|
|
15
|
-
return yargs
|
|
16
|
-
.positional('outPath', {
|
|
17
|
-
default: '.',
|
|
18
|
-
describe: 'Location for the swift solution'
|
|
19
|
-
})
|
|
20
|
-
.option('tag', {
|
|
21
|
-
alias: 't',
|
|
22
|
-
describe: 'The version tag or branch to clone'
|
|
23
|
-
})
|
|
24
|
-
.option('list', {
|
|
25
|
-
alias: 'l',
|
|
26
|
-
describe: 'Lists all release versions'
|
|
27
|
-
})
|
|
28
|
-
.option('nightly', {
|
|
29
|
-
alias: 'n',
|
|
30
|
-
describe: 'Will pull #HEAD, as default is latest release'
|
|
31
|
-
})
|
|
32
|
-
.option('force', {})
|
|
33
|
-
},
|
|
34
|
-
handler: (argv) => {
|
|
35
|
-
if (argv.verbose) console.info(`Downloading latest swift to :${argv.outPath}`)
|
|
36
|
-
handleSwift(argv)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function handleSwift(argv) {
|
|
42
|
-
if (argv.list) {
|
|
43
|
-
console.log(await getVersions(false))
|
|
44
|
-
} else {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (argv.verbose) console.info(`Executing
|
|
52
|
-
|
|
53
|
-
if (error) {
|
|
54
|
-
console.log(`error: ${error.message}`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (stderr) {
|
|
58
|
-
console.log(stderr);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
console.log(stdout);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async function getVersions(latest) {
|
|
67
|
-
let res = await fetch(`https://api.github.com/repos/dynamicweb/swift/releases${latest ? '/latest' : ''}`, {
|
|
68
|
-
method: 'GET',
|
|
69
|
-
agent: agent
|
|
70
|
-
});
|
|
71
|
-
if (res.ok) {
|
|
72
|
-
let body = await res.json()
|
|
73
|
-
if (latest) {
|
|
74
|
-
return body.tag_name
|
|
75
|
-
} else {
|
|
76
|
-
return body.map(a => a.tag_name)
|
|
77
|
-
}
|
|
78
|
-
}
|
|
1
|
+
import { execFile } from 'child_process';
|
|
2
|
+
import { Agent } from 'https';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fetch from 'node-fetch';
|
|
5
|
+
|
|
6
|
+
const agent = new Agent({
|
|
7
|
+
rejectUnauthorized: false
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export function swiftCommand() {
|
|
11
|
+
return {
|
|
12
|
+
command: 'swift [outPath]',
|
|
13
|
+
describe: 'Downloads latest swift version to outPath',
|
|
14
|
+
builder: (yargs) => {
|
|
15
|
+
return yargs
|
|
16
|
+
.positional('outPath', {
|
|
17
|
+
default: '.',
|
|
18
|
+
describe: 'Location for the swift solution'
|
|
19
|
+
})
|
|
20
|
+
.option('tag', {
|
|
21
|
+
alias: 't',
|
|
22
|
+
describe: 'The version tag or branch to clone'
|
|
23
|
+
})
|
|
24
|
+
.option('list', {
|
|
25
|
+
alias: 'l',
|
|
26
|
+
describe: 'Lists all release versions'
|
|
27
|
+
})
|
|
28
|
+
.option('nightly', {
|
|
29
|
+
alias: 'n',
|
|
30
|
+
describe: 'Will pull #HEAD, as default is latest release'
|
|
31
|
+
})
|
|
32
|
+
.option('force', {})
|
|
33
|
+
},
|
|
34
|
+
handler: async (argv) => {
|
|
35
|
+
if (argv.verbose) console.info(`Downloading latest swift to :${argv.outPath}`)
|
|
36
|
+
await handleSwift(argv)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function handleSwift(argv) {
|
|
42
|
+
if (argv.list) {
|
|
43
|
+
console.log(await getVersions(false))
|
|
44
|
+
} else {
|
|
45
|
+
const repo = argv.nightly
|
|
46
|
+
? 'dynamicweb/swift'
|
|
47
|
+
: `dynamicweb/swift#${argv.tag ? argv.tag : await getVersions(true)}`;
|
|
48
|
+
const args = ['degit', repo];
|
|
49
|
+
if (argv.force) args.push('--force');
|
|
50
|
+
args.push(path.resolve(argv.outPath));
|
|
51
|
+
if (argv.verbose) console.info(`Executing: npx ${args.join(' ')}`)
|
|
52
|
+
execFile('npx', args, (error, stdout, stderr) => {
|
|
53
|
+
if (error) {
|
|
54
|
+
console.log(`error: ${error.message}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (stderr) {
|
|
58
|
+
console.log(stderr);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(stdout);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function getVersions(latest) {
|
|
67
|
+
let res = await fetch(`https://api.github.com/repos/dynamicweb/swift/releases${latest ? '/latest' : ''}`, {
|
|
68
|
+
method: 'GET',
|
|
69
|
+
agent: agent
|
|
70
|
+
});
|
|
71
|
+
if (res.ok) {
|
|
72
|
+
let body = await res.json()
|
|
73
|
+
if (latest) {
|
|
74
|
+
return body.tag_name
|
|
75
|
+
} else {
|
|
76
|
+
return body.map(a => a.tag_name)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
79
|
}
|
package/bin/downloader.js
CHANGED
|
@@ -1,67 +1,70 @@
|
|
|
1
|
-
import { Response } from 'node-fetch';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Extracts the file name from the HTTP response.
|
|
6
|
-
* @param {Response} res - The HTTP response object.
|
|
7
|
-
* @returns {string} The extracted file name.
|
|
8
|
-
*/
|
|
9
|
-
export function getFileNameFromResponse(res, dirPath) {
|
|
10
|
-
const header = res.headers.get('content-disposition');
|
|
11
|
-
const parts = header?.split(';');
|
|
12
|
-
|
|
13
|
-
if (!parts) {
|
|
14
|
-
const msg = `No files found in directory '${dirPath}', if you want to download all folders recursively include the -r flag`;
|
|
15
|
-
throw new Error(msg);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return parts[1].split('=')[1].
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Attempts to extract the file name from an HTTP response.
|
|
23
|
-
* If extraction fails, logs the error message to the console.
|
|
24
|
-
*
|
|
25
|
-
* @param {Object} res - The HTTP response object to extract the file name from.
|
|
26
|
-
* @param {string} dirPath - The directory path to use for file name resolution.
|
|
27
|
-
* @
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
* @param {
|
|
44
|
-
* @
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
res.body.on("
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
receivedBytes
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
1
|
+
import { Response } from 'node-fetch';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extracts the file name from the HTTP response.
|
|
6
|
+
* @param {Response} res - The HTTP response object.
|
|
7
|
+
* @returns {string} The extracted file name.
|
|
8
|
+
*/
|
|
9
|
+
export function getFileNameFromResponse(res, dirPath) {
|
|
10
|
+
const header = res.headers.get('content-disposition');
|
|
11
|
+
const parts = header?.split(';');
|
|
12
|
+
|
|
13
|
+
if (!parts) {
|
|
14
|
+
const msg = `No files found in directory '${dirPath}', if you want to download all folders recursively include the -r flag`;
|
|
15
|
+
throw new Error(msg);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return parts[1].split('=')[1].replaceAll('+', ' ');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Attempts to extract the file name from an HTTP response.
|
|
23
|
+
* If extraction fails, logs the error message to the console.
|
|
24
|
+
*
|
|
25
|
+
* @param {Object} res - The HTTP response object to extract the file name from.
|
|
26
|
+
* @param {string} dirPath - The directory path to use for file name resolution.
|
|
27
|
+
* @param {boolean} verbose - Whether to log missing download information.
|
|
28
|
+
* @returns {string|null} The extracted file name, or null if extraction fails.
|
|
29
|
+
*/
|
|
30
|
+
export function tryGetFileNameFromResponse(res, dirPath, verbose = false) {
|
|
31
|
+
try {
|
|
32
|
+
return getFileNameFromResponse(res, dirPath);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (verbose) {
|
|
35
|
+
console.log(err.message);
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Downloads a file with progress reporting.
|
|
43
|
+
* @param {Response} res - The response from which to read the stream data.
|
|
44
|
+
* @param {string} filePath - The path to write the file to.
|
|
45
|
+
* @param {Object} options - Options for the download.
|
|
46
|
+
* @param {(receivedBytes: number, elapsedMillis: number, isFirstChunk: boolean) => void} options.onData - Callback invoked with each data chunk.
|
|
47
|
+
* @returns {Promise<void>} Resolves when the download is complete.
|
|
48
|
+
*/
|
|
49
|
+
export function downloadWithProgress(res, filePath, options) {
|
|
50
|
+
const fileStream = fs.createWriteStream(filePath);
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
let receivedBytes = 0;
|
|
53
|
+
let startTime = Date.now();
|
|
54
|
+
|
|
55
|
+
res.body.pipe(fileStream);
|
|
56
|
+
res.body.on("error", reject);
|
|
57
|
+
fileStream.on("finish", resolve);
|
|
58
|
+
|
|
59
|
+
res.body.on("data", chunk => {
|
|
60
|
+
const isFirstChunk = receivedBytes === 0;
|
|
61
|
+
const elapsed = Date.now() - startTime;
|
|
62
|
+
|
|
63
|
+
receivedBytes += chunk.length;
|
|
64
|
+
|
|
65
|
+
if (options?.onData) {
|
|
66
|
+
options.onData(receivedBytes, elapsed, isFirstChunk);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
package/bin/extractor.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import extract from 'extract-zip';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Extracts files from a zip archive with progress reporting.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} filePath - The path to the zip file to extract.
|
|
7
|
-
* @param {string} destinationPath - The directory where files will be extracted.
|
|
8
|
-
* @param {Object} [options] - Optional settings.
|
|
9
|
-
* @param {(processedEntries: number, totalEntries: number, percent: string) => void} [options.onEntry] - Callback invoked on each entry extracted.
|
|
10
|
-
* Receives the number of processed files, total entry count, and percent complete as arguments.
|
|
11
|
-
* @returns {Promise<void>} A promise that resolves when extraction is complete.
|
|
12
|
-
*/
|
|
13
|
-
export function extractWithProgress(filePath, destinationPath, options) {
|
|
14
|
-
let processedEntries = 0;
|
|
15
|
-
|
|
16
|
-
return extract(filePath, {
|
|
17
|
-
dir: destinationPath,
|
|
18
|
-
onEntry: (_, zipFile) => {
|
|
19
|
-
processedEntries++;
|
|
20
|
-
|
|
21
|
-
const percent = Math.floor((processedEntries / zipFile.entryCount) * 100).toFixed(0);
|
|
22
|
-
|
|
23
|
-
if (options?.onEntry) {
|
|
24
|
-
options.onEntry(processedEntries, zipFile.entryCount, percent);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}, function (err) {});
|
|
28
|
-
}
|
|
1
|
+
import extract from 'extract-zip';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts files from a zip archive with progress reporting.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} filePath - The path to the zip file to extract.
|
|
7
|
+
* @param {string} destinationPath - The directory where files will be extracted.
|
|
8
|
+
* @param {Object} [options] - Optional settings.
|
|
9
|
+
* @param {(processedEntries: number, totalEntries: number, percent: string) => void} [options.onEntry] - Callback invoked on each entry extracted.
|
|
10
|
+
* Receives the number of processed files, total entry count, and percent complete as arguments.
|
|
11
|
+
* @returns {Promise<void>} A promise that resolves when extraction is complete.
|
|
12
|
+
*/
|
|
13
|
+
export function extractWithProgress(filePath, destinationPath, options) {
|
|
14
|
+
let processedEntries = 0;
|
|
15
|
+
|
|
16
|
+
return extract(filePath, {
|
|
17
|
+
dir: destinationPath,
|
|
18
|
+
onEntry: (_, zipFile) => {
|
|
19
|
+
processedEntries++;
|
|
20
|
+
|
|
21
|
+
const percent = Math.floor((processedEntries / zipFile.entryCount) * 100).toFixed(0);
|
|
22
|
+
|
|
23
|
+
if (options?.onEntry) {
|
|
24
|
+
options.onEntry(processedEntries, zipFile.entryCount, percent);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}, function (err) {});
|
|
28
|
+
}
|