hfs 3.1.1 → 3.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": [
6
6
  "file server",
@@ -24,7 +24,7 @@
24
24
  "test": "node --import tsx --test tests/test.ts",
25
25
  "test-with-server": "sh -c 'npm run port-is-free && tsc && rm -rf tests/work tests/tmp && (node dist/src --cwd tests/work --config tests & echo $! > .server_pid) && sleep 2 && node --import tsx --test \"$@\" tests/test.ts; _exit=$?; if [ -f ./.server_pid ]; then SERVER_PID=$(cat ./.server_pid); kill \"$SERVER_PID\" 2>/dev/null || true; rm -f ./.server_pid; fi; exit $_exit' --",
26
26
  "port-is-free": "node -e \"const port=process.argv[1]||8081;process.exit(await fetch('http://localhost:'+port).then(() => console.log('BUSY')||1, () => 0))\" --",
27
- "test-ui": "npx playwright test frontend && npx playwright test serial && npx playwright test admin-vfs",
27
+ "test-ui": "npm run port-is-free -- 8081 && rm -rf tests/work tests/work2 && npx playwright test frontend && npx playwright test serial && npx playwright test admin-vfs",
28
28
  "test-with-ui": "sh -c 'npm run port-is-free -- 3005 && npm run start-frontend & npm run port-is-free -- 3006 && npm run start-admin & cross-env TEST_WITH_UI=1 npx playwright test --ui \"$@\"' --",
29
29
  "pub": "cd dist && npm publish",
30
30
  "dist": "STASHED=; if ! git diff-index --quiet HEAD --; then git stash push -m 'dist' && STASHED=1; fi; CI=1 FORCE_COLOR=1 npm run dist-uncommitted || (EXIT_CODE=$?; [ -n \"$STASHED\" ] && git stash pop; exit $EXIT_CODE); [ -n \"$STASHED\" ] && git stash pop",
@@ -82,7 +82,8 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@gregoranders/csv": "^0.0.13",
85
- "@rejetto/kvstorage": "^0.17.6",
85
+ "@rejetto/kvstorage": "^0.17.7",
86
+ "@rejetto/nat-upnp": "^2.1.3",
86
87
  "acme-client": "^5.4.0",
87
88
  "busboy": "^1.6.0",
88
89
  "crc-32": "^1.2.2",
@@ -102,7 +103,6 @@
102
103
  "lodash": "^4.17.21",
103
104
  "mime-types": "*",
104
105
  "minimist": "^1.2.8",
105
- "nat-upnp-rejetto": "^2.1.2",
106
106
  "node-forge": "^1.3.1",
107
107
  "open": "^8.4.0",
108
108
  "picomatch": "^4.0.3",
package/src/first.js CHANGED
@@ -1,32 +1,50 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.quitting = void 0;
6
+ exports.exitCode = exports.quitting = void 0;
4
7
  exports.onProcessExit = onProcessExit;
8
+ exports.quit = quit;
5
9
  exports.onFirstEvent = onFirstEvent;
10
+ const lodash_1 = __importDefault(require("lodash"));
11
+ const assert_1 = __importDefault(require("assert"));
6
12
  const cbsOnExit = new Set();
