hfs 0.57.26 → 0.57.27-beta2

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 (71) hide show
  1. package/admin/assets/{index-CHMhmNYS.js → index-DSkhXlWX.js} +103 -103
  2. package/admin/assets/{sha512-Dsv49IAs.js → sha512-CoddZKeD.js} +1 -1
  3. package/admin/index.html +1 -1
  4. package/frontend/assets/{index-legacy-BI8icwFp.js → index-legacy-C4Gclsbz.js} +4 -4
  5. package/frontend/assets/{sha512-legacy-D82alHBq.js → sha512-legacy-C-FMk44u.js} +1 -1
  6. package/frontend/index.html +1 -1
  7. package/package.json +4 -3
  8. package/src/QuickZipStream.js +11 -8
  9. package/src/SendList.js +10 -8
  10. package/src/ThrottledStream.js +8 -6
  11. package/src/acme.js +5 -7
  12. package/src/adminApis.js +6 -7
  13. package/src/api.accounts.js +4 -6
  14. package/src/api.auth.js +7 -11
  15. package/src/api.get_file_list.js +11 -16
  16. package/src/api.lang.js +1 -1
  17. package/src/api.monitor.js +2 -3
  18. package/src/api.net.js +1 -2
  19. package/src/api.plugins.js +8 -10
  20. package/src/api.vfs.js +14 -19
  21. package/src/apiMiddleware.js +4 -4
  22. package/src/auth.js +6 -9
  23. package/src/basicWeb.js +1 -2
  24. package/src/block.js +1 -1
  25. package/src/commands.js +9 -9
  26. package/src/comments.js +4 -5
  27. package/src/config.js +8 -9
  28. package/src/connections.js +11 -6
  29. package/src/consoleLog.js +6 -6
  30. package/src/const.js +4 -4
  31. package/src/cross.js +19 -21
  32. package/src/customHtml.js +3 -4
  33. package/src/ddns.js +4 -4
  34. package/src/debounceAsync.js +3 -3
  35. package/src/errorPages.js +2 -3
  36. package/src/events.js +7 -10
  37. package/src/fileAttr.js +6 -6
  38. package/src/first.js +13 -8
  39. package/src/frontEndApis.js +5 -5
  40. package/src/geo.js +3 -5
  41. package/src/github.js +14 -17
  42. package/src/i18n.js +3 -4
  43. package/src/icons.js +1 -1
  44. package/src/index.js +2 -3
  45. package/src/lang.js +5 -6
  46. package/src/listen.js +25 -28
  47. package/src/log.js +18 -17
  48. package/src/middlewares.js +12 -15
  49. package/src/misc.js +9 -11
  50. package/src/nat.js +14 -16
  51. package/src/outboundProxy.js +7 -7
  52. package/src/perm.js +7 -12
  53. package/src/persistence.js +1 -1
  54. package/src/plugins.js +62 -73
  55. package/src/roots.js +1 -2
  56. package/src/selfCheck.js +5 -4
  57. package/src/serveFile.js +6 -7
  58. package/src/serveGuiAndSharedFiles.js +7 -9
  59. package/src/serveGuiFiles.js +11 -20
  60. package/src/stat.js +40 -0
  61. package/src/statWorker.js +11 -0
  62. package/src/throttler.js +5 -7
  63. package/src/update.js +7 -7
  64. package/src/upload.js +11 -13
  65. package/src/util-files.js +42 -32
  66. package/src/util-http.js +80 -29
  67. package/src/util-os.js +2 -4
  68. package/src/vfs.js +77 -68
  69. package/src/walkDir.js +9 -9
  70. package/src/watchLoad.js +4 -4
  71. package/src/zip.js +5 -6
package/src/cross.js CHANGED
@@ -101,7 +101,7 @@ __exportStar(require("./cross-const"), exports);
101
101
  exports.WEBSITE = 'https://rejetto.com/hfs/';
102
102
  exports.REPO_URL = `https://github.com/${cross_const_1.HFS_REPO}/`;
103
103
  exports.WIKI_URL = exports.REPO_URL + 'wiki/';
104
- exports.MINUTE = 60000;
104
+ exports.MINUTE = 60_000;
105
105
  exports.HOUR = 60 * exports.MINUTE;
106
106
  exports.DAY = 24 * exports.HOUR;
107
107
  exports.MAX_TILE_SIZE = 10;
@@ -146,10 +146,9 @@ function isWhoObject(v) {
146
146
  }
147
147
  const MULTIPLIERS = ['', 'K', 'M', 'G', 'T'];
