@camstack/addon-provider-rtsp 1.2.73 → 1.2.74

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/dist/addon.js CHANGED
@@ -33636,6 +33636,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33636
33636
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33637
33637
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33638
33638
  }
33639
+ /**
33640
+ * A top-level persisted row with NO configuration is a leftover, not a
33641
+ * device — quarantine it instead of dialling it.
33642
+ *
33643
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33644
+ * already in flight (D376), and then dialled at every single boot: eight
33645
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33646
+ * each one the same Zod refusal on `host` and `password`. The bound was
33647
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33648
+ * about the row ever stopped costing attempts and log lines.
33649
+ *
33650
+ * No retry can add a configuration that is not there. The row is reported
33651
+ * once, with its `deviceId`, and set aside.
33652
+ *
33653
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33654
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33655
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33656
+ * genuinely create configuration-free top-level devices turn this off.
33657
+ */
33658
+ quarantineConfiglessTopLevelRestores = true;
33659
+ isUnrestorableShell(saved) {
33660
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33661
+ if (saved.parentDeviceId !== null) return false;
33662
+ const config = saved.config;
33663
+ if (config === void 0) return false;
33664
+ return Object.keys(config).length === 0;
33665
+ }
33666
+ quarantineShell(saved) {
33667
+ this._permanentRestoreFailures.set(saved.id, {
33668
+ deviceId: saved.id,
33669
+ stableId: saved.stableId,
33670
+ type: saved.type,
33671
+ attempts: 0,
33672
+ lastError: "persisted row has no configuration",
33673
+ failedAt: Date.now()
33674
+ });
33675
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33676
+ tags: {
33677
+ deviceId: saved.id,
33678
+ stableId: saved.stableId
33679
+ },
33680
+ meta: {
33681
+ type: saved.type,
33682
+ provider: this.providerName
33683
+ }
33684
+ });
33685
+ }
33639
33686
  cancelRestoreRetries() {
33640
33687
  this._restoreRetryScheduler?.cancel();
33641
33688
  this._restoreRetryScheduler = null;
@@ -33820,6 +33867,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33820
33867
  });
33821
33868
  return;
33822
33869
  }
33870
+ if (this.isUnrestorableShell(saved)) {
33871
+ this.quarantineShell(saved);
33872
+ return;
33873
+ }
33823
33874
  try {
33824
33875
  await attemptRestore(saved);
33825
33876
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33612,6 +33612,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33612
33612
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33613
33613
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33614
33614
  }
33615
+ /**
33616
+ * A top-level persisted row with NO configuration is a leftover, not a
33617
+ * device — quarantine it instead of dialling it.
33618
+ *
33619
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33620
+ * already in flight (D376), and then dialled at every single boot: eight
33621
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33622
+ * each one the same Zod refusal on `host` and `password`. The bound was
33623
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33624
+ * about the row ever stopped costing attempts and log lines.
33625
+ *
33626
+ * No retry can add a configuration that is not there. The row is reported
33627
+ * once, with its `deviceId`, and set aside.
33628
+ *
33629
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33630
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33631
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33632
+ * genuinely create configuration-free top-level devices turn this off.
33633
+ */
33634
+ quarantineConfiglessTopLevelRestores = true;
33635
+ isUnrestorableShell(saved) {
33636
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33637
+ if (saved.parentDeviceId !== null) return false;
33638
+ const config = saved.config;
33639
+ if (config === void 0) return false;
33640
+ return Object.keys(config).length === 0;
33641
+ }
33642
+ quarantineShell(saved) {
33643
+ this._permanentRestoreFailures.set(saved.id, {
33644
+ deviceId: saved.id,
33645
+ stableId: saved.stableId,
33646
+ type: saved.type,
33647
+ attempts: 0,
33648
+ lastError: "persisted row has no configuration",
33649
+ failedAt: Date.now()
33650
+ });
33651
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33652
+ tags: {
33653
+ deviceId: saved.id,
33654
+ stableId: saved.stableId
33655
+ },
33656
+ meta: {
33657
+ type: saved.type,
33658
+ provider: this.providerName
33659
+ }
33660
+ });
33661
+ }
33615
33662
  cancelRestoreRetries() {
33616
33663
  this._restoreRetryScheduler?.cancel();
33617
33664
  this._restoreRetryScheduler = null;
@@ -33796,6 +33843,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33796
33843
  });
33797
33844
  return;
33798
33845
  }
33846
+ if (this.isUnrestorableShell(saved)) {
33847
+ this.quarantineShell(saved);
33848
+ return;
33849
+ }
33799
33850
  try {
33800
33851
  await attemptRestore(saved);
33801
33852
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-rtsp",
3
- "version": "1.2.73",
3
+ "version": "1.2.74",
4
4
  "description": "Generic RTSP camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",