@e-mc/file-manager 0.12.8 → 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 +363 -356
  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]];
184
+ }
185
+ function isMatch(value, globs) {
186
+ return !globs[1].some(match => match(value)) && globs[0].some(match => match(value));
216
187
  }
217
- function filterPaths(rootDir, values, include, exclude, options = {}) {
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
  }
@@ -2195,12 +2156,9 @@ class FileManager extends core_1.Host {
2195
2156
  }
2196
2157
  removeCwd(value) {
2197
2158
  if (typeof value === 'string') {
2198
- let baseDir = this.baseDirectory + path.sep, leading = value.substring(0, baseDir.length);
2199
- if (core_1.Host.PLATFORM_WIN32) {
2200
- baseDir = baseDir.toLowerCase();
2201
- leading = leading.toLowerCase();
2202
- }
2203
- if (leading === baseDir) {
2159
+ const baseDir = this.baseDirectory + path.sep;
2160
+ const leading = value.substring(0, baseDir.length);
2161
+ if (leading === baseDir || core_1.Host.PLATFORM_WIN32 && leading.toLowerCase() === baseDir.toLowerCase()) {
2204
2162
  return value.substring(baseDir.length);
2205
2163
  }
2206
2164
  }
@@ -2251,7 +2209,7 @@ class FileManager extends core_1.Host {
2251
2209
  if (localUri && !this.assets.find(item => item.localUri === localUri && !item.invalid)) {
2252
2210
  this.filesToRemove.add(localUri);
2253
2211
  }
2254
- (this.#replacedAssets ||= {})[core_1.Host.joinPath(file.pathname, file.filename)] = file;
2212
+ (this.#replacedAssets ||= {})[path.join(file.pathname, file.filename)] = file;
2255
2213
  }
2256
2214
  }
2257
2215
  unsetContent(file);
@@ -2311,7 +2269,7 @@ class FileManager extends core_1.Host {
2311
2269
  }
2312
2270
  performFinalize(override) {
2313
2271
  const state = this.finalizeState;
2314
- 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)) {
2315
2273
  this.clearProcessTimeout();
2316
2274
  this.#delayed = Infinity;
2317
2275
  if (this.aborted) {
@@ -2347,9 +2305,7 @@ class FileManager extends core_1.Host {
2347
2305
  this.emit('end', items, errors, this.collectLog());
2348
2306
  this.done = true;
2349
2307
  })
2350
- .catch((err) => {
2351
- this.abortFinalize(err);
2352
- });
2308
+ .catch((err) => this.abortFinalize(err));
2353
2309
  }
2354
2310
  }
2355
2311
  hasDocument(instance, document) {
@@ -2542,7 +2498,7 @@ class FileManager extends core_1.Host {
2542
2498
  return fs.readFileSync(localUri);
2543
2499
  }
2544
2500
  catch (err) {
2545
- if (!(base64 && core_1.Host.isErrorCode(err, 'ENOENT', 'EACCES'))) {
2501
+ if (!(base64 && (0, types_1.isErrorCode)(err, 'ENOENT', 'EACCES'))) {
2546
2502
  this.writeFail(["Unable to read file", path.basename(localUri)], err, 32);
2547
2503
  base64 = undefined;
2548
2504
  }
@@ -2700,6 +2656,11 @@ class FileManager extends core_1.Host {
2700
2656
  this.filesQueued.add(output ||= localUri);
2701
2657
  return output;
2702
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
+ }
2703
2664
  async findMime(file, rename) {
2704
2665
  const localUri = file.localUri;
2705
2666
  if (!localUri) {
@@ -2816,9 +2777,7 @@ class FileManager extends core_1.Host {
2816
2777
  file.mimeType = mimeType;
2817
2778
  data.mimeType = mimeType;
2818
2779
  }
2819
- const errorAsset = (instance, err, type) => {
2820
- this.rejectModule(err, type, { instance, hint: path.basename(localUri) });
2821
- };
2780
+ const errorAsset = (instance, err, type) => this.rejectModule(err, type, { instance, hint: path.basename(localUri) });
2822
2781
  if (file.tasks) {
2823
2782
  const taskName = [];
2824
2783
  let handler;
@@ -2830,16 +2789,12 @@ class FileManager extends core_1.Host {
2830
2789
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2831
2790
  instance.using(data)
2832
2791
  .finally(() => this.closeThread(instance, data))
2833
- .catch((err) => {
2834
- errorAsset(instance, err);
2835
- });
2792
+ .catch((err) => errorAsset(instance, err));
2836
2793
  }
2837
2794
  }
2838
2795
  else if (!this.aborted) {
2839
2796
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2840
- await instance.using(data).catch((err) => {
2841
- errorAsset(instance, err);
2842
- });
2797
+ await instance.using(data).catch((err) => errorAsset(instance, err));
2843
2798
  }
2844
2799
  taskName.push(moduleName);
2845
2800
  }
@@ -2857,16 +2812,12 @@ class FileManager extends core_1.Host {
2857
2812
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2858
2813
  instance.using(data, command)
2859
2814
  .finally(() => this.closeThread(instance, data))
2860
- .catch((err) => {
2861
- errorAsset(instance, err, 2048);
2862
- });
2815
+ .catch((err) => errorAsset(instance, err, 2048));
2863
2816
  }
2864
2817
  }
2865
2818
  else if (!this.aborted) {
2866
2819
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2867
- await instance.using(data, command).catch((err) => {
2868
- errorAsset(instance, err, 2048);
2869
- });
2820
+ await instance.using(data, command).catch((err) => errorAsset(instance, err, 2048));
2870
2821
  }
2871
2822
  }
2872
2823
  }
@@ -2881,16 +2832,12 @@ class FileManager extends core_1.Host {
2881
2832
  if (this.openThread(instance, data, this.getProcessTimeout(handler))) {
2882
2833
  instance.using(data)
2883
2834
  .finally(() => this.closeThread(instance, data))
2884
- .catch((err) => {
2885
- errorAsset(instance, err);
2886
- });
2835
+ .catch((err) => errorAsset(instance, err));
2887
2836
  }
2888
2837
  }
2889
2838
  else if (!this.aborted) {
2890
2839
  this.addProcessTimeout(instance, file, this.getProcessTimeout(handler));
2891
- await instance.using(data).catch((err) => {
2892
- errorAsset(instance, err);
2893
- });
2840
+ await instance.using(data).catch((err) => errorAsset(instance, err));
2894
2841
  }
2895
2842
  }
2896
2843
  }
