@tiflis-io/tiflis-code-tunnel 0.3.0 → 0.3.2

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 (2) hide show
  1. package/dist/main.js +34 -5
  2. package/package.json +2 -2
package/dist/main.js CHANGED
@@ -967,29 +967,51 @@ var WebSocketServerWrapper = class {
967
967
  return this.wss?.clients.size ?? 0;
968
968
  }
969
969
  /**
970
- * Closes the WebSocket server gracefully.
970
+ * Closes the WebSocket server gracefully with timeout.
971
+ * @param timeoutMs - Maximum time to wait for graceful close (default: 5000ms)
971
972
  */
972
- async close() {
973
+ async close(timeoutMs = 5e3) {
973
974
  if (this.heartbeatInterval) {
974
975
  clearInterval(this.heartbeatInterval);
975
976
  this.heartbeatInterval = null;
976
977
  }
977
978
  const wss = this.wss;
978
979
  if (!wss) return;
980
+ const clientCount = wss.clients.size;
981
+ this.logger.info({ clientCount }, "Closing WebSocket server");
979
982
  wss.clients.forEach((client) => {
980
- client.close(1001, "Server shutting down");
983
+ try {
984
+ client.close(1001, "Server shutting down");
985
+ } catch {
986
+ }
981
987
  });
982
- return new Promise((resolve, reject) => {
988
+ const closePromise = new Promise((resolve, reject) => {
983
989
  wss.close((err) => {
984
990
  if (err) {
985
991
  this.logger.error({ error: err }, "Error closing WebSocket server");
986
992
  reject(err);
987
993
  } else {
988
- this.logger.info("WebSocket server closed");
994
+ this.logger.info("WebSocket server closed gracefully");
989
995
  resolve();
990
996
  }
991
997
  });
992
998
  });
999
+ const timeoutPromise = new Promise((resolve) => {
1000
+ setTimeout(() => {
1001
+ this.logger.warn(
1002
+ { timeoutMs, remainingClients: wss.clients.size },
1003
+ "WebSocket graceful close timed out, forcing termination"
1004
+ );
1005
+ wss.clients.forEach((client) => {
1006
+ try {
1007
+ client.terminate();
1008
+ } catch {
1009
+ }
1010
+ });
1011
+ resolve();
1012
+ }, timeoutMs);
1013
+ });
1014
+ await Promise.race([closePromise, timeoutPromise]);
993
1015
  }
994
1016
  };
995
1017
 
@@ -2111,14 +2133,21 @@ async function bootstrap() {
2111
2133
  logger.fatal({ error }, "Failed to start server");
2112
2134
  process.exit(1);
2113
2135
  }
2136
+ const SHUTDOWN_TIMEOUT_MS = 1e4;
2114
2137
  const shutdown = async (signal) => {
2115
2138
  logger.info({ signal }, "Shutdown signal received");
2139
+ const forceExitTimer = setTimeout(() => {
2140
+ logger.error("Shutdown timed out, forcing exit");
2141
+ process.exit(1);
2142
+ }, SHUTDOWN_TIMEOUT_MS);
2116
2143
  try {
2117
2144
  await wsServer.close();
2118
2145
  await app.close();
2146
+ clearTimeout(forceExitTimer);
2119
2147
  logger.info("Shutdown complete");
2120
2148
  process.exit(0);
2121
2149
  } catch (error) {
2150
+ clearTimeout(forceExitTimer);
2122
2151
  logger.error({ error }, "Error during shutdown");
2123
2152
  process.exit(1);
2124
2153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiflis-io/tiflis-code-tunnel",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Tunnel server for tiflis-code - reverse proxy for workstation connections",
5
5
  "author": "Roman Barinov <rbarinov@gmail.com>",
6
6
  "license": "FSL-1.1-NC",
@@ -53,7 +53,7 @@
53
53
  "vitest": "^2.1.8"
54
54
  },
55
55
  "engines": {
56
- "node": ">=22.0.0"
56
+ "node": ">=24.0.0"
57
57
  },
58
58
  "scripts": {
59
59
  "dev": "tsx watch src/main.ts",