spinuptui 0.21.0 → 0.21.1

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/CHANGELOG.md CHANGED
@@ -11,6 +11,20 @@ versions; such changes are called out here.
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.21.1] - 2026-07-10
15
+
16
+ ### Fixed
17
+ - **Uptime Kuma connection now falls back to HTTP long-polling.** The client
18
+ previously connected over WebSocket only; a reverse proxy or CDN in front of
19
+ a Kuma instance that doesn't forward the WebSocket upgrade headers made the
20
+ connection fail outright instead of degrading gracefully. It now tries
21
+ polling as a fallback transport.
22
+ - **Connection failures no longer swallow the real reason.** A failed connect
23
+ could render as `Couldn't reach Uptime Kuma at https://…: ` with nothing
24
+ after the colon — the underlying error's message was empty and got used
25
+ as-is. The error message now falls back through the error's description and
26
+ code before giving up, so a connection failure is actually diagnosable.
27
+
14
28
  ## [0.21.0] - 2026-07-09
15
29
 
16
30
  ### Added
@@ -1059,7 +1073,8 @@ Initial tagged release.
1059
1073
  ### Notes
1060
1074
  - Read-only release: works with a SpinupWP **Read Only** API token.
1061
1075
 
1062
- [Unreleased]: https://github.com/mwender/spinupwp-tui/compare/v0.21.0...HEAD
1076
+ [Unreleased]: https://github.com/mwender/spinupwp-tui/compare/v0.21.1...HEAD
1077
+ [0.21.1]: https://github.com/mwender/spinupwp-tui/compare/v0.21.0...v0.21.1
1063
1078
  [0.21.0]: https://github.com/mwender/spinupwp-tui/compare/v0.20.1...v0.21.0
1064
1079
  [0.20.1]: https://github.com/mwender/spinupwp-tui/compare/v0.20.0...v0.20.1
1065
1080
  [0.20.0]: https://github.com/mwender/spinupwp-tui/compare/v0.19.1...v0.20.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spinuptui",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "description": "SpinupTUI — a terminal dashboard for browsing, monitoring, and managing your SpinupWP servers and sites.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -67,6 +67,23 @@ interface Ack {
67
67
  const ACK_TIMEOUT_MS = 15_000
68
68
  const CONNECT_TIMEOUT_MS = 10_000
69
69
 
70
+ // engine.io wraps most transport failures in a TransportError whose `.message`
71
+ // is a fixed literal ("websocket error") with the real reason tucked away in
72
+ // `.description`; a few failure modes (a WebSocket constructor throwing
73
+ // synchronously — seen from Bun's net/tls shim on cert issues) skip that
74
+ // wrapper and hand us a bare Error whose `.message` can be empty. Walk every
75
+ // place a reason might live rather than trusting `.message` alone, so a
76
+ // connect failure never renders as a bare trailing colon.
77
+ function describeConnectError(e: unknown): string {
78
+ const err = e as { message?: string; description?: unknown; code?: string } | undefined
79
+ if (err?.message) return err.message
80
+ const desc = err?.description
81
+ if (desc instanceof Error && desc.message) return desc.message
82
+ if (typeof desc === "string" && desc) return desc
83
+ if (err?.code) return err.code
84
+ return String(e)
85
+ }
86
+
70
87
  // 32 chars, [A-Za-z0-9] — same shape the Kuma UI generates for push monitors.
71
88
  // (Kuma stores whatever the client sends; the token is chosen client-side.)
72
89
  export function genPushToken(): string {
@@ -155,7 +172,10 @@ export class KumaClient {
155
172
 
156
173
  static async connect(url: string): Promise<KumaClient> {
157
174
  const base = url.replace(/\/+$/, "")
158
- const socket = io(base, { transports: ["websocket"], reconnection: false, timeout: CONNECT_TIMEOUT_MS })
175
+ // Polling as a fallback (not websocket-only): a reverse proxy/CDN in front of
176
+ // Kuma that doesn't forward the WebSocket Upgrade headers otherwise hard-fails
177
+ // the connection outright instead of degrading gracefully.
178
+ const socket = io(base, { transports: ["websocket", "polling"], reconnection: false, timeout: CONNECT_TIMEOUT_MS })
159
179
  const client = new KumaClient(socket)
160
180
  await new Promise<void>((resolve, reject) => {
161
181
  const t = setTimeout(() => {
@@ -169,7 +189,7 @@ export class KumaClient {
169
189
  socket.once("connect_error", (e: Error) => {
170
190
  clearTimeout(t)
171
191
  socket.disconnect()
172
- reject(new KumaError(`Couldn't reach Uptime Kuma at ${base}: ${e.message}`))
192
+ reject(new KumaError(`Couldn't reach Uptime Kuma at ${base}: ${describeConnectError(e)}`))
173
193
  })
174
194
  })
175
195
  return client