kahu-signalk 0.0.17 → 0.0.18

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": "kahu-signalk",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "Contribute AIS and ARPA targets from your vessel to crowdsourcing for marine safety!",
5
5
  "keywords": [
6
6
  "signalk-node-server-plugin",
@@ -75,7 +75,7 @@ class Connector {
75
75
  const content = response.Response;
76
76
 
77
77
  if (content["kahu.ErrorResponseMessage"] !== undefined) {
78
- throw content.Error.exception;
78
+ throw content["kahu.ErrorResponseMessage"].exception;
79
79
  } else if (type !== undefined && content[type] === undefined) {
80
80
  throw (
81
81
  "Received response for wrong method: expected " +
package/plugin/mayara.js CHANGED
@@ -25,6 +25,7 @@ class MayaraIngestor {
25
25
  this.reconnectMs = 1000;
26
26
  this.wsFailCount = 0;
27
27
  this.destroyed = false;
28
+ this.halted = false;
28
29
 
29
30
  this.radarIds = [];
30
31
  this.targetUuids = new Map();
@@ -53,9 +54,10 @@ class MayaraIngestor {
53
54
  }
54
55
 
55
56
  async start() {
57
+ if (this.halted) return;
56
58
  this._log("start", { message: "starting ingestor" });
57
59
  await this._discoverRadars();
58
- this._connectWebSocket();
60
+ if (!this.halted) this._connectWebSocket();
59
61
  }
60
62
 
61
63
  async destroy() {
@@ -77,7 +79,43 @@ class MayaraIngestor {
77
79
  this._log("stop", { message: "ingestor destroyed" });
78
80
  }
79
81
 
82
+ _halt(reason, details = {}) {
83
+ if (this.halted || this.destroyed) return;
84
+ this.halted = true;
85
+ this._log("halted", { reason, ...details });
86
+
87
+ if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
88
+ if (this.pollTimer) clearInterval(this.pollTimer);
89
+ this.reconnectTimer = null;
90
+ this.pollTimer = null;
91
+
92
+ if (this.ws) {
93
+ try {
94
+ this.ws.close();
95
+ } catch (err) {
96
+ this._log("ws_close_error", { error: err.message });
97
+ }
98
+ }
99
+ this.ws = null;
100
+ }
101
+
102
+ _isServerUnreachable(err) {
103
+ if (!err) return false;
104
+ const message = String(err?.message || "").toLowerCase();
105
+ const code = String(err?.code || err?.cause?.code || "").toUpperCase();
106
+ return (
107
+ code === "ECONNREFUSED" ||
108
+ code === "ENOTFOUND" ||
109
+ code === "EHOSTUNREACH" ||
110
+ code === "ETIMEDOUT" ||
111
+ message.includes("fetch failed") ||
112
+ message.includes("connection refused") ||
113
+ message.includes("host unreachable")
114
+ );
115
+ }
116
+
80
117
  async _discoverRadars() {
118
+ if (this.destroyed || this.halted) return;
81
119
  this._log("discover", { message: "requesting radar list" });
82
120
  try {
83
121
  const res = await fetch(
@@ -102,6 +140,12 @@ class MayaraIngestor {
102
140
  this._log("discover_ok", { radarCount: this.radarIds.length });
103
141
  } catch (err) {
104
142
  this._log("discover_error", { error: err.message });
143
+ if (this._isServerUnreachable(err)) {
144
+ this._halt("discover-unreachable", {
145
+ error: err.message,
146
+ code: err?.code || err?.cause?.code || null,
147
+ });
148
+ }
105
149
  }
106
150
  }
107
151
 
@@ -114,7 +158,7 @@ class MayaraIngestor {
114
158
  }
115
159
 
116
160
  _connectWebSocket() {
117
- if (this.destroyed) return;
161
+ if (this.destroyed || this.halted) return;
118
162
  const wsUrl = this._buildWsUrl();
119
163
  this._log("ws_connecting", { wsUrl });
120
164
 
@@ -138,12 +182,20 @@ class MayaraIngestor {
138
182
  });
139
183
 
140
184
  this.ws.on("message", (raw) => this._handleDelta(raw));
141
- this.ws.on("error", (err) => this._log("ws_error", { error: err.message }));
185
+ this.ws.on("error", (err) => {
186
+ this._log("ws_error", { error: err.message });
187
+ if (this._isServerUnreachable(err)) {
188
+ this._halt("ws-unreachable", {
189
+ error: err.message,
190
+ code: err?.code || err?.cause?.code || null,
191
+ });
192
+ }
193
+ });
142
194
  this.ws.on("close", () => this._scheduleReconnect());
143
195
  }
144
196
 
145
197
  _scheduleReconnect() {
146
- if (this.destroyed) return;
198
+ if (this.destroyed || this.halted) return;
147
199
  this.wsFailCount += 1;
148
200
  this._log(
149
201
  "ws_close",
@@ -263,7 +315,7 @@ class MayaraIngestor {
263
315
  }
264
316
 
265
317
  _startHttpFallback() {
266
- if (this.pollTimer || this.destroyed) return;
318
+ if (this.pollTimer || this.destroyed || this.halted) return;
267
319
  this._log("http_fallback_start", { reason: "ws-unstable" });
268
320
  this.pollTimer = setInterval(async () => {
269
321
  if (this.destroyed) return;