@e-mc/file-manager 0.6.0 → 0.7.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.
- package/index.js +268 -14
- package/package.json +10 -10
package/index.js
CHANGED
|
@@ -126,6 +126,76 @@ function resetAssets() {
|
|
|
126
126
|
this.reset();
|
|
127
127
|
FileManager.sanitizeAssets(this.assets);
|
|
128
128
|
}
|
|
129
|
+
function recurseDir(output, subDirs, options) {
|
|
130
|
+
const { ignore, sortBy, recursive } = options;
|
|
131
|
+
const items = fs.readdirSync(path.join(...subDirs), { withFileTypes: true })
|
|
132
|
+
.filter(item => item.name)
|
|
133
|
+
.sort((a, b) => {
|
|
134
|
+
if (sortBy > 0) {
|
|
135
|
+
if (a.isDirectory() && !b.isDirectory()) {
|
|
136
|
+
return sortBy & 1 /* READDIR_SORT.DIRECTORY */ ? -1 : 1;
|
|
137
|
+
}
|
|
138
|
+
if (!a.isDirectory() && b.isDirectory()) {
|
|
139
|
+
return sortBy & 1 /* READDIR_SORT.DIRECTORY */ ? 1 : -1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return (a.name < b.name ? -1 : 1) * (2 /* READDIR_SORT.DESCENDING */ & 2 ? -1 : 1);
|
|
143
|
+
});
|
|
144
|
+
for (const item of items) {
|
|
145
|
+
if (!item.isDirectory()) {
|
|
146
|
+
const pathname = path.join(item.path, item.name);
|
|
147
|
+
if (!ignore.includes(pathname)) {
|
|
148
|
+
output.push(pathname);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (recursive) {
|
|
152
|
+
recurseDir(output, subDirs.concat(item.name), options);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return output;
|
|
156
|
+
}
|
|
157
|
+
function checkHash(data, fromBuffer, options) {
|
|
158
|
+
if (options) {
|
|
159
|
+
let algorithm, digest, value;
|
|
160
|
+
if ((0, types_1.isObject)(options)) {
|
|
161
|
+
({ algorithm, digest, value } = options);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
value = options;
|
|
165
|
+
}
|
|
166
|
+
if ((0, types_1.isString)(value)) {
|
|
167
|
+
if (!fromBuffer) {
|
|
168
|
+
try {
|
|
169
|
+
data = fs.readFileSync(data);
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return value === core_1.Host.asHash(data, { algorithm, digest });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function filterPaths(values, include, exclude) {
|
|
181
|
+
if (include) {
|
|
182
|
+
if ((0, types_1.isString)(include)) {
|
|
183
|
+
include = [include];
|
|
184
|
+
}
|
|
185
|
+
if ((0, types_1.isArray)(include)) {
|
|
186
|
+
return values.filter(value => core_1.Permission.match(value, include));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (exclude) {
|
|
190
|
+
if ((0, types_1.isString)(exclude)) {
|
|
191
|
+
exclude = [exclude];
|
|
192
|
+
}
|
|
193
|
+
if ((0, types_1.isArray)(exclude)) {
|
|
194
|
+
return values.filter(value => !core_1.Permission.match(value, exclude));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return values;
|
|
198
|
+
}
|
|
129
199
|
function abortedHost() {
|
|
130
200
|
if (this.finalizeState === 6 /* FINALIZE_STATE.ABORTED */) {
|
|
131
201
|
this.restarting = false;
|
|
@@ -195,6 +265,8 @@ function collectErrors() {
|
|
|
195
265
|
function rejectModule(err, type, hint) {
|
|
196
266
|
this.writeFail(["Handled rejection" /* ERR_MESSAGE.HANDLED_REJECTION */, this.moduleName + (hint ? ': ' + hint : '')], err, type);
|
|
197
267
|
}
|
|
268
|
+
const checksumFile = (algorithm) => "checksum" /* HASH_OUTPUT.FILENAME */ + '.' + ((0, types_1.isString)(algorithm) ? algorithm.toLowerCase() : "sha256" /* HASH_OUTPUT.ALGORITHM */);
|
|
269
|
+
const checksumError = (algorithm) => new Error("Invalid parameters" /* ERR_MESSAGE.PARAMETERS */ + ` (${algorithm || "sha256" /* HASH_OUTPUT.ALGORITHM */})`);
|
|
198
270
|
const isFunction = (value) => typeof value === 'function';
|
|
199
271
|
const ignoreAsset = (item, exists) => item.invalid || (0, types_1.hasBit)(item.flags, 1 /* ASSET_FLAG.IGNORE */ | (!exists ? 128 /* ASSET_FLAG.EXISTS */ : 0));
|
|
200
272
|
class HttpDiskCache {
|
|
@@ -557,6 +629,146 @@ class FileManager extends core_1.Host {
|
|
|
557
629
|
});
|
|
558
630
|
return assets;
|
|
559
631
|
}
|
|
632
|
+
static async writeChecksum(root, to, options) {
|
|
633
|
+
if ((0, types_1.isObject)(to)) {
|
|
634
|
+
options = to;
|
|
635
|
+
to = undefined;
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
options || (options = {});
|
|
639
|
+
}
|
|
640
|
+
const { algorithm, digest, sortBy = 0, recursive = false, ignore = [], include, exclude, verbose = false, joinRoot } = options;
|
|
641
|
+
to || (to = checksumFile(algorithm));
|
|
642
|
+
let result = [];
|
|
643
|
+
try {
|
|
644
|
+
const filename = path.basename(to);
|
|
645
|
+
to = joinRoot ? path.join(root, to) : path.resolve(to);
|
|
646
|
+
recurseDir(result, [root], { ignore: [...ignore, to], sortBy, recursive });
|
|
647
|
+
const output = [];
|
|
648
|
+
for (const pathname of result = filterPaths(result, include, exclude)) {
|
|
649
|
+
if (recursive === 1 && path.basename(pathname) === filename) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
const current = await this.readHash(pathname, { algorithm, digest }) + ' ' + (path.sep === '\\' ? pathname.replace(/\\/g, '/') : pathname).substring(root.length).replace(/^\//, '');
|
|
653
|
+
if (verbose) {
|
|
654
|
+
process.stdout.write(current + '\n');
|
|
655
|
+
}
|
|
656
|
+
output.push(current);
|
|
657
|
+
}
|
|
658
|
+
if (output.length) {
|
|
659
|
+
fs.writeFileSync(to, output.join('\n'), 'utf-8');
|
|
660
|
+
options.outPath = to;
|
|
661
|
+
}
|
|
662
|
+
else {
|
|
663
|
+
if (this.isPath(to)) {
|
|
664
|
+
fs.unlinkSync(to);
|
|
665
|
+
}
|
|
666
|
+
if (options.throwsEmpty) {
|
|
667
|
+
throw checksumError(algorithm);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
catch (err) {
|
|
672
|
+
if (options.throwsEmpty) {
|
|
673
|
+
throw err;
|
|
674
|
+
}
|
|
675
|
+
this.writeFail(["Unable to read directory" /* ERR_MESSAGE.READ_DIRECTORY */, root], err, 32 /* LOG_TYPE.FILE */);
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
return result;
|
|
679
|
+
}
|
|
680
|
+
static async verifyChecksum(root, from, options) {
|
|
681
|
+
if ((0, types_1.isObject)(from)) {
|
|
682
|
+
options = from;
|
|
683
|
+
from = undefined;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
options || (options = {});
|
|
687
|
+
}
|
|
688
|
+
const { algorithm, digest, sortBy = 0, recursive = false, ignore = [], include, exclude, verbose = true, joinRoot } = options;
|
|
689
|
+
const parent = recursive === 1 && typeof verbose !== 'number';
|
|
690
|
+
from || (from = checksumFile(algorithm));
|
|
691
|
+
let fail = [], missing = [];
|
|
692
|
+
try {
|
|
693
|
+
const filename = path.basename(from);
|
|
694
|
+
const files = [];
|
|
695
|
+
from = joinRoot ? path.join(root, filename) : path.resolve(from);
|
|
696
|
+
recurseDir(files, [root], { ignore: [...ignore, from], sortBy, recursive });
|
|
697
|
+
const items = fs.readFileSync(from, 'utf-8').split('\n').map(item => {
|
|
698
|
+
const index = item.indexOf(' ');
|
|
699
|
+
return [item.substring(0, index), path.join(root, item.substring(index + 1))];
|
|
700
|
+
});
|
|
701
|
+
const checked = include || exclude ? filterPaths(items.map(item => item[1]), include, exclude) : null;
|
|
702
|
+
let valid;
|
|
703
|
+
for (const [previous, pathname] of items) {
|
|
704
|
+
if (checked !== null && !checked.includes(pathname) || recursive === 1 && path.basename(pathname) === filename) {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
if (files.includes(pathname)) {
|
|
708
|
+
const hash = await this.readHash(pathname, { algorithm, digest });
|
|
709
|
+
if (hash !== previous) {
|
|
710
|
+
fail.push(pathname);
|
|
711
|
+
}
|
|
712
|
+
else if (verbose) {
|
|
713
|
+
process.stdout.write("+" /* SUMDIR_STATUS.PASS */ + ' ' + pathname + '\n');
|
|
714
|
+
}
|
|
715
|
+
valid = true;
|
|
716
|
+
}
|
|
717
|
+
else if (!ignore.includes(pathname)) {
|
|
718
|
+
missing.push(pathname);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (parent) {
|
|
722
|
+
const nested = files.filter(value => path.basename(value) === filename);
|
|
723
|
+
if (nested.length) {
|
|
724
|
+
const current = items.map(item => item[1]);
|
|
725
|
+
const tasks = nested.map(pathname => this.verifyChecksum(path.dirname(pathname), filename, { ...options, ignore: current, joinRoot: true, verbose: (verbose ? 1 : 0) }));
|
|
726
|
+
await Promise.all(tasks).then(group => {
|
|
727
|
+
for (const item of group) {
|
|
728
|
+
if (item) {
|
|
729
|
+
const [f, m] = item;
|
|
730
|
+
if (f.length) {
|
|
731
|
+
fail.push(...f);
|
|
732
|
+
}
|
|
733
|
+
if (m.length) {
|
|
734
|
+
missing.push(...m);
|
|
735
|
+
}
|
|
736
|
+
valid = true;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (valid) {
|
|
743
|
+
options.outPath = from;
|
|
744
|
+
}
|
|
745
|
+
else if (options.throwsEmpty) {
|
|
746
|
+
throw checksumError(algorithm);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
catch (err) {
|
|
750
|
+
if (options.throwsEmpty) {
|
|
751
|
+
throw err;
|
|
752
|
+
}
|
|
753
|
+
this.writeFail(["Unable to read directory" /* ERR_MESSAGE.READ_DIRECTORY */, root], err, 32 /* LOG_TYPE.FILE */);
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
if (parent) {
|
|
757
|
+
if (fail.length) {
|
|
758
|
+
fail = Array.from(new Set(fail));
|
|
759
|
+
}
|
|
760
|
+
if (missing.length) {
|
|
761
|
+
missing = Array.from(new Set(missing));
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (verbose === true) {
|
|
765
|
+
const max = Math.max(...fail.concat(missing).map(item => item.length));
|
|
766
|
+
const writeLog = (items, symbol) => items.forEach((value, index) => process.stdout.write(`${symbol} ${value.padEnd(max)} (${(index + 1).toString()})\n`));
|
|
767
|
+
writeLog(fail, "-" /* SUMDIR_STATUS.FAIL */);
|
|
768
|
+
writeLog(missing, "?" /* SUMDIR_STATUS.MISSING */);
|
|
769
|
+
}
|
|
770
|
+
return [fail, missing];
|
|
771
|
+
}
|
|
560
772
|
static createFileThread(host, file) {
|
|
561
773
|
return new FileThread(host, file, 0);
|
|
562
774
|
}
|
|
@@ -2582,6 +2794,10 @@ class FileManager extends core_1.Host {
|
|
|
2582
2794
|
return true;
|
|
2583
2795
|
};
|
|
2584
2796
|
const fileReceived = (item, localUri, err, fetched, binary) => {
|
|
2797
|
+
if (item.checksum && !err && !checkHash(item.buffer || localUri, !!item.buffer, item.checksum)) {
|
|
2798
|
+
err = (0, types_1.errorValue)("Checksum did not match" /* ERR_MESSAGE.FAILED_CHECKSUM */, item.uri);
|
|
2799
|
+
this.filesToRemove.add(localUri);
|
|
2800
|
+
}
|
|
2585
2801
|
if (err) {
|
|
2586
2802
|
item.invalid = true;
|
|
2587
2803
|
this.completeAsyncTask(err, localUri);
|
|
@@ -2690,7 +2906,7 @@ class FileManager extends core_1.Host {
|
|
|
2690
2906
|
if (!cached) {
|
|
2691
2907
|
setBuffer(item);
|
|
2692
2908
|
}
|
|
2693
|
-
if (core_1.Host.isPath(localUri)) {
|
|
2909
|
+
if (core_1.Host.isPath(localUri) && (!item.checksumOutput || checkHash(localUri, false, item.checksumOutput))) {
|
|
2694
2910
|
item.flags |= 128 /* ASSET_FLAG.EXISTS */;
|
|
2695
2911
|
if (!etag || item.fetchType !== 1 /* FETCH_TYPE.HTTP */) {
|
|
2696
2912
|
if (!(0, types_1.isEmpty)(bundleId) && bundleIndex === 0) {
|
|
@@ -2708,13 +2924,15 @@ class FileManager extends core_1.Host {
|
|
|
2708
2924
|
else if (cached) {
|
|
2709
2925
|
try {
|
|
2710
2926
|
const buffer = cached[1];
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
item.
|
|
2927
|
+
if (!item.checksum || checkHash(buffer, true, item.checksum)) {
|
|
2928
|
+
fs.writeFileSync(localUri, buffer);
|
|
2929
|
+
if (item.willChange && Buffer.isBuffer(buffer)) {
|
|
2930
|
+
item.buffer = buffer;
|
|
2931
|
+
}
|
|
2932
|
+
this.performAsyncTask();
|
|
2933
|
+
fileReceived(item, localUri, null, true);
|
|
2934
|
+
continue;
|
|
2714
2935
|
}
|
|
2715
|
-
this.performAsyncTask();
|
|
2716
|
-
fileReceived(item, localUri, null, true);
|
|
2717
|
-
continue;
|
|
2718
2936
|
}
|
|
2719
2937
|
catch {
|
|
2720
2938
|
}
|
|
@@ -3157,13 +3375,6 @@ class FileManager extends core_1.Host {
|
|
|
3157
3375
|
return cloud ? cloud_1.default.finalize.call(this, cloud).catch(err => rejectModule.call(cloud, err, 64 /* LOG_TYPE.CLOUD */)) : Promise.resolve();
|
|
3158
3376
|
}
|
|
3159
3377
|
async finalizeCleanup() {
|
|
3160
|
-
if (this.Compress) {
|
|
3161
|
-
const tasks = [];
|
|
3162
|
-
this.assets.forEach(item => item.compress && !ignoreAsset(item, true) && tasks.push(this.compressFile(item, false)));
|
|
3163
|
-
if (tasks.length) {
|
|
3164
|
-
await Promise.allSettled(tasks);
|
|
3165
|
-
}
|
|
3166
|
-
}
|
|
3167
3378
|
if (this.emptyDir.size) {
|
|
3168
3379
|
for (const value of Array.from(this.emptyDir).reverse()) {
|
|
3169
3380
|
try {
|
|
@@ -3237,11 +3448,54 @@ class FileManager extends core_1.Host {
|
|
|
3237
3448
|
return Promise.reject((0, types_1.createAbortError)());
|
|
3238
3449
|
}
|
|
3239
3450
|
removeFiles();
|
|
3451
|
+
for (const item of this.assets) {
|
|
3452
|
+
if (item.checksumOutput && !ignoreAsset(item)) {
|
|
3453
|
+
const localUri = item.localUri;
|
|
3454
|
+
if (!checkHash(localUri, false, item.checksumOutput)) {
|
|
3455
|
+
item.invalid = true;
|
|
3456
|
+
filesToRemove.add(localUri);
|
|
3457
|
+
this.writeFail(["Invalid checksum" /* ERR_MESSAGE.CHECKSUM */, path.basename(localUri)], (0, types_1.errorValue)("Checksum did not match" /* ERR_MESSAGE.FAILED_CHECKSUM */, localUri), { type: 32 /* LOG_TYPE.FILE */, startTime });
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
removeFiles();
|
|
3240
3462
|
await this.finalizeCloud().catch(err => rejectModule.call(this, err, 64 /* LOG_TYPE.CLOUD */));
|
|
3241
3463
|
if (this.aborted) {
|
|
3242
3464
|
return Promise.reject((0, types_1.createAbortError)());
|
|
3243
3465
|
}
|
|
3244
3466
|
removeFiles();
|
|
3467
|
+
if (this.Compress) {
|
|
3468
|
+
const tasks = [];
|
|
3469
|
+
this.assets.forEach(item => item.compress && !ignoreAsset(item, true) && tasks.push(this.compressFile(item, false)));
|
|
3470
|
+
if (tasks.length) {
|
|
3471
|
+
await Promise.allSettled(tasks);
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
let checksum = this.config.checksum;
|
|
3475
|
+
if (checksum) {
|
|
3476
|
+
if (typeof checksum === 'boolean' || checksum === 1) {
|
|
3477
|
+
checksum = { recursive: checksum };
|
|
3478
|
+
}
|
|
3479
|
+
else if ((0, types_1.isString)(checksum)) {
|
|
3480
|
+
const items = checksum.split('.');
|
|
3481
|
+
checksum = items.length > 1 ? { algorithm: items[items.length - 1], filename: checksum } : { algorithm: checksum };
|
|
3482
|
+
}
|
|
3483
|
+
if ((0, types_1.isPlainObject)(checksum)) {
|
|
3484
|
+
const baseDirectory = this.baseDirectory;
|
|
3485
|
+
checksum.joinRoot = true;
|
|
3486
|
+
checksum.throwsEmpty = true;
|
|
3487
|
+
const sumTime = LOG_TIMEELAPSED ? process.hrtime() : 0;
|
|
3488
|
+
try {
|
|
3489
|
+
const files = (await FileManager.writeChecksum(baseDirectory, checksum.filename, checksum));
|
|
3490
|
+
if (sumTime) {
|
|
3491
|
+
this.writeTimeElapsed(checksum.algorithm || "sha256" /* HASH_OUTPUT.ALGORITHM */, [baseDirectory, files.length + (files.length === 1 ? ' file' : ' files')], sumTime, { ...core_1.Host.LOG_STYLE_WARN });
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
catch (err) {
|
|
3495
|
+
this.writeFail(["Unable to read directory" /* ERR_MESSAGE.READ_DIRECTORY */, path.basename(baseDirectory)], err, { type: 32 /* LOG_TYPE.FILE */, startTime });
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3245
3499
|
await this.finalizeCleanup().catch(err => rejectModule.call(this, err, 1 /* LOG_TYPE.SYSTEM */));
|
|
3246
3500
|
removeFiles();
|
|
3247
3501
|
if (LOG_TIMEELAPSED) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e-mc/file-manager",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "FileManager constructor for E-mc.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"license": "BSD 3-Clause",
|
|
21
21
|
"homepage": "https://github.com/anpham6/e-mc#readme",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@e-mc/cloud": "0.
|
|
24
|
-
"@e-mc/compress": "0.
|
|
25
|
-
"@e-mc/core": "0.
|
|
26
|
-
"@e-mc/document": "0.
|
|
27
|
-
"@e-mc/image": "0.
|
|
28
|
-
"@e-mc/request": "0.
|
|
29
|
-
"@e-mc/task": "0.
|
|
30
|
-
"@e-mc/types": "0.
|
|
31
|
-
"@e-mc/watch": "0.
|
|
23
|
+
"@e-mc/cloud": "0.7.0",
|
|
24
|
+
"@e-mc/compress": "0.7.0",
|
|
25
|
+
"@e-mc/core": "0.7.0",
|
|
26
|
+
"@e-mc/document": "0.7.0",
|
|
27
|
+
"@e-mc/image": "0.7.0",
|
|
28
|
+
"@e-mc/request": "0.7.0",
|
|
29
|
+
"@e-mc/task": "0.7.0",
|
|
30
|
+
"@e-mc/types": "0.7.0",
|
|
31
|
+
"@e-mc/watch": "0.7.0"
|
|
32
32
|
}
|
|
33
33
|
}
|