@camstack/addon-provider-petkit 0.2.73 → 0.2.75

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
@@ -34595,6 +34595,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34595
34595
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34596
34596
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34597
34597
  }
34598
+ /**
34599
+ * A top-level persisted row with NO configuration is a leftover, not a
34600
+ * device — quarantine it instead of dialling it.
34601
+ *
34602
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34603
+ * already in flight (D376), and then dialled at every single boot: eight
34604
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34605
+ * each one the same Zod refusal on `host` and `password`. The bound was
34606
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34607
+ * about the row ever stopped costing attempts and log lines.
34608
+ *
34609
+ * No retry can add a configuration that is not there. The row is reported
34610
+ * once, with its `deviceId`, and set aside.
34611
+ *
34612
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34613
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34614
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34615
+ * genuinely create configuration-free top-level devices turn this off.
34616
+ */
34617
+ quarantineConfiglessTopLevelRestores = true;
34618
+ isUnrestorableShell(saved) {
34619
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34620
+ if (saved.parentDeviceId !== null) return false;
34621
+ const config = saved.config;
34622
+ if (config === void 0) return false;
34623
+ return Object.keys(config).length === 0;
34624
+ }
34625
+ quarantineShell(saved) {
34626
+ this._permanentRestoreFailures.set(saved.id, {
34627
+ deviceId: saved.id,
34628
+ stableId: saved.stableId,
34629
+ type: saved.type,
34630
+ attempts: 0,
34631
+ lastError: "persisted row has no configuration",
34632
+ failedAt: Date.now()
34633
+ });
34634
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34635
+ tags: {
34636
+ deviceId: saved.id,
34637
+ stableId: saved.stableId
34638
+ },
34639
+ meta: {
34640
+ type: saved.type,
34641
+ provider: this.providerName
34642
+ }
34643
+ });
34644
+ }
34598
34645
  cancelRestoreRetries() {
34599
34646
  this._restoreRetryScheduler?.cancel();
34600
34647
  this._restoreRetryScheduler = null;
@@ -34779,6 +34826,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34779
34826
  });
34780
34827
  return;
34781
34828
  }
34829
+ if (this.isUnrestorableShell(saved)) {
34830
+ this.quarantineShell(saved);
34831
+ return;
34832
+ }
34782
34833
  try {
34783
34834
  await attemptRestore(saved);
34784
34835
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -34594,6 +34594,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34594
34594
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34595
34595
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34596
34596
  }
34597
+ /**
34598
+ * A top-level persisted row with NO configuration is a leftover, not a
34599
+ * device — quarantine it instead of dialling it.
34600
+ *
34601
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34602
+ * already in flight (D376), and then dialled at every single boot: eight
34603
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34604
+ * each one the same Zod refusal on `host` and `password`. The bound was
34605
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34606
+ * about the row ever stopped costing attempts and log lines.
34607
+ *
34608
+ * No retry can add a configuration that is not there. The row is reported
34609
+ * once, with its `deviceId`, and set aside.
34610
+ *
34611
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34612
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34613
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34614
+ * genuinely create configuration-free top-level devices turn this off.
34615
+ */
34616
+ quarantineConfiglessTopLevelRestores = true;
34617
+ isUnrestorableShell(saved) {
34618
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34619
+ if (saved.parentDeviceId !== null) return false;
34620
+ const config = saved.config;
34621
+ if (config === void 0) return false;
34622
+ return Object.keys(config).length === 0;
34623
+ }
34624
+ quarantineShell(saved) {
34625
+ this._permanentRestoreFailures.set(saved.id, {
34626
+ deviceId: saved.id,
34627
+ stableId: saved.stableId,
34628
+ type: saved.type,
34629
+ attempts: 0,
34630
+ lastError: "persisted row has no configuration",
34631
+ failedAt: Date.now()
34632
+ });
34633
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34634
+ tags: {
34635
+ deviceId: saved.id,
34636
+ stableId: saved.stableId
34637
+ },
34638
+ meta: {
34639
+ type: saved.type,
34640
+ provider: this.providerName
34641
+ }
34642
+ });
34643
+ }
34597
34644
  cancelRestoreRetries() {
34598
34645
  this._restoreRetryScheduler?.cancel();
34599
34646
  this._restoreRetryScheduler = null;
@@ -34778,6 +34825,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34778
34825
  });
34779
34826
  return;
34780
34827
  }
34828
+ if (this.isUnrestorableShell(saved)) {
34829
+ this.quarantineShell(saved);
34830
+ return;
34831
+ }
34781
34832
  try {
34782
34833
  await attemptRestore(saved);
34783
34834
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-petkit",
3
- "version": "0.2.73",
3
+ "version": "0.2.75",
4
4
  "description": "PetKit smart-feeder device-provider addon for CamStack — wraps the @apocaliss92/nodepetkit PetKit cloud client",
5
5
  "keywords": [
6
6
  "camstack",