@camstack/addon-matter-broker 0.2.72 → 0.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
@@ -33565,6 +33565,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33565
33565
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33566
33566
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33567
33567
  }
33568
+ /**
33569
+ * A top-level persisted row with NO configuration is a leftover, not a
33570
+ * device — quarantine it instead of dialling it.
33571
+ *
33572
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33573
+ * already in flight (D376), and then dialled at every single boot: eight
33574
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33575
+ * each one the same Zod refusal on `host` and `password`. The bound was
33576
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33577
+ * about the row ever stopped costing attempts and log lines.
33578
+ *
33579
+ * No retry can add a configuration that is not there. The row is reported
33580
+ * once, with its `deviceId`, and set aside.
33581
+ *
33582
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33583
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33584
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33585
+ * genuinely create configuration-free top-level devices turn this off.
33586
+ */
33587
+ quarantineConfiglessTopLevelRestores = true;
33588
+ isUnrestorableShell(saved) {
33589
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33590
+ if (saved.parentDeviceId !== null) return false;
33591
+ const config = saved.config;
33592
+ if (config === void 0) return false;
33593
+ return Object.keys(config).length === 0;
33594
+ }
33595
+ quarantineShell(saved) {
33596
+ this._permanentRestoreFailures.set(saved.id, {
33597
+ deviceId: saved.id,
33598
+ stableId: saved.stableId,
33599
+ type: saved.type,
33600
+ attempts: 0,
33601
+ lastError: "persisted row has no configuration",
33602
+ failedAt: Date.now()
33603
+ });
33604
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33605
+ tags: {
33606
+ deviceId: saved.id,
33607
+ stableId: saved.stableId
33608
+ },
33609
+ meta: {
33610
+ type: saved.type,
33611
+ provider: this.providerName
33612
+ }
33613
+ });
33614
+ }
33568
33615
  cancelRestoreRetries() {
33569
33616
  this._restoreRetryScheduler?.cancel();
33570
33617
  this._restoreRetryScheduler = null;
@@ -33749,6 +33796,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33749
33796
  });
33750
33797
  return;
33751
33798
  }
33799
+ if (this.isUnrestorableShell(saved)) {
33800
+ this.quarantineShell(saved);
33801
+ return;
33802
+ }
33752
33803
  try {
33753
33804
  await attemptRestore(saved);
33754
33805
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -33563,6 +33563,53 @@ var BaseDeviceProvider = class extends BaseAddon {
33563
33563
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
33564
33564
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
33565
33565
  }
33566
+ /**
33567
+ * A top-level persisted row with NO configuration is a leftover, not a
33568
+ * device — quarantine it instead of dialling it.
33569
+ *
33570
+ * Device 614 was deleted, resurrected as a config-less shell by a write
33571
+ * already in flight (D376), and then dialled at every single boot: eight
33572
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
33573
+ * each one the same Zod refusal on `host` and `password`. The bound was
33574
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
33575
+ * about the row ever stopped costing attempts and log lines.
33576
+ *
33577
+ * No retry can add a configuration that is not there. The row is reported
33578
+ * once, with its `deviceId`, and set aside.
33579
+ *
33580
+ * Top-level only: an accessory child legitimately carries an empty blob (it
33581
+ * lives off its parent), and a hub-adopted child with an emptied blob is
33582
+ * repaired by `healSavedConfig` from its parent instead. Providers that
33583
+ * genuinely create configuration-free top-level devices turn this off.
33584
+ */
33585
+ quarantineConfiglessTopLevelRestores = true;
33586
+ isUnrestorableShell(saved) {
33587
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
33588
+ if (saved.parentDeviceId !== null) return false;
33589
+ const config = saved.config;
33590
+ if (config === void 0) return false;
33591
+ return Object.keys(config).length === 0;
33592
+ }
33593
+ quarantineShell(saved) {
33594
+ this._permanentRestoreFailures.set(saved.id, {
33595
+ deviceId: saved.id,
33596
+ stableId: saved.stableId,
33597
+ type: saved.type,
33598
+ attempts: 0,
33599
+ lastError: "persisted row has no configuration",
33600
+ failedAt: Date.now()
33601
+ });
33602
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
33603
+ tags: {
33604
+ deviceId: saved.id,
33605
+ stableId: saved.stableId
33606
+ },
33607
+ meta: {
33608
+ type: saved.type,
33609
+ provider: this.providerName
33610
+ }
33611
+ });
33612
+ }
33566
33613
  cancelRestoreRetries() {
33567
33614
  this._restoreRetryScheduler?.cancel();
33568
33615
  this._restoreRetryScheduler = null;
@@ -33747,6 +33794,10 @@ var BaseDeviceProvider = class extends BaseAddon {
33747
33794
  });
33748
33795
  return;
33749
33796
  }
33797
+ if (this.isUnrestorableShell(saved)) {
33798
+ this.quarantineShell(saved);
33799
+ return;
33800
+ }
33750
33801
  try {
33751
33802
  await attemptRestore(saved);
33752
33803
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.2.72",
3
+ "version": "0.2.74",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",