@camstack/system 1.2.475 → 1.2.476

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.
Files changed (86) hide show
  1. package/dist/{agent-https-BsqT0qni.js → agent-https-DkXBXEvz.js} +1 -1
  2. package/dist/{agent-https-wA8gGLc1.mjs → agent-https-vpf_D97z.mjs} +1 -1
  3. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  6. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  7. package/dist/builtins/alerts/alerts.addon.js +1 -1
  8. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  9. package/dist/builtins/autotrack/index.js +1 -1
  10. package/dist/builtins/autotrack/index.mjs +1 -1
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
  13. package/dist/builtins/calendars/calendars.addon.js +1 -1
  14. package/dist/builtins/calendars/calendars.addon.mjs +1 -1
  15. package/dist/builtins/camera-grid/index.js +1 -1
  16. package/dist/builtins/camera-grid/index.mjs +1 -1
  17. package/dist/builtins/composer/composer-live-set.d.ts +9 -1
  18. package/dist/builtins/composer/composer.addon.js +130 -11
  19. package/dist/builtins/composer/composer.addon.mjs +130 -11
  20. package/dist/builtins/composer/composer.d.ts +8 -1
  21. package/dist/builtins/composer/composition-runtime.d.ts +2 -0
  22. package/dist/builtins/composer/composition-target.d.ts +6 -0
  23. package/dist/builtins/composer/owned-fields-target.d.ts +9 -0
  24. package/dist/builtins/console-logging/index.js +1 -1
  25. package/dist/builtins/console-logging/index.mjs +1 -1
  26. package/dist/builtins/core-blocks/block-supervisor.d.ts +19 -1
  27. package/dist/builtins/core-blocks/composition-api.d.ts +7 -1
  28. package/dist/builtins/core-blocks/core-blocks.addon.js +108 -10
  29. package/dist/builtins/core-blocks/core-blocks.addon.mjs +108 -10
  30. package/dist/builtins/device-history/device-history.addon.js +1 -1
  31. package/dist/builtins/device-history/device-history.addon.mjs +1 -1
  32. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  33. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  34. package/dist/builtins/hub-forwarder/index.js +1 -1
  35. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  36. package/dist/builtins/keychain/index.js +3 -3
  37. package/dist/builtins/keychain/index.mjs +3 -3
  38. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  39. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  40. package/dist/builtins/local-auth/local-auth.addon.js +2 -2
  41. package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
  42. package/dist/builtins/local-network/local-network.addon.js +2 -2
  43. package/dist/builtins/local-network/local-network.addon.mjs +2 -2
  44. package/dist/builtins/loki-logging/index.js +1 -1
  45. package/dist/builtins/loki-logging/index.mjs +1 -1
  46. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  47. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  48. package/dist/builtins/platform-probe/index.js +1 -1
  49. package/dist/builtins/platform-probe/index.mjs +1 -1
  50. package/dist/builtins/record-entries/index.js +1 -1
  51. package/dist/builtins/record-entries/index.mjs +1 -1
  52. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  53. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  54. package/dist/builtins/snapshot/index.js +1 -1
  55. package/dist/builtins/snapshot/index.mjs +1 -1
  56. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  57. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  58. package/dist/builtins/sqlite-storage/index.js +1 -1
  59. package/dist/builtins/sqlite-storage/index.mjs +1 -1
  60. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
  61. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
  62. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  63. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  64. package/dist/builtins/system-config/system-config.addon.js +3 -3
  65. package/dist/builtins/system-config/system-config.addon.mjs +3 -3
  66. package/dist/builtins/winston-logging/index.js +1 -1
  67. package/dist/builtins/winston-logging/index.mjs +1 -1
  68. package/dist/{composition-sources-CiFkyAcf.mjs → composition-sources-B5nK7pHt.mjs} +1 -1
  69. package/dist/{composition-sources-DeUSUid6.js → composition-sources-BNOP9mFp.js} +1 -1
  70. package/dist/{constants-3Wj5OyEk.js → constants-D4VigrMM.js} +1 -1
  71. package/dist/{constants-DZJAkne8.mjs → constants-jbhoFV7w.mjs} +1 -1
  72. package/dist/{device-ca-DiV5xcMz.mjs → device-ca-CvBZeyVX.mjs} +1 -1
  73. package/dist/{device-ca-ftrfVdc0.js → device-ca-DhyQeXsa.js} +1 -1
  74. package/dist/{dist-DygMs3Qc.mjs → dist-B_V9r61Z.mjs} +21 -2
  75. package/dist/{dist-BtDdPdZ7.js → dist-CNU7cdFf.js} +21 -2
  76. package/dist/file-host-worker.js +1 -1
  77. package/dist/file-host-worker.mjs +1 -1
  78. package/dist/{hub-mdns-ciao-Bc9PnWu3.mjs → hub-mdns-ciao-BuUg0gpI.mjs} +2 -2
  79. package/dist/{hub-mdns-ciao-B1VcdfT1.js → hub-mdns-ciao-DFidf52M.js} +2 -2
  80. package/dist/index.js +7 -7
  81. package/dist/index.mjs +7 -7
  82. package/dist/{restore-archive-TDkQfmLO.js → restore-archive-BtCheeWu.js} +1 -1
  83. package/dist/{restore-archive-D_Pwryre.mjs → restore-archive-CDo4Zlmx.mjs} +1 -1
  84. package/dist/{wal-maintenance-D3UwIKX5.js → wal-maintenance-B7xBeIFi.js} +0 -0
  85. package/dist/{wal-maintenance-rLv4uOmp.mjs → wal-maintenance-DItlr0Yu.mjs} +0 -0
  86. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require("./chunk-Cek0wNdY.js");