148
148
  function formatBytes(n, { post = 'B', k = 0, digits = NaN, sep = ' ' } = {}) {
149
- var _a;
150
149
  if (isNaN(Number(n)) || n < 0)
151
150
  return '';
152
- k || (k = (_a = formatBytes.k) !== null && _a !== void 0 ? _a : 1024); // default value
151
+ k ||= formatBytes.k ?? 1024; // default value
153
152
  const i = n && Math.min(MULTIPLIERS.length - 1, Math.floor(Math.log2(n) / Math.log2(k)));
154
153
  n /= k ** i;
155
154
  const nAsString = i && !isNaN(digits) ? n.toFixed(digits)
@@ -176,7 +175,11 @@ function wait(ms, val) {
176
175
  }
177
176
  // throws after ms
178
177
  function haveTimeout(ms, job, error) {
179
- return Promise.race([job, wait(ms).then(() => { throw error || Error('timeout'); })]);
178
+ let h;
179
+ return Promise.race([
180
+ job.finally(() => clearTimeout(h)), // don't leave pending timeout if the job is done first
181
+ new Promise((_resolve, reject) => h = setTimeout(() => reject(error || Error('timeout')), ms))
182
+ ]);
180
183
  }
181
184
  function objSameKeys(src, newValue) {
182
185
  return Object.fromEntries(Object.entries(src).map(([k, v]) => [k, newValue(v, k)]));
@@ -227,7 +230,7 @@ function try_(cb, onException) {
227
230
  return cb();
228
231
  }
229
232
  catch (e) {
230
- return onException === null || onException === void 0 ? void 0 : onException(e);
233
+ return onException?.(e);
231
234
  }
232
235
  }
233
236
  function with_(par, cb) {
@@ -253,8 +256,7 @@ function pendingPromise() {
253
256
  return Object.assign(ret, takeOut);
254
257
  }
255
258
  function basename(path) {
256
- var _a;
257
- return ((_a = path.match(/([^\\/]+)[\\/]*$/)) === null || _a === void 0 ? void 0 : _a[1]) || '';
259
+ return path.match(/([^\\/]+)[\\/]*$/)?.[1] || '';
258
260
  }
259
261
  function dirname(path) {
260
262
  return path.slice(0, Math.max(0, path.lastIndexOf('/', path.length - 1)));
@@ -263,8 +265,8 @@ function tryJson(s, except) {
263
265
  try {
264
266
  return s && JSON.parse(s);
265
267
  }
266
- catch (_a) {
267
- return except === null || except === void 0 ? void 0 : except(s);
268
+ catch {
269
+ return except?.(s);
268
270
  }
269
271
  }
270
272
  function swap(obj, k1, k2) {
@@ -377,7 +379,7 @@ function repeat(everyMs, cb) {
377
379
  try {
378
380
  await cb(stopIt);
379
381
  } // you can use stopIt passed as a parameter or the returned value, whatever makes you happy
380
- catch (_a) { }
382
+ catch { }
381
383
  await wait(everyMs);
382
384
  }
383
385
  })();
@@ -418,17 +420,15 @@ function isTimestampString(v) {
418
420
  return typeof v === 'string' && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d+)?Z*$/.test(v);
419
421
  }
420
422
  function isEqualLax(a, b, overrideRule) {
421
- var _a;
422
- return (_a = overrideRule === null || overrideRule === void 0 ? void 0 : overrideRule(a, b)) !== null && _a !== void 0 ? _a : (a == b || a && b && typeof a === 'object' && typeof b === 'object'
423
+ return overrideRule?.(a, b) ?? (a == b || a && b && typeof a === 'object' && typeof b === 'object'
423
424
  && Object.entries(a).every(([k, v]) => isEqualLax(v, b[k], overrideRule))
424
425
  && Object.entries(b).every(([k, v]) => k in a /*already checked*/ || isEqualLax(v, a[k], overrideRule)));
425
426
  }
426
427
  function xlate(input, table) {
427
- var _a;
428
- return (_a = table[input]) !== null && _a !== void 0 ? _a : input;
428
+ return table[input] ?? input;
429
429
  }
430
430
  function normalizeHost(host) {
431
- return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host === null || host === void 0 ? void 0 : host.split(':')[0];
431
+ return host[0] === '[' ? host.slice(1, host.indexOf(']')) : host?.split(':')[0];
432
432
  }
433
433
  function isIpLocalHost(ip) {
434
434
  return ip === '::1' || ip.endsWith('127.0.0.1');
@@ -480,9 +480,8 @@ function makeMatcher(mask, emptyMaskReturns = false) {
480
480
  }
481
481
  // this is caching all matchers, so don't use it with frequently changing masks. Benchmarks revealed that _.memoize make it slower than not using it, while this simple cache can speed up to 30x
482
482
  function matches(s, mask, emptyMaskReturns = false) {
483
- var _a, _b;
484
- const cache = (_a = matches).cache || (_a.cache = {});
485
- return (cache[_b = mask + (emptyMaskReturns ? '1' : '0')] || (cache[_b] = makeMatcher(mask, emptyMaskReturns)))(s);
483
+ const cache = matches.cache ||= {};
484
+ return (cache[mask + (emptyMaskReturns ? '1' : '0')] ||= makeMatcher(mask, emptyMaskReturns))(s);
486
485
  }
487
486
  // if delimiter is specified, it is prefixed to symbols. If it contains a space, the part after the space is considered as suffix.
488
487
  function replace(s, symbols, delimiter = '') {
@@ -513,7 +512,7 @@ function safeDecodeURIComponent(s) {
513
512
  try {
514
513
  return decodeURIComponent(s);
515
514
  }
516
- catch (_a) {
515
+ catch {
517
516
  return s;
518
517
  }
519
518
  }
@@ -543,9 +542,8 @@ function toMutable(value) {
543
542
  return Array.isArray(value) ? value.slice() : { ...value };
544
543
  }
545
544
  function shortenAgent(agent) {
546
- var _a;
547
545
  return lodash_1.default.findKey(BROWSERS, re => re.test(agent))
548
- || ((_a = /^[^/(]+ ?/.exec(agent)) === null || _a === void 0 ? void 0 : _a[0])
546
+ || /^[^/(]+ ?/.exec(agent)?.[0]
549
547
  || agent;
550
548
  }
551
549
  const BROWSERS = {
package/src/customHtml.js CHANGED
@@ -22,7 +22,6 @@ exports.disableCustomHtml = (0, config_1.defineConfig)(misc_1.CFG.disable_custom
22
22
  function watchLoadCustomHtml(folder = '') {
23
23
  const sections = new Map();
24
24
  const res = (0, watchLoad_1.watchLoad)((0, misc_1.prefix)('', folder, '/') + FILE, data => {
25
- var _a;
26
25
  const re = /^\[([^\]]+)] *$/gm;
27
26
  sections.clear();
28
27
  if (!data)
@@ -31,10 +30,10 @@ function watchLoadCustomHtml(folder = '') {
31
30
  do {
32
31
  let last = re.lastIndex;
33
32
  const match = re.exec(data);
34
- const content = data.slice(last, !match ? undefined : re.lastIndex - (((_a = match === null || match === void 0 ? void 0 : match[0]) === null || _a === void 0 ? void 0 : _a.length) || 0)).trim();
33
+ const content = data.slice(last, !match ? undefined : re.lastIndex - (match?.[0]?.length || 0)).trim();
35
34
  if (content)
36
35
  sections.set(name, content);
37
- name = match === null || match === void 0 ? void 0 : match[1];
36
+ name = match?.[1];
38
37
  } while (name);
39
38
  });
40
39
  return Object.assign(res, { sections });
@@ -50,7 +49,7 @@ function getAllSections() {
50
49
  return Object.fromEntries(all.map(x => [x, getSection(x)]));
51
50
  }
52
51
  async function saveCustomHtml(sections) {
53
- const text = Object.entries(sections).filter(([k, v]) => v === null || v === void 0 ? void 0 : v.trim()).map(([k, v]) => `[${k}]\n${v}\n\n`).join('');
52
+ const text = Object.entries(sections).filter(([k, v]) => v?.trim()).map(([k, v]) => `[${k}]\n${v}\n\n`).join('');
54
53
  await (0, promises_1.writeFile)(FILE, text);
55
54
  exports.customHtml.sections.clear();
56
55
  for (const [k, v] of Object.entries(sections))
package/src/ddns.js CHANGED
@@ -22,8 +22,8 @@ let stopFetching;
22
22
  let lastIPs;
23
23
  let lastMap;
24
24
  events_1.default.onListeners(EVENT, cbs => {
25
- stopFetching === null || stopFetching === void 0 ? void 0 : stopFetching();
26
- if (!(cbs === null || cbs === void 0 ? void 0 : cbs.size))
25
+ stopFetching?.();
26
+ if (!cbs?.size)
27
27
  return;
28
28
  stopFetching = (0, cross_1.repeat)(cross_1.HOUR, async () => {
29
29
  const IPs = await (0, nat_1.getPublicIps)();
@@ -41,7 +41,7 @@ events_1.default.onListeners(EVENT, cbs => {
41
41
  let stopListening;
42
42
  let last;
43
43
  dynamicDnsUrl.sub(v => {
44
- stopListening === null || stopListening === void 0 ? void 0 : stopListening();
44
+ stopListening?.();
45
45
  if (!v)
46
46
  return;
47
47
  stopListening = events_1.default.on(EVENT, async () => {
@@ -60,7 +60,7 @@ dynamicDnsUrl.sub(v => {
60
60
  }));
61
61
  last = lodash_1.default.find(all, 'error') || all[0]; // the system is designed for just one result, and we give precedence to errors
62
62
  events_1.default.emit('dynamicDnsError', last);
63
- console.log('dynamic dns update', (last === null || last === void 0 ? void 0 : last.error) || 'ok');
63
+ console.log('dynamic dns update', last?.error || 'ok');
64
64
  }, { callNow: true });
65
65
  });
66
66
  async function get_dynamic_dns_error() {
@@ -19,7 +19,7 @@ callback, options = {}) {
19
19
  const interceptingWrapper = (...args) => latestDebouncer = debouncer(...args);
20
20
  return Object.assign(interceptingWrapper, {
21
21
  clearRetain: () => latestCallback = undefined,
22
- flush: () => runningCallback !== null && runningCallback !== void 0 ? runningCallback : exec(),
22
+ flush: () => runningCallback ?? exec(),
23
23
  isWorking: () => runningCallback,
24
24
  ...cancelable && {
25
25
  cancel() {
@@ -32,10 +32,10 @@ callback, options = {}) {
32
32
  if (reuseRunning && runningCallback)
33
33
  return runningCallback;
34
34
  const now = Date.now();
35
- if (latestCallback && now - latestTimestamp < (latestHasFailed ? retainFailure !== null && retainFailure !== void 0 ? retainFailure : retain : retain))
35
+ if (latestCallback && now - latestTimestamp < (latestHasFailed ? retainFailure ?? retain : retain))
36
36
  return await latestCallback;
37
37
  whoIsWaiting = args;
38
- waitingSince || (waitingSince = now);
38
+ waitingSince ||= now;
39
39
  const waitingCap = maxWait - (now - (waitingSince || started));
40
40
  const waitFor = Math.min(waitingCap, leading ? wait - (now - started) : wait);
41
41
  if (waitFor > 0)
package/src/errorPages.js CHANGED
@@ -11,7 +11,6 @@ function getErrorSections() {
11
11
  }
12
12
  // to be used with errors whose recipient is possibly human
13
13
  async function sendErrorPage(ctx, code = ctx.status) {
14
- var _a, _b;
15
14
  ctx.type = 'text';
16
15
  ctx.set('content-disposition', ''); // reset ctx.attachment (or forceDownload)
17
16
  ctx.status = code;
@@ -21,8 +20,8 @@ async function sendErrorPage(ctx, code = ctx.status) {
21
20
  const lang = await (0, lang_1.getLangData)(ctx);
22
21
  if (!lang)
23
22
  return;
24
- const trans = (_a = Object.values(lang)[0]) === null || _a === void 0 ? void 0 : _a.translate;
25
- ctx.body = (_b = trans === null || trans === void 0 ? void 0 : trans[msg]) !== null && _b !== void 0 ? _b : msg;
23
+ const trans = Object.values(lang)[0]?.translate;
24
+ ctx.body = trans?.[msg] ?? msg;
26
25
  const errorPage = (0, customHtml_1.getSection)(ctx.status === cross_1.HTTP_UNAUTHORIZED ? 'unauthorized' : String(ctx.status));
27
26
  if (!errorPage)
28
27
  return;
package/src/events.js CHANGED
@@ -4,11 +4,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.BetterEventEmitter = void 0;
5
5
  const LISTENERS_SUFFIX = '\0listeners';
6
6
  class BetterEventEmitter {
7
- constructor() {
8
- this.listeners = new Map();
9
- this.preventDefault = Symbol();
10
- this.stop = this.preventDefault; // legacy pre-0.54 (introduced in 0.53)
11
- }
7
+ listeners = new Map();
8
+ preventDefault = Symbol();
9
+ stop = this.preventDefault; // legacy pre-0.54 (introduced in 0.53)
12
10
  on(event, listener, { warnAfter = 10, callNow = false } = {}) {
13
11
  if (typeof event === 'string')
14
12
  event = [event];
@@ -25,7 +23,7 @@ class BetterEventEmitter {
25
23
  try {
26
24
  listener();
27
25
  }
28
- catch (_a) { }
26
+ catch { }
29
27
  return () => {
30
28
  for (const e of event) {
31
29
  const cbs = this.listeners.get(e);
@@ -47,7 +45,7 @@ class BetterEventEmitter {
47
45
  off = this.on(event, function (...args) {
48
46
  off();
49
47
  resolve(args.slice(0, -1)); // remove the extra argument at the end of our emit()
50
- return listener === null || listener === void 0 ? void 0 : listener(...args);
48
+ return listener?.(...args);
51
49
  });
52
50
  });
53
51
  return Object.assign(off, { then: pro.then.bind(pro) });
@@ -60,12 +58,11 @@ class BetterEventEmitter {
60
58
  };
61
59
  }
62
60
  anyListener(event) {
63
- var _a;
64
- return Boolean((_a = this.listeners.get(event)) === null || _a === void 0 ? void 0 : _a.size);
61
+ return Boolean(this.listeners.get(event)?.size);
65
62
  }
66
63
  emit(event, ...args) {
67
64
  let cbs = this.listeners.get(event);
68
- if (!(cbs === null || cbs === void 0 ? void 0 : cbs.size))
65
+ if (!cbs?.size)
69
66
  return;
70
67
  const output = [];
71
68
  let prevented = false;
package/src/fileAttr.js CHANGED
@@ -24,10 +24,9 @@ if ((0, fs_1.existsSync)(FN))
24
24
  const FILE_ATTR_PREFIX = 'user.hfs.'; // user. prefix to be linux compatible
25
25
  /* @param v must be JSON-able or undefined */
26
26
  async function storeFileAttr(path, k, v) {
27
- var _a, _b;
28
27
  const s = await (0, util_files_1.statWithTimeout)(path).catch(() => null);
29
28
  // since we don't have fsx.remove, we simulate it with an empty string
30
- if (s && await (fsx === null || fsx === void 0 ? void 0 : fsx.set(path, FILE_ATTR_PREFIX + k, v === undefined ? '' : JSON.stringify(v)).then(() => 1, () => 0))) {
29
+ if (s && await fsx?.set(path, FILE_ATTR_PREFIX + k, v === undefined ? '' : JSON.stringify(v)).then(() => 1, () => 0)) {
31
30
  if (const_1.IS_WINDOWS)
32
31
  (0, promises_2.utimes)(path, s.atime, s.mtime); // restore timestamps, necessary only on Windows
33
32
  return true;
@@ -39,14 +38,15 @@ async function storeFileAttr(path, k, v) {
39
38
  else
40
39
  await fileAttrDb.open(FN);
41
40
  // pipe should be a safe separator
42
- return (_b = await ((_a = fileAttrDb.put(`${path}|${k}`, v)) === null || _a === void 0 ? void 0 : _a.catch((e) => {
41
+ return await fileAttrDb.put(`${path}|${k}`, v)?.catch((e) => {
43
42
  console.error("couldn't store metadata on", path, String(e.message || e));
44
43
  return false;
45
- }))) !== null && _b !== void 0 ? _b : true; // if put is undefined, the value was already there
44
+ }) ?? true; // if put is undefined, the value was already there
46
45
  }
47
46
  async function loadFileAttr(path, k) {
48
- var _a;
49
- return (_a = await (fsx === null || fsx === void 0 ? void 0 : fsx.get(path, FILE_ATTR_PREFIX + k).then((x) => x === '' ? undefined : (0, cross_1.tryJson)(String(x)), () => fileAttrDb.isOpen() ? fileAttrDb.get(`${path}|${k}`) : null))) !== null && _a !== void 0 ? _a : undefined; // normalize, as we get null instead of undefined on windows
47
+ return await fsx?.get(path, FILE_ATTR_PREFIX + k)
48
+ .then((x) => x === '' ? undefined : (0, cross_1.tryJson)(String(x)), () => fileAttrDb.isOpen() ? fileAttrDb.get(`${path}|${k}`) : null)
49
+ ?? undefined; // normalize, as we get null instead of undefined on windows
50
50
  }
51
51
  async function purgeFileAttr() {
52
52
  let n = 0;
package/src/first.js CHANGED
@@ -10,17 +10,22 @@ function onProcessExit(cb) {
10
10
  }
11
11
  exports.quitting = false;
12
12
  onProcessExit(() => exports.quitting = true);
13
- onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal => Promise.allSettled(Array.from(cbs).map(cb => cb(signal))).then(() => {
14
- console.log('quitting');
13
+ onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP']).then(([signal]) => Promise.allSettled(Array.from(cbs).map(cb => cb(signal))).then(() => {
14
+ console.log('quitting', signal || '');
15
15
  process.exit(0);
16
16
  }));
17
- function onFirstEvent(emitter, events, cb) {
18
- let already = false;
19
- for (const e of events)
20
- emitter.once(e, (...args) => {
17
+ function onFirstEvent(emitter, events) {
18
+ return new Promise(resolve => {
19
+ let already = false;
20
+ for (const e of events)
21
+ emitter.on(e, listener);
22
+ function listener(...args) {
21
23
  if (already)
22
24
  return;
23
25
  already = true;
24
- cb(...args);
25
- });
26
+ for (const e of events)
27
+ emitter.removeListener(e, listener);
28
+ resolve(args);
29
+ }
30
+ });
26
31
  }
@@ -70,7 +70,7 @@ exports.frontEndApis = {
70
70
  });
71
71
  },
72
72
  async get_file_details({ uris }, ctx) {
73
- if (typeof (uris === null || uris === void 0 ? void 0 : uris[0]) !== 'string')
73
+ if (typeof uris?.[0] !== 'string')
74
74
  return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad uris');
75
75
  const isAdmin = (0, adminApis_1.ctxAdminAccess)(ctx);
76
76
  return {
@@ -114,7 +114,7 @@ exports.frontEndApis = {
114
114
  const node = await (0, vfs_1.urlToNode)(uri, ctx);
115
115
  if (!node)
116
116
  throw new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
117
- if ((0, vfs_1.isRoot)(node) || dest.includes('/') || (0, util_files_1.dirTraversal)(dest))
117
+ if ((0, vfs_1.isRoot)(node) || dest.includes('/') || (0, util_files_1.hasDirTraversal)(dest))
118
118
  throw new apiMiddleware_1.ApiError(const_1.HTTP_FORBIDDEN);
119
119
  if (!(0, vfs_1.hasPermission)(node, 'can_delete', ctx))
120
120
  throw new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
@@ -147,12 +147,12 @@ exports.frontEndApis = {
147
147
  if (typeof from1 !== 'string')
148
148
  return const_1.HTTP_BAD_REQUEST;
149
149
  const srcNode = await (0, vfs_1.urlToNode)(from1, ctx);
150
- const src = srcNode === null || srcNode === void 0 ? void 0 : srcNode.source;
150
+ const src = srcNode?.source;
151
151
  if (!src)
152
152
  return const_1.HTTP_NOT_FOUND;
153
153
  const dest = (0, path_1.join)(destNode.source, (0, path_1.basename)(src));
154
154
  if (lodash_1.default.isFunction(override))
155
- return override === null || override === void 0 ? void 0 : override(srcNode, dest);
155
+ return override?.(srcNode, dest);
156
156
  return (0, vfs_1.statusCodeForMissingPerm)(srcNode, 'can_delete', ctx)
157
157
  || (0, promises_1.rename)(src, dest).catch(async (e) => {
158
158
  if (e.code !== 'EXDEV')
@@ -200,7 +200,7 @@ exports.frontEndApis = {
200
200
  let bytes = 0;
201
201
  let files = 0;
202
202
  for await (const n of (0, vfs_1.walkNode)(folder, { ctx, onlyFiles: true, depth: Infinity })) {
203
- bytes += await (0, vfs_1.nodeStats)(n).then(x => (x === null || x === void 0 ? void 0 : x.size) || 0, () => 0);
203
+ bytes += await (0, vfs_1.nodeStats)(n).then(x => x?.size || 0, () => 0);
204
204
  files++;
205
205
  partialFolderSize[id] = { bytes, files };
206
206
  }
package/src/geo.js CHANGED
@@ -20,10 +20,9 @@ setInterval(checkFiles, misc_1.DAY); // keep updated at run-time
20
20
  // benchmark: memoize can make this 44x faster
21
21
  exports.ip2country = lodash_1.default.memoize((ip) => ip2location.getCountryShortAsync(ip).then(v => v === '-' ? '' : v, () => ''));
22
22
  const geoFilter = async (ctx, next) => {
23
- var _a;
24
23
  if (enabled.get() && !(0, misc_1.isLocalHost)(ctx) && !(0, misc_1.isIpLan)(ctx.ip)) {
25
24
  const { connection } = ctx.state;
26
- const country = (_a = connection.country) !== null && _a !== void 0 ? _a : (connection.country = await (0, exports.ip2country)(ctx.ip));
25
+ const country = connection.country ??= await (0, exports.ip2country)(ctx.ip);
27
26
  if (country)
28
27
  (0, connections_1.updateConnection)(connection, { country });
29
28
  if (!ctx.state.skipFilters && allow.get() !== null)
@@ -37,7 +36,6 @@ function isOpen() {
37
36
  return Boolean(ip2location.getPackageVersion());
38
37
  }
39
38
  async function checkFiles() {
40
- var _a, _b;
41
39
  if (!enabled.get())
42
40
  return;
43
41
  const ZIP_FILE = 'IP2LOCATION-LITE-DB1.IPV6.BIN';
@@ -57,11 +55,11 @@ async function checkFiles() {
57
55
  ip2location.close();
58
56
  await (0, promises_1.unlink)(LOCAL_FILE).catch(() => { });
59
57
  await (0, promises_1.rename)(TEMP, LOCAL_FILE);
60
- (_b = (_a = exports.ip2country.cache).clear) === null || _b === void 0 ? void 0 : _b.call(_a);
58
+ exports.ip2country.cache.clear?.();
61
59
  console.log(`${name} download completed`);
62
60
  }
63
61
  catch (e) {
64
- console.error(`Failed to download ${name}${mtime ? ", falling back on old data" : ''}:`, (e === null || e === void 0 ? void 0 : e.message) || String(e));
62
+ console.error(`Failed to download ${name}${mtime ? ", falling back on old data" : ''}:`, e?.message || String(e));
65
63
  }
66
64
  else if (isOpen())
67
65
  return;
package/src/github.js CHANGED
@@ -39,15 +39,13 @@ function getGithubDefaultBranch(repo) {
39
39
  if (!repo.includes('/'))
40
40
  throw 'malformed repo';
41
41
  return branchCache.try(repo, async () => {
42
- var _a;
43
42
  for (const b of ['main', 'master']) // try to not consume api quota
44
43
  if (await (0, misc_1.httpString)(`https://github.com/${repo}/raw/refs/heads/${b}/dist/plugin.js`, { method: 'HEAD', noRedirect: true }).then(() => 1, () => 0))
45
44
  return b;
46
- return (_a = (await getRepoInfo(repo))) === null || _a === void 0 ? void 0 : _a.default_branch;
45
+ return (await getRepoInfo(repo))?.default_branch;
47
46
  });
48
47
  }
49
48
  async function downloadPlugin(repo, { branch = '', overwrite = false } = {}) {
50
- var _a, _b, _c;
51
49
  if (typeof repo !== 'string')
52
50
  repo = repo.main;
53
51
  if (exports.downloading[repo])
@@ -63,19 +61,19 @@ async function downloadPlugin(repo, { branch = '', overwrite = false } = {}) {
63
61
  if (customRepo) { // custom repo
64
62
  if (!pl)
65
63
  throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, "bad repo");
66
- const customRepo = (((_b = (_a = pl).getData) === null || _b === void 0 ? void 0 : _b.call(_a)) || pl).repo;
67
- let url = customRepo === null || customRepo === void 0 ? void 0 : customRepo.zip;
64
+ const customRepo = (pl.getData?.() || pl).repo;
65
+ let url = customRepo?.zip;
68
66
  if (!url)
69
67
  throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, "bad plugin");
70
68
  if (!url.includes('//'))
71
69
  url = customRepo.web + url;
72
- return await go(url, pl === null || pl === void 0 ? void 0 : pl.id, (_c = customRepo.zipRoot) !== null && _c !== void 0 ? _c : DIST_ROOT);
70
+ return await go(url, pl?.id, customRepo.zipRoot ?? DIST_ROOT);
73
71
  }
74
- branch || (branch = await getGithubDefaultBranch(repo));
72
+ branch ||= await getGithubDefaultBranch(repo);
75
73
  const short = repo.split('/')[1]; // second part, repo without the owner
76
74
  if (!short)
77
75
  throw new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, "bad repo");
78
- const folder = overwrite && (pl === null || pl === void 0 ? void 0 : pl.id) // use existing folder
76
+ const folder = overwrite && pl?.id // use existing folder
79
77
  || (getFolder2repo().hasOwnProperty(short) ? repo.replace('/', '-') // longer form only if another plugin is using short form, to avoid overwriting
80
78
  : short.replace(/^hfs-/, ''));
81
79
  const GITHUB_ZIP_ROOT = short + '-' + branch; // GitHub puts everything within this folder
@@ -137,7 +135,7 @@ async function downloadPlugin(repo, { branch = '', overwrite = false } = {}) {
137
135
  await (0, promises_1.rename)(tempInstallPath, installPath)
138
136
  .catch(e => { throw e.code !== 'ENOENT' ? e : new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, "missing main file"); });
139
137
  if (wasRunning)
140
- if (await (0, misc_1.waitFor)(() => (0, plugins_1.getPluginInfo)(folder), { timeout: 10000 }))
138
+ if (await (0, misc_1.waitFor)(() => (0, plugins_1.getPluginInfo)(folder), { timeout: 10_000 }))
141
139
  void (0, plugins_1.startPlugin)(folder) // don't wait, in case it fails to start. We still use startPlugin instead of enablePlugin, as it will take care of disabling other themes.
142
140
  .catch(console.warn);
143
141
  events_1.default.emit('pluginDownloaded', { id: folder, repo });
@@ -167,7 +165,7 @@ async function readOnlinePlugin(repo, branch = '') {
167
165
  main = pl.repo.web + main;
168
166
  return (0, plugins_1.parsePluginSource)(main, await (0, misc_1.httpString)(main)); // use 'repo' as 'id' client-side
169
167
  }
170
- branch || (branch = await getGithubDefaultBranch(repo));
168
+ branch ||= await getGithubDefaultBranch(repo);
171
169
  const res = await readGithubFile(`${repo}/${branch}/${DIST_ROOT}/${plugins_1.PLUGIN_MAIN_FILE}`);
172
170
  const pl = (0, plugins_1.parsePluginSource)(repo, res); // use 'repo' as 'id' client-side
173
171
  pl.branch = branch;
@@ -175,13 +173,13 @@ async function readOnlinePlugin(repo, branch = '') {
175
173
  }
176
174
  async function readOnlineCompatiblePlugin(repo, branch = '') {
177
175
  const pl = await readOnlinePlugin(repo, branch);
178
- if (!(pl === null || pl === void 0 ? void 0 : pl.apiRequired))
176
+ if (!pl?.apiRequired)
179
177
  return; // mandatory field
180
178
  if (!pl.badApi)
181
179
  return pl;
182
180
  // we try other branches (starting with 'api')
183
181
  const res = await apiGithub('repos/' + repo + '/branches');
184
- const branches = res.map((x) => x === null || x === void 0 ? void 0 : x.name)
182
+ const branches = res.map((x) => x?.name)
185
183
  .filter((x) => typeof x === 'string' && x.startsWith('api'))
186
184
  .sort().reverse();
187
185
  for (const branch of branches) {
@@ -234,7 +232,7 @@ async function* apiGithubPaginated(uri) {
234
232
  }
235
233
  }
236
234
  async function isPluginBlacklisted(repo) {
237
- return (0, exports.getProjectInfo)().then(x => { var _a, _b; return ((_b = (_a = x === null || x === void 0 ? void 0 : x.repo_blacklist) === null || _a === void 0 ? void 0 : _a[repo]) === null || _b === void 0 ? void 0 : _b.message) || ''; }, () => undefined);
235
+ return (0, exports.getProjectInfo)().then(x => x?.repo_blacklist?.[repo]?.message || '', () => undefined);
238
236
  }
239
237
  async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
240
238
  // github doesn't allow complex search, so we have to do it multiple times and merge the results
@@ -245,7 +243,6 @@ async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
245
243
  const list = await Promise.all(searches.map(x => (0, misc_1.asyncGeneratorToArray)(apiGithubPaginated(`search/repositories?q=topic:hfs-plugin+${x}`))));
246
244
  const deduped = lodash_1.default.uniqBy(list.flat(), x => x.full_name);
247
245
  return new misc_1.AsapStream(deduped.map(async (it) => {
248
- var _a;
249
246
  const repo = it.full_name;
250
247
  if (skipRepos.includes(repo) || await isPluginBlacklisted(repo))
251
248
  return;
@@ -255,7 +252,7 @@ async function searchPlugins(text = '', { skipRepos = [''] } = {}) {
255
252
  Object.assign(pl, {
256
253
  repo, // overwrite parsed value, that may be wrong
257
254
  downloading: exports.downloading[repo],
258
- license: (_a = it.license) === null || _a === void 0 ? void 0 : _a.spdx_id,
255
+ license: it.license?.spdx_id,
259
256
  }, lodash_1.default.pick(it, ['pushed_at', 'stargazers_count', 'default_branch']));
260
257
  return pl;
261
258
  }));
@@ -273,7 +270,7 @@ exports.getProjectInfo = (0, misc_1.debounceAsync)(() => argv_1.argv.central ===
273
270
  .then(o => {
274
271
  if (o)
275
272
  cachedCentralInfo.set(o);
276
- o || (o = { ...cachedCentralInfo.get() || builtIn }); // fall back to built-in
273
+ o ||= { ...cachedCentralInfo.get() || builtIn }; // fall back to built-in
277
274
  // merge byVersions info in the main object, but collect alerts separately, to preserve multiple instances
278
275
  const allAlerts = [o.alert];
279
276
  for (const [ver, more] of Object.entries((0, misc_1.popKey)(o, 'byVersion') || {}))
@@ -296,4 +293,4 @@ exports.getProjectInfo = (0, misc_1.debounceAsync)(() => argv_1.argv.central ===
296
293
  (0, plugins_1.enablePlugin)(p.id, false);
297
294
  }
298
295
  return o;
299
- }), { retain: misc_1.HOUR, retainFailure: 60000 });
296
+ }), { retain: misc_1.HOUR, retainFailure: 60_000 });
package/src/i18n.js CHANGED
@@ -21,11 +21,10 @@ function i18nFromTranslations(translations, embedded = 'en') {
21
21
  }
22
22
  // If one of the keys is an "id", that should be the first. If one of the keys should work as a fallback, that should be the last. Use 'fallback' parameter if you don't want the fallback to work as a key.
23
23
  function t(keyOrTpl, params, fallback) {
24
- var _a, _b;
25
24
  if (!keyOrTpl)
26
25
  return '';
27
26
  // memoize?
28
- const keys = isTemplateStringsArray(keyOrTpl) ? [(fallback !== null && fallback !== void 0 ? fallback : (fallback = keyOrTpl[0]))]
27
+ const keys = isTemplateStringsArray(keyOrTpl) ? [(fallback ??= keyOrTpl[0])]
29
28
  : Array.isArray(keyOrTpl) ? keyOrTpl : [keyOrTpl];
30
29
  if (typeof params === 'string' && !fallback) {
31
30
  fallback = params;
@@ -38,7 +37,7 @@ function i18nFromTranslations(translations, embedded = 'en') {
38
37
  if (!state.disabled)
39
38
  for (const key of keys) {
40
39
  for (const lang of searchLangs)
41
- if (found = (_b = (_a = state.translations[selectedLang = lang]) === null || _a === void 0 ? void 0 : _a.translate) === null || _b === void 0 ? void 0 : _b[key])
40
+ if (found = state.translations[selectedLang = lang]?.translate?.[key])
42
41
  break;
43
42
  if (found)
44
43
  break;
@@ -111,7 +110,7 @@ function i18nFromTranslations(translations, embedded = 'en') {
111
110
  yield [s.slice(ofs), false];
112
111
  }
113
112
  function isTemplateStringsArray(x) {
114
- return (x === null || x === void 0 ? void 0 : x.raw) && Array.isArray(x);
113
+ return x?.raw && Array.isArray(x);
115
114
  }
116
115
  return {
117
116
  t,
package/src/icons.js CHANGED
@@ -24,7 +24,7 @@ function watchIconsFolder(parentFolder, cb) {
24
24
  }
25
25
  cb(res);
26
26
  }
27
- catch (_a) {
27
+ catch {
28
28
  cb(undefined);
29
29
  } // no such dir
30
30
  }), true);
package/src/index.js CHANGED
@@ -4,7 +4,6 @@
4
4
  var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  return (mod && mod.__esModule) ? mod : { "default": mod };
6
6
  };
7
- var _a;
8
7
  Object.defineProperty(exports, "__esModule", { value: true });
9
8
  exports.app = void 0;
10
9
  const koa_1 = __importDefault(require("koa"));
@@ -39,7 +38,7 @@ if (new config_1.Version(process.versions.node).olderThan('18.15.0')) {
39
38
  process.exit(2);
40
39
  }
41
40
  process.title = 'HFS ' + const_1.VERSION;
42
- const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(','))
41
+ const keys = process.env.COOKIE_SIGN_KEYS?.split(',')
43
42
  || [(0, misc_1.randomId)(30)]; // randomness at start gives some extra security, btu also invalidates existing sessions
44
43
  exports.app = new koa_1.default({ keys });
45
44
  exports.app.use(middlewares_1.sessionMiddleware)
@@ -78,7 +77,7 @@ process.on('uncaughtException', (err) => {
78
77
  try {
79
78
  console.error("uncaught:", err);
80
79
  }
81
- catch (_a) { } // in case we are writing to a closed terminal, we may throw with "write eio at afterwritedispatched", causing an infinite loop
80
+ catch { } // in case we are writing to a closed terminal, we may throw with "write eio at afterwritedispatched", causing an infinite loop
82
81
  });
83
82
  // this warning is scaring users, and has been removed in node 20.12.0 https://github.com/nodejs/node/pull/51204
84
83
  const original = process.emitWarning;
package/src/lang.js CHANGED
@@ -24,7 +24,7 @@ function code2file(code) {
24
24
  function file2code(fn) {
25
25
  return fn.replace(PREFIX, '').replace(SUFFIX, '');
26
26
  }
27
- const cache = (0, expiringCache_1.expiringCache)(3000); // 3 seconds for both a good dx and acceptable performance
27
+ const cache = (0, expiringCache_1.expiringCache)(3_000); // 3 seconds for both a good dx and acceptable performance
28
28
  async function getLangData(ctxOrLangCsv) {
29
29
  if (typeof ctxOrLangCsv !== 'string') {
30
30
  const ctx = ctxOrLangCsv;
@@ -35,7 +35,6 @@ async function getLangData(ctxOrLangCsv) {
35
35
  }
36
36
  const csv = ctxOrLangCsv.toLowerCase();
37
37
  return cache.try(csv, async () => {
38
- var _a;
39
38
  const langs = csv.split(',');
40
39
  const ret = {};
41
40
  let i = 0;
@@ -47,7 +46,7 @@ async function getLangData(ctxOrLangCsv) {
47
46
  try {
48
47
  ret[k] = JSON.parse(await (0, promises_1.readFile)(fn, 'utf8'));
49
48
  } // allow external files to override embedded translations
50
- catch (_b) {
49
+ catch {
51
50
  if ((0, misc_1.hasProp)(embedded_1.default, k))
52
51
  ret[k] = lodash_1.default.cloneDeep(embedded_1.default[k]);
53
52
  else {
@@ -60,9 +59,9 @@ async function getLangData(ctxOrLangCsv) {
60
59
  }
61
60
  }
62
61
  }
63
- const fromPlugins = (0, misc_1.onlyTruthy)(await Promise.all((0, plugins_1.mapPlugins)(async (pl) => { var _a; return (_a = (0, misc_1.tryJson)(await (0, promises_1.readFile)((0, path_1.join)(pl.folder, fn), 'utf8').catch(() => ''))) === null || _a === void 0 ? void 0 : _a.translate; })));
62
+ const fromPlugins = (0, misc_1.onlyTruthy)(await Promise.all((0, plugins_1.mapPlugins)(async (pl) => (0, misc_1.tryJson)(await (0, promises_1.readFile)((0, path_1.join)(pl.folder, fn), 'utf8').catch(() => ''))?.translate)));
64
63
  if (fromPlugins.length)
65
- lodash_1.default.defaults((_a = (ret[k] || (ret[k] = {}))).translate || (_a.translate = {}), ...fromPlugins); // be sure we have an entry for k
64
+ lodash_1.default.defaults((ret[k] ||= {}).translate ||= {}, ...fromPlugins); // be sure we have an entry for k
66
65
  i++;
67
66
  }
68
67
  return ret;
@@ -71,7 +70,7 @@ async function getLangData(ctxOrLangCsv) {
71
70
  let forceLangData;
72
71
  let undo;
73
72
  (0, config_1.defineConfig)(misc_1.CFG.force_lang, '', v => {
74
- undo === null || undo === void 0 ? void 0 : undo();
73
+ undo?.();
75
74
  if (!v)
76
75
  return forceLangData = undefined;
77
76
  const translation = embedded_1.default[v];