@camstack/addon-provider-hikvision 1.2.81 → 1.2.82

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
@@ -34225,6 +34225,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34225
34225
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34226
34226
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34227
34227
  }
34228
+ /**
34229
+ * A top-level persisted row with NO configuration is a leftover, not a
34230
+ * device — quarantine it instead of dialling it.
34231
+ *
34232
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34233
+ * already in flight (D376), and then dialled at every single boot: eight
34234
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34235
+ * each one the same Zod refusal on `host` and `password`. The bound was
34236
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34237
+ * about the row ever stopped costing attempts and log lines.
34238
+ *
34239
+ * No retry can add a configuration that is not there. The row is reported
34240
+ * once, with its `deviceId`, and set aside.
34241
+ *
34242
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34243
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34244
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34245
+ * genuinely create configuration-free top-level devices turn this off.
34246
+ */
34247
+ quarantineConfiglessTopLevelRestores = true;
34248
+ isUnrestorableShell(saved) {
34249
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34250
+ if (saved.parentDeviceId !== null) return false;
34251
+ const config = saved.config;
34252
+ if (config === void 0) return false;
34253
+ return Object.keys(config).length === 0;
34254
+ }
34255
+ quarantineShell(saved) {
34256
+ this._permanentRestoreFailures.set(saved.id, {
34257
+ deviceId: saved.id,
34258
+ stableId: saved.stableId,
34259
+ type: saved.type,
34260
+ attempts: 0,
34261
+ lastError: "persisted row has no configuration",
34262
+ failedAt: Date.now()
34263
+ });
34264
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34265
+ tags: {
34266
+ deviceId: saved.id,
34267
+ stableId: saved.stableId
34268
+ },
34269
+ meta: {
34270
+ type: saved.type,
34271
+ provider: this.providerName
34272
+ }
34273
+ });
34274
+ }
34228
34275
  cancelRestoreRetries() {
34229
34276
  this._restoreRetryScheduler?.cancel();
34230
34277
  this._restoreRetryScheduler = null;
@@ -34409,6 +34456,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34409
34456
  });
34410
34457
  return;
34411
34458
  }
34459
+ if (this.isUnrestorableShell(saved)) {
34460
+ this.quarantineShell(saved);
34461
+ return;
34462
+ }
34412
34463
  try {
34413
34464
  await attemptRestore(saved);
34414
34465
  } catch (err) {
package/dist/addon.mjs CHANGED
@@ -34226,6 +34226,53 @@ var BaseDeviceProvider = class extends BaseAddon {
34226
34226
  const ids = [...this._permanentRestoreFailures.keys()].join(", ");
34227
34227
  return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) — restart the ${this.providerName} provider to retry`;
34228
34228
  }
34229
+ /**
34230
+ * A top-level persisted row with NO configuration is a leftover, not a
34231
+ * device — quarantine it instead of dialling it.
34232
+ *
34233
+ * Device 614 was deleted, resurrected as a config-less shell by a write
34234
+ * already in flight (D376), and then dialled at every single boot: eight
34235
+ * `Failed to restore device — bounded retry scheduled` lines in one day,
34236
+ * each one the same Zod refusal on `host` and `password`. The bound was
34237
+ * real, but it is a bound PER PROCESS, and the runner restarts — so nothing
34238
+ * about the row ever stopped costing attempts and log lines.
34239
+ *
34240
+ * No retry can add a configuration that is not there. The row is reported
34241
+ * once, with its `deviceId`, and set aside.
34242
+ *
34243
+ * Top-level only: an accessory child legitimately carries an empty blob (it
34244
+ * lives off its parent), and a hub-adopted child with an emptied blob is
34245
+ * repaired by `healSavedConfig` from its parent instead. Providers that
34246
+ * genuinely create configuration-free top-level devices turn this off.
34247
+ */
34248
+ quarantineConfiglessTopLevelRestores = true;
34249
+ isUnrestorableShell(saved) {
34250
+ if (!this.quarantineConfiglessTopLevelRestores) return false;
34251
+ if (saved.parentDeviceId !== null) return false;
34252
+ const config = saved.config;
34253
+ if (config === void 0) return false;
34254
+ return Object.keys(config).length === 0;
34255
+ }
34256
+ quarantineShell(saved) {
34257
+ this._permanentRestoreFailures.set(saved.id, {
34258
+ deviceId: saved.id,
34259
+ stableId: saved.stableId,
34260
+ type: saved.type,
34261
+ attempts: 0,
34262
+ lastError: "persisted row has no configuration",
34263
+ failedAt: Date.now()
34264
+ });
34265
+ this.ctx.logger.error("Persisted device row has NO configuration — quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
34266
+ tags: {
34267
+ deviceId: saved.id,
34268
+ stableId: saved.stableId
34269
+ },
34270
+ meta: {
34271
+ type: saved.type,
34272
+ provider: this.providerName
34273
+ }
34274
+ });
34275
+ }
34229
34276
  cancelRestoreRetries() {
34230
34277
  this._restoreRetryScheduler?.cancel();
34231
34278
  this._restoreRetryScheduler = null;
@@ -34410,6 +34457,10 @@ var BaseDeviceProvider = class extends BaseAddon {
34410
34457
  });
34411
34458
  return;
34412
34459
  }
34460
+ if (this.isUnrestorableShell(saved)) {
34461
+ this.quarantineShell(saved);
34462
+ return;
34463
+ }
34413
34464
  try {
34414
34465
  await attemptRestore(saved);
34415
34466
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-provider-hikvision",
3
- "version": "1.2.81",
3
+ "version": "1.2.82",
4
4
  "description": "Hikvision camera device provider addon for CamStack — ISAPI over HTTP(S) with digest auth (snapshot, alarm stream, RTSP discovery)",
5
5
  "keywords": [
6
6
  "camstack",