2
- const require_dist = require("./dist-BtDdPdZ7.js");
2
+ const require_dist = require("./dist-CNU7cdFf.js");
3
3
  const require_x509_es = require("./x509.es-WtnRNyYS.js");
4
4
  let zod = require("zod");
5
5
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { qi as defineRecordKinds } from "./dist-DygMs3Qc.mjs";
1
+ import { qi as defineRecordKinds } from "./dist-B_V9r61Z.mjs";
2
2
  import { C as backdatedNotBefore, S as withUtcTimeEncoding, _ as NameConstraints, a as KeyUsageFlags, c as Pkcs10CertificateRequest, d as SubjectKeyIdentifierExtension, f as X509Certificate$1, g as GeneralSubtrees, h as GeneralSubtree, i as Extension, l as Pkcs10CertificateRequestGenerator, m as cryptoProvider, n as BasicConstraintsExtension, o as KeyUsagesExtension, p as X509CertificateGenerator, r as ExtendedKeyUsageExtension, s as PemConverter, t as AuthorityKeyIdentifierExtension, u as SubjectAlternativeNameExtension, v as GeneralName, x as AsnConvert } from "./x509.es-BRiJgOvu.mjs";
3
3
  import { z } from "zod";
4
4
  import { X509Certificate, createHash, createPrivateKey, randomBytes, webcrypto } from "node:crypto";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Oa as EventCategory, _t as addonPagesCapability, ei as errMsg, ti as BaseAddon } from "../../dist-DygMs3Qc.mjs";
1
+ import { Oa as EventCategory, _t as addonPagesCapability, ei as errMsg, ti as BaseAddon } from "../../dist-B_V9r61Z.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let node_crypto = require("node:crypto");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { Oa as EventCategory, ei as errMsg, ti as BaseAddon, yt as addonWidgetsCapability } from "../../dist-DygMs3Qc.mjs";
1
+ import { Oa as EventCategory, ei as errMsg, ti as BaseAddon, yt as addonWidgetsCapability } from "../../dist-B_V9r61Z.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
8
8
  //#region src/builtins/alerts/alert-episode-policy.ts
