@camstack/addon-provider-onvif 1.2.72 → 1.2.73

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
@@ -30090,6 +30090,53 @@ var BaseDeviceProvider = class extends BaseAddon {
30090
30090
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
30091
30091
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
30092
30092
  }
30093
+ /**
30094
+ * A top-level persisted row with NO configuration is a leftover, not a
30095
+ * device — quarantine it instead of dialling it.
30096
+ *
30097
+ * Device 614 was deleted, resurrected as a config-less shell by a write
30098
+ * already in flight (D376), and then dialled at every single boot: eight
30099
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
30100
+ * each one the same Zod refusal on `host` and `password`. The bound was
30101
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
30102
+ * about the row ever stopped costing attempts and log lines.
30103
+ *
30104
+ * No retry can add a configuration that is not there. The row is reported
30105
+ * once, with its `deviceId`, and set aside.
30106
+ *
30107
+ * Top-level only: an accessory child legitimately carries an empty blob (it
30108
+ * lives off its parent), and a hub-adopted child with an emptied blob is
30109
+ * repaired by `healSavedConfig` from its parent instead. Providers that
30110
+ * genuinely create configuration-free top-level devices turn this off.
30111
+ */
30112
+ quarantineConfiglessTopLevelRestores = true;
30113
+ isUnrestorableShell(saved) {
30114
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
30115
+ if (saved.parentDeviceId !== null) return false;
30116
+ const config = saved.config;
30117
+ if (config === void 0) return false;
30118
+ return Object.keys(config).length === 0;
30119
+ }
30120
+ quarantineShell(saved) {
30121
+ this._permanentRestoreFailures.set(saved.id, {
30122
+ deviceId: saved.id,
30123
+ stableId: saved.stableId,
30124
+ type: saved.type,
30125
+ attempts: 0,
30126
+ lastError: "persisted row has no configuration",
30127
+ failedAt: Date.now()
30128
+ });
30129
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
30130
+ tags: {
30131
+ deviceId: saved.id,
30132
+ stableId: saved.stableId
30133
+ },
30134
+ meta: {
30135
+ type: saved.type,
30136
+ provider: this.providerName
30137
+ }
30138
+ });
30139
+ }
30093
30140
  cancelRestoreRetries() {
30094
30141
  this._restoreRetryScheduler?.cancel();
30095
30142
  this._restoreRetryScheduler = null;
@@ -30274,6 +30321,10 @@ var BaseDeviceProvider = class extends BaseAddon {
30274
30321
  });
30275
30322
  return;
30276
30323
  }
30324
+ if (this.isUnrestorableShell(saved)) {
30325
+ this.quarantineShell(saved);
30326
+ return;
30327
+ }
30277
30328
  try {
30278
30329
  await attemptRestore(saved);
30279
30330
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -30091,6 +30091,53 @@ var BaseDeviceProvider = class extends BaseAddon {
30091
30091
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
30092
30092
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
30093
30093
  }
30094
+ /**
30095
+ * A top-level persisted row with NO configuration is a leftover, not a
30096
+ * device — quarantine it instead of dialling it.
30097
+ *
30098
+ * Device 614 was deleted, resurrected as a config-less shell by a write
30099
+ * already in flight (D376), and then dialled at every single boot: eight
30100
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
30101
+ * each one the same Zod refusal on `host` and `password`. The bound was
30102
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
30103
+ * about the row ever stopped costing attempts and log lines.
30104
+ *
30105
+ * No retry can add a configuration that is not there. The row is reported
30106
+ * once, with its `deviceId`, and set aside.
30107
+ *
30108
+ * Top-level only: an accessory child legitimately carries an empty blob (it
30109
+ * lives off its parent), and a hub-adopted child with an emptied blob is
30110
+ * repaired by `healSavedConfig` from its parent instead. Providers that
30111
+ * genuinely create configuration-free top-level devices turn this off.
30112
+ */
30113
+ quarantineConfiglessTopLevelRestores = true;
30114
+ isUnrestorableShell(saved) {
30115
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
30116
+ if (saved.parentDeviceId !== null) return false;
30117
+ const config = saved.config;
30118
+ if (config === void 0) return false;
30119
+ return Object.keys(config).length === 0;
30120
+ }
30121
+ quarantineShell(saved) {
30122
+ this._permanentRestoreFailures.set(saved.id, {
30123
+ deviceId: saved.id,
30124
+ stableId: saved.stableId,
30125
+ type: saved.type,
30126
+ attempts: 0,
30127
+ lastError: "persisted row has no configuration",
30128
+ failedAt: Date.now()
30129
+ });
30130
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
30131
+ tags: {
30132
+ deviceId: saved.id,
30133
+ stableId: saved.stableId
30134
+ },
30135
+ meta: {
30136
+ type: saved.type,
30137
+ provider: this.providerName
30138
+ }
30139
+ });
30140
+ }
30094
30141
  cancelRestoreRetries() {
30095
30142
  this._restoreRetryScheduler?.cancel();
30096
30143
  this._restoreRetryScheduler = null;
@@ -30275,6 +30322,10 @@ var BaseDeviceProvider = class extends BaseAddon {
30275
30322
  });
30276
30323
  return;
30277
30324
  }
30325
+ if (this.isUnrestorableShell(saved)) {
30326
+ this.quarantineShell(saved);
30327
+ return;
30328
+ }
30278
30329
  try {
30279
30330
  await attemptRestore(saved);
30280
30331
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-onvif",
3
- "version": "1.2.72",
3
+ "version": "1.2.73",
4
4
  "description": "ONVIF camera device provider addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",