@e-mc/file-manager 0.12.9 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +29 -40
  2. package/index.js +356 -346
  3. package/package.json +11 -11
package/index.js CHANGED
@@ -42,7 +42,8 @@ const MEMORY = {
42
42
  EXCLUDE: [],
43
43
  PURGE: 0.25,
44
44
  DISK_MIN: Infinity,
45
- DISK_MAX: 0
45
+ DISK_MAX: 0,
46
+ FILE_COUNT: false
46
47
  };
47
48
  const LOGGER = {
48
49
  TITLE_SEP: '|',
@@ -108,11 +109,6 @@ function withinSizeRange(uri, value, pattern) {
108
109
  }
109
110
  return true;
110
111
  }
111
- function clearAssets(host) {
112
- for (const item of host.assets) {
113
- unsetContent(item);
114
- }
115
- }
116
112
  function unsetContent(item) {
117
113
  if ('buffer' in item) {
118
114
  item.buffer = null;
@@ -135,10 +131,6 @@ function bundleTorrent(host, files, mimeType, encoding) {
135
131
  }
136
132
  return output;
137
133
  }
138
- function resetAssets(host) {
139
- host.reset();
140
- FileManager.sanitizeAssets(host.assets);
141
- }
142
134
  function recurseDir(output, subDirs, options) {
143
135
  const { ignore, sortBy, recursive } = options;
144
136
  const baseDir = path.join(...subDirs);
@@ -168,60 +160,46 @@ function recurseDir(output, subDirs, options) {
168
160
  }
169
161
  return output;
170
162
  }
171
- function checkHash(host, localUri, output, options, data) {
172
- let algorithm, digestEncoding, value;
173
- if ((0, types_1.isObject)(options)) {
174
- ({ algorithm, digestEncoding = options.digest, value } = options);
175
- }
176
- else {
177
- value = options;
178
- }
179
- if ((0, types_1.isString)(value)) {
180
- algorithm ||= "sha256";
181
- if (!data && localUri) {
182
- try {
183
- data = fs.readFileSync(localUri);
184
- }
185
- catch (err) {
186
- host.addLog(host.statusType.WARN, err, { source: `${algorithm}:${value}` });
187
- return output && core_1.Host.isErrorCode(err, 'ENOENT');
188
- }
189
- }
190
- if (data && (value = value.toLowerCase()) === core_1.Host.asHash(data, algorithm, digestEncoding)) {
191
- host.formatMessage(32, algorithm, ["Checksum matched" + (output ? ' (output)' : ''), localUri && path.basename(localUri)], value, { ...core_1.Host.LOG_STYLE_INFO, queue: true });
192
- return true;
193
- }
194
- return false;
195
- }
196
- return true;
197
- }
198
- function validatePaths(rootDir, values, patterns, include, options) {
199
- const items = patterns.map(value => {
163
+ function createMatchNegate(patterns, options) {
164
+ const base = [];
165
+ const baseAbs = [];
166
+ const negate = [];
167
+ const negateAbs = [];
168
+ for (let value of patterns) {
200
169
  let invert = false;
201
- if (!include && value.startsWith('!')) {
170
+ if (value.startsWith('!')) {
202
171
  value = value.substring(1);
203
172
  invert = true;
204
173
  }
205
174
  value = core_1.Permission.toPosix(value);
206
- return [pm(value, getPmOptions(value, options)), path.isAbsolute(value), invert];
207
- });
208
- return values.filter(value => {
209
- for (const [pattern, absolute, invert] of items) {
210
- if (pattern(absolute ? value : value.substring(rootDir.length))) {
211
- return invert ? !include : include;
212
- }
175
+ const match = pm(value, getPmOptions(value, options));
176
+ if (path.isAbsolute(value)) {
177
+ (invert ? negateAbs : baseAbs).push(match);
213
178
  }
214
- return !include;
215
- });
179
+ else {
180
+ (invert ? negate : base).push(match);
181
+ }
182
+ }
183
+ return [[base, negate], [baseAbs, negateAbs]];
216
184
  }
217
- function filterPaths(rootDir, values, include, exclude, options = {}) {
185
+ function isMatch(value, globs) {
186
+ return !globs[1].some(match => match(value)) && globs[0].some(match => match(value));
187
+ }
188
+ function filterPaths(rootDir, values, options = {}) {
189
+ let { include, exclude } = options;
218
190
  rootDir = core_1.Host.normalizePath(rootDir, 2);
219
191
  if (include) {
220
192
  if ((0, types_1.isString)(include)) {
221
193
  include = [include];
222
194
  }
223
195
  if ((0, types_1.isArray)(include)) {
224
- values = validatePaths(rootDir, values, include, true, options);
196
+ const [glob, globAbs] = createMatchNegate(include, options);
197
+ for (let i = 0; i < values.length; ++i) {
198
+ const value = values[i];
199
+ if (!isMatch(value, glob) && !isMatch(value.substring(rootDir.length), globAbs)) {
200
+ values.splice(i--, 1);
201
+ }
202
+ }
225
203
  }
226
204
  }
227
205
  if (exclude) {
@@ -229,7 +207,13 @@ function filterPaths(rootDir, values, include, exclude, options = {}) {
229
207
  exclude = [exclude];
230
208
  }
231
209
  if ((0, types_1.isArray)(exclude)) {
232
- return validatePaths(rootDir, values, exclude, false, options);
210
+ const [glob, globAbs] = createMatchNegate(exclude, options);
211
+ for (let i = 0; i < values.length; ++i) {
212
+ const value = values[i];
213
+ if (isMatch(value, glob) || isMatch(value.substring(rootDir.length), globAbs)) {
214
+ values.splice(i--, 1);
215
+ }
216
+ }
233
217
  }
234
218
  }
235
219
  return values;
@@ -243,30 +227,6 @@ function formatMinutes(elapsed) {
243
227
  }
244
228
  return (0, types_1.formatTime)(elapsed, ':');
245
229
  }
246
- function observeFile(host, instance) {
247
- instance.on('file:delete', (value, options) => host.delete(value, !!options?.emptyDir));
248
- instance.on('file:copy', value => host.add(value));
249
- instance.on('file:move', (value, options) => {
250
- host.add(value);
251
- const src = options?.outSrc;
252
- if (src) {
253
- host.delete(src);
254
- }
255
- });
256
- instance.on('dir:remove', value => {
257
- if (host.removeCwd(value)) {
258
- value = matchPathname(value);
259
- if (!value.endsWith(path.sep)) {
260
- value += path.sep;
261
- }
262
- for (const name of host.files) {
263
- if (matchPathname(name).startsWith(value)) {
264
- host.files.delete(name);
265
- }
266
- }
267
- }
268
- });
269
- }
270
230
  function setHttpCacheLimit(value, type, disk) {
271
231
  if (value === undefined) {
272
232
  return;
@@ -301,22 +261,6 @@ function setHttpCacheLimit(value, type, disk) {
301
261
  }
302
262
  }
303
263
  }
304
- function removeFiles(host, items) {
305
- if (items.size > 0) {
306
- for (const value of items) {
307
- host.deleteFile(value, { emptyDir: true });
308
- }
309
- items.clear();
310
- }
311
- }
312
- function instanceImage(host, mimeMap, mimeType, constructor, ...params) {
313
- const instance = new constructor(...params);
314
- instance.host = host;
315
- instance.init(host.config);
316
- observeFile(host, instance);
317
- mimeMap.set(mimeType, { constructor, instance, params });
318
- return instance;
319
- }
320
264
  function clearQueue(data, attr) {
321
265
  if (attr && data[attr]) {
322
266
  for (const item of data[attr]) {
@@ -341,6 +285,14 @@ function hasEtag(item, cacheable, fallback) {
341
285
  return fallback;
342
286
  }
343
287
  }
288
+ function equalAddress(value, other) {
289
+ if (value === other) {
290
+ return true;
291
+ }
292
+ const a = decodeURIComponent(value);
293
+ const b = decodeURIComponent(other);
294
+ return a === b || a === other || value === b;
295
+ }
344
296
  function processHeaders(item, headers, lastModified, mainEtag) {
345
297
  const contentLength = parseInt(headers['content-length']);
346
298
  if (contentLength > 0) {
@@ -360,49 +312,6 @@ function processHeaders(item, headers, lastModified, mainEtag) {
360
312
  return item.lastModified = headers['last-modified'];
361
313
  }
362
314
  }
363
- function errorPermission(host, item) {
364
- host.writeFail(["Unable to read file", item.uri], (0, types_1.errorValue)("Operation not permitted", item.uri || "Unknown"), 8192);
365
- item.invalid = true;
366
- }
367
- function copyDownload(host, item, queued) {
368
- const uriMap = new Map();
369
- for (const file of queued) {
370
- const destUri = file.localUri;
371
- let items = uriMap.get(destUri);
372
- if (!items) {
373
- const pathname = path.dirname(destUri);
374
- if (!core_1.Host.createDir(pathname)) {
375
- file.invalid = true;
376
- continue;
377
- }
378
- uriMap.set(destUri, items = []);
379
- }
380
- file.etag = item.etag;
381
- file.lastModified = item.lastModified;
382
- items.push(file);
383
- }
384
- const buffer = item.sourceUTF8 || item.buffer;
385
- for (const [destUri, items] of uriMap) {
386
- try {
387
- if (buffer) {
388
- fs.writeFileSync(destUri, buffer);
389
- }
390
- else {
391
- fs.copyFileSync(item.localUri, destUri);
392
- }
393
- for (const queue of items) {
394
- host.performAsyncTask();
395
- void host.transformAsset(new FileThread(host, queue, 1));
396
- }
397
- }
398
- catch (err) {
399
- for (const queue of items) {
400
- queue.invalid = true;
401
- }
402
- host.writeFail([buffer ? "Unable to write buffer" : "Unable to copy file", path.basename(item.localUri)], err, 32);
403
- }
404
- }
405
- }
406
315
  function setBufferTarget(item, buffer) {
407
316
  if (typeof buffer === 'string') {
408
317
  item.sourceUTF8 = buffer;
@@ -411,19 +320,8 @@ function setBufferTarget(item, buffer) {
411
320
  item.buffer = buffer;
412
321
  }
413
322
  }
414
- function writeBufferCache(host, localUri, buffer, encoding) {
415
- fs.writeFileSync(localUri, buffer);
416
- host.addDownload(Buffer.byteLength(buffer, encoding), types_1.DOWNLOAD_TYPE.CACHE);
417
- }
418
323
  async function doVerifyChecksum(root, from, options, nested) {
419
- if ((0, types_1.isObject)(from)) {
420
- options = from;
421
- from = undefined;
422
- }
423
- else {
424
- options ||= {};
425
- }
426
- const { digestEncoding = options.digest, sortBy = 0, recursive = false, ignore = [], include, exclude, verbose = true, joinRoot } = options;
324
+ const { digestEncoding = options.digest, sortBy = 0, recursive = false, ignore = [], verbose = true, joinRoot } = options;
427
325
  const parent = recursive === 1 && typeof verbose !== 'number';
428
326
  let algorithm = options.algorithm;
429
327
  from ||= checksumFile(algorithm);
@@ -456,7 +354,7 @@ async function doVerifyChecksum(root, from, options, nested) {
456
354
  const index = item.indexOf(' ');
457
355
  return [item.substring(0, index), path.join(root, item.substring(index + 1))];
458
356
  });
459
- const checked = include || exclude ? filterPaths(root, items.map(item => item[1]), include, exclude, options) : null;
357
+ const checked = options.include || options.exclude ? filterPaths(root, items.map(item => item[1]), options) : null;
460
358
  let valid = false;
461
359
  for (const [previous, pathname] of items) {
462
360
  if (checked !== null && !checked.includes(pathname) || recursive === 1 && path.basename(pathname) === filename) {
@@ -505,7 +403,7 @@ async function doVerifyChecksum(root, from, options, nested) {
505
403
  options.outPath = from;
506
404
  }
507
405
  else if (options.throwsEmpty) {
508
- throw checksumError(algorithm);
406
+ throw errorChecksum(algorithm);
509
407
  }
510
408
  }
511
409
  catch (err) {
@@ -569,20 +467,20 @@ function setLogMinWidth() {
569
467
  LOGGER.PERCENT_WIDTH = LOGGER.MIN_WIDTH + 8;
570
468
  LOGGER.STAT_WIDTH = LOGGER.PERCENT_WIDTH + 11 + 13;
571
469
  }
572
- const closeResponse = (client) => client?.destroy();
470
+ const downloadStats = () => [[0, 0], [0, 0], [0, 0]];
573
471
  const isCacheable = (item) => item.initialValue?.cacheable !== false;
574
- const getPmOptions = (value, { matchBase = value.startsWith('*') && !value.includes('/'), dot = false } = {}) => ({ matchBase, nocase: core_1.Host.PLATFORM_WIN32, dot });
472
+ const getPmOptions = (value, { matchBase = value.startsWith('*') && !value.includes('/'), dot = false } = {}) => ({ nocase: core_1.Host.PLATFORM_WIN32, matchBase, dot });
575
473
  const isDownloadAll = (type) => type === 4 || type === 0;
576
474
  const hasFiles = (type, uri) => type === 3 || type === 4 || type === 0 && request_1.isRclone(uri);
577
475
  const hasIncremental = (value) => value === "etag" || value === "exists";
578
476
  const matchPathname = (value) => core_1.Host.PLATFORM_WIN32 ? value.toLowerCase() : value;
579
- const equalAddress = (value, other) => value === other || decodeURIComponent(value) === decodeURIComponent(other);
580
477
  const formatPercent = (value) => Math.round(value).toString().padStart(3) + '%';
478
+ const formatLength = (value, length) => `${length} ${value + (length === 1 ? '' : 's')}`;
581
479
  const checkEOF = (value) => /\n$/.test(value) ? value : value + '\n';
582
480
  const checksumFile = (algorithm) => "checksum" + '.' + ((0, types_1.isString)(algorithm) ? algorithm.toLowerCase() : "sha256");
583
- const checksumError = (algorithm) => new Error("Invalid parameters" + ` (${algorithm || "sha256"})`);
584
481
  const ignoreAsset = (item, exists) => item.invalid || (0, types_1.hasBit)(item.flags, 1 | (!exists ? 128 : 0));
585
- const validateChecksum = (host, item, buffer, localUri = item.localUri) => !item.checksum || checkHash(host, localUri, false, item.checksum, buffer);
482
+ const validateChecksum = (host, item, buffer, localUri = item.localUri) => !item.checksum || host.checkHash(item.checksum, buffer, localUri);
483
+ const errorChecksum = (algorithm) => (0, types_1.errorValue)("Invalid parameters", algorithm || "sha256");
586
484
  const errorAbsolute = (hint) => (0, types_1.errorValue)("Path is not absolute", hint);
587
485
  class Scheduler {
588
486
  id = 0;
@@ -835,6 +733,12 @@ class HttpMemoryCache extends HttpDiskCache {
835
733
  toDisk = [MEMORY.DISK_MIN, MEMORY.DISK_MAX];
836
734
  constructor(host, enabled) {
837
735
  super(host, enabled && core_1.Host.enabled("memory.settings.users", host.username));
736
+ if (MEMORY.INCLUDE.length > 0) {
737
+ this.include = MEMORY.INCLUDE;
738
+ }
739
+ if (MEMORY.EXCLUDE.length > 0) {
740
+ this.exclude = MEMORY.EXCLUDE;
741
+ }
838
742
  }
839
743
  has(uri) {
840
744
  return this.expires > 0 && super.has(uri);
@@ -1005,7 +909,7 @@ class ProcessFile {
1005
909
  file.invalid = true;
1006
910
  host.completeAsyncTask(err, localUri);
1007
911
  }
1008
- else if (file.checksum && !checked && !checkHash(host, localUri, false, file.checksum, file.buffer)) {
912
+ else if (file.checksum && !checked && !host.checkHash(file.checksum, file.buffer, localUri)) {
1009
913
  file.invalid = true;
1010
914
  host.filesToRemove.add(localUri);
1011
915
  host.completeAsyncTask();
@@ -1026,6 +930,46 @@ class ProcessFile {
1026
930
  }
1027
931
  }
1028
932
  }
933
+ copyDownload(item, queued) {
934
+ const host = this.host;
935
+ const uriMap = new Map();
936
+ const buffer = item.sourceUTF8 || item.buffer;
937
+ for (const file of queued) {
938
+ const destUri = file.localUri;
939
+ let items = uriMap.get(destUri);
940
+ if (!items) {
941
+ const pathname = path.dirname(destUri);
942
+ if (!core_1.Host.createDir(pathname)) {
943
+ file.invalid = true;
944
+ continue;
945
+ }
946
+ uriMap.set(destUri, items = []);
947
+ }
948
+ file.etag = item.etag;
949
+ file.lastModified = item.lastModified;
950
+ items.push(file);
951
+ }
952
+ for (const [destUri, items] of uriMap) {
953
+ try {
954
+ if (buffer) {
955
+ fs.writeFileSync(destUri, buffer);
956
+ }
957
+ else {
958
+ fs.copyFileSync(item.localUri, destUri);
959
+ }
960
+ for (const queue of items) {
961
+ host.performAsyncTask();
962
+ void host.transformAsset(new FileThread(host, queue, 1));
963
+ }
964
+ }
965
+ catch (err) {
966
+ for (const queue of items) {
967
+ queue.invalid = true;
968
+ }
969
+ host.writeFail([buffer ? "Unable to write buffer" : "Unable to copy file", path.basename(item.localUri)], err, 32);
970
+ }
971
+ }
972
+ }
1029
973
  async finalize(incremental) {
1030
974
  const { host, file, localUri, groupData } = this;
1031
975
  const bundleStart = file.bundleIndex === 0 && !(0, types_1.isEmpty)(file.bundleId) ? host.incremental !== "staging" && host.retryLimit === RETRY_LIMIT ? 2 : 1 : 0;
@@ -1157,7 +1101,7 @@ class ProcessFile {
1157
1101
  else {
1158
1102
  queue.buffer = data;
1159
1103
  }
1160
- copyDownload(host, queue, downloaded);
1104
+ this.copyDownload(queue, downloaded);
1161
1105
  }
1162
1106
  else {
1163
1107
  for (const item of downloaded) {
@@ -1166,17 +1110,13 @@ class ProcessFile {
1166
1110
  }
1167
1111
  }
1168
1112
  if (tempFile && (!pipeTo || core_1.Host.isPath(pipeTo) || !core_1.Host.renameFile(tempFile, pipeTo, false))) {
1169
- queueMicrotask(() => {
1170
- fs.unlink(tempFile, () => { });
1171
- });
1113
+ queueMicrotask(() => fs.unlink(tempFile, () => { }));
1172
1114
  }
1173
1115
  resolve();
1174
1116
  }, (err) => {
1175
1117
  queue.invalid = true;
1176
1118
  if (tempFile) {
1177
- queueMicrotask(() => {
1178
- fs.unlink(tempFile, () => { });
1179
- });
1119
+ queueMicrotask(() => fs.unlink(tempFile, () => { }));
1180
1120
  }
1181
1121
  reject(err);
1182
1122
  }, 1);
@@ -1217,7 +1157,7 @@ class ProcessFile {
1217
1157
  }
1218
1158
  else {
1219
1159
  checkEtag = false;
1220
- errorPermission(host, file);
1160
+ host.handleFilePermission(file);
1221
1161
  }
1222
1162
  }
1223
1163
  }
@@ -1258,7 +1198,7 @@ class ProcessFile {
1258
1198
  const processed = processing[localUri];
1259
1199
  const downloaded = downloading[uri];
1260
1200
  if ((0, types_1.isArray)(downloaded)) {
1261
- copyDownload(host, file, downloaded);
1201
+ this.copyDownload(file, downloaded);
1262
1202
  }
1263
1203
  if (processed) {
1264
1204
  for (const item of processed) {
@@ -1363,12 +1303,14 @@ class FileManager extends core_1.Host {
1363
1303
  if (!super.loadSettings(settings, permission, password)) {
1364
1304
  return false;
1365
1305
  }
1366
- const { process: proc, download, request, error, logger } = settings;
1367
- if (proc?.thread) {
1368
- const limit = (0, util_2.asInt)(proc.thread.sub_limit);
1369
- if (limit > 0) {
1370
- PROCESS_SUB_LIMIT = limit;
1371
- }
1306
+ const { process: proc, memory, download, request, error, logger } = settings;
1307
+ const sub_limit = (0, util_2.asInt)(proc?.thread?.sub_limit);
1308
+ if (sub_limit > 0) {
1309
+ PROCESS_SUB_LIMIT = sub_limit;
1310
+ }
1311
+ const file_count = memory?.settings?.stats?.file_count;
1312
+ if (typeof file_count === 'boolean') {
1313
+ MEMORY.FILE_COUNT = file_count;
1372
1314
  }
1373
1315
  if ((0, types_1.isPlainObject)(request)) {
1374
1316
  let { timeout, disk, buffer, connect } = request;
@@ -1387,7 +1329,7 @@ class FileManager extends core_1.Host {
1387
1329
  request_1.loadSettings({ process: settings.process, request, download }, password);
1388
1330
  }
1389
1331
  if (error) {
1390
- const limit = error.retry_limit !== undefined ? (0, util_2.asInt)(error.retry_limit) : (0, util_2.asInt)(error.recursion_limit);
1332
+ const limit = 'retry_limit' in error ? (0, util_2.asInt)(error.retry_limit) : (0, util_2.asInt)(error.recursion_limit);
1391
1333
  if (limit >= 0 && limit < Infinity) {
1392
1334
  RETRY_LIMIT = limit;
1393
1335
  }
@@ -1506,7 +1448,7 @@ class FileManager extends core_1.Host {
1506
1448
  else {
1507
1449
  options ||= {};
1508
1450
  }
1509
- const { algorithm, digestEncoding = options.digest, sortBy = 0, recursive = false, ignore = [], include, exclude, verbose = false, joinRoot } = options;
1451
+ const { algorithm, digestEncoding = options.digest, sortBy = 0, recursive = false, ignore = [], verbose = false, joinRoot } = options;
1510
1452
  to ||= checksumFile(algorithm);
1511
1453
  let result = [];
1512
1454
  try {
@@ -1514,7 +1456,7 @@ class FileManager extends core_1.Host {
1514
1456
  to = joinRoot ? path.join(root, to) : path.resolve(to);
1515
1457
  recurseDir(result, [root], { ignore: [...ignore, to], sortBy, recursive });
1516
1458
  const output = [];
1517
- for (const pathname of result = filterPaths(root, result, include, exclude, options)) {
1459
+ for (const pathname of result = filterPaths(root, result, options)) {
1518
1460
  if (recursive === 1 && path.basename(pathname) === filename) {
1519
1461
  continue;
1520
1462
  }
@@ -1533,7 +1475,7 @@ class FileManager extends core_1.Host {
1533
1475
  fs.unlinkSync(to);
1534
1476
  }
1535
1477
  if (options.throwsEmpty) {
1536
- throw checksumError(algorithm);
1478
+ throw errorChecksum(algorithm);
1537
1479
  }
1538
1480
  }
1539
1481
  }
@@ -1547,7 +1489,11 @@ class FileManager extends core_1.Host {
1547
1489
  return result;
1548
1490
  }
1549
1491
  static async verifyChecksum(root, from, options) {
1550
- return doVerifyChecksum(root, from, options, false);
1492
+ if ((0, types_1.isObject)(from)) {
1493
+ options = from;
1494
+ from = undefined;
1495
+ }
1496
+ return doVerifyChecksum(root, from, options || {}, false);
1551
1497
  }
1552
1498
  static createFileThread(host, file) {
1553
1499
  return new FileThread(host, file, 0);
@@ -1632,7 +1578,7 @@ class FileManager extends core_1.Host {
1632
1578
  #timerMain = null;
1633
1579
  #retryLimit = RETRY_LIMIT;
1634
1580
  #scheduler = new Scheduler();
1635
- #downloadStats = [[0, 0], [0, 0], [0, 0]];
1581
+ #downloadStats = downloadStats();
1636
1582
  #replacedAssets = null;
1637
1583
  #processTimeout = null;
1638
1584
  #diffSource = null;
@@ -1651,27 +1597,37 @@ class FileManager extends core_1.Host {
1651
1597
  this.permission = permission && core_1.Host.isPermission(permission) ? permission : core_1.Host.getPermissionFromSettings();
1652
1598
  this.sessionId = (++SESSION_ID === SESSION_LIMIT ? SESSION_ID = 1 : SESSION_ID).toString();
1653
1599
  const assets = config.assets || [];
1654
- let targeted;
1600
+ const targeted = [];
1655
1601
  for (let i = 0, length = assets.length; i < length; ++i) {
1656
1602
  const item = assets[i];
1657
- const encoding = item.encoding;
1603
+ let encoding = item.encoding;
1658
1604
  if (item.document) {
1659
1605
  this.documentAssets.push(item);
1660
1606
  }
1661
1607
  if (item.tasks) {
1662
1608
  this.taskAssets.push(item);
1663
1609
  }
1664
- if (encoding && encoding !== 'utf8' && encoding !== 'utf-8') {
1665
- item.encoding = encoding.endsWith('be') ? undefined : (0, types_1.getEncoding)(encoding);
1666
- }
1667
1610
  if ((0, types_1.usingFlag)(item)) {
1668
- (targeted ||= []).push(item);
1611
+ targeted.push(item);
1669
1612
  }
1670
1613
  item.id ||= ++ASSET_ID;
1614
+ if (encoding) {
1615
+ switch (encoding) {
1616
+ case 'utf8':
1617
+ break;
1618
+ case 'utf-8':
1619
+ item.encoding = 'utf8';
1620
+ break;
1621
+ default:
1622
+ encoding = (0, types_1.getEncoding)(encoding);
1623
+ item.encoding = encoding === 'utf8' ? undefined : encoding;
1624
+ break;
1625
+ }
1626
+ }
1671
1627
  }
1672
1628
  this._assets = assets.slice(0);
1673
1629
  this.#incremental = config.incremental === "staging" ? "staging" : "none";
1674
- if (targeted) {
1630
+ if (targeted.length > 0) {
1675
1631
  this.using(...targeted);
1676
1632
  }
1677
1633
  if (Array.isArray(config.dataSource)) {
@@ -1688,12 +1644,6 @@ class FileManager extends core_1.Host {
1688
1644
  }
1689
1645
  this.cacheToDisk = new HttpDiskCache(this, DISK.ENABLED);
1690
1646
  this.cacheToMemory = new HttpMemoryCache(this, MEMORY.ENABLED);
1691
- if (MEMORY.INCLUDE.length > 0) {
1692
- this.cacheToMemory.include = MEMORY.INCLUDE;
1693
- }
1694
- if (MEMORY.EXCLUDE.length > 0) {
1695
- this.cacheToMemory.exclude = MEMORY.EXCLUDE;
1696
- }
1697
1647
  if (typeof config.threads === 'number') {
1698
1648
  this.setTaskLimit(config.threads);
1699
1649
  }
@@ -1741,7 +1691,7 @@ class FileManager extends core_1.Host {
1741
1691
  torrent.push(value);
1742
1692
  }
1743
1693
  }
1744
- if ((type & types_1.FILE_TYPE.SOURCEMAP) || (type & types_1.FILE_TYPE.COMPRESSED)) {
1694
+ if (type & (types_1.FILE_TYPE.COMPRESSED | types_1.FILE_TYPE.SOURCEMAP)) {
1745
1695
  const connected = parent.descendants;
1746
1696
  if (!connected) {
1747
1697
  parent.descendants = [value];
@@ -1752,6 +1702,10 @@ class FileManager extends core_1.Host {
1752
1702
  }
1753
1703
  }
1754
1704
  }
1705
+ else {
1706
+ this.addLog(3, `File ignored: ${value}`, { source: parent?.uri });
1707
+ this.emit('file:permission', value, 'write');
1708
+ }
1755
1709
  return this;
1756
1710
  }
1757
1711
  delete(value, emptyDir = true) {
@@ -1823,7 +1777,7 @@ class FileManager extends core_1.Host {
1823
1777
  this.processAssets(emptyDir);
1824
1778
  }
1825
1779
  else {
1826
- this.writeFail('Unable to restart during finalization', (0, types_1.errorMessage)('Session ID', this.sessionId));
1780
+ this.writeFail(['Unable to restart session', formatLength('asset', this.assets.length)], (0, types_1.errorMessage)('sessionId', this.sessionId));
1827
1781
  }
1828
1782
  }
1829
1783
  else {
@@ -1841,7 +1795,7 @@ class FileManager extends core_1.Host {
1841
1795
  const instance = new cloud_1(args[0], args[1]);
1842
1796
  instance.host = this;
1843
1797
  instance.init(this.config);
1844
- observeFile(this, instance);
1798
+ this.observeFile(instance);
1845
1799
  return this.Cloud = instance;
1846
1800
  }
1847
1801
  }
@@ -1874,11 +1828,11 @@ class FileManager extends core_1.Host {
1874
1828
  this.close();
1875
1829
  this._pendingResult = null;
1876
1830
  }
1877
- clearAssets(this);
1831
+ this.#releaseMemory();
1878
1832
  this.cleared = false;
1879
1833
  this.#delayed = 0;
1880
1834
  this.#scheduler = new Scheduler();
1881
- this.#downloadStats = [[0, 0], [0, 0], [0, 0]];
1835
+ this.#downloadStats = downloadStats();
1882
1836
  this.clearStorage();
1883
1837
  this.finalizeState = 0;
1884
1838
  return true;
@@ -1921,7 +1875,7 @@ class FileManager extends core_1.Host {
1921
1875
  if (this.aborted) {
1922
1876
  return;
1923
1877
  }
1924
- const target = args.shift();
1878
+ let target = args.shift();
1925
1879
  switch (name) {
1926
1880
  case 'document': {
1927
1881
  const Module = this.#tryPackage(target, name, 4);
@@ -1929,7 +1883,7 @@ class FileManager extends core_1.Host {
1929
1883
  const instance = new Module(...args);
1930
1884
  instance.host = this;
1931
1885
  instance.init(this.getDocumentAssets(instance), this.config);
1932
- observeFile(this, instance);
1886
+ this.observeFile(instance);
1933
1887
  this.Document.push({ instance, constructor: Module, params: args });
1934
1888
  return instance;
1935
1889
  }
@@ -1941,7 +1895,7 @@ class FileManager extends core_1.Host {
1941
1895
  const instance = new Module(...args);
1942
1896
  instance.host = this;
1943
1897
  instance.init(this.config);
1944
- observeFile(this, instance);
1898
+ this.observeFile(instance);
1945
1899
  this.Task.push({ instance, constructor: Module, params: args });
1946
1900
  return instance;
1947
1901
  }
@@ -1972,7 +1926,7 @@ class FileManager extends core_1.Host {
1972
1926
  }
1973
1927
  instance.host = this;
1974
1928
  instance.init(this.config);
1975
- observeFile(this, instance);
1929
+ this.observeFile(instance);
1976
1930
  return this.Cloud = instance;
1977
1931
  }
1978
1932
  case 'watch': {
@@ -2062,31 +2016,38 @@ class FileManager extends core_1.Host {
2062
2016
  }
2063
2017
  return this.Watch = instance;
2064
2018
  }
2065
- case 'image': {
2019
+ case 'image':
2066
2020
  if (target instanceof Map) {
2067
- const mimeMap = new Map();
2068
- for (const [mimeType, constructor] of target) {
2069
- if (core_1.Host.constructorOf(constructor, 'image')) {
2070
- instanceImage(this, mimeMap, mimeType, constructor, args[0]);
2071
- }
2072
- }
2073
- if (mimeMap.size > 0) {
2074
- this.Image = mimeMap;
2075
- }
2021
+ args = [args[0]];
2022
+ this.Image = new Map();
2076
2023
  }
2077
2024
  else {
2078
- const Module = this.#tryPackage(target, name, 2048);
2079
- if (Module) {
2080
- return instanceImage(this, this.Image ??= new Map(), 'handler', Module, ...args);
2025
+ const Module = this.#tryPackage(target, 'image', 2048);
2026
+ if (!Module) {
2027
+ return;
2028
+ }
2029
+ target = [['handler', Module]];
2030
+ this.Image ||= new Map();
2031
+ }
2032
+ for (const [mimeType, Module] of target) {
2033
+ if (core_1.Host.constructorOf(Module, 'image')) {
2034
+ const instance = new Module(...args);
2035
+ instance.host = this;
2036
+ instance.init(this.config);
2037
+ this.observeFile(instance);
2038
+ this.Image.set(mimeType, { constructor: Module, params: args, instance });
2081
2039
  }
2082
2040
  }
2041
+ if (this.Image.size > 0) {
2042
+ return this.Image.get('handler')?.instance;
2043
+ }
2044
+ this.Image = null;
2083
2045
  break;
2084
- }
2085
2046
  case 'compress': {
2086
2047
  const instance = new compress_1(target);
2087
2048
  instance.host = this;
2088
2049
  instance.init();
2089
- observeFile(this, instance);
2050
+ this.observeFile(instance);
2090
2051
  return this.Compress = instance;
2091
2052
  }
2092
2053
  }
@@ -2248,7 +2209,7 @@ class FileManager extends core_1.Host {
2248
2209
  if (localUri && !this.assets.find(item => item.localUri === localUri && !item.invalid)) {
2249
2210
  this.filesToRemove.add(localUri);
2250
2211
  }
2251
- (this.#replacedAssets ||= {})[core_1.Host.joinPath(file.pathname, file.filename)] = file;
2212
+ (this.#replacedAssets ||= {})[path.join(file.pathname, file.filename)] = file;
2252
2213
  }
2253
2214
  }
2254
2215
  unsetContent(file);
@@ -2308,7 +2269,7 @@ class FileManager extends core_1.Host {
2308
2269
  }
2309
2270
  performFinalize(override) {
2310
2271
  const state = this.finalizeState;
2311
- if (state === 0 && (this.cleared && this.#delayed <= 0 || override) || (this.aborted || state === 3) && override) {
2272
+ if (state === 0 && (this.cleared && this.#delayed <= 0 || override) || override && (this.aborted || state === 3)) {
2312
2273
  this.clearProcessTimeout();
2313
2274
  this.#delayed = Infinity;
2314
2275
  if (this.aborted) {
@@ -2344,9 +2305,7 @@ class FileManager extends core_1.Host {
2344
2305
  this.emit('end', items, errors, this.collectLog());
2345
2306
  this.done = true;
2346
2307
  })
2347
- .catch((err) => {
2348
- this.abortFinalize(err);
2349
- });
2308
+ .catch((err) => this.abortFinalize(err));
2350
2309
  }
2351
2310
  }
2352
2311
  hasDocument(instance, document) {
@@ -2539,7 +2498,7 @@ class FileManager extends core_1.Host {
2539
2498
  return fs.readFileSync(localUri);
2540
2499
  }
2541
2500
  catch (err) {
2542
- if (!(base64 && core_1.Host.isErrorCode(err, 'ENOENT', 'EACCES'))) {
2501
+ if (!(base64 && (0, types_1.isErrorCode)(err, 'ENOENT', 'EACCES'))) {
2543
2502
  this.writeFail(["Unable to read file", path.basename(localUri)], err, 32);
2544
2503
  base64 = undefined;
2545
2504
  }
@@ -2697,6 +2656,11 @@ class FileManager extends core_1.Host {
2697
2656
  this.filesQueued.add(output ||= localUri);
2698
2657
  return output;
2699
2658
  }
2659
+ handleFilePermission(file) {
2660
+ this.writeFail(["Unable to read file", file.uri && path.basename(file.uri)], (0, types_1.errorValue)("Operation not permitted", file.uri || file.filename), 8192);
2661
+ file.invalid = true;
2662
+ this.emit('asset:permission', file);
2663
+ }
2700
2664
  async findMime(file, rename) {
2701
2665
  const localUri = file.localUri;
2702
2666
  if (!localUri) {
@@ -2813,9 +2777,7 @@ class FileManager extends core_1.Host {
2813
2777
  file.mimeType = mimeType;
2814
2778
  data.mimeType = mimeType;
2815
2779
  }
2816
- const errorAsset = (instance, err, type) => {
2817
- this.rejectModule(err, type, { instance, hint: path.basename(localUri) });
2818
- };
2780
+ const errorAsset = (instance, err, type) => this.rejectModule(err, type, { instance, hint: path.basename(localUri) });
2819
2781
  if (file.tasks) {
2820
2782
  const taskName = [];
2821
2783
  let handler;
@@ -2827,16 +2789,12 @@ class FileManager extends core_1.Host {
2827
2789
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2828
2790
  instance.using(data)
2829
2791
  .finally(() => this.closeThread(instance, data))
2830
- .catch((err) => {
2831
- errorAsset(instance, err);
2832
- });
2792
+ .catch((err) => errorAsset(instance, err));
2833
2793
  }
2834
2794
  }
2835
2795
  else if (!this.aborted) {
2836
2796
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2837
- await instance.using(data).catch((err) => {
2838
- errorAsset(instance, err);
2839
- });
2797
+ await instance.using(data).catch((err) => errorAsset(instance, err));
2840
2798
  }
2841
2799
  taskName.push(moduleName);
2842
2800
  }
@@ -2854,16 +2812,12 @@ class FileManager extends core_1.Host {
2854
2812
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2855
2813
  instance.using(data, command)
2856
2814
  .finally(() => this.closeThread(instance, data))
2857
- .catch((err) => {
2858
- errorAsset(instance, err, 2048);
2859
- });
2815
+ .catch((err) => errorAsset(instance, err, 2048));
2860
2816
  }
2861
2817
  }
2862
2818
  else if (!this.aborted) {
2863
2819
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2864
- await instance.using(data, command).catch((err) => {
2865
- errorAsset(instance, err, 2048);
2866
- });
2820
+ await instance.using(data, command).catch((err) => errorAsset(instance, err, 2048));
2867
2821
  }
2868
2822
  }
2869
2823
  }
@@ -2878,16 +2832,12 @@ class FileManager extends core_1.Host {
2878
2832
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2879
2833
  instance.using(data)
2880
2834
  .finally(() => this.closeThread(instance, data))
2881
- .catch((err) => {
2882
- errorAsset(instance, err);
2883
- });
2835
+ .catch((err) => errorAsset(instance, err));
2884
2836
  }
2885
2837
  }
2886
2838
  else if (!this.aborted) {
2887
2839
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2888
- await instance.using(data).catch((err) => {
2889
- errorAsset(instance, err);
2890
- });
2840
+ await instance.using(data).catch((err) => errorAsset(instance, err));
2891
2841
  }
2892
2842
  }
2893
2843
  }
@@ -3010,11 +2960,53 @@ class FileManager extends core_1.Host {
3010
2960
  getDownload(type = 0) {
3011
2961
  return this.#downloadStats[type] || [NaN, 0];
3012
2962
  }
2963
+ checkHash(checksum, data, uri) {
2964
+ let algorithm, digestEncoding, value;
2965
+ if ((0, types_1.isObject)(checksum)) {
2966
+ ({ algorithm, digestEncoding = checksum.digest, value } = checksum);
2967
+ }
2968
+ else {
2969
+ value = checksum;
2970
+ }
2971
+ let output;
2972
+ if ((0, types_1.isPlainObject)(data)) {
2973
+ ({ data, uri, output } = data);
2974
+ }
2975
+ else if ((0, types_1.isPlainObject)(uri)) {
2976
+ ({ uri, output } = uri);
2977
+ }
2978
+ if ((0, types_1.isString)(value)) {
2979
+ algorithm ||= "sha256";
2980
+ if (!data) {
2981
+ if (!uri) {
2982
+ return false;
2983
+ }
2984
+ try {
2985
+ data = fs.readFileSync(uri);
2986
+ }
2987
+ catch (err) {
2988
+ this.addLog(3, err, { source: `${algorithm}:${value}` });
2989
+ return output ? (0, types_1.isErrorCode)(err, 'ENOENT') : false;
2990
+ }
2991
+ }
2992
+ if ((value = value.toLowerCase()) === core_1.Host.asHash(data, algorithm, digestEncoding)) {
2993
+ if (uri instanceof URL) {
2994
+ uri = (0, node_url_1.fileURLToPath)(uri);
2995
+ }
2996
+ this.formatMessage(32, algorithm, ["Checksum matched" + (output ? ' (output)' : ''), uri && path.basename(uri)], value, { ...core_1.Host.LOG_STYLE_INFO, queue: true });
2997
+ return true;
2998
+ }
2999
+ return false;
3000
+ }
3001
+ return true;
3002
+ }
3013
3003
  async fetchObject(uri, options = {}) {
3014
3004
  if (typeof options === 'string') {
3015
3005
  options = { format: options };
3016
3006
  }
3017
- options.format ||= 'json';
3007
+ else {
3008
+ options.format ||= 'json';
3009
+ }
3018
3010
  return this.Request.get(uri, options).then(data => typeof data === 'object' ? data : null);
3019
3011
  }
3020
3012
  async fetchBuffer(uri, options) {
@@ -3039,19 +3031,12 @@ class FileManager extends core_1.Host {
3039
3031
  if (typeof options === 'string') {
3040
3032
  options = { pathname: options };
3041
3033
  }
3042
- options.pathname ||= this.baseDirectory;
3034
+ else {
3035
+ options.pathname ||= this.baseDirectory;
3036
+ }
3043
3037
  return request_1.isRclone(uri) ? this.Request.rclone(uri, options) : this.Request.aria2c(uri, options);
3044
3038
  }
3045
3039
  async start(emptyDir) {
3046
- const listener = (resolve) => {
3047
- const callback = (files, errors, status) => {
3048
- if (!this.restarting) {
3049
- this.removeListener('end', callback);
3050
- resolve({ files, errors, status, aborted: this.aborted });
3051
- }
3052
- };
3053
- return callback;
3054
- };
3055
3040
  if (this.aborted) {
3056
3041
  this.clearStorage();
3057
3042
  const pending = this._pendingResult;
@@ -3063,7 +3048,7 @@ class FileManager extends core_1.Host {
3063
3048
  return pending;
3064
3049
  }
3065
3050
  return new Promise(resolve => {
3066
- this.on('end', listener(resolve));
3051
+ this.on('end', this.#onStartEnd(resolve));
3067
3052
  queueMicrotask(() => {
3068
3053
  if (!this.#didAbortHost()) {
3069
3054
  this.restarting = false;
@@ -3087,11 +3072,11 @@ class FileManager extends core_1.Host {
3087
3072
  }
3088
3073
  this.finalizeState = 0;
3089
3074
  case 2:
3090
- resetAssets(this);
3075
+ this.resetAssets();
3091
3076
  break;
3092
3077
  }
3093
3078
  return this._pendingResult ||= new Promise((resolve, reject) => {
3094
- this.on('end', listener(resolve));
3079
+ this.on('end', this.#onStartEnd(resolve));
3095
3080
  try {
3096
3081
  this.processAssets(emptyDir);
3097
3082
  }
@@ -3130,7 +3115,7 @@ class FileManager extends core_1.Host {
3130
3115
  case 4:
3131
3116
  return;
3132
3117
  case 2:
3133
- resetAssets(this);
3118
+ this.resetAssets();
3134
3119
  break;
3135
3120
  }
3136
3121
  if (this.queued) {
@@ -3304,7 +3289,7 @@ class FileManager extends core_1.Host {
3304
3289
  setBuffer(item);
3305
3290
  }
3306
3291
  const checksumOutput = item.checksumOutput;
3307
- if (core_1.Host.isPath(localUri) && (!checksumOutput || checkHash(this, localUri, true, checksumOutput))) {
3292
+ if (core_1.Host.isPath(localUri) && (!checksumOutput || this.checkHash(checksumOutput, { uri: localUri, output: true }))) {
3308
3293
  item.flags |= 128;
3309
3294
  if (!etag || item.fetchType !== 1 || checksumOutput) {
3310
3295
  if (bundleIndex === 0 && !(0, types_1.isEmpty)(bundleId)) {
@@ -3322,12 +3307,12 @@ class FileManager extends core_1.Host {
3322
3307
  else if (cached) {
3323
3308
  try {
3324
3309
  const buffer = cached[1];
3325
- if (!item.checksum || checkHash(this, localUri, false, item.checksum, buffer)) {
3310
+ if (!item.checksum || this.checkHash(item.checksum, buffer, localUri)) {
3326
3311
  fs.writeFileSync(localUri, buffer);
3327
3312
  if (item.willChange && Buffer.isBuffer(buffer)) {
3328
3313
  item.buffer = buffer;
3329
3314
  }
3330
- if (!(checksumOutput && (0, types_1.isEmpty)(bundleId) && checkHash(this, localUri, true, checksumOutput, buffer))) {
3315
+ if (!(checksumOutput && (0, types_1.isEmpty)(bundleId) && this.checkHash(checksumOutput, { data: buffer, uri: localUri, output: true }))) {
3331
3316
  this.performAsyncTask();
3332
3317
  const target = new ProcessFile(this, item, localUri, groupData);
3333
3318
  target.received(null, incremental, true, false, true);
@@ -3453,7 +3438,7 @@ class FileManager extends core_1.Host {
3453
3438
  downloading[uri].push(item);
3454
3439
  }
3455
3440
  else if (type === 2 && !this.canRead(uri)) {
3456
- errorPermission(this, item);
3441
+ this.handleFilePermission(item);
3457
3442
  }
3458
3443
  else {
3459
3444
  if (!checkEtag) {
@@ -3470,7 +3455,7 @@ class FileManager extends core_1.Host {
3470
3455
  if (checkEtag) {
3471
3456
  item.flags &= ~128;
3472
3457
  }
3473
- closeResponse(client);
3458
+ client?.destroy();
3474
3459
  const location = request.url.toString();
3475
3460
  request.encoding = encoding;
3476
3461
  request.pipeTo = localUri;
@@ -3536,13 +3521,13 @@ class FileManager extends core_1.Host {
3536
3521
  let buffer;
3537
3522
  if (cached) {
3538
3523
  if (etagDir === cached[5] && (!encoding || encoding === cached[2])) {
3539
- if (item.checksum && !checkHash(this, localUri, false, item.checksum, cached[1])) {
3524
+ if (item.checksum && !this.checkHash(item.checksum, cached[1], localUri)) {
3540
3525
  tempDir = undefined;
3541
3526
  }
3542
3527
  else {
3543
3528
  if (checkEtag) {
3544
3529
  this.completeAsyncTask(null, localUri);
3545
- closeResponse(client);
3530
+ client?.destroy();
3546
3531
  return;
3547
3532
  }
3548
3533
  buffer = cached[1];
@@ -3561,7 +3546,7 @@ class FileManager extends core_1.Host {
3561
3546
  if (!checkEtag) {
3562
3547
  if (cacheBuffer && !buffer) {
3563
3548
  buffer = fs.readFileSync(pipeAs, encoding);
3564
- if (item.checksum && !checkHash(this, localUri, false, item.checksum, buffer)) {
3549
+ if (item.checksum && !this.checkHash(item.checksum, buffer, localUri)) {
3565
3550
  downloadUri(opts, etagDir);
3566
3551
  return;
3567
3552
  }
@@ -3570,20 +3555,22 @@ class FileManager extends core_1.Host {
3570
3555
  }
3571
3556
  }
3572
3557
  if (checkDest(pipeAs, localUri)) {
3573
- if (buffer) {
3574
- writeBufferCache(this, localUri, buffer, encoding);
3575
- }
3576
- else if (item.checksum) {
3577
- buffer = fs.readFileSync(pipeAs, encoding);
3578
- if (!checkHash(this, localUri, false, item.checksum, buffer)) {
3579
- downloadUri(opts, etagDir);
3580
- return;
3558
+ if (!buffer) {
3559
+ if (item.checksum) {
3560
+ buffer = fs.readFileSync(pipeAs, encoding);
3561
+ if (!this.checkHash(item.checksum, buffer, localUri)) {
3562
+ downloadUri(opts, etagDir);
3563
+ return;
3564
+ }
3565
+ }
3566
+ else {
3567
+ fs.copyFileSync(pipeAs, localUri);
3568
+ this.addDownload(pipeAs, types_1.DOWNLOAD_TYPE.CACHE);
3581
3569
  }
3582
- writeBufferCache(this, localUri, buffer, encoding);
3583
3570
  }
3584
- else {
3585
- fs.copyFileSync(pipeAs, localUri);
3586
- this.addDownload(pipeAs, types_1.DOWNLOAD_TYPE.CACHE);
3571
+ if (buffer) {
3572
+ fs.writeFileSync(localUri, buffer);
3573
+ this.addDownload(Buffer.byteLength(buffer, encoding), types_1.DOWNLOAD_TYPE.CACHE);
3587
3574
  }
3588
3575
  }
3589
3576
  if (item.willChange && buffer) {
@@ -3594,7 +3581,7 @@ class FileManager extends core_1.Host {
3594
3581
  else {
3595
3582
  this.completeAsyncTask(null, localUri);
3596
3583
  }
3597
- closeResponse(client);
3584
+ client?.destroy();
3598
3585
  return;
3599
3586
  }
3600
3587
  if (buffer) {
@@ -3605,7 +3592,7 @@ class FileManager extends core_1.Host {
3605
3592
  item.flags &= ~128;
3606
3593
  }
3607
3594
  target.received(null, incremental, true, false, true);
3608
- closeResponse(client);
3595
+ client?.destroy();
3609
3596
  if (pipeAs) {
3610
3597
  fs.writeFile(pipeAs, buffer, () => { });
3611
3598
  }
@@ -3619,7 +3606,7 @@ class FileManager extends core_1.Host {
3619
3606
  downloadUri(opts, etagDir);
3620
3607
  }
3621
3608
  else if (statusCode < 400) {
3622
- closeResponse(client);
3609
+ client?.destroy();
3623
3610
  const location = res.headers.location;
3624
3611
  if (location && ++redirects <= HTTP_CLIENT.redirectLimit) {
3625
3612
  try {
@@ -3754,7 +3741,7 @@ class FileManager extends core_1.Host {
3754
3741
  }
3755
3742
  }
3756
3743
  else if (!this.canRead(uri)) {
3757
- errorPermission(this, item);
3744
+ this.handleFilePermission(item);
3758
3745
  }
3759
3746
  else if (!target.queued(pathname, emptyDir, false)) {
3760
3747
  this.performAsyncTask();
@@ -3809,8 +3796,8 @@ class FileManager extends core_1.Host {
3809
3796
  const barLength = processTask.barLength;
3810
3797
  const barIncrement = 100 / barLength;
3811
3798
  const initial = processTask.logCurrent !== logCurrent;
3812
- let redraw = false, scrollHeight = 0, logDelayed;
3813
- if ((logDelayed = core_1.Host.getLogDelayed()) && logDelayed.length > 0) {
3799
+ let redraw = false, scrollHeight = 0, logDelayed = core_1.Host.getLogDelayed();
3800
+ if (logDelayed.length > 0) {
3814
3801
  logDelayed = logDelayed.filter(item => typeof item[4].progressBar !== 'boolean');
3815
3802
  scrollHeight = Math.min(logDelayed.length, LOGGER.PROGRESS_SCROLLBUFFER);
3816
3803
  }
@@ -3836,7 +3823,7 @@ class FileManager extends core_1.Host {
3836
3823
  PROCESS_STDOUT.moveCursor(0, -(statusHeight + 1 + 1 + (scrollHeight > 0 ? 1 + scrollHeight : 0)));
3837
3824
  }
3838
3825
  processTask.updated = currentTime;
3839
- const summary = new TaskStatus(-1, chalk.bold(`${statusHeight} requests`.padEnd(LOGGER.VALUE_WIDTH)));
3826
+ const summary = new TaskStatus(-1, chalk.bold(formatLength('request', statusHeight).padEnd(LOGGER.VALUE_WIDTH)));
3840
3827
  const spacer = LOGGER.PROGRESS_SPACER;
3841
3828
  let finished = processTask.pending === 0;
3842
3829
  for (let i = 0; i <= statusHeight; ++i) {
@@ -4041,12 +4028,14 @@ class FileManager extends core_1.Host {
4041
4028
  ++scheduler.count;
4042
4029
  target = new Promise((resolve, reject) => {
4043
4030
  const writable = fs.createWriteStream(uri);
4044
- writable.on('finish', () => {
4031
+ writable
4032
+ .on('error', reject)
4033
+ .on('finish', () => {
4045
4034
  resolve(1);
4046
4035
  });
4047
- writable.on('error', reject);
4048
- data.on('error', reject);
4049
- data.pipe(writable);
4036
+ data
4037
+ .on('error', reject)
4038
+ .pipe(writable);
4050
4039
  });
4051
4040
  }
4052
4041
  }
@@ -4120,9 +4109,7 @@ class FileManager extends core_1.Host {
4120
4109
  async finalizeDocument() {
4121
4110
  for (const { instance, constructor } of this.Document) {
4122
4111
  if (constructor.finalize && instance.assets.length > 0) {
4123
- await constructor.finalize.call(this, instance).catch((err) => {
4124
- this.rejectModule(err, 4, { instance });
4125
- });
4112
+ await constructor.finalize.call(this, instance).catch((err) => this.rejectModule(err, 4, { instance }));
4126
4113
  if (this.aborted) {
4127
4114
  return;
4128
4115
  }
@@ -4134,9 +4121,7 @@ class FileManager extends core_1.Host {
4134
4121
  for (const { instance, constructor } of this.Task) {
4135
4122
  const items = assets.filter(item => item.tasks.find(data => data.handler === instance.moduleName));
4136
4123
  if (items.length > 0) {
4137
- await constructor.finalize.call(this, instance, items).catch((err) => {
4138
- this.rejectModule(err, 4, { instance });
4139
- });
4124
+ await constructor.finalize.call(this, instance, items).catch((err) => this.rejectModule(err, 4, { instance }));
4140
4125
  if (this.aborted) {
4141
4126
  return;
4142
4127
  }
@@ -4147,9 +4132,7 @@ class FileManager extends core_1.Host {
4147
4132
  async finalizeCloud() {
4148
4133
  const instance = this.Cloud;
4149
4134
  if (instance) {
4150
- await cloud_1.finalize.call(this, instance).catch((err) => {
4151
- this.rejectModule(err, 64, { instance });
4152
- });
4135
+ await cloud_1.finalize.call(this, instance).catch((err) => this.rejectModule(err, 64, { instance }));
4153
4136
  }
4154
4137
  }
4155
4138
  async finalizeChecksum() {
@@ -4162,7 +4145,7 @@ class FileManager extends core_1.Host {
4162
4145
  }
4163
4146
  else if ((0, types_1.isString)(checksum)) {
4164
4147
  const items = checksum.split('.');
4165
- checksum = items.length > 1 ? { algorithm: items[items.length - 1], filename: checksum } : { algorithm: checksum };
4148
+ checksum = items.length > 1 ? { algorithm: items.at(-1), filename: checksum } : { algorithm: checksum };
4166
4149
  }
4167
4150
  if ((0, types_1.isPlainObject)(checksum)) {
4168
4151
  const baseDirectory = this.baseDirectory;
@@ -4187,9 +4170,7 @@ class FileManager extends core_1.Host {
4187
4170
  }
4188
4171
  for (const { instance, constructor } of this.Document) {
4189
4172
  if (constructor.cleanup && instance.assets.length > 0) {
4190
- await constructor.cleanup.call(this, instance).catch((err) => {
4191
- this.rejectModule(err, 4, { instance });
4192
- });
4173
+ await constructor.cleanup.call(this, instance).catch((err) => this.rejectModule(err, 4, { instance }));
4193
4174
  }
4194
4175
  }
4195
4176
  }
@@ -4201,35 +4182,30 @@ class FileManager extends core_1.Host {
4201
4182
  this.writeTimeElapsed('READY', 'Finalizing assets...', this.startTime, { ...core_1.Host.LOG_STYLE_WARN, showCpu: true });
4202
4183
  }
4203
4184
  const startTime = process.hrtime.bigint();
4204
- const filesToRemove = this.filesToRemove;
4205
4185
  for (const [file, output] of this.filesToCompare) {
4206
4186
  const localUri = file.localUri;
4207
4187
  let minFile = localUri, minSize = (0, util_2.getSize)(minFile);
4208
4188
  for (const other of output) {
4209
4189
  const size = (0, util_2.getSize)(other);
4210
4190
  if (minSize === 0 || size > 0 && size < minSize) {
4211
- filesToRemove.add(minFile);
4191
+ this.filesToRemove.add(minFile);
4212
4192
  minFile = other;
4213
4193
  minSize = size;
4214
4194
  }
4215
4195
  else {
4216
- filesToRemove.add(other);
4196
+ this.filesToRemove.add(other);
4217
4197
  }
4218
4198
  }
4219
4199
  if (minFile !== localUri) {
4220
4200
  this.replace(file, minFile);
4221
4201
  }
4222
4202
  }
4223
- removeFiles(this, filesToRemove);
4224
- await this.finalizeCompress(this.assets.filter(item => item.compress && !ignoreAsset(item))).catch((err) => {
4225
- this.rejectModule(err, 8);
4226
- });
4203
+ this.removeFiles();
4204
+ await this.finalizeCompress(this.assets.filter(item => item.compress && !ignoreAsset(item))).catch((err) => this.rejectModule(err, 8));
4227
4205
  if (this.aborted) {
4228
4206
  return (0, types_1.createAbortError)(true);
4229
4207
  }
4230
- await this.finalizeDocument().catch((err) => {
4231
- this.rejectModule(err, 4);
4232
- });
4208
+ await this.finalizeDocument().catch((err) => this.rejectModule(err, 4));
4233
4209
  if (this.aborted) {
4234
4210
  return (0, types_1.createAbortError)(true);
4235
4211
  }
@@ -4243,32 +4219,27 @@ class FileManager extends core_1.Host {
4243
4219
  }
4244
4220
  }
4245
4221
  }
4246
- removeFiles(this, filesToRemove);
4247
- await this.finalizeTask(this.taskAssets.filter(item => item.tasks?.find(data => !data.preceding) && item.localUri && this.has(item.localUri) && !ignoreAsset(item))).catch((err) => {
4248
- this.rejectModule(err, 4);
4249
- });
4222
+ this.removeFiles();
4223
+ await this.finalizeTask(this.taskAssets.filter(item => item.tasks?.find(data => !data.preceding) && this.has(item.localUri) && !ignoreAsset(item))).catch((err) => this.rejectModule(err, 4));
4250
4224
  if (this.aborted) {
4251
4225
  return (0, types_1.createAbortError)(true);
4252
4226
  }
4253
- removeFiles(this, filesToRemove);
4254
4227
  for (const item of this.assets) {
4255
4228
  if (item.checksumOutput && !ignoreAsset(item)) {
4256
4229
  const localUri = item.localUri;
4257
- if (!filesToRemove.has(localUri) && !checkHash(this, localUri, true, item.checksumOutput)) {
4230
+ if (!this.filesToRemove.has(localUri) && !this.checkHash(item.checksumOutput, { uri: localUri, output: true })) {
4258
4231
  item.invalid = true;
4259
- filesToRemove.add(localUri);
4232
+ this.filesToRemove.add(localUri);
4260
4233
  this.writeFail(["Checksum did not match", path.basename(localUri)], (0, types_1.errorValue)(localUri, "Invalid checksum"), { type: 32, startTime, queue: true });
4261
4234
  }
4262
4235
  }
4263
4236
  }
4264
- removeFiles(this, filesToRemove);
4265
- await this.finalizeCloud().catch((err) => {
4266
- this.rejectModule(err, 64);
4267
- });
4237
+ this.removeFiles();
4238
+ await this.finalizeCloud().catch((err) => this.rejectModule(err, 64));
4268
4239
  if (this.aborted) {
4269
4240
  return (0, types_1.createAbortError)(true);
4270
4241
  }
4271
- removeFiles(this, filesToRemove);
4242
+ this.removeFiles();
4272
4243
  if (this.Compress) {
4273
4244
  const tasks = [];
4274
4245
  for (const item of this.assets) {
@@ -4280,46 +4251,68 @@ class FileManager extends core_1.Host {
4280
4251
  await Promise.allSettled(tasks);
4281
4252
  }
4282
4253
  }
4283
- await this.finalizeChecksum().catch((err) => {
4284
- this.rejectModule(err, 32);
4285
- });
4286
- await this.finalizeCleanup().catch((err) => {
4287
- this.rejectModule(err, 1);
4288
- });
4289
- removeFiles(this, filesToRemove);
4254
+ await this.finalizeChecksum().catch((err) => this.rejectModule(err, 32));
4255
+ await this.finalizeCleanup().catch((err) => this.rejectModule(err, 1));
4256
+ this.removeFiles();
4290
4257
  this.closeMessage(startTime);
4291
4258
  this.Watch?.start(this.assets);
4292
4259
  }
4260
+ removeFiles() {
4261
+ const filesToRemove = this.filesToRemove;
4262
+ if (filesToRemove.size > 0) {
4263
+ for (const value of filesToRemove) {
4264
+ this.deleteFile(value, { emptyDir: true });
4265
+ }
4266
+ filesToRemove.clear();
4267
+ }
4268
+ }
4293
4269
  close() {
4294
4270
  this.Request.close();
4271
+ this.#releaseMemory();
4272
+ }
4273
+ observeFile(instance) {
4274
+ instance.on('file:delete', (value, options) => this.delete(value, !!options?.emptyDir));
4275
+ instance.on('file:copy', value => this.add(value));
4276
+ instance.on('file:move', (value, options) => {
4277
+ this.add(value);
4278
+ const src = options?.outSrc;
4279
+ if (src) {
4280
+ this.delete(src);
4281
+ }
4282
+ });
4283
+ instance.on('dir:remove', value => {
4284
+ if (this.removeCwd(value)) {
4285
+ value = matchPathname(value);
4286
+ if (!value.endsWith(path.sep)) {
4287
+ value += path.sep;
4288
+ }
4289
+ for (const name of this.files) {
4290
+ if (matchPathname(name).startsWith(value)) {
4291
+ this.files.delete(name);
4292
+ }
4293
+ }
4294
+ }
4295
+ });
4295
4296
  }
4296
4297
  startMessage() {
4297
4298
  if (!this.silent) {
4298
- this.formatMessage(128, 'START', [new Date().toLocaleString(), this.assets.length + ' assets'], this.baseDirectory, { ...core_1.Host.LOG_STYLE_SUCCESS });
4299
+ this.formatMessage(128, 'START', [new Date().toLocaleString(), formatLength('asset', this.assets.length)], this.baseDirectory, { ...core_1.Host.LOG_STYLE_SUCCESS });
4299
4300
  }
4300
4301
  }
4301
4302
  closeMessage(startTime) {
4302
4303
  if (LOG_TIMEELAPSED) {
4303
- const [http, disk, cache] = this.#downloadStats;
4304
4304
  const errorCount = this.errorCount;
4305
4305
  const message = [];
4306
4306
  if (errorCount > 0) {
4307
4307
  message.push('ERROR ' + errorCount);
4308
4308
  }
4309
- let [size, count] = http;
4310
- if (count > 0) {
4311
- message.push('HTTP ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4312
- }
4313
- [size, count] = disk;
4314
- if (count > 0) {
4315
- message.push('DISK ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4316
- }
4317
- [size, count] = cache;
4318
- if (count > 0) {
4319
- message.push('CACHE ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4320
- }
4309
+ this.#downloadStats.forEach(([size, count], index) => {
4310
+ if (count > 0) {
4311
+ message.push(`${types_1.DOWNLOAD_TYPE[index]} ` + (count > 1 && MEMORY.FILE_COUNT ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4312
+ }
4313
+ });
4321
4314
  if (MEMORY.SIZE > 0) {
4322
- message.push('BUFFER ' + (MEMORY.CACHE.size > 1 ? MEMORY.CACHE.size + ' / ' : '') + (0, types_1.formatSize)(MEMORY.SIZE));
4315
+ message.push('BUFFER ' + (MEMORY.CACHE.size > 1 && MEMORY.FILE_COUNT ? MEMORY.CACHE.size + ' / ' : '') + (0, types_1.formatSize)(MEMORY.SIZE));
4323
4316
  }
4324
4317
  this.writeTimeElapsed('CLOSE', ['No further modifications', message.length > 0 ? message.join(chalk.blackBright(LOGGER.MESSAGE_SEP)) : ''], startTime, { ...core_1.Host.LOG_STYLE_WARN });
4325
4318
  }
@@ -4346,7 +4339,7 @@ class FileManager extends core_1.Host {
4346
4339
  const oldMax = lastHunk.oldStart + lastHunk.oldLines;
4347
4340
  const newMax = lastHunk.newStart + lastHunk.newLines;
4348
4341
  const posLength = Math.max(Math.max(oldMax, newMax).toString().length, Math.min(oldMax, newMax).toString().length);
4349
- addHeader(pathname, length + ` hunk${length > 1 ? 's' : ''}`);
4342
+ addHeader(pathname, formatLength('hunk', length));
4350
4343
  for (const { lines, oldLines, oldStart, newStart, newLines } of hunks) {
4351
4344
  for (let i = 0, j = oldStart, k = oldLines, l = 0, q = lines.length, added = false; i < q && k > 0; ++i) {
4352
4345
  const line = lines[i].substring(1);
@@ -4410,6 +4403,10 @@ class FileManager extends core_1.Host {
4410
4403
  this._pendingResult = null;
4411
4404
  this.clearStorage();
4412
4405
  }
4406
+ resetAssets() {
4407
+ this.reset();
4408
+ FileManager.sanitizeAssets(this.assets);
4409
+ }
4413
4410
  abortFinalize(err) {
4414
4411
  if (!this.restarting) {
4415
4412
  this.writeFail(["Transaction was not completed", this.baseDirectory], err);
@@ -4460,14 +4457,18 @@ class FileManager extends core_1.Host {
4460
4457
  }
4461
4458
  }
4462
4459
  this.close();
4463
- clearAssets(this);
4464
4460
  });
4465
4461
  }
4466
4462
  else {
4467
- clearAssets(this);
4463
+ this.#releaseMemory();
4468
4464
  }
4469
4465
  return result;
4470
4466
  }
4467
+ #releaseMemory() {
4468
+ for (const item of this.assets) {
4469
+ unsetContent(item);
4470
+ }
4471
+ }
4471
4472
  #nextScheduled(scheduler, pid) {
4472
4473
  if (pid > 0) {
4473
4474
  const found = scheduler.status.find(item => item.id === pid);
@@ -4502,6 +4503,15 @@ class FileManager extends core_1.Host {
4502
4503
  const timeout = this.#processTimeout;
4503
4504
  return timeout && name in timeout ? timeout[name] : PROCESS_TIMEOUT[name];
4504
4505
  }
4506
+ #onStartEnd(resolve) {
4507
+ const callback = (files, errors, status) => {
4508
+ if (!this.restarting) {
4509
+ this.removeListener('end', callback);
4510
+ resolve({ files, errors, status, aborted: this.aborted });
4511
+ }
4512
+ };
4513
+ return callback;
4514
+ }
4505
4515
  set restarting(value) {
4506
4516
  this.finalizeState = (this.#restarting = value) ? 3 : 0;
4507
4517
  }