svelte-realtime 0.6.0-next.1 → 0.6.0-next.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/client.js +36 -3
  2. package/package.json +1 -1
package/client.js CHANGED
@@ -253,13 +253,37 @@ const _healthStore = writable(/** @type {'healthy' | 'degraded'} */ ('healthy'))
253
253
  /** @type {(() => void) | null} */
254
254
  let _healthUnsub = null;
255
255
 
256
+ // Two independent inputs OR into the single health state: a server-pushed
257
+ // degraded/recovered event on the system topic, and the connection's local
258
+ // internal flow-control pressure (a queued/refused flow-controlled send).
259
+ // Tracked separately so neither input clobbers the other - health is degraded
260
+ // while EITHER is degraded, healthy only when BOTH are clear.
261
+ let _healthServerDegraded = false;
262
+ let _healthFlowDegraded = false;
263
+
264
+ function _recomputeHealth() {
265
+ _healthStore.set(_healthServerDegraded || _healthFlowDegraded ? 'degraded' : 'healthy');
266
+ }
267
+
256
268
  function _ensureHealthSubscription() {
257
269
  if (_healthUnsub) return;
258
- _healthUnsub = on(_HEALTH_TOPIC).subscribe((envelope) => {
270
+ const offTopic = on(_HEALTH_TOPIC).subscribe((envelope) => {
259
271
  if (!envelope) return;
260
- if (envelope.event === 'degraded') _healthStore.set('degraded');
261
- else if (envelope.event === 'recovered') _healthStore.set('healthy');
272
+ if (envelope.event === 'degraded') { _healthServerDegraded = true; _recomputeHealth(); }
273
+ else if (envelope.event === 'recovered') { _healthServerDegraded = false; _recomputeHealth(); }
262
274
  });
275
+ // Fold the connection's internal flow-control health in as a second,
276
+ // OR-ed input. A boolean is the only thing that crosses this accessor;
277
+ // no internal accounting value surfaces. Older adapter connections that
278
+ // predate the accessor simply do not contribute this input.
279
+ let offFlow = () => {};
280
+ try {
281
+ const conn = _connect();
282
+ if (conn && typeof conn._onLeaseDegraded === 'function') {
283
+ offFlow = conn._onLeaseDegraded((d) => { _healthFlowDegraded = !!d; _recomputeHealth(); });
284
+ }
285
+ } catch { /* connection not configured yet; flow health stays clear */ }
286
+ _healthUnsub = () => { offTopic(); offFlow(); };
263
287
  }
264
288
 
265
289
  /**
@@ -297,9 +321,18 @@ export function _resetHealth() {
297
321
  _healthUnsub();
298
322
  _healthUnsub = null;
299
323
  }
324
+ _healthServerDegraded = false;
325
+ _healthFlowDegraded = false;
300
326
  _healthStore.set('healthy');
301
327
  }
302
328
 
329
+ // Flow control is owned end to end by the adapter connection's send gate: it
330
+ // advertises the capability, paces its own flow-controlled sends against the
331
+ // server's window, and reports a single degraded boolean. The realtime layer
332
+ // consumes that boolean through conn._onLeaseDegraded in
333
+ // _ensureHealthSubscription above and ORs it into realtime.health. There is no
334
+ // realtime-owned mirror of the gate - a second copy would only drift.
335
+
303
336
  function _registerTopicErrorSetter(topic, setError) {
304
337
  let set = _streamErrorByTopic.get(topic);
305
338
  if (!set) { set = new Set(); _streamErrorByTopic.set(topic, set); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-realtime",
3
- "version": "0.6.0-next.1",
3
+ "version": "0.6.0-next.2",
4
4
  "publishConfig": {
5
5
  "tag": "next"
6
6
  },