@@ -3013,11 +2960,53 @@ class FileManager extends core_1.Host {
3013
2960
  getDownload(type = 0) {
3014
2961
  return this.#downloadStats[type] || [NaN, 0];
3015
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
+ }
3016
3003
  async fetchObject(uri, options = {}) {
3017
3004
  if (typeof options === 'string') {
3018
3005
  options = { format: options };
3019
3006
  }
3020
- options.format ||= 'json';
3007
+ else {
3008
+ options.format ||= 'json';
3009
+ }
3021
3010
  return this.Request.get(uri, options).then(data => typeof data === 'object' ? data : null);
3022
3011
  }
3023
3012
  async fetchBuffer(uri, options) {
@@ -3042,19 +3031,12 @@ class FileManager extends core_1.Host {
3042
3031
  if (typeof options === 'string') {
3043
3032
  options = { pathname: options };
3044
3033
  }
3045
- options.pathname ||= this.baseDirectory;
3034
+ else {
3035
+ options.pathname ||= this.baseDirectory;
3036
+ }
3046
3037
  return request_1.isRclone(uri) ? this.Request.rclone(uri, options) : this.Request.aria2c(uri, options);
3047
3038
  }
3048
3039
  async start(emptyDir) {
3049
- const listener = (resolve) => {
3050
- const callback = (files, errors, status) => {
3051
- if (!this.restarting) {
3052
- this.removeListener('end', callback);
3053
- resolve({ files, errors, status, aborted: this.aborted });
3054
- }
3055
- };
3056
- return callback;
3057
- };
3058
3040
  if (this.aborted) {
3059
3041
  this.clearStorage();
3060
3042
  const pending = this._pendingResult;
@@ -3066,7 +3048,7 @@ class FileManager extends core_1.Host {
3066
3048
  return pending;
3067
3049
  }
3068
3050
  return new Promise(resolve => {
3069
- this.on('end', listener(resolve));
3051
+ this.on('end', this.#onStartEnd(resolve));
3070
3052
  queueMicrotask(() => {
3071
3053
  if (!this.#didAbortHost()) {
3072
3054
  this.restarting = false;
@@ -3090,11 +3072,11 @@ class FileManager extends core_1.Host {
3090
3072
  }
3091
3073
  this.finalizeState = 0;
3092
3074
  case 2:
3093
- resetAssets(this);
3075
+ this.resetAssets();
3094
3076
  break;
3095
3077
  }
3096
3078
  return this._pendingResult ||= new Promise((resolve, reject) => {
3097
- this.on('end', listener(resolve));
3079
+ this.on('end', this.#onStartEnd(resolve));
3098
3080
  try {
3099
3081
  this.processAssets(emptyDir);
3100
3082
  }
@@ -3133,7 +3115,7 @@ class FileManager extends core_1.Host {
3133
3115
  case 4:
3134
3116
  return;
3135
3117
  case 2:
3136
- resetAssets(this);
3118
+ this.resetAssets();
3137
3119
  break;
3138
3120
  }
3139
3121
  if (this.queued) {
@@ -3307,7 +3289,7 @@ class FileManager extends core_1.Host {
3307
3289
  setBuffer(item);
3308
3290
  }
3309
3291
  const checksumOutput = item.checksumOutput;
3310
- 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 }))) {
3311
3293
  item.flags |= 128;
3312
3294
  if (!etag || item.fetchType !== 1 || checksumOutput) {
3313
3295
  if (bundleIndex === 0 && !(0, types_1.isEmpty)(bundleId)) {
@@ -3325,12 +3307,12 @@ class FileManager extends core_1.Host {
3325
3307
  else if (cached) {
3326
3308
  try {
3327
3309
  const buffer = cached[1];
3328
- if (!item.checksum || checkHash(this, localUri, false, item.checksum, buffer)) {
3310
+ if (!item.checksum || this.checkHash(item.checksum, buffer, localUri)) {
3329
3311
  fs.writeFileSync(localUri, buffer);
3330
3312
  if (item.willChange && Buffer.isBuffer(buffer)) {
3331
3313
  item.buffer = buffer;
3332
3314
  }
3333
- 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 }))) {
3334
3316
  this.performAsyncTask();
3335
3317
  const target = new ProcessFile(this, item, localUri, groupData);
3336
3318
  target.received(null, incremental, true, false, true);
@@ -3456,7 +3438,7 @@ class FileManager extends core_1.Host {
3456
3438
  downloading[uri].push(item);
3457
3439
  }
3458
3440
  else if (type === 2 && !this.canRead(uri)) {
3459
- errorPermission(this, item);
3441
+ this.handleFilePermission(item);
3460
3442
  }
3461
3443
  else {
3462
3444
  if (!checkEtag) {
@@ -3473,7 +3455,7 @@ class FileManager extends core_1.Host {
3473
3455
  if (checkEtag) {
3474
3456
  item.flags &= ~128;
3475
3457
  }
3476
- closeResponse(client);
3458
+ client?.destroy();
3477
3459
  const location = request.url.toString();
3478
3460
  request.encoding = encoding;
3479
3461
  request.pipeTo = localUri;
@@ -3539,13 +3521,13 @@ class FileManager extends core_1.Host {
3539
3521
  let buffer;
3540
3522
  if (cached) {
3541
3523
  if (etagDir === cached[5] && (!encoding || encoding === cached[2])) {
3542
- if (item.checksum && !checkHash(this, localUri, false, item.checksum, cached[1])) {
3524
+ if (item.checksum && !this.checkHash(item.checksum, cached[1], localUri)) {
3543
3525
  tempDir = undefined;
3544
3526
  }
3545
3527
  else {
3546
3528
  if (checkEtag) {
3547
3529
  this.completeAsyncTask(null, localUri);
3548
- closeResponse(client);
3530
+ client?.destroy();
3549
3531
  return;
3550
3532
  }
3551
3533
  buffer = cached[1];
@@ -3564,7 +3546,7 @@ class FileManager extends core_1.Host {
3564
3546
  if (!checkEtag) {
3565
3547
  if (cacheBuffer && !buffer) {
3566
3548
  buffer = fs.readFileSync(pipeAs, encoding);
3567
- if (item.checksum && !checkHash(this, localUri, false, item.checksum, buffer)) {
3549
+ if (item.checksum && !this.checkHash(item.checksum, buffer, localUri)) {
3568
3550
  downloadUri(opts, etagDir);
3569
3551
  return;
3570
3552
  }
@@ -3573,20 +3555,22 @@ class FileManager extends core_1.Host {
3573
3555
  }
3574
3556
  }
3575
3557
  if (checkDest(pipeAs, localUri)) {
3576
- if (buffer) {
3577
- writeBufferCache(this, localUri, buffer, encoding);
3578
- }
3579
- else if (item.checksum) {
3580
- buffer = fs.readFileSync(pipeAs, encoding);
3581
- if (!checkHash(this, localUri, false, item.checksum, buffer)) {
3582
- downloadUri(opts, etagDir);
3583
- 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);
3584
3569
  }
3585
- writeBufferCache(this, localUri, buffer, encoding);
3586
3570
  }
3587
- else {
3588
- fs.copyFileSync(pipeAs, localUri);
3589
- 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);
3590
3574
  }
3591
3575
  }
3592
3576
  if (item.willChange && buffer) {
@@ -3597,7 +3581,7 @@ class FileManager extends core_1.Host {
3597
3581
  else {
3598
3582
  this.completeAsyncTask(null, localUri);
3599
3583
  }
3600
- closeResponse(client);
3584
+ client?.destroy();
3601
3585
  return;
3602
3586
  }
3603
3587
  if (buffer) {
@@ -3608,7 +3592,7 @@ class FileManager extends core_1.Host {
3608
3592
  item.flags &= ~128;
3609
3593
  }
3610
3594
  target.received(null, incremental, true, false, true);
3611
- closeResponse(client);
3595
+ client?.destroy();
3612
3596
  if (pipeAs) {
3613
3597
  fs.writeFile(pipeAs, buffer, () => { });
3614
3598
  }
@@ -3622,7 +3606,7 @@ class FileManager extends core_1.Host {
3622
3606
  downloadUri(opts, etagDir);
3623
3607
  }
3624
3608
  else if (statusCode < 400) {
3625
- closeResponse(client);
3609
+ client?.destroy();
3626
3610
  const location = res.headers.location;
3627
3611
  if (location && ++redirects <= HTTP_CLIENT.redirectLimit) {
3628
3612
  try {
@@ -3757,7 +3741,7 @@ class FileManager extends core_1.Host {
3757
3741
  }
3758
3742
  }
3759
3743
  else if (!this.canRead(uri)) {
3760
- errorPermission(this, item);
3744
+ this.handleFilePermission(item);
3761
3745
  }
3762
3746
  else if (!target.queued(pathname, emptyDir, false)) {
3763
3747
  this.performAsyncTask();
@@ -3812,8 +3796,8 @@ class FileManager extends core_1.Host {
3812
3796
  const barLength = processTask.barLength;
3813
3797
  const barIncrement = 100 / barLength;
3814
3798
  const initial = processTask.logCurrent !== logCurrent;
3815
- let redraw = false, scrollHeight = 0, logDelayed;
3816
- 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) {
3817
3801
  logDelayed = logDelayed.filter(item => typeof item[4].progressBar !== 'boolean');
3818
3802
  scrollHeight = Math.min(logDelayed.length, LOGGER.PROGRESS_SCROLLBUFFER);
3819
3803
  }
@@ -3839,7 +3823,7 @@ class FileManager extends core_1.Host {
3839
3823
  PROCESS_STDOUT.moveCursor(0, -(statusHeight + 1 + 1 + (scrollHeight > 0 ? 1 + scrollHeight : 0)));
3840
3824
  }
3841
3825
  processTask.updated = currentTime;
3842
- 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)));
3843
3827
  const spacer = LOGGER.PROGRESS_SPACER;
3844
3828
  let finished = processTask.pending === 0;
3845
3829
  for (let i = 0; i <= statusHeight; ++i) {
@@ -3940,11 +3924,11 @@ class FileManager extends core_1.Host {
3940
3924
  if (cleared) {
3941
3925
  last = item;
3942
3926
  }
3927
+ else if (!redraw && scroll[i] === item) {
3928
+ PROCESS_STDOUT.moveCursor(0, 1);
3929
+ continue;
3930
+ }
3943
3931
  else {
3944
- if (!redraw && scroll[i] === item) {
3945
- PROCESS_STDOUT.moveCursor(0, 1);
3946
- continue;
3947
- }
3948
3932
  const options = { ...item[4], messageTextWrap, broadcastId: '' };
3949
3933
  if (!useColor) {
3950
3934
  options.useColor = false;
@@ -4044,12 +4028,14 @@ class FileManager extends core_1.Host {
4044
4028
  ++scheduler.count;
4045
4029
  target = new Promise((resolve, reject) => {
4046
4030
  const writable = fs.createWriteStream(uri);
4047
- writable.on('finish', () => {
4031
+ writable
4032
+ .on('error', reject)
4033
+ .on('finish', () => {
4048
4034
  resolve(1);
4049
4035
  });
4050
- writable.on('error', reject);
4051
- data.on('error', reject);
4052
- data.pipe(writable);
4036
+ data
4037
+ .on('error', reject)
4038
+ .pipe(writable);
4053
4039
  });
4054
4040
  }
4055
4041
  }
@@ -4123,9 +4109,7 @@ class FileManager extends core_1.Host {
4123
4109
  async finalizeDocument() {
4124
4110
  for (const { instance, constructor } of this.Document) {
4125
4111
  if (constructor.finalize && instance.assets.length > 0) {
4126
- await constructor.finalize.call(this, instance).catch((err) => {
4127
- this.rejectModule(err, 4, { instance });
4128
- });
4112
+ await constructor.finalize.call(this, instance).catch((err) => this.rejectModule(err, 4, { instance }));
4129
4113
  if (this.aborted) {
4130
4114
  return;
4131
4115
  }
@@ -4137,9 +4121,7 @@ class FileManager extends core_1.Host {
4137
4121
  for (const { instance, constructor } of this.Task) {
4138
4122
  const items = assets.filter(item => item.tasks.find(data => data.handler === instance.moduleName));
4139
4123
  if (items.length > 0) {
4140
- await constructor.finalize.call(this, instance, items).catch((err) => {
4141
- this.rejectModule(err, 4, { instance });
4142
- });
4124
+ await constructor.finalize.call(this, instance, items).catch((err) => this.rejectModule(err, 4, { instance }));
4143
4125
  if (this.aborted) {
4144
4126
  return;
4145
4127
  }
@@ -4150,9 +4132,7 @@ class FileManager extends core_1.Host {
4150
4132
  async finalizeCloud() {
4151
4133
  const instance = this.Cloud;
4152
4134
  if (instance) {
4153
- await cloud_1.finalize.call(this, instance).catch((err) => {
4154
- this.rejectModule(err, 64, { instance });
4155
- });
4135
+ await cloud_1.finalize.call(this, instance).catch((err) => this.rejectModule(err, 64, { instance }));
4156
4136
  }
4157
4137
  }
4158
4138
  async finalizeChecksum() {
@@ -4165,7 +4145,7 @@ class FileManager extends core_1.Host {
4165
4145
  }
4166
4146
  else if ((0, types_1.isString)(checksum)) {
4167
4147
  const items = checksum.split('.');
4168
- 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 };
4169
4149
  }
4170
4150
  if ((0, types_1.isPlainObject)(checksum)) {
4171
4151
  const baseDirectory = this.baseDirectory;
@@ -4190,9 +4170,7 @@ class FileManager extends core_1.Host {
4190
4170
  }
4191
4171
  for (const { instance, constructor } of this.Document) {
4192
4172
  if (constructor.cleanup && instance.assets.length > 0) {
4193
- await constructor.cleanup.call(this, instance).catch((err) => {
4194
- this.rejectModule(err, 4, { instance });
4195
- });
4173
+ await constructor.cleanup.call(this, instance).catch((err) => this.rejectModule(err, 4, { instance }));
4196
4174
  }
4197
4175
  }
4198
4176
  }
@@ -4204,35 +4182,30 @@ class FileManager extends core_1.Host {
4204
4182
  this.writeTimeElapsed('READY', 'Finalizing assets...', this.startTime, { ...core_1.Host.LOG_STYLE_WARN, showCpu: true });
4205
4183
  }
4206
4184
  const startTime = process.hrtime.bigint();
4207
- const filesToRemove = this.filesToRemove;
4208
4185
  for (const [file, output] of this.filesToCompare) {
4209
4186
  const localUri = file.localUri;
4210
4187
  let minFile = localUri, minSize = (0, util_2.getSize)(minFile);
4211
4188
  for (const other of output) {
4212
4189
  const size = (0, util_2.getSize)(other);
4213
4190
  if (minSize === 0 || size > 0 && size < minSize) {
4214
- filesToRemove.add(minFile);
4191
+ this.filesToRemove.add(minFile);
4215
4192
  minFile = other;
4216
4193
  minSize = size;
4217
4194
  }
4218
4195
  else {
4219
- filesToRemove.add(other);
4196
+ this.filesToRemove.add(other);
4220
4197
  }
4221
4198
  }
4222
4199
  if (minFile !== localUri) {
4223
4200
  this.replace(file, minFile);
4224
4201
  }
4225
4202
  }
4226
- removeFiles(this, filesToRemove);
4227
- await this.finalizeCompress(this.assets.filter(item => item.compress && !ignoreAsset(item))).catch((err) => {
4228
- this.rejectModule(err, 8);
4229
- });
4203
+ this.removeFiles();
4204
+ await this.finalizeCompress(this.assets.filter(item => item.compress && !ignoreAsset(item))).catch((err) => this.rejectModule(err, 8));
4230
4205
  if (this.aborted) {
4231
4206
  return (0, types_1.createAbortError)(true);
4232
4207
  }
4233
- await this.finalizeDocument().catch((err) => {
4234
- this.rejectModule(err, 4);
4235
- });
4208
+ await this.finalizeDocument().catch((err) => this.rejectModule(err, 4));
4236
4209
  if (this.aborted) {
4237
4210
  return (0, types_1.createAbortError)(true);
4238
4211
  }
@@ -4246,32 +4219,27 @@ class FileManager extends core_1.Host {
4246
4219
  }
4247
4220
  }
4248
4221
  }
4249
- removeFiles(this, filesToRemove);
4250
- await this.finalizeTask(this.taskAssets.filter(item => item.tasks?.find(data => !data.preceding) && item.localUri && this.has(item.localUri) && !ignoreAsset(item))).catch((err) => {
4251
- this.rejectModule(err, 4);
4252
- });
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));
4253
4224
  if (this.aborted) {
4254
4225
  return (0, types_1.createAbortError)(true);
4255
4226
  }
4256
- removeFiles(this, filesToRemove);
4257
4227
  for (const item of this.assets) {
4258
4228
  if (item.checksumOutput && !ignoreAsset(item)) {
4259
4229
  const localUri = item.localUri;
4260
- if (!filesToRemove.has(localUri) && !checkHash(this, localUri, true, item.checksumOutput)) {
4230
+ if (!this.filesToRemove.has(localUri) && !this.checkHash(item.checksumOutput, { uri: localUri, output: true })) {
4261
4231
  item.invalid = true;
4262
- filesToRemove.add(localUri);
4232
+ this.filesToRemove.add(localUri);
4263
4233
  this.writeFail(["Checksum did not match", path.basename(localUri)], (0, types_1.errorValue)(localUri, "Invalid checksum"), { type: 32, startTime, queue: true });
4264
4234
  }
4265
4235
  }
4266
4236
  }
4267
- removeFiles(this, filesToRemove);
4268
- await this.finalizeCloud().catch((err) => {
4269
- this.rejectModule(err, 64);
4270
- });
4237
+ this.removeFiles();
4238
+ await this.finalizeCloud().catch((err) => this.rejectModule(err, 64));
4271
4239
  if (this.aborted) {
4272
4240
  return (0, types_1.createAbortError)(true);
4273
4241
  }
4274
- removeFiles(this, filesToRemove);
4242
+ this.removeFiles();
4275
4243
  if (this.Compress) {
4276
4244
  const tasks = [];
4277
4245
  for (const item of this.assets) {
@@ -4283,46 +4251,68 @@ class FileManager extends core_1.Host {
4283
4251
  await Promise.allSettled(tasks);
4284
4252
  }
4285
4253
  }
4286
- await this.finalizeChecksum().catch((err) => {
4287
- this.rejectModule(err, 32);
4288
- });
4289
- await this.finalizeCleanup().catch((err) => {
4290
- this.rejectModule(err, 1);
4291
- });
4292
- 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();
4293
4257
  this.closeMessage(startTime);
4294
4258
  this.Watch?.start(this.assets);
4295
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
+ }
4296
4269
  close() {
4297
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
+ });
4298
4296
  }
4299
4297
  startMessage() {
4300
4298
  if (!this.silent) {
4301
- 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 });
4302
4300
  }
4303
4301
  }
4304
4302
  closeMessage(startTime) {
4305
4303
  if (LOG_TIMEELAPSED) {
4306
- const [http, disk, cache] = this.#downloadStats;
4307
4304
  const errorCount = this.errorCount;
4308
4305
  const message = [];
4309
4306
  if (errorCount > 0) {
4310
4307
  message.push('ERROR ' + errorCount);
4311
4308
  }
4312
- let [size, count] = http;
4313
- if (count > 0) {
4314
- message.push('HTTP ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4315
- }
4316
- [size, count] = disk;
4317
- if (count > 0) {
4318
- message.push('DISK ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4319
- }
4320
- [size, count] = cache;
4321
- if (count > 0) {
4322
- message.push('CACHE ' + (count > 1 ? count + ' / ' : '') + (0, types_1.formatSize)(size));
4323
- }
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
+ });
4324
4314
  if (MEMORY.SIZE > 0) {
4325
- 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));
4326
4316
  }
4327
4317
  this.writeTimeElapsed('CLOSE', ['No further modifications', message.length > 0 ? message.join(chalk.blackBright(LOGGER.MESSAGE_SEP)) : ''], startTime, { ...core_1.Host.LOG_STYLE_WARN });
4328
4318
  }
@@ -4349,7 +4339,7 @@ class FileManager extends core_1.Host {
4349
4339
  const oldMax = lastHunk.oldStart + lastHunk.oldLines;
4350
4340
  const newMax = lastHunk.newStart + lastHunk.newLines;
4351
4341
  const posLength = Math.max(Math.max(oldMax, newMax).toString().length, Math.min(oldMax, newMax).toString().length);
4352
- addHeader(pathname, length + ` hunk${length > 1 ? 's' : ''}`);
4342
+ addHeader(pathname, formatLength('hunk', length));
4353
4343
  for (const { lines, oldLines, oldStart, newStart, newLines } of hunks) {
4354
4344
  for (let i = 0, j = oldStart, k = oldLines, l = 0, q = lines.length, added = false; i < q && k > 0; ++i) {
4355
4345
  const line = lines[i].substring(1);
@@ -4413,6 +4403,10 @@ class FileManager extends core_1.Host {
4413
4403
  this._pendingResult = null;
4414
4404
  this.clearStorage();
4415
4405
  }
4406
+ resetAssets() {
4407
+ this.reset();
4408
+ FileManager.sanitizeAssets(this.assets);
4409
+ }
4416
4410
  abortFinalize(err) {
4417
4411
  if (!this.restarting) {
4418
4412
  this.writeFail(["Transaction was not completed", this.baseDirectory], err);
@@ -4463,14 +4457,18 @@ class FileManager extends core_1.Host {
4463
4457
  }
4464
4458
  }
4465
4459
  this.close();
4466
- clearAssets(this);
4467
4460
  });
4468
4461
  }
4469
4462
  else {
4470
- clearAssets(this);
4463
+ this.#releaseMemory();
4471
4464
  }
4472
4465
  return result;
4473
4466
  }
4467
+ #releaseMemory() {
4468
+ for (const item of this.assets) {
4469
+ unsetContent(item);
4470
+ }
4471
+ }
4474
4472
  #nextScheduled(scheduler, pid) {
4475
4473
  if (pid > 0) {
4476
4474
  const found = scheduler.status.find(item => item.id === pid);
@@ -4505,6 +4503,15 @@ class FileManager extends core_1.Host {
4505
4503
  const timeout = this.#processTimeout;
4506
4504
  return timeout && name in timeout ? timeout[name] : PROCESS_TIMEOUT[name];
4507
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
+ }
4508
4515
  set restarting(value) {
4509
4516
  this.finalizeState = (this.#restarting = value) ? 3 : 0;
4510
4517
  }