@homebridge-plugins/homebridge-august 3.1.4 → 3.2.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.
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ const child = require('node:child_process')
4
+ const fs = require('node:fs')
5
+
6
+ const bump = (process.argv[2] || 'patch').toLowerCase()
7
+ const allowed = new Set(['major', 'minor', 'patch'])
8
+
9
+ if (!allowed.has(bump)) {
10
+ console.error(`Invalid bump level '${bump}'. Use major, minor, or patch.`)
11
+ process.exit(1)
12
+ }
13
+
14
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
15
+
16
+ function inc(v, level) {
17
+ const parts = v.split('-')[0].split('+')[0].split('.').map(Number)
18
+ if (level === 'major') {
19
+ parts[0] += 1
20
+ parts[1] = 0
21
+ parts[2] = 0
22
+ } else if (level === 'minor') {
23
+ parts[1] += 1
24
+ parts[2] = 0
25
+ } else {
26
+ parts[2] += 1
27
+ }
28
+ return parts.join('.')
29
+ }
30
+
31
+ function latestStable(name) {
32
+ try {
33
+ const versions = JSON.parse(child.execSync(`npm info ${name} versions --json`).toString('utf8').trim())
34
+ const stable = (Array.isArray(versions) ? versions : [])
35
+ .filter(v => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/.test(v) && !v.includes('-'))
36
+ stable.sort((a, b) => {
37
+ const ap = a.split('.').map(Number)
38
+ const bp = b.split('.').map(Number)
39
+ for (let i = 0; i < 3; i++) {
40
+ if (ap[i] !== bp[i]) return bp[i] - ap[i]
41
+ }
42
+ return 0
43
+ })
44
+ return stable[0] || ''
45
+ } catch {
46
+ return ''
47
+ }
48
+ }
49
+
50
+ const base = latestStable(pkg.name) || pkg.version
51
+ process.stdout.write(inc(base, bump))
package/CHANGELOG.md CHANGED
@@ -1,3 +1,30 @@
1
+ # [3.2.0](https://github.com/homebridge-plugins/homebridge-august/compare/v3.1.4...v3.2.0) (2026-04-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **platform:** use august-yale's typed exceptions for error classification ([93219ac](https://github.com/homebridge-plugins/homebridge-august/commit/93219accdf8571330b8f3fefa029d727ddf7a6c3))
7
+
8
+
9
+
10
+ ## [3.1.4](https://github.com/homebridge-plugins/homebridge-august/compare/v3.1.3...v3.1.4) (2026-04-27)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * **platform:** destroy August client on session refresh instead of end() ([a12f2b3](https://github.com/homebridge-plugins/homebridge-august/commit/a12f2b328263523eef3b2549383f00ce6dfbf0a1))
16
+
17
+
18
+ ### Features
19
+
20
+ * **lock:** wire PubNub reconnect signal into ConnectivityManager ([462b22c](https://github.com/homebridge-plugins/homebridge-august/commit/462b22c5b53e125d3ec31eceea538a74360f5760))
21
+ * **platform:** add ConnectivityManager (unwired) ([6377ac4](https://github.com/homebridge-plugins/homebridge-august/commit/6377ac435320aa884e82b32f25f163b2bd9fde6a))
22
+ * **platform:** platform-level serial poller, drop per-lock interval/retry ([aa973a4](https://github.com/homebridge-plugins/homebridge-august/commit/aa973a4e46cd4252f335a5c6988f94148cafefbf))
23
+ * **platform:** route pushChanges through ConnectivityManager ([41080f4](https://github.com/homebridge-plugins/homebridge-august/commit/41080f490cd71b504e22f56a80ae6e1c8c9eb016))
24
+ * **platform:** wire ConnectivityManager into AugustPlatform ([0ce28e3](https://github.com/homebridge-plugins/homebridge-august/commit/0ce28e359a68720ed278e17097f61262ea8705b4))
25
+
26
+
27
+
1
28
  ## [3.1.2](https://github.com/homebridge-plugins/homebridge-august/compare/v3.1.1...v3.1.2) (2026-04-18)
2
29
 
3
30
  ### Bug Fixes
@@ -27,9 +27,13 @@ export declare class ConnectivityManager {
27
27
  private state;
28
28
  private client?;
29
29
  private probeTimer?;
30
+ private heartbeatTimer?;
30
31
  private backoffIndex;
31
32
  private rebuildInFlight?;
32
33
  private listeners;
34
+ private offlineSinceMs?;
35
+ private probeAttempts;
36
+ private lastFailureMessage?;
33
37
  constructor(log: Logging, credentialsFactory: () => Promise<credentials>,
34
38
  /**
35
39
  * Called whenever the manager builds or rebuilds the August client.
@@ -83,6 +87,9 @@ export declare class ConnectivityManager {
83
87
  private rebuildClient;
84
88
  private classify;
85
89
  private transitionTo;
90
+ private startHeartbeat;
91
+ private stopHeartbeat;
92
+ private emitHeartbeat;
86
93
  private clearProbeTimer;
87
94
  }
88
95
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"connectivity-manager.d.ts","sourceRoot":"","sources":["../src/connectivity-manager.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,MAAwB,MAAM,aAAa,CAAA;AAElD,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAAA;AAyBjF,UAAU,cAAc;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,KAAK,EAAE,MAAM;CAI1B;AAED,qBAAa,mBAAmB;IAS5B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe;IAjBlC,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAC,CAAgB;IACnC,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,eAAe,CAAC,CAAe;IACvC,OAAO,CAAC,SAAS,CAAwE;gBAGtE,GAAG,EAAE,OAAO,EACZ,kBAAkB,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC;IAC/D;;;;;;OAMG;IACc,eAAe,GAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAe;IAG7E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;;;;;;;;;;;OAYG;IACG,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgCzB;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IASzB,QAAQ,IAAI,iBAAiB;IAI7B;;;;;OAKG;IACH,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B;;;;OAIG;IACH,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAKxF,QAAQ,IAAI,IAAI;IAUhB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,aAAa;YAQP,QAAQ;YA8BR,aAAa;IAkB3B,OAAO,CAAC,QAAQ;IAoBhB,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,eAAe;CAMxB"}
1
+ {"version":3,"file":"connectivity-manager.d.ts","sourceRoot":"","sources":["../src/connectivity-manager.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAEhD,OAAO,MAAiE,MAAM,aAAa,CAAA;AAE3F,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,CAAA;AA6BjF,UAAU,cAAc;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAED,qBAAa,YAAa,SAAQ,KAAK;gBACzB,KAAK,EAAE,MAAM;CAI1B;AAED,qBAAa,mBAAmB;IAiB5B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe;IAzBlC,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,MAAM,CAAC,CAAQ;IACvB,OAAO,CAAC,UAAU,CAAC,CAAgB;IACnC,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,eAAe,CAAC,CAAe;IACvC,OAAO,CAAC,SAAS,CAAwE;IAKzF,OAAO,CAAC,cAAc,CAAC,CAAQ;IAC/B,OAAO,CAAC,aAAa,CAAI;IACzB,OAAO,CAAC,kBAAkB,CAAC,CAAQ;gBAGhB,GAAG,EAAE,OAAO,EACZ,kBAAkB,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC;IAC/D;;;;;;OAMG;IACc,eAAe,GAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAe;IAG7E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ3B;;;;;;;;;;;;OAYG;IACG,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAClC,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAgCzB;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IAYzB,QAAQ,IAAI,iBAAiB;IAI7B;;;;;OAKG;IACH,SAAS,IAAI,MAAM,GAAG,SAAS;IAI/B;;;;OAIG;IACH,aAAa,CAAC,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAKxF,QAAQ,IAAI,IAAI;IAWhB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,aAAa;YAQP,QAAQ;YAqER,aAAa;IAkB3B,OAAO,CAAC,QAAQ;IAkDhB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,eAAe;CAMxB"}
@@ -1,4 +1,4 @@
1
- import August, { TimeoutError } from 'august-yale';
1
+ import August, { AbortedError, InvalidAuth, NetworkError, TimeoutError } from 'august-yale';
2
2
  /**
3
3
  * Backoff schedule for probes after the network is detected as broken.
4
4
  * Capped at 5 minutes; ±10% jitter is applied at use to avoid synchronized
@@ -7,16 +7,20 @@ import August, { TimeoutError } from 'august-yale';
7
7
  const BACKOFF_SCHEDULE_MS = [5_000, 10_000, 20_000, 40_000, 80_000, 160_000, 300_000];
8
8
  /** Probe timeout — short on purpose. A healthy August API responds in <1s. */
9
9
  const PROBE_TIMEOUT_MS = 8_000;
10
- /** Network error codes that classify() treats as connectivity failures. */
11
- const NETWORK_ERROR_CODES = new Set([
12
- 'ECONNRESET',
13
- 'ENETUNREACH',
14
- 'EHOSTUNREACH',
15
- 'ENOTFOUND',
16
- 'ECONNREFUSED',
17
- 'EAI_AGAIN',
18
- 'ETIMEDOUT',
19
- ]);
10
+ /**
11
+ * How often to emit a heartbeat log line while offline.
12
+ *
13
+ * Without it the log goes silent after the initial healthy → degraded →
14
+ * offline transition, making it impossible to distinguish "state machine
15
+ * is working hard, probes are failing every backoff slot" from "state
16
+ * machine is silently stuck". The heartbeat surfaces probe attempts and
17
+ * the most recent failure so future regressions are debuggable from the
18
+ * log alone.
19
+ *
20
+ * One hour matches the typical "I checked on it later" cadence; not so
21
+ * frequent it becomes log spam during a real ISP outage.
22
+ */
23
+ const OFFLINE_HEARTBEAT_INTERVAL_MS = 60 * 60 * 1000;
20
24
  export class OfflineError extends Error {
21
25
  constructor(label) {
22
26
  super(`Operation skipped — connectivity is offline: ${label}`);
@@ -30,9 +34,16 @@ export class ConnectivityManager {
30
34
  state = 'healthy';
31
35
  client;
32
36
  probeTimer;
37
+ heartbeatTimer;
33
38
  backoffIndex = 0;
34
39
  rebuildInFlight;
35
40
  listeners = new Set();
41
+ // Diagnostic counters. Reset on transition out of offline. Used by the
42
+ // heartbeat log so an ongoing outage is visible in the log without
43
+ // having to trace per-probe details.
44
+ offlineSinceMs;
45
+ probeAttempts = 0;
46
+ lastFailureMessage;
36
47
  constructor(log, credentialsFactory,
37
48
  /**
38
49
  * Called whenever the manager builds or rebuilds the August client.
@@ -107,7 +118,10 @@ export class ConnectivityManager {
107
118
  if (this.state === 'healthy') {
108
119
  return;
109
120
  }
110
- this.log.info('Connectivity: PubNub reconnect signal received — probing immediately');
121
+ const offlineFor = this.offlineSinceMs
122
+ ? ` (offline for ${Math.floor((Date.now() - this.offlineSinceMs) / 1000)}s)`
123
+ : '';
124
+ this.log.info(`Connectivity: PubNub reconnect signal received in ${this.state}${offlineFor} — probing immediately`);
111
125
  this.clearProbeTimer();
112
126
  void this.runProbe();
113
127
  }
@@ -134,6 +148,7 @@ export class ConnectivityManager {
134
148
  }
135
149
  shutdown() {
136
150
  this.clearProbeTimer();
151
+ this.stopHeartbeat();
137
152
  this.client?.destroy();
138
153
  this.client = undefined;
139
154
  this.onClientChanged(undefined);
@@ -174,25 +189,61 @@ export class ConnectivityManager {
174
189
  this.probeTimer = setTimeout(() => void this.runProbe(), jittered);
175
190
  }
176
191
  async runProbe() {
177
- if (!this.client) {
178
- return;
192
+ this.probeAttempts++;
193
+ let client = this.client;
194
+ if (!client) {
195
+ // No client at all — rebuild from credentials. This branch is
196
+ // hit only at startup if init() hasn't completed; in steady
197
+ // state we always have a client.
198
+ this.log.warn(`Connectivity: probe attempt #${this.probeAttempts} — no client to probe; trying to (re)build`);
199
+ try {
200
+ await this.rebuildClient('probe with no client');
201
+ }
202
+ catch (e) {
203
+ this.lastFailureMessage = `probe-build: ${e?.message ?? e}`;
204
+ this.scheduleProbe();
205
+ return;
206
+ }
207
+ client = this.client;
208
+ if (!client) {
209
+ // rebuildClient resolved but didn't actually populate this.client.
210
+ // Defensive: don't proceed without a client; reschedule and try
211
+ // again on the next backoff slot.
212
+ this.lastFailureMessage = 'probe-build: rebuildClient produced no client';
213
+ this.scheduleProbe();
214
+ return;
215
+ }
179
216
  }
217
+ // Reset the transport BEFORE probing.
218
+ //
219
+ // The previous implementation probed against the existing client's
220
+ // socket pool. That pool may hold stale half-open sockets from
221
+ // before the outage; probing against them just keeps failing on
222
+ // dead connections, so the system stayed offline even after the
223
+ // network had recovered. The PubNub-triggered immediate probe
224
+ // hit the same wall.
225
+ //
226
+ // resetTransport() throws away the dispatcher and creates a fresh
227
+ // one without losing auth state, the session token, or
228
+ // configuration. Distinct from destroy() + new August() — that
229
+ // would force a re-auth round-trip on the next call, which itself
230
+ // can fail on a still-flaky network and leave us stuck. With just
231
+ // a transport reset, the next request gets a clean connection
232
+ // pool and reuses the cached session token; if it succeeds, we're
233
+ // healthy in one round-trip.
234
+ client.resetTransport();
235
+ this.log.info(`Connectivity: probe attempt #${this.probeAttempts} (state=${this.state})`);
180
236
  try {
181
- // Cheap authenticated call against the EXISTING client. .locks()
182
- // returns a small map and is the lightest endpoint. Race against a
183
- // short timeout — the goal is "is the network working", not
184
- // "is everything healthy".
185
237
  await Promise.race([
186
- this.client.locks(),
238
+ client.locks(),
187
239
  new Promise((_resolve, reject) => setTimeout(() => reject(new TimeoutError('probe timed out')), PROBE_TIMEOUT_MS)),
188
240
  ]);
189
- // Network is back. Rebuild the client to shed any half-open sockets
190
- // accumulated before the outage, then mark healthy.
191
- await this.rebuildClient('probe succeeded');
241
+ this.log.info(`Connectivity: probe attempt #${this.probeAttempts} succeeded`);
192
242
  this.transitionTo('healthy');
193
243
  this.backoffIndex = 0;
194
244
  }
195
- catch {
245
+ catch (e) {
246
+ this.lastFailureMessage = `${this.classify(e)}: ${e?.message ?? e}`;
196
247
  if (this.state !== 'offline') {
197
248
  this.log.warn('Connectivity: probe failed — entering offline state');
198
249
  this.transitionTo('offline');
@@ -220,22 +271,51 @@ export class ConnectivityManager {
220
271
  }
221
272
  }
222
273
  classify(e) {
223
- const err = e;
224
- if (err?.name === 'TimeoutError') {
274
+ // august-yale wraps every transport-level fetch failure into one of
275
+ // its typed exceptions before the error reaches us. We rely on those
276
+ // types here rather than re-implementing undici's 24-class taxonomy.
277
+ //
278
+ // - NetworkError (and its TimeoutError subclass) covers connect
279
+ // timeouts, socket resets, headers/body timeouts, DNS failures,
280
+ // TLS proxy failures, malformed responses, etc.
281
+ //
282
+ // - AbortedError reflects an in-flight request racing with our own
283
+ // teardown (e.g. during rebuildClient). NOT 'network': the consumer
284
+ // intent was to tear down, so we don't want this to drive the
285
+ // state machine into degraded.
286
+ //
287
+ // - InvalidAuth is august-yale's auth-failure type. Maps to 'auth'.
288
+ //
289
+ // Anything else (including programmer errors and server-answered
290
+ // 4xx/5xx that august-yale leaves unwrapped) is 'transient' — bubble
291
+ // back to the caller, don't change connectivity state.
292
+ if (e instanceof NetworkError) {
225
293
  return 'network';
226
294
  }
227
- if (err?.code && NETWORK_ERROR_CODES.has(err.code)) {
228
- return 'network';
295
+ if (e instanceof AbortedError) {
296
+ return 'transient';
229
297
  }
230
- if (err?.message && /ETIMEDOUT|ECONNRESET|ENOTFOUND|ENETUNREACH/i.test(err.message)) {
298
+ if (e instanceof TimeoutError) {
299
+ // Defensive: TimeoutError is also a NetworkError so the first
300
+ // check should match. Kept for clarity in case of future
301
+ // exception hierarchy changes upstream.
231
302
  return 'network';
232
303
  }
233
- if (err?.statusCode === 502 || err?.statusCode === 503 || err?.statusCode === 504) {
234
- return 'network';
304
+ if (e instanceof InvalidAuth) {
305
+ return 'auth';
235
306
  }
307
+ // Fallback for HTTP-level errors that august-yale leaves unwrapped
308
+ // (e.g. server answered with a non-2xx without bare-401 semantics).
309
+ const err = e;
236
310
  if (err?.statusCode === 401) {
237
311
  return 'auth';
238
312
  }
313
+ if (err?.statusCode === 502 || err?.statusCode === 503 || err?.statusCode === 504) {
314
+ // 5xx responses where the server is reachable but reporting an
315
+ // upstream issue. Treat as network — these are recoverable by
316
+ // waiting and retrying (matches the state-machine semantic).
317
+ return 'network';
318
+ }
239
319
  return 'transient';
240
320
  }
241
321
  transitionTo(next) {
@@ -245,6 +325,19 @@ export class ConnectivityManager {
245
325
  }
246
326
  this.state = next;
247
327
  this.log.info(`Connectivity: ${prev} -> ${next}`);
328
+ // Heartbeat lifecycle: start when entering offline, stop when leaving.
329
+ // offlineSinceMs is set on entry; probe-attempt count is NOT reset
330
+ // here because runProbe() already incremented it for the failure
331
+ // that triggered this transition.
332
+ if (next === 'offline') {
333
+ this.offlineSinceMs = Date.now();
334
+ this.startHeartbeat();
335
+ }
336
+ else if (prev === 'offline') {
337
+ this.stopHeartbeat();
338
+ this.offlineSinceMs = undefined;
339
+ this.probeAttempts = 0;
340
+ }
248
341
  for (const l of this.listeners) {
249
342
  try {
250
343
  l(next, prev);
@@ -254,6 +347,31 @@ export class ConnectivityManager {
254
347
  }
255
348
  }
256
349
  }
350
+ startHeartbeat() {
351
+ this.stopHeartbeat();
352
+ this.heartbeatTimer = setInterval(() => this.emitHeartbeat(), OFFLINE_HEARTBEAT_INTERVAL_MS);
353
+ // Allow Node to exit if this is the only outstanding timer; we
354
+ // don't want the heartbeat to keep the process alive on shutdown.
355
+ if (typeof this.heartbeatTimer.unref === 'function') {
356
+ this.heartbeatTimer.unref();
357
+ }
358
+ }
359
+ stopHeartbeat() {
360
+ if (this.heartbeatTimer) {
361
+ clearInterval(this.heartbeatTimer);
362
+ this.heartbeatTimer = undefined;
363
+ }
364
+ }
365
+ emitHeartbeat() {
366
+ if (this.state !== 'offline' || this.offlineSinceMs === undefined) {
367
+ return;
368
+ }
369
+ const minutes = Math.floor((Date.now() - this.offlineSinceMs) / 60000);
370
+ const lastFailure = this.lastFailureMessage ?? 'unknown';
371
+ this.log.warn(`Connectivity: still offline after ${minutes} min — `
372
+ + `${this.probeAttempts} probe attempt${this.probeAttempts === 1 ? '' : 's'}, `
373
+ + `last failure: ${lastFailure}`);
374
+ }
257
375
  clearProbeTimer() {
258
376
  if (this.probeTimer) {
259
377
  clearTimeout(this.probeTimer);
@@ -1 +1 @@
1
- {"version":3,"file":"connectivity-manager.js","sourceRoot":"","sources":["../src/connectivity-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connectivity-manager.js","sourceRoot":"","sources":["../src/connectivity-manager.ts"],"names":[],"mappings":"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"}
package/dist/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@homebridge-plugins/homebridge-august",
3
3
  "displayName": "August",
4
4
  "type": "module",
5
- "version": "3.1.4",
5
+ "version": "3.2.1",
6
6
  "description": "The August plugin allows you to access your August & Yale device(s) from HomeKit.",
7
7
  "author": {
8
8
  "name": "donavanbecker",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@homebridge/plugin-ui-utils": "^2.2.3",
65
- "august-yale": "^1.1.8",
65
+ "august-yale": "^1.2.1",
66
66
  "rxjs": "^7.8.2"
67
67
  },
68
68
  "devDependencies": {
@@ -1 +1 @@
1
- <!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @homebridge-plugins/homebridge-august</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-august"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@homebridge-plugins/homebridge-august</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=&gt;</span> <span class="tsd-signature-type">void</span></div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/homebridge-plugins/homebridge-august/blob/7aa04c9920e4f249142a4d51ef7cae1598b732f6/src/index.ts#L26">index.ts:26</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@homebridge-plugins/homebridge-august</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
1
+ <!DOCTYPE html><html class="default" lang="en" data-base="../"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>default | @homebridge-plugins/homebridge-august</title><meta name="description" content="Documentation for @homebridge-plugins/homebridge-august"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="../assets/style.css"/><link rel="stylesheet" href="../assets/highlight.css"/><script defer src="../assets/main.js"></script><script async src="../assets/icons.js" id="tsd-icons-script"></script><script async src="../assets/search.js" id="tsd-search-script"></script><script async src="../assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => window.app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><a href="../index.html" class="title">@homebridge-plugins/homebridge-august</a><div id="tsd-toolbar-links"></div><button id="tsd-search-trigger" class="tsd-widget" aria-label="Search"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-search"></use></svg></button><dialog id="tsd-search" aria-label="Search"><input role="combobox" id="tsd-search-input" aria-controls="tsd-search-results" aria-autocomplete="list" aria-expanded="true" autocapitalize="off" autocomplete="off" placeholder="Search the docs" maxLength="100"/><ul role="listbox" id="tsd-search-results"></ul><div id="tsd-search-status" aria-live="polite" aria-atomic="true"><div>Preparing search index...</div></div></dialog><a href="#" class="tsd-widget menu" id="tsd-toolbar-menu-trigger" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-menu"></use></svg></a></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><ul class="tsd-breadcrumb" aria-label="Breadcrumb"><li><a href="" aria-current="page">default</a></li></ul><h1>Variable default</h1></div><div class="tsd-signature"><span class="tsd-kind-variable">default</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span> <span class="tsd-signature-symbol">=&gt;</span> <span class="tsd-signature-type">void</span></div><div class="tsd-type-declaration"><h4>Type Declaration</h4><ul class="tsd-parameters"><li class="tsd-parameter-signature"><ul class="tsd-signatures"><li class="tsd-signature" id="__type"><span class="tsd-signature-symbol">(</span><span class="tsd-kind-parameter">api</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">API</span><span class="tsd-signature-symbol">)</span><span class="tsd-signature-symbol">:</span> <span class="tsd-signature-type">void</span></li><li class="tsd-description"><div class="tsd-parameters"><h4 class="tsd-parameters-title">Parameters</h4><ul class="tsd-parameter-list"><li><span><span class="tsd-kind-parameter">api</span>: <span class="tsd-signature-type">API</span></span></li></ul></div><h4 class="tsd-returns-title">Returns <span class="tsd-signature-type">void</span></h4></li></ul></li></ul></div><aside class="tsd-sources"><ul><li>Defined in <a href="https://github.com/homebridge-plugins/homebridge-august/blob/6ea4f34cb710cbff333e71d84b048bb61d34ea64/src/index.ts#L26">index.ts:26</a></li></ul></aside></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true"><use href="../assets/icons.svg#icon-chevronDown"></use></svg><h3>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="../modules.html">@homebridge-plugins/homebridge-august</a><ul class="tsd-small-nested-navigation" id="tsd-nav-container"><li>Loading...</li></ul></nav></div></div></div><footer></footer><div class="overlay"></div></body></html>
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@homebridge-plugins/homebridge-august",
3
3
  "displayName": "August",
4
4
  "type": "module",
5
- "version": "3.1.4",
5
+ "version": "3.2.1",
6
6
  "description": "The August plugin allows you to access your August & Yale device(s) from HomeKit.",
7
7
  "author": {
8
8
  "name": "donavanbecker",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@homebridge/plugin-ui-utils": "^2.2.3",
65
- "august-yale": "^1.1.8",
65
+ "august-yale": "^1.2.1",
66
66
  "rxjs": "^7.8.2"
67
67
  },
68
68
  "devDependencies": {
@@ -17,16 +17,49 @@ import { ConnectivityManager, OfflineError } from '../src/connectivity-manager.j
17
17
 
18
18
  // Mock the august-yale module. We need:
19
19
  // - August: a constructor we can instantiate, with details() and destroy()
20
- // - TimeoutError: a real error class so `name === 'TimeoutError'` works
20
+ // - The full exception hierarchy that classify() now uses for routing:
21
+ // NetworkError, TimeoutError (subclass), AbortedError, InvalidAuth.
21
22
  vi.mock('august-yale', () => {
22
- class TimeoutError extends Error {
23
- constructor(message?: string) {
23
+ class YaleApiError extends Error {
24
+ public originalError?: Error
25
+ constructor(message?: string, originalError?: Error) {
24
26
  super(message)
27
+ this.name = 'YaleApiError'
28
+ this.originalError = originalError
29
+ }
30
+ }
31
+ class NetworkError extends YaleApiError {
32
+ public code?: string
33
+ constructor(message?: string, originalError?: Error, code?: string) {
34
+ super(message, originalError)
35
+ this.name = 'NetworkError'
36
+ this.code = code
37
+ }
38
+ }
39
+ class TimeoutError extends NetworkError {
40
+ constructor(message?: string, originalError?: Error, code?: string) {
41
+ super(message, originalError, code)
25
42
  this.name = 'TimeoutError'
26
43
  }
27
44
  }
45
+ class AbortedError extends YaleApiError {
46
+ public code?: string
47
+ constructor(message?: string, originalError?: Error, code?: string) {
48
+ super(message, originalError)
49
+ this.name = 'AbortedError'
50
+ this.code = code
51
+ }
52
+ }
53
+ class InvalidAuth extends YaleApiError {
54
+ constructor(message?: string, originalError?: Error) {
55
+ super(message, originalError)
56
+ this.name = 'InvalidAuth'
57
+ }
58
+ }
28
59
  // Each new August() returns a unique object so tests can assert which
29
- // client is being used (initial vs. rebuilt).
60
+ // client is being used (initial vs. rebuilt). Tests can also override
61
+ // the methods of the NEXT client to be built by setting fields on
62
+ // `nextClientOverride`. After one use the override is cleared.
30
63
  let nextId = 0
31
64
  // eslint-disable-next-line prefer-arrow-callback
32
65
  const MockAugust = vi.fn().mockImplementation(function () {
@@ -38,12 +71,17 @@ vi.mock('august-yale', () => {
38
71
  lock: vi.fn().mockResolvedValue(undefined),
39
72
  unlock: vi.fn().mockResolvedValue(undefined),
40
73
  destroy: vi.fn(),
74
+ resetTransport: vi.fn(),
41
75
  end: vi.fn(),
42
76
  }
43
77
  })
44
78
  return {
45
79
  default: MockAugust,
80
+ YaleApiError,
81
+ NetworkError,
46
82
  TimeoutError,
83
+ AbortedError,
84
+ InvalidAuth,
47
85
  }
48
86
  })
49
87
 
@@ -188,23 +226,57 @@ describe('ConnectivityManager', () => {
188
226
  expect(m.getState()).toBe('degraded')
189
227
  })
190
228
 
191
- it('treats 502/503/504 as network errors', async () => {
229
+ it('treats NetworkError as network and transitions to degraded', async () => {
230
+ // NetworkError covers all transport-level failures from august-yale —
231
+ // socket reset, DNS failure, malformed response, etc. — without us
232
+ // having to enumerate undici's 24 error classes.
192
233
  const m = new ConnectivityManager(makeLog(), fakeCredentials)
193
234
  await m.init()
194
- const err = Object.assign(new Error('Bad Gateway'), { statusCode: 502 })
195
- await m.execute('test', async () => { throw err })
235
+ const { NetworkError } = await import('august-yale')
236
+ const result = await m.execute('test', async () => { throw new NetworkError('socket hang up', undefined, 'ECONNRESET') })
237
+ expect(result).toBeUndefined()
196
238
  expect(m.getState()).toBe('degraded')
197
239
  })
198
240
 
199
- it('treats ECONNRESET / ENETUNREACH / ENOTFOUND as network errors', async () => {
200
- for (const code of ['ECONNRESET', 'ENETUNREACH', 'ENOTFOUND', 'ECONNREFUSED']) {
201
- const m = new ConnectivityManager(makeLog(), fakeCredentials)
202
- await m.init()
203
- const err = Object.assign(new Error(code), { code })
204
- await m.execute('test', async () => { throw err })
205
- expect(m.getState(), `code=${code}`).toBe('degraded')
206
- m.shutdown()
207
- }
241
+ it('treats InvalidAuth as auth and rebuilds without changing state', async () => {
242
+ const onClientChanged = vi.fn()
243
+ const m = new ConnectivityManager(makeLog(), fakeCredentials, onClientChanged)
244
+ await m.init()
245
+ expect(onClientChanged).toHaveBeenCalledTimes(1)
246
+ const { InvalidAuth } = await import('august-yale')
247
+ const result = await m.execute('test', async () => { throw new InvalidAuth('session expired') })
248
+ expect(result).toBeUndefined()
249
+ await vi.runAllTimersAsync()
250
+ expect(onClientChanged).toHaveBeenCalledTimes(2)
251
+ expect(m.getState()).toBe('healthy')
252
+ })
253
+
254
+ it('treats AbortedError as transient and does NOT change connectivity state', async () => {
255
+ // AbortedError reflects a request racing with our own teardown
256
+ // (e.g. inside rebuildClient). Treating it as 'network' would
257
+ // make the state machine fight itself: every rebuild produces
258
+ // ClientDestroyedError -> degraded -> probe -> rebuild ->
259
+ // ClientDestroyedError -> ...
260
+ const m = new ConnectivityManager(makeLog(), fakeCredentials)
261
+ await m.init()
262
+ const { AbortedError } = await import('august-yale')
263
+ const err = new AbortedError('client destroyed', undefined, 'UND_ERR_DESTROYED')
264
+ await expect(
265
+ m.execute('test', async () => { throw err }),
266
+ ).rejects.toBe(err)
267
+ // State stays healthy — no probe scheduled.
268
+ expect(m.getState()).toBe('healthy')
269
+ })
270
+
271
+ it('treats 502/503/504 as network errors (HTTP-level fallback)', async () => {
272
+ // Most transport failures are now wrapped upstream as NetworkError,
273
+ // but if august-yale ever surfaces a clean HTTP 5xx response, we
274
+ // still want to recognize 502/503/504 as network-class and probe.
275
+ const m = new ConnectivityManager(makeLog(), fakeCredentials)
276
+ await m.init()
277
+ const err = Object.assign(new Error('Bad Gateway'), { statusCode: 502 })
278
+ await m.execute('test', async () => { throw err })
279
+ expect(m.getState()).toBe('degraded')
208
280
  })
209
281
  })
210
282
 
@@ -213,7 +285,6 @@ describe('ConnectivityManager', () => {
213
285
  const onClientChanged = vi.fn()
214
286
  const m = new ConnectivityManager(makeLog(), fakeCredentials, onClientChanged)
215
287
  await m.init()
216
- const client = m.getClient() as any
217
288
  expect(onClientChanged).toHaveBeenCalledTimes(1)
218
289
 
219
290
  // Network failure: degrades and schedules probe.
@@ -221,15 +292,17 @@ describe('ConnectivityManager', () => {
221
292
  await m.execute('test', async () => { throw new TimeoutError('boom') })
222
293
  expect(m.getState()).toBe('degraded')
223
294
 
224
- // Probe will call client.locks() — ensure it succeeds.
225
- client.locks.mockResolvedValue({})
295
+ // Probe calls resetTransport() on the existing client and then its
296
+ // .locks(). Default mock = locks resolves, so probe succeeds.
226
297
 
227
298
  // Advance time past the first backoff slot (~5s plus jitter).
228
299
  await vi.advanceTimersByTimeAsync(7_000)
229
300
 
230
- // Probe success → rebuild + healthy. onClientChanged fires again.
301
+ // Probe success → healthy. onClientChanged was called only once
302
+ // (at init). The resetTransport-based design does NOT swap the
303
+ // client instance on recovery; it just recycles its transport.
231
304
  expect(m.getState()).toBe('healthy')
232
- expect(onClientChanged).toHaveBeenCalledTimes(2)
305
+ expect(onClientChanged).toHaveBeenCalledTimes(1)
233
306
  })
234
307
 
235
308
  it('escalates from degraded to offline when probe fails, with longer backoff', async () => {
@@ -238,14 +311,18 @@ describe('ConnectivityManager', () => {
238
311
  const client = m.getClient() as any
239
312
 
240
313
  const { TimeoutError } = await import('august-yale')
241
- await m.execute('test', async () => { throw new TimeoutError('boom') })
242
- expect(m.getState()).toBe('degraded')
243
314
 
244
- // Probe will fail (locks() rejects with timeout).
245
- client.locks.mockImplementation(() => new Promise((_resolve, reject) => {
315
+ // Make the existing client's .locks() always fail. With the
316
+ // resetTransport-based design, probes reuse this client, so a
317
+ // single mockImplementation is sufficient for every probe attempt.
318
+ client.locks = vi.fn().mockImplementation(() => new Promise((_resolve, reject) => {
246
319
  setTimeout(() => reject(new TimeoutError('still down')), 100)
247
320
  }))
248
321
 
322
+ // Trigger degraded.
323
+ await m.execute('test', async () => { throw new TimeoutError('boom') })
324
+ expect(m.getState()).toBe('degraded')
325
+
249
326
  // Advance past first backoff + probe timeout.
250
327
  await vi.advanceTimersByTimeAsync(15_000)
251
328
  expect(m.getState()).toBe('offline')
@@ -275,6 +352,74 @@ describe('ConnectivityManager', () => {
275
352
  expect(result).toBe('recovered')
276
353
  expect(m.getState()).toBe('healthy')
277
354
  })
355
+
356
+ it('probes call resetTransport() on the existing client (sheds stale sockets)', async () => {
357
+ // Regression test for the production bug: the original
358
+ // implementation probed against the existing client without
359
+ // resetting its socket pool. The pool kept stale half-open sockets
360
+ // from before the outage, probes failed on those sockets, system
361
+ // stayed offline.
362
+ //
363
+ // The fix: probe still uses the existing client (so we keep auth
364
+ // state), but resetTransport() is called first to throw away the
365
+ // dispatcher and its stale connections. New transport, same auth.
366
+ const m = new ConnectivityManager(makeLog(), fakeCredentials)
367
+ await m.init()
368
+ const client = m.getClient() as any
369
+
370
+ // Sanity: the mock client has a resetTransport method.
371
+ expect(typeof client.resetTransport).toBe('function')
372
+
373
+ // Trigger degraded.
374
+ const { TimeoutError } = await import('august-yale')
375
+ await m.execute('test', async () => { throw new TimeoutError('boom') })
376
+ expect(m.getState()).toBe('degraded')
377
+
378
+ // Advance past the first backoff: probe runs, default mock has
379
+ // .locks() resolving, recovery completes.
380
+ await vi.advanceTimersByTimeAsync(7_000)
381
+ expect(m.getState()).toBe('healthy')
382
+
383
+ // The probe must have called resetTransport on the existing
384
+ // client. This is the structural assertion that locks in the fix.
385
+ expect(client.resetTransport).toHaveBeenCalled()
386
+ // The same client instance is still in place (we did NOT
387
+ // construct a new client — this distinguishes the resetTransport
388
+ // approach from the previous fresh-client approach).
389
+ expect(m.getClient()).toBe(client)
390
+ })
391
+
392
+ it('failed probe leaves the existing client in place (no leak, no rotation)', async () => {
393
+ // With resetTransport(), failed probes don't construct a temp
394
+ // client, so there's nothing to leak. The existing client stays
395
+ // the current client throughout the offline period — only its
396
+ // transport gets recycled, not the August instance itself.
397
+ const m = new ConnectivityManager(makeLog(), fakeCredentials)
398
+ await m.init()
399
+ const client = m.getClient() as any
400
+
401
+ const { TimeoutError } = await import('august-yale')
402
+
403
+ // Make the client's .locks() always fail.
404
+ client.locks = vi.fn().mockImplementation(() => new Promise((_, reject) =>
405
+ setTimeout(() => reject(new TimeoutError('still down')), 100),
406
+ ))
407
+
408
+ // Trigger degraded.
409
+ await m.execute('test', async () => { throw new TimeoutError('boom') })
410
+ expect(m.getState()).toBe('degraded')
411
+
412
+ // Advance past first probe → offline.
413
+ await vi.advanceTimersByTimeAsync(15_000)
414
+ expect(m.getState()).toBe('offline')
415
+
416
+ // The existing client is still the current client; nothing got
417
+ // destroyed or replaced.
418
+ expect(m.getClient()).toBe(client)
419
+ expect(client.destroy).not.toHaveBeenCalled()
420
+ // resetTransport was called for each probe attempt.
421
+ expect(client.resetTransport).toHaveBeenCalled()
422
+ })
278
423
  })
279
424
 
280
425
  describe('onPubNubReconnect — fast recovery', () => {
@@ -282,21 +427,23 @@ describe('ConnectivityManager', () => {
282
427
  const onClientChanged = vi.fn()
283
428
  const m = new ConnectivityManager(makeLog(), fakeCredentials, onClientChanged)
284
429
  await m.init()
285
- const client = m.getClient() as any
286
430
 
287
431
  const { TimeoutError } = await import('august-yale')
288
432
  await m.execute('test', async () => { throw new TimeoutError('boom') })
289
433
  expect(m.getState()).toBe('degraded')
290
434
 
291
- client.locks.mockResolvedValue({})
435
+ // PubNub reconnect → immediate probe. Default mock has .locks()
436
+ // resolving, so the probe (which calls resetTransport on the
437
+ // existing client and then locks()) succeeds.
292
438
  m.onPubNubReconnect()
293
439
 
294
440
  // Probe runs synchronously inside onPubNubReconnect's microtask;
295
441
  // need to flush promises but no timer advance required.
296
442
  await vi.runAllTimersAsync()
297
443
  expect(m.getState()).toBe('healthy')
298
- // Initial build + rebuild after successful probe.
299
- expect(onClientChanged).toHaveBeenCalledTimes(2)
444
+ // onClientChanged was only called once (at init). Recovery via
445
+ // resetTransport does not swap the client instance.
446
+ expect(onClientChanged).toHaveBeenCalledTimes(1)
300
447
  })
301
448
 
302
449
  it('is a no-op when already healthy', async () => {
@@ -377,4 +524,67 @@ describe('ConnectivityManager', () => {
377
524
  expect(good).toHaveBeenCalledTimes(1)
378
525
  })
379
526
  })
527
+
528
+ describe('offline heartbeat', () => {
529
+ // Helper: drive the manager into offline state and capture the log.
530
+ async function getOfflineWithLog() {
531
+ const log = makeLog()
532
+ const m = new ConnectivityManager(log, fakeCredentials)
533
+ await m.init()
534
+ const client = m.getClient() as any
535
+
536
+ const { TimeoutError } = await import('august-yale')
537
+
538
+ // Make every probe fail by making the existing client's .locks
539
+ // always reject. With the resetTransport-based design, probes
540
+ // reuse this client across attempts, so a single mockImplementation
541
+ // covers every probe in the offline period.
542
+ client.locks = vi.fn().mockImplementation(() => new Promise((_resolve, reject) =>
543
+ setTimeout(() => reject(new TimeoutError('still down')), 100),
544
+ ))
545
+
546
+ // Trigger degraded.
547
+ await m.execute('test', async () => { throw new TimeoutError('boom') })
548
+
549
+ // Drive past first probe so we land in offline.
550
+ await vi.advanceTimersByTimeAsync(15_000)
551
+ return { m, log, client }
552
+ }
553
+
554
+ it('emits a heartbeat after one hour of being offline', async () => {
555
+ const { log } = await getOfflineWithLog()
556
+
557
+ // Less than an hour: no heartbeat yet.
558
+ await vi.advanceTimersByTimeAsync(30 * 60_000)
559
+ const beforeHour = log._calls.filter(c => c.msg.includes('still offline')).length
560
+ expect(beforeHour).toBe(0)
561
+
562
+ // Past one hour: heartbeat fires. Probes are still failing in the
563
+ // background, so we filter for the heartbeat-specific message.
564
+ await vi.advanceTimersByTimeAsync(35 * 60_000)
565
+ const afterHour = log._calls.filter(c => c.msg.includes('still offline')).length
566
+ expect(afterHour).toBeGreaterThan(0)
567
+ // Heartbeat content includes attempt count and last failure.
568
+ const heartbeatLine = log._calls.find(c => c.msg.includes('still offline'))
569
+ expect(heartbeatLine?.msg).toMatch(/\d+ probe attempts?/)
570
+ expect(heartbeatLine?.msg).toContain('last failure')
571
+ })
572
+
573
+ it('stops emitting heartbeats after recovery', async () => {
574
+ const { m, log, client } = await getOfflineWithLog()
575
+
576
+ // Allow recovery on the next probe by making .locks resolve again.
577
+ client.locks = vi.fn().mockResolvedValue({})
578
+
579
+ // Skip ahead enough for a probe to fire and succeed.
580
+ await vi.advanceTimersByTimeAsync(60 * 1000)
581
+ expect(m.getState()).toBe('healthy')
582
+
583
+ const beforeIdle = log._calls.filter(c => c.msg.includes('still offline')).length
584
+ // Advance two more hours — should NOT see new heartbeat lines.
585
+ await vi.advanceTimersByTimeAsync(2 * 60 * 60_000)
586
+ const afterIdle = log._calls.filter(c => c.msg.includes('still offline')).length
587
+ expect(afterIdle).toBe(beforeIdle)
588
+ })
589
+ })
380
590
  })