7
- function onProcessExit(cb) {
8
- cbsOnExit.add(cb);
9
- return () => cbsOnExit.delete(cb);
13
+ function onProcessExit(cb, order = 10) {
14
+ (0, assert_1.default)(Number.isInteger(order) && order >= 0, 'order must be an integer >= 0');
15
+ const rec = { cb, order };
16
+ cbsOnExit.add(rec);
17
+ return () => cbsOnExit.delete(rec);
10
18
  }
11
19
  exports.quitting = false;
20
+ exports.exitCode = 0;
12
21
  // 'exit' event is handled as the last resort, but it's not compatible with async callbacks
13
- onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP'], signal => {
22
+ onFirstEvent(process, ['exit', 'SIGQUIT', 'SIGTERM', 'SIGINT', 'SIGHUP', 'beforeExit'], async (signal) => {
14
23
  console.log('Quitting with signal:', signal || 'unknown');
15
24
  exports.quitting = true;
16
- return Promise.allSettled(Array.from(cbsOnExit).map(cb => {
17
- try {
18
- return cb(signal);
19
- }
20
- // keep exit moving even when a synchronous cleanup fails after partially shutting down
21
- catch (e) {
22
- console.error("Error while quitting", e);
23
- return Promise.reject(e);
24
- }
25
- })).then(() => {
26
- console.debug('Process exit');
27
- process.exit(0);
28
- });
25
+ const byOrder = lodash_1.default.groupBy(Array.from(cbsOnExit), 'order'); // this will be inherently ordered because keys are positive integers
26
+ for (const recs of Object.values(byOrder)) {
27
+ const ret = Promise.allSettled(recs.map(({ cb }) => {
28
+ try {
29
+ return cb(signal);
30
+ }
31
+ // keep exit moving even when a synchronous cleanup fails after partially shutting down
32
+ catch (e) {
33
+ console.error("Error while quitting", e);
34
+ return Promise.reject(e);
35
+ }
36
+ }));
37
+ if (signal !== 'exit') // exit is sync
38
+ await ret;
39
+ }
40
+ cbsOnExit.clear();
41
+ console.debug('Process exit');
42
+ process.exit(exports.exitCode);
29
43
  });
44
+ function quit(code = 0) {
45
+ exports.exitCode = code;
46
+ process.emit('SIGINT');
47
+ }
30
48
  // keep calling cb in a sync fashion – returning a promise instead would break the code for argv.updating (update.ts)
31
49
  function onFirstEvent(emitter, events, cb) {
32
50
  let already = false;
package/src/listen.js CHANGED
@@ -70,8 +70,8 @@ const consoleLog_1 = require("./consoleLog");
70
70
  const first_1 = require("./first");
71
71
  let httpSrv;
72
72
  let httpsSrv;
73
- // update relaunch can keep a bridge process alive, so we proactively close listeners here to release ports before the next binary binds
74
- (0, first_1.onProcessExit)(() => Promise.all([stopServer(httpSrv), stopServer(httpsSrv)]));
73
+ // the update relaunch can keep a bridge process alive, so we proactively close listeners here to release ports before the next binary binds; do it before (5) the storage file is closed, because sockets write there
74
+ (0, first_1.onProcessExit)(() => Promise.all([stopServer(httpSrv), stopServer(httpsSrv)]), 5);
75
75
  const openBrowserAtStart = (0, config_1.defineConfig)('open_browser_at_start', true);
76
76
  exports.baseUrl = (0, config_1.defineConfig)(misc_1.CFG.base_url, '', x => /(?<=\/\/)[^\/]+/.exec(x)?.[0]); // compiled is host only
77
77
  async function getBaseUrlOrDefault() {
package/src/nat.js CHANGED
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getNatInfo = exports.getPublicIps = exports.upnpClient = exports.defaultBaseUrl = void 0;
7
7
  const valtio_1 = require("valtio");
8
- const nat_upnp_rejetto_1 = require("nat-upnp-rejetto");
8
+ const nat_upnp_1 = require("@rejetto/nat-upnp");
9
9
  const debounceAsync_1 = require("./debounceAsync");
10
10
  const cross_1 = require("./cross");
11
11
  const github_1 = require("./github");
@@ -30,7 +30,7 @@ exports.defaultBaseUrl = (0, valtio_1.proxy)({
30
30
  return `${this.proto}://${(0, cross_1.ipForUrl)(ip || 'localhost')}${!port || port === defPort ? '' : ':' + port}`;
31
31
  }
32
32
  });
33
- exports.upnpClient = new nat_upnp_rejetto_1.Client({ timeout: 4_000 });
33
+ exports.upnpClient = new nat_upnp_1.Client({ timeout: 4_000 });
34
34
  const originalMethod = exports.upnpClient.getGateway;
35
35
  // other client methods call getGateway too, so this will ensure they reuse this same result
36
36
  exports.upnpClient.getGateway = (0, debounceAsync_1.debounceAsync)(() => originalMethod.apply(exports.upnpClient), { retain: cross_1.HOUR, retainFailure: 30_000 });
package/src/update.js CHANGED
@@ -201,7 +201,8 @@ async function update(tagOrUrl = '') {
201
201
  (0, child_process_1.spawnSync)((0, util_os_1.cmdEscape)(newBin), ['--updating', binFile, '--cwd .'], { shell: true, stdio: [0, 1, 2] }); // sync necessary to work on Mac by double-click
202
202
  });
203
203
  console.log("Quitting");
204
- setTimeout(() => process.exit(100)); // non-zero, otherwise some service managers (like Shawl) won't restart the process
204
+ setTimeout(() => (0, first_1.quit)(100), // non-zero, otherwise some service managers (like Shawl) won't restart the process.
205
+ 200); // give time to return (and caller to complete, eg: rest api to reply)
205
206
  }
206
207
  catch (e) {
207
208
  plugins_1.pluginsWatcher.unpause();
package/src/webdav.js CHANGED
@@ -75,9 +75,12 @@ const webdav = async (ctx, next) => {
75
75
  const ua = ctx.get('user-agent');
76
76
  if (path.includes('/._') && ua?.startsWith('WebDAVFS')) { // too much spam from Finder for these files that can contain metas
77
77
  ctx.state.dontLog = true;
78
+ ctx.state.webdavDetected = true;
78
79
  return ctx.status = cross_1.HTTP_FORBIDDEN;
79
80
  }
80
81
  const isWebdavAuthRequest = WEBDAV_METHODS.has(ctx.method) || WEBDAV_HINT_HEADERS.some(h => ctx.get(h));
82
+ if (isWebdavAuthRequest)
83
+ ctx.state.webdavDetected = true;
81
84
  if (isWebdavAuthRequest && ua && (0, auth_1.getCurrentUsername)(ctx))
82
85
  webdavDetectedAgents.try(webdavAgentKey(ctx, ua), () => true);
83
86
  if (ctx.method === 'OPTIONS') {