9
9
  /**
@@ -1,4 +1,4 @@
1
- import { Oa as EventCategory, St as alertsCapability, Wi as createEvent, bn as isAlertEpisodeSeverity, bt as alertEpisodeId, ei as errMsg, i as AlertSchema, qi as defineRecordKinds, ti as BaseAddon, xt as alertEpisodePayload, yn as isAlertEpisodeOpen } from "../../dist-DygMs3Qc.mjs";
1
+ import { Oa as EventCategory, St as alertsCapability, Wi as createEvent, bn as isAlertEpisodeSeverity, bt as alertEpisodeId, ei as errMsg, i as AlertSchema, qi as defineRecordKinds, ti as BaseAddon, xt as alertEpisodePayload, yn as isAlertEpisodeOpen } from "../../dist-B_V9r61Z.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
3
  //#region src/builtins/alerts/alert-episode-policy.ts
4
4
  /**
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  //#region src/builtins/autotrack/autotrack-config.ts
8
8
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
9
9
  deadZoneFraction: .5,
@@ -1,4 +1,4 @@
1
- import { Et as bareAddonId, I as FIRST_LEVEL_MACRO_CLASSES, Oa as EventCategory, ei as errMsg, nr as ptzAutotrackCapability, ta as isEvent, ti as BaseAddon } from "../../dist-DygMs3Qc.mjs";
1
+ import { Et as bareAddonId, I as FIRST_LEVEL_MACRO_CLASSES, Oa as EventCategory, ei as errMsg, nr as ptzAutotrackCapability, ta as isEvent, ti as BaseAddon } from "../../dist-B_V9r61Z.mjs";
2
2
  //#region src/builtins/autotrack/autotrack-config.ts
3
3
  var DEFAULT_AUTOTRACK_LOOP_CONFIG = {
4
4
  deadZoneFraction: .5,
@@ -3,9 +3,9 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  const require_chunk = require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  const require_read_all_rows$1 = require("../../read-all-rows-CVoO1ONM.js");
8
- const require_restore_archive = require("../../restore-archive-TDkQfmLO.js");
8
+ const require_restore_archive = require("../../restore-archive-BtCheeWu.js");
9
9
  let node_crypto = require("node:crypto");
10
10
  let node_fs_promises = require("node:fs/promises");
11
11
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
@@ -1,6 +1,6 @@
1
- import { Ca as toNodeId, Dr as storageOccupancyCapability, Oa as EventCategory, Tt as backupCapability, Vn as mayWriteToLocation, la as nodePin, ti as BaseAddon } from "../../dist-DygMs3Qc.mjs";
1
+ import { Ca as toNodeId, Dr as storageOccupancyCapability, Oa as EventCategory, Tt as backupCapability, Vn as mayWriteToLocation, la as nodePin, ti as BaseAddon } from "../../dist-B_V9r61Z.mjs";
2
2
  import { t as require_read_all_rows } from "../../read-all-rows-TaPqdWNF.mjs";
3
- import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-D_Pwryre.mjs";
3
+ import { _ as backupRecordStoresOf, a as BackupSnapshotRefusedError, d as ENCRYPTED_BACKUP_SUFFIX, f as generateBackupKeyPair, g as backupOrchestratorRecordKinds, i as BackupCancelledError, l as BackupKeyFormatError, m as parseBackupSecretKey, n as precheckRestoreKey, o as DEFAULT_BACKUP_DATABASES, p as parseBackupPublicKey, r as prepareRestoreArchive, s as SystemBackupService } from "../../restore-archive-CDo4Zlmx.mjs";
4
4
  import { randomUUID } from "node:crypto";
5
5
  import * as fsp from "node:fs/promises";
6
6
  import * as path$1 from "node:path";
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/calendars/calendar-color.ts
@@ -1,4 +1,4 @@
1
- import { Mt as calendarsCapability, g as CalendarPreferencesSchema, qi as defineRecordKinds, ti as BaseAddon } from "../../dist-DygMs3Qc.mjs";
1
+ import { Mt as calendarsCapability, g as CalendarPreferencesSchema, qi as defineRecordKinds, ti as BaseAddon } from "../../dist-B_V9r61Z.mjs";
2
2
  import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/calendars/calendar-color.ts
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
7
  let zod = require("zod");
8
8
  let _camstack_types_node = require("@camstack/types/node");
9
9
  let node_path = require("node:path");
@@ -1,4 +1,4 @@
1
- import { $i as hydrateSchema, $r as DeviceType, Br as buildFfmpegArgs, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Oa as EventCategory, Sr as snapshotCapability, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Wi as createEvent, X as MOTION_CLOSE_AFTER_MS, Zr as DeviceFeature, a as BaseDevice, ei as errMsg, kr as streamCatalogCapability, qt as declarationOwnerNodeId, ti as BaseAddon } from "../../dist-DygMs3Qc.mjs";
1
+ import { $i as hydrateSchema, $r as DeviceType, Br as buildFfmpegArgs, Gn as motionCapability, Hn as memoizePinnedFfmpeg, N as DeclaredDevices, Nt as cameraGridLayoutCapability, Oa as EventCategory, Sr as snapshotCapability, T as DERIVED_CAMERA_SILENCED_CAP_NAMES, Wi as createEvent, X as MOTION_CLOSE_AFTER_MS, Zr as DeviceFeature, a as BaseDevice, ei as errMsg, kr as streamCatalogCapability, qt as declarationOwnerNodeId, ti as BaseAddon } from "../../dist-B_V9r61Z.mjs";
2
2
  import { z } from "zod";
3
3
  import { FfmpegProcess } from "@camstack/types/node";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import type { ClaimGate, ClaimKey } from "./claim-gate.js";
1
+ import { type ClaimGate, type ClaimKey } from "./claim-gate.js";
2
2
  import type { PlannedBlock } from "./composer-plan.js";
3
3
  import type { LiveComposition } from "./composer-verdict.js";
4
4
  import type { TrackedSourceRequest } from "./source-tracker.js";
@@ -20,3 +20,11 @@ export declare function trackedSourceRequests(live: Iterable<LiveComposition>):
20
20
  * are. A disabled block wants none (rule 7).
21
21
  */
22
22
  export declare function wantedClaims(live: ReadonlyMap<string, LiveComposition>, claimGate: Pick<ClaimGate, "claimsOf">): readonly ClaimKey[];
23
+ /**
24
+ * Claims an operator has already confirmed are no longer wanted: a deleted
25
+ * block (`retiredBlockIds`), or a cap an edit dropped. Released on this apply,
26
+ * not after a second one — a missing block in a single snapshot is still the
27
+ * two-apply gate (`wantedClaims`). A disabled block is not here: its runtime
28
+ * hands the fields back itself.
29
+ */
30
+ export declare function confirmedReleaseKeys(retiredBlockIds: readonly string[], planned: readonly PlannedBlock[], previous: ReadonlyMap<string, LiveComposition>, claimGate: Pick<ClaimGate, "claimsOf">): readonly ClaimKey[];
@@ -3,8 +3,8 @@ Object.defineProperties(exports, {
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
5
  require("../../chunk-Cek0wNdY.js");
6
- const require_dist = require("../../dist-BtDdPdZ7.js");
7
- const require_composition_sources = require("../../composition-sources-DeUSUid6.js");
6
+ const require_dist = require("../../dist-CNU7cdFf.js");
7
+ const require_composition_sources = require("../../composition-sources-BNOP9mFp.js");
8
8
  let zod = require("zod");
9
9
  //#region src/builtins/shared/device-state-changed.ts
10
10
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -793,6 +793,41 @@ function wantedClaims(live, claimGate) {
793
793
  for (const owner of held) wanted.push(...claimGate.claimsOf(owner));
794
794
  return wanted;
795
795
  }
796
+ /**
797
+ * Claims an operator has already confirmed are no longer wanted: a deleted
798
+ * block (`retiredBlockIds`), or a cap an edit dropped. Released on this apply,
799
+ * not after a second one — a missing block in a single snapshot is still the
800
+ * two-apply gate (`wantedClaims`). A disabled block is not here: its runtime
801
+ * hands the fields back itself.
802
+ */
803
+ function confirmedReleaseKeys(retiredBlockIds, planned, previous, claimGate) {
804
+ const retired = new Set(retiredBlockIds);
805
+ const seen = /* @__PURE__ */ new Set();
806
+ const keys = [];
807
+ const push = (key) => {
808
+ const id = claimKey(key);
809
+ if (seen.has(id)) return;
810
+ seen.add(id);
811
+ keys.push(key);
812
+ };
813
+ for (const owner of retired) for (const key of claimGate.claimsOf(owner)) push(key);
814
+ const plannedById = new Map(planned.map((block) => [block.block.id, block]));
815
+ for (const [blockId, entry] of previous) {
816
+ if (retired.has(blockId)) continue;
817
+ const next = plannedById.get(blockId);
818
+ if (next === void 0 || !next.block.enabled) continue;
819
+ if (entry.planned.block.updatedAt === next.block.updatedAt) continue;
820
+ for (const key of claimGate.claimsOf(blockId)) if (!planStillWantsClaim(next, key)) push(key);
821
+ }
822
+ return keys;
823
+ }
824
+ /** True when this plan still holds `key` — including while the plan cannot say, so a bad read releases nothing. */
825
+ function planStillWantsClaim(planned, key) {
826
+ const target = planned.target;
827
+ if (target.kind !== "existing") return false;
828
+ if (target.waiting !== null || target.gone !== null || target.deviceId === null || planned.problems.length > 0) return true;
829
+ return target.deviceId === key.deviceId && target.features.some((feature) => feature.cap === key.capName);
830
+ }
796
831
  //#endregion
797
832
  //#region src/builtins/composer/composition-declarations.ts
798
833
  /**
@@ -1718,6 +1753,13 @@ var CompositionRuntime = class {
1718
1753
  this.stopped = true;
1719
1754
  this.deps.timer.disarm();
1720
1755
  }
1756
+ /** `stop`, then wait until the target has sent nothing further. */
1757
+ quiesce() {
1758
+ this.stop();
1759
+ const quiesce = this.deps.target.quiesce;
1760
+ if (quiesce === void 0) return Promise.resolve();
1761
+ return quiesce.call(this.deps.target);
1762
+ }
1721
1763
  state() {
1722
1764
  return {
1723
1765
  failure: this.failure,
@@ -2221,6 +2263,10 @@ var OwnedFieldsTarget = class {
2221
2263
  listeners = [];
2222
2264
  /** Per cap, bumped by every `withhold`: a claim that settles under another epoch claimed a stale field set and does not land (I-2). */
2223
2265
  epoch = /* @__PURE__ */ new Map();
2266
+ /** Set by `quiesce`: nothing further is sent. */
2267
+ closed = false;
2268
+ idle = Promise.resolve();
2269
+ markIdle = null;
2224
2270
  constructor(deps) {
2225
2271
  this.deps = deps;
2226
2272
  this.id = deps.deviceId;
@@ -2245,6 +2291,27 @@ var OwnedFieldsTarget = class {
2245
2291
  }
2246
2292
  /** `device-status` belongs to the native device; availability lives in the block report only. */
2247
2293
  setAvailable(_available) {}
2294
+ /**
2295
+ * The block has stopped. Writes still queued are dropped; the call in flight
2296
+ * is allowed to finish, and then nothing else is sent.
2297
+ */
2298
+ quiesce() {
2299
+ this.closed = true;
2300
+ const dropped = [...this.pending.keys()];
2301
+ this.pending = /* @__PURE__ */ new Map();
2302
+ for (const cap of dropped) this.deps.logger.info("composed write dropped — the block stopped before it was sent", {
2303
+ tags: { deviceId: this.deps.deviceId },
2304
+ meta: {
2305
+ cap,
2306
+ owner: this.deps.owner
2307
+ }
2308
+ });
2309
+ if (this.inFlight.size === 0) return Promise.resolve();
2310
+ if (this.markIdle === null) this.idle = new Promise((resolve) => {
2311
+ this.markIdle = resolve;
2312
+ });
2313
+ return this.idle;
2314
+ }
2248
2315
  onAsyncFailure(listener) {
2249
2316
  this.listeners = [...this.listeners, listener];
2250
2317
  }
@@ -2260,6 +2327,17 @@ var OwnedFieldsTarget = class {
2260
2327
  return this.deps.fieldsOf(cap).filter((f) => held === void 0 || !held.has(f));
2261
2328
  }
2262
2329
  enqueue(cap, op) {
2330
+ if (this.closed) {
2331
+ this.deps.logger.info("composed write dropped — the block stopped before it was sent", {
2332
+ tags: { deviceId: this.deps.deviceId },
2333
+ meta: {
2334
+ cap,
2335
+ owner: this.deps.owner,
2336
+ op: op.kind
2337
+ }
2338
+ });
2339
+ return;
2340
+ }
2263
2341
  if (!this.inFlight.has(cap)) {
2264
2342
  this.send(cap, op);
2265
2343
  return;
@@ -2282,6 +2360,11 @@ var OwnedFieldsTarget = class {
2282
2360
  }
2283
2361
  settled(cap) {
2284
2362
  this.inFlight = new Set([...this.inFlight].filter((c) => c !== cap));
2363
+ if (this.closed) {
2364
+ this.pending = /* @__PURE__ */ new Map();
2365
+ if (this.inFlight.size === 0) this.markIdle?.();
2366
+ return;
2367
+ }
2285
2368
  const next = this.pending.get(cap);
2286
2369
  if (next === void 0) return;
2287
2370
  this.pending = new Map([...this.pending].filter(([c]) => c !== cap));
@@ -2698,7 +2781,11 @@ function verdictOf(blockId, entry, unsynced, grafts = null) {
2698
2781
  * filter in `composer.addon.ts` stays load-bearing. Its fields are CLAIMED on
2699
2782
  * the hub under the block id (`OwnedFieldsTarget`), and released through the
2700
2783
  * `ClaimGate` — two applies must agree, seeded at boot from the claims index —
2701
- * when the block is gone, disabled, or lost the feature. Its own replaced
2784
+ * when the block merely vanished from one snapshot. An operator delete
2785
+ * (`retiredBlockIds`) or an edit that drops a cap releases those claims on
2786
+ * THIS apply, after the old runtime has quiesced and before any replacement
2787
+ * claims; a release that does not land is logged with `tags: { deviceId }` and
2788
+ * returned on the apply. Its own replaced
2702
2789
  * fields are read in the NATIVE view (rule 6). Its target row gone on two
2703
2790
  * agreeing index reads fails the block by name and ungrafts what it added —
2704
2791
  * whether the loss was seen on `device.unregistered` or on a later plan; its
@@ -2846,12 +2933,16 @@ var Composer = class {
2846
2933
  };
2847
2934
  });
2848
2935
  await this.composed.reconcile(gate.integrationId, declarations, planned);
2936
+ const releasing = confirmedReleaseKeys(input.retiredBlockIds ?? [], planned, this.live, this.claimGate);
2849
2937
  const rebuilt = await this.rebuildRuntimes(planned, declarations, restart, rebuildErrors);
2938
+ await Promise.all(rebuilt.stopped.map((runtime) => runtime.quiesce()));
2939
+ const unreleasedClaims = await this.releaseConfirmed(releasing);
2850
2940
  await this.syncSources(rebuilt);
2851
2941
  await this.reconcileClaims();
2852
2942
  return {
2853
2943
  blocks: planned.map((p) => this.verdict(p.block.id)),
2854
- acted: true
2944
+ acted: true,
2945
+ unreleasedClaims
2855
2946
  };
2856
2947
  }
2857
2948
  plan(block, restart, claimsWait) {
@@ -2872,7 +2963,13 @@ var Composer = class {
2872
2963
  async rebuildRuntimes(planned, declarations, restart, rebuildErrors) {
2873
2964
  const reset = /* @__PURE__ */ new Set();
2874
2965
  const fresh = /* @__PURE__ */ new Set();
2966
+ const stopped = [];
2875
2967
  const next = /* @__PURE__ */ new Map();
2968
+ const halt = (runtime) => {
2969
+ if (runtime === null) return;
2970
+ runtime.stop();
2971
+ stopped.push(runtime);
2972
+ };
2876
2973
  for (const p of planned) {
2877
2974
  const previous = this.live.get(p.block.id);
2878
2975
  const lost = lostTarget(p, previous?.planned);
@@ -2943,7 +3040,7 @@ var Composer = class {
2943
3040
  enabled: p.block.enabled,
2944
3041
  settled: p.problems.length === 0
2945
3042
  });
2946
- previous?.runtime?.stop();
3043
+ halt(previous?.runtime ?? null);
2947
3044
  if (runtime !== null) fresh.add(runtime);
2948
3045
  next.set(p.block.id, {
2949
3046
  planned: p,
@@ -2958,12 +3055,15 @@ var Composer = class {
2958
3055
  for (const [blockId, entry] of this.live) {
2959
3056
  if (next.has(blockId)) continue;
2960
3057
  if (held.has(require_dist.composedDeviceStableId(blockId))) next.set(blockId, entry);
2961
- else entry.runtime?.stop();
3058
+ else halt(entry.runtime);
2962
3059
  }
2963
3060
  this.live = next;
2964
3061
  const liveRuntimes = new Set([...next.values()].map((e) => e.runtime));
2965
3062
  this.unsynced = new Set([...[...this.unsynced].filter((r) => liveRuntimes.has(r)), ...fresh]);
2966
- return { reset };
3063
+ return {
3064
+ reset,
3065
+ stopped
3066
+ };
2967
3067
  }
2968
3068
  ungraftGone(blockId, target, deviceId) {
2969
3069
  this.grafts.gone(deviceId);
@@ -3047,21 +3147,40 @@ var Composer = class {
3047
3147
  if (!this.claimSeed.done) return;
3048
3148
  for (const key of this.claimGate.filter(wantedClaims(this.live, this.claimGate))) await this.release(key);
3049
3149
  }
3150
+ /** Confirmed releases, awaited. A failure is the string the hub gave; the gate retries it later. */
3151
+ async releaseConfirmed(keys) {
3152
+ const unreleased = [];
3153
+ for (const key of keys) {
3154
+ const error = await this.release(key);
3155
+ if (error === null) continue;
3156
+ unreleased.push({
3157
+ deviceId: key.deviceId,
3158
+ capName: key.capName,
3159
+ owner: key.owner,
3160
+ error
3161
+ });
3162
+ }
3163
+ return unreleased;
3164
+ }
3165
+ /** Null when the hub confirmed the release (or held nothing of ours). */
3050
3166
  async release(key) {
3051
3167
  let outcome;
3052
3168
  try {
3053
3169
  outcome = await this.deps.claims.releaseClaim(key);
3054
3170
  } catch (err) {
3055
- this.claimGate.releaseFailed(key, require_dist.errMsg(err));
3056
- return;
3171
+ const error = require_dist.errMsg(err);
3172
+ this.claimGate.releaseFailed(key, error);
3173
+ return error;
3057
3174
  }
3058
3175
  if (!outcome.ok && !RELEASE_SETTLED_CODES.has(outcome.code)) {
3059
- this.claimGate.releaseFailed(key, `${outcome.code}: ${outcome.message}`);
3060
- return;
3176
+ const error = `${outcome.code}: ${outcome.message}`;
3177
+ this.claimGate.releaseFailed(key, error);
3178
+ return error;
3061
3179
  }
3062
3180
  this.claimGate.released(key, outcome.ok ? "released" : outcome.code);
3063
3181
  await this.grafts.released(key.deviceId, key.capName);
3064
3182
  await this.onClaimEvent(key.deviceId, key.capName, key.owner, "free");
3183
+ return null;
3065
3184
  }
3066
3185
  /**
3067
3186
  * Rule 5: a customization whose target row is gone — the tracker's two
@@ -1,5 +1,5 @@
1
- import { $n as planComposition, $r as DeviceType, Gr as compositionItemFieldPath, Hr as composedDeviceRef, It as composerCapability, Jr as createStatefulExpressionMemory, Kr as compositionSliceKey, Lt as compositionFieldKey, N as DeclaredDevices, Oa as EventCategory, Ot as buildCapabilityLookup, Ur as composedDeviceStableId, Vr as cloneStatefulExpressionMemory, Wi as createEvent, Wr as compositionFeatureEnabled, Xr as toExpressionValue, Yr as disarmRoseMemory, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as errMsg, qr as compositionSourceKey, r as ALL_CAPABILITY_DEFINITIONS, ti as BaseAddon, zr as COMPOSER_ADDON_ID } from "../../dist-DygMs3Qc.mjs";
2
- import { n as normalizedNativeOwner } from "../../composition-sources-CiFkyAcf.mjs";
1
+ import { $n as planComposition, $r as DeviceType, Gr as compositionItemFieldPath, Hr as composedDeviceRef, It as composerCapability, Jr as createStatefulExpressionMemory, Kr as compositionSliceKey, Lt as compositionFieldKey, N as DeclaredDevices, Oa as EventCategory, Ot as buildCapabilityLookup, Ur as composedDeviceStableId, Vr as cloneStatefulExpressionMemory, Wi as createEvent, Wr as compositionFeatureEnabled, Xr as toExpressionValue, Yr as disarmRoseMemory, a as BaseDevice, cn as evaluateCompositionField, d as COMPOSED_ITEM_FIXED_FIELDS, dn as expressionContainsBuiltinCall, ei as errMsg, qr as compositionSourceKey, r as ALL_CAPABILITY_DEFINITIONS, ti as BaseAddon, zr as COMPOSER_ADDON_ID } from "../../dist-B_V9r61Z.mjs";
2
+ import { n as normalizedNativeOwner } from "../../composition-sources-B5nK7pHt.mjs";
3
3
  import { z } from "zod";
4
4
  //#region src/builtins/shared/device-state-changed.ts
5
5
  /** Structural narrowing helper for untrusted bus payloads. */
@@ -788,6 +788,41 @@ function wantedClaims(live, claimGate) {
788
788
  for (const owner of held) wanted.push(...claimGate.claimsOf(owner));
789
789
  return wanted;
790
790
  }
791
+ /**
792
+ * Claims an operator has already confirmed are no longer wanted: a deleted
793
+ * block (`retiredBlockIds`), or a cap an edit dropped. Released on this apply,
794
+ * not after a second one — a missing block in a single snapshot is still the
795
+ * two-apply gate (`wantedClaims`). A disabled block is not here: its runtime
796
+ * hands the fields back itself.
797
+ */
798
+ function confirmedReleaseKeys(retiredBlockIds, planned, previous, claimGate) {
799
+ const retired = new Set(retiredBlockIds);
800
+ const seen = /* @__PURE__ */ new Set();
801
+ const keys = [];
802
+ const push = (key) => {
803
+ const id = claimKey(key);
804
+ if (seen.has(id)) return;
805
+ seen.add(id);
806
+ keys.push(key);
807
+ };
808
+ for (const owner of retired) for (const key of claimGate.claimsOf(owner)) push(key);
809
+ const plannedById = new Map(planned.map((block) => [block.block.id, block]));
810
+ for (const [blockId, entry] of previous) {
811
+ if (retired.has(blockId)) continue;
812
+ const next = plannedById.get(blockId);
813
+ if (next === void 0 || !next.block.enabled) continue;
814
+ if (entry.planned.block.updatedAt === next.block.updatedAt) continue;
815
+ for (const key of claimGate.claimsOf(blockId)) if (!planStillWantsClaim(next, key)) push(key);
816
+ }
817
+ return keys;
818
+ }
819
+ /** True when this plan still holds `key` — including while the plan cannot say, so a bad read releases nothing. */
820
+ function planStillWantsClaim(planned, key) {
821
+ const target = planned.target;
822
+ if (target.kind !== "existing") return false;
823
+ if (target.waiting !== null || target.gone !== null || target.deviceId === null || planned.problems.length > 0) return true;
824
+ return target.deviceId === key.deviceId && target.features.some((feature) => feature.cap === key.capName);
825
+ }
791
826
  //#endregion
792
827
  //#region src/builtins/composer/composition-declarations.ts
793
828
  /**
@@ -1713,6 +1748,13 @@ var CompositionRuntime = class {
1713
1748
  this.stopped = true;
1714
1749
  this.deps.timer.disarm();
1715
1750
  }
1751
+ /** `stop`, then wait until the target has sent nothing further. */
1752
+ quiesce() {
1753
+ this.stop();
1754
+ const quiesce = this.deps.target.quiesce;
1755
+ if (quiesce === void 0) return Promise.resolve();
1756
+ return quiesce.call(this.deps.target);
1757
+ }
1716
1758
  state() {
1717
1759
  return {
1718
1760
  failure: this.failure,
@@ -2216,6 +2258,10 @@ var OwnedFieldsTarget = class {
2216
2258
  listeners = [];
2217
2259
  /** Per cap, bumped by every `withhold`: a claim that settles under another epoch claimed a stale field set and does not land (I-2). */
2218
2260
  epoch = /* @__PURE__ */ new Map();
2261
+ /** Set by `quiesce`: nothing further is sent. */
2262
+ closed = false;
2263
+ idle = Promise.resolve();
2264
+ markIdle = null;
2219
2265
  constructor(deps) {
2220
2266
  this.deps = deps;
2221
2267
  this.id = deps.deviceId;
@@ -2240,6 +2286,27 @@ var OwnedFieldsTarget = class {
2240
2286
  }
2241
2287
  /** `device-status` belongs to the native device; availability lives in the block report only. */
2242
2288
  setAvailable(_available) {}
2289
+ /**
2290
+ * The block has stopped. Writes still queued are dropped; the call in flight
2291
+ * is allowed to finish, and then nothing else is sent.
2292
+ */
2293
+ quiesce() {
2294
+ this.closed = true;
2295
+ const dropped = [...this.pending.keys()];
2296
+ this.pending = /* @__PURE__ */ new Map();
2297
+ for (const cap of dropped) this.deps.logger.info("composed write dropped — the block stopped before it was sent", {
2298
+ tags: { deviceId: this.deps.deviceId },
2299
+ meta: {
2300
+ cap,
2301
+ owner: this.deps.owner
2302
+ }
2303
+ });
2304
+ if (this.inFlight.size === 0) return Promise.resolve();
2305
+ if (this.markIdle === null) this.idle = new Promise((resolve) => {
2306
+ this.markIdle = resolve;
2307
+ });
2308
+ return this.idle;
2309
+ }
2243
2310
  onAsyncFailure(listener) {
2244
2311
  this.listeners = [...this.listeners, listener];
2245
2312
  }
@@ -2255,6 +2322,17 @@ var OwnedFieldsTarget = class {
2255
2322
  return this.deps.fieldsOf(cap).filter((f) => held === void 0 || !held.has(f));
2256
2323
  }
2257
2324
  enqueue(cap, op) {
2325
+ if (this.closed) {
2326
+ this.deps.logger.info("composed write dropped — the block stopped before it was sent", {
2327
+ tags: { deviceId: this.deps.deviceId },
2328
+ meta: {
2329
+ cap,
2330
+ owner: this.deps.owner,
2331
+ op: op.kind
2332
+ }
2333
+ });
2334
+ return;
2335
+ }
2258
2336
  if (!this.inFlight.has(cap)) {
2259
2337
  this.send(cap, op);
2260
2338
  return;
@@ -2277,6 +2355,11 @@ var OwnedFieldsTarget = class {
2277
2355
  }
2278
2356
  settled(cap) {
2279
2357
  this.inFlight = new Set([...this.inFlight].filter((c) => c !== cap));
2358
+ if (this.closed) {
2359
+ this.pending = /* @__PURE__ */ new Map();
2360
+ if (this.inFlight.size === 0) this.markIdle?.();
2361
+ return;
2362
+ }
2280
2363
  const next = this.pending.get(cap);
2281
2364
  if (next === void 0) return;
2282
2365
  this.pending = new Map([...this.pending].filter(([c]) => c !== cap));
@@ -2693,7 +2776,11 @@ function verdictOf(blockId, entry, unsynced, grafts = null) {
2693
2776
  * filter in `composer.addon.ts` stays load-bearing. Its fields are CLAIMED on
2694
2777
  * the hub under the block id (`OwnedFieldsTarget`), and released through the
2695
2778
  * `ClaimGate` — two applies must agree, seeded at boot from the claims index —
2696
- * when the block is gone, disabled, or lost the feature. Its own replaced
2779
+ * when the block merely vanished from one snapshot. An operator delete
2780
+ * (`retiredBlockIds`) or an edit that drops a cap releases those claims on
2781
+ * THIS apply, after the old runtime has quiesced and before any replacement
2782
+ * claims; a release that does not land is logged with `tags: { deviceId }` and
2783
+ * returned on the apply. Its own replaced
2697
2784
  * fields are read in the NATIVE view (rule 6). Its target row gone on two
2698
2785
  * agreeing index reads fails the block by name and ungrafts what it added —
2699
2786
  * whether the loss was seen on `device.unregistered` or on a later plan; its
@@ -2841,12 +2928,16 @@ var Composer = class {
2841
2928
  };
2842
2929
  });
2843
2930
  await this.composed.reconcile(gate.integrationId, declarations, planned);
2931
+ const releasing = confirmedReleaseKeys(input.retiredBlockIds ?? [], planned, this.live, this.claimGate);
2844
2932
  const rebuilt = await this.rebuildRuntimes(planned, declarations, restart, rebuildErrors);
2933
+ await Promise.all(rebuilt.stopped.map((runtime) => runtime.quiesce()));
2934
+ const unreleasedClaims = await this.releaseConfirmed(releasing);
2845
2935
  await this.syncSources(rebuilt);
2846
2936
  await this.reconcileClaims();
2847
2937
  return {
2848
2938
  blocks: planned.map((p) => this.verdict(p.block.id)),
2849
- acted: true
2939
+ acted: true,
2940
+ unreleasedClaims
2850
2941
  };
2851
2942
  }
2852
2943
  plan(block, restart, claimsWait) {
@@ -2867,7 +2958,13 @@ var Composer = class {
2867
2958
  async rebuildRuntimes(planned, declarations, restart, rebuildErrors) {
2868
2959
  const reset = /* @__PURE__ */ new Set();
2869
2960
  const fresh = /* @__PURE__ */ new Set();
2961
+ const stopped = [];
2870
2962
  const next = /* @__PURE__ */ new Map();
2963
+ const halt = (runtime) => {
2964
+ if (runtime === null) return;
2965
+ runtime.stop();
2966
+ stopped.push(runtime);
2967
+ };
2871
2968
  for (const p of planned) {
2872
2969
  const previous = this.live.get(p.block.id);
2873
2970
  const lost = lostTarget(p, previous?.planned);
@@ -2938,7 +3035,7 @@ var Composer = class {
2938
3035
  enabled: p.block.enabled,
2939
3036
  settled: p.problems.length === 0
2940
3037
  });
2941
- previous?.runtime?.stop();
3038
+ halt(previous?.runtime ?? null);
2942
3039
  if (runtime !== null) fresh.add(runtime);
2943
3040
  next.set(p.block.id, {
2944
3041
  planned: p,
@@ -2953,12 +3050,15 @@ var Composer = class {
2953
3050
  for (const [blockId, entry] of this.live) {
2954
3051
  if (next.has(blockId)) continue;
2955
3052
  if (held.has(composedDeviceStableId(blockId))) next.set(blockId, entry);
2956
- else entry.runtime?.stop();
3053
+ else halt(entry.runtime);
2957
3054
  }
2958
3055
  this.live = next;
2959
3056
  const liveRuntimes = new Set([...next.values()].map((e) => e.runtime));
2960
3057
  this.unsynced = new Set([...[...this.unsynced].filter((r) => liveRuntimes.has(r)), ...fresh]);
2961
- return { reset };
3058
+ return {
3059
+ reset,
3060
+ stopped
3061
+ };
2962
3062
  }
2963
3063
  ungraftGone(blockId, target, deviceId) {
2964
3064
  this.grafts.gone(deviceId);
@@ -3042,21 +3142,40 @@ var Composer = class {
3042
3142
  if (!this.claimSeed.done) return;
3043
3143
  for (const key of this.claimGate.filter(wantedClaims(this.live, this.claimGate))) await this.release(key);
3044
3144
  }
3145
+ /** Confirmed releases, awaited. A failure is the string the hub gave; the gate retries it later. */
3146
+ async releaseConfirmed(keys) {
3147
+ const unreleased = [];
3148
+ for (const key of keys) {
3149
+ const error = await this.release(key);
3150
+ if (error === null) continue;
3151
+ unreleased.push({
3152
+ deviceId: key.deviceId,
3153
+ capName: key.capName,
3154
+ owner: key.owner,
3155
+ error
3156
+ });
3157
+ }
3158
+ return unreleased;
3159
+ }
3160
+ /** Null when the hub confirmed the release (or held nothing of ours). */
3045
3161
  async release(key) {
3046
3162
  let outcome;
3047
3163
  try {
3048
3164
  outcome = await this.deps.claims.releaseClaim(key);
3049
3165
  } catch (err) {
3050
- this.claimGate.releaseFailed(key, errMsg(err));
3051
- return;
3166
+ const error = errMsg(err);
3167
+ this.claimGate.releaseFailed(key, error);
3168
+ return error;
3052
3169
  }
3053
3170
  if (!outcome.ok && !RELEASE_SETTLED_CODES.has(outcome.code)) {
3054
- this.claimGate.releaseFailed(key, `${outcome.code}: ${outcome.message}`);
3055
- return;
3171
+ const error = `${outcome.code}: ${outcome.message}`;
3172
+ this.claimGate.releaseFailed(key, error);
3173
+ return error;
3056
3174
  }
3057
3175
  this.claimGate.released(key, outcome.ok ? "released" : outcome.code);
3058
3176
  await this.grafts.released(key.deviceId, key.capName);
3059
3177
  await this.onClaimEvent(key.deviceId, key.capName, key.owner, "free");
3178
+ return null;
3060
3179
  }
3061
3180
  /**
3062
3181
  * Rule 5: a customization whose target row is gone — the tracker's two