@camstack/addon-pipeline 1.2.62 → 1.2.63

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.
@@ -13623,6 +13623,7 @@ var StreamBrokerManager = class StreamBrokerManager {
13623
13623
  nonOwnerSkipLogged = /* @__PURE__ */ new Set();
13624
13624
  deviceOverridePersister = null;
13625
13625
  profileMapPersister = null;
13626
+ deviceCatalogOperations = /* @__PURE__ */ new Map();
13626
13627
  eventBus = null;
13627
13628
  /**
13628
13629
  * Per-broker stream health flag set by the watchdog. `undefined`
@@ -14952,6 +14953,9 @@ var StreamBrokerManager = class StreamBrokerManager {
14952
14953
  * through the device-unregistered path.
14953
14954
  */
14954
14955
  async reconcileDeviceCatalog(deviceId) {
14956
+ await this.runDeviceCatalogOperation(deviceId, () => this.reconcileDeviceCatalogNow(deviceId));
14957
+ }
14958
+ async reconcileDeviceCatalogNow(deviceId) {
14955
14959
  if (!this.api) return;
14956
14960
  let descriptors;
14957
14961
  try {
@@ -15138,6 +15142,9 @@ var StreamBrokerManager = class StreamBrokerManager {
15138
15142
  * Drop a device's cam-streams entirely — fired on `device.unregistered`.
15139
15143
  */
15140
15144
  async retractDevice(deviceId, opts = {}) {
15145
+ await this.runDeviceCatalogOperation(deviceId, () => this.retractDeviceNow(deviceId, opts));
15146
+ }
15147
+ async retractDeviceNow(deviceId, opts) {
15141
15148
  const existing = this.cameraStreams.get(deviceId);
15142
15149
  if (!existing) return;
15143
15150
  for (const camStreamId of Array.from(existing.keys())) await this.retractCameraStream({
@@ -15146,6 +15153,22 @@ var StreamBrokerManager = class StreamBrokerManager {
15146
15153
  preserveAssignment: opts.preserveAssignments === true
15147
15154
  });
15148
15155
  }
15156
+ async runDeviceCatalogOperation(deviceId, operation) {
15157
+ const previous = this.deviceCatalogOperations.get(deviceId) ?? Promise.resolve();
15158
+ let release;
15159
+ const turn = new Promise((resolve) => {
15160
+ release = resolve;
15161
+ });
15162
+ const queued = previous.catch(() => {}).then(() => turn);
15163
+ this.deviceCatalogOperations.set(deviceId, queued);
15164
+ await previous.catch(() => {});
15165
+ try {
15166
+ return await operation();
15167
+ } finally {
15168
+ release();
15169
+ if (this.deviceCatalogOperations.get(deviceId) === queued) this.deviceCatalogOperations.delete(deviceId);
15170
+ }
15171
+ }
15149
15172
  /**
15150
15173
  * Lazily (re)create the broker instance for a `brokerId` if its cam-stream
15151
15174
  * is known. The audio/frame subscribe paths call this so a consumer's
@@ -13617,6 +13617,7 @@ var StreamBrokerManager = class StreamBrokerManager {
13617
13617
  nonOwnerSkipLogged = /* @__PURE__ */ new Set();
13618
13618
  deviceOverridePersister = null;
13619
13619
  profileMapPersister = null;
13620
+ deviceCatalogOperations = /* @__PURE__ */ new Map();
13620
13621
  eventBus = null;
13621
13622
  /**
13622
13623
  * Per-broker stream health flag set by the watchdog. `undefined`
@@ -14946,6 +14947,9 @@ var StreamBrokerManager = class StreamBrokerManager {
14946
14947
  * through the device-unregistered path.
14947
14948
  */
14948
14949
  async reconcileDeviceCatalog(deviceId) {
14950
+ await this.runDeviceCatalogOperation(deviceId, () => this.reconcileDeviceCatalogNow(deviceId));
14951
+ }
14952
+ async reconcileDeviceCatalogNow(deviceId) {
14949
14953
  if (!this.api) return;
14950
14954
  let descriptors;
14951
14955
  try {
@@ -15132,6 +15136,9 @@ var StreamBrokerManager = class StreamBrokerManager {
15132
15136
  * Drop a device's cam-streams entirely — fired on `device.unregistered`.
15133
15137
  */
15134
15138
  async retractDevice(deviceId, opts = {}) {
15139
+ await this.runDeviceCatalogOperation(deviceId, () => this.retractDeviceNow(deviceId, opts));
15140
+ }
15141
+ async retractDeviceNow(deviceId, opts) {
15135
15142
  const existing = this.cameraStreams.get(deviceId);
15136
15143
  if (!existing) return;
15137
15144
  for (const camStreamId of Array.from(existing.keys())) await this.retractCameraStream({
@@ -15140,6 +15147,22 @@ var StreamBrokerManager = class StreamBrokerManager {
15140
15147
  preserveAssignment: opts.preserveAssignments === true
15141
15148
  });
15142
15149
  }
15150
+ async runDeviceCatalogOperation(deviceId, operation) {
15151
+ const previous = this.deviceCatalogOperations.get(deviceId) ?? Promise.resolve();
15152
+ let release;
15153
+ const turn = new Promise((resolve) => {
15154
+ release = resolve;
15155
+ });
15156
+ const queued = previous.catch(() => {}).then(() => turn);
15157
+ this.deviceCatalogOperations.set(deviceId, queued);
15158
+ await previous.catch(() => {});
15159
+ try {
15160
+ return await operation();
15161
+ } finally {
15162
+ release();
15163
+ if (this.deviceCatalogOperations.get(deviceId) === queued) this.deviceCatalogOperations.delete(deviceId);
15164
+ }
15165
+ }
15143
15166
  /**
15144
15167
  * Lazily (re)create the broker instance for a `brokerId` if its cam-stream
15145
15168
  * is known. The audio/frame subscribe paths call this so a consumer's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.62",
3
+ "version": "1.2.63",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",