@camstack/system 1.2.313 → 1.2.315

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 (111) hide show
  1. package/dist/addon-runner.js +1 -1
  2. package/dist/addon-runner.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 +1 -1
  12. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
  13. package/dist/builtins/camera-grid/index.js +1 -1
  14. package/dist/builtins/camera-grid/index.mjs +1 -1
  15. package/dist/builtins/composer/claim-gate.d.ts +32 -0
  16. package/dist/builtins/composer/composed-device-set.d.ts +38 -0
  17. package/dist/builtins/composer/composed-device.d.ts +4 -3
  18. package/dist/builtins/composer/composer-grafts.d.ts +54 -0
  19. package/dist/builtins/composer/composer-plan.d.ts +96 -0
  20. package/dist/builtins/composer/composer-verdict.d.ts +19 -0
  21. package/dist/builtins/composer/composer.addon.js +2717 -477
  22. package/dist/builtins/composer/composer.addon.mjs +2722 -482
  23. package/dist/builtins/composer/composer.d.ts +71 -52
  24. package/dist/builtins/composer/composition-runtime.d.ts +93 -9
  25. package/dist/builtins/composer/confirmed-seed.d.ts +32 -0
  26. package/dist/builtins/composer/existing-target.d.ts +96 -0
  27. package/dist/builtins/composer/field-record.d.ts +50 -0
  28. package/dist/builtins/composer/graft-host.d.ts +47 -0
  29. package/dist/builtins/composer/held-claims.d.ts +12 -0
  30. package/dist/builtins/composer/owned-fields-target.d.ts +55 -0
  31. package/dist/builtins/composer/slice-assembler.d.ts +10 -0
  32. package/dist/builtins/composer/source-readings.d.ts +122 -0
  33. package/dist/builtins/composer/source-tracker.d.ts +35 -42
  34. package/dist/builtins/console-logging/index.js +1 -1
  35. package/dist/builtins/console-logging/index.mjs +1 -1
  36. package/dist/builtins/core-blocks/composition-api.d.ts +7 -4
  37. package/dist/builtins/core-blocks/composition-peers.d.ts +9 -0
  38. package/dist/builtins/core-blocks/composition-sources.d.ts +16 -3
  39. package/dist/builtins/core-blocks/core-block-store.d.ts +8 -1
  40. package/dist/builtins/core-blocks/core-blocks.addon.d.ts +4 -3
  41. package/dist/builtins/core-blocks/core-blocks.addon.js +255 -79
  42. package/dist/builtins/core-blocks/core-blocks.addon.mjs +255 -79
  43. package/dist/builtins/device-manager/claimed-status-overlay.d.ts +12 -0
  44. package/dist/builtins/device-manager/device-manager.addon.d.ts +16 -0
  45. package/dist/builtins/device-manager/device-manager.addon.js +1825 -317
  46. package/dist/builtins/device-manager/device-manager.addon.mjs +1825 -317
  47. package/dist/builtins/device-manager/device-provider-context.d.ts +24 -2
  48. package/dist/builtins/device-manager/device-row-store.d.ts +2 -0
  49. package/dist/builtins/device-manager/device-state-claim-views.d.ts +77 -0
  50. package/dist/builtins/device-manager/device-state-claims.d.ts +43 -0
  51. package/dist/builtins/device-manager/device-state-mirror.d.ts +145 -61
  52. package/dist/builtins/device-manager/field-claims-index.d.ts +69 -0
  53. package/dist/builtins/device-manager/field-ownership.d.ts +53 -0
  54. package/dist/builtins/device-manager/migrate-device.d.ts +27 -0
  55. package/dist/builtins/device-manager/migrate-guard.d.ts +7 -0
  56. package/dist/builtins/device-manager/migrate-hardware-state.d.ts +55 -0
  57. package/dist/builtins/device-manager/migration-refused.d.ts +23 -0
  58. package/dist/builtins/device-manager/mirror-row-writer.d.ts +138 -0
  59. package/dist/builtins/device-manager/runtime-state-persist-gate.d.ts +4 -0
  60. package/dist/builtins/doorbell/binding-mirror.d.ts +1 -0
  61. package/dist/builtins/doorbell/doorbell-composition-migration.d.ts +59 -0
  62. package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +3 -0
  63. package/dist/builtins/doorbell/virtual-doorbell.addon.js +315 -10
  64. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +315 -10
  65. package/dist/builtins/hub-forwarder/index.js +1 -1
  66. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  67. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  68. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  69. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  70. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  71. package/dist/builtins/local-network/local-network.addon.js +1 -1
  72. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  73. package/dist/builtins/loki-logging/index.js +1 -1
  74. package/dist/builtins/loki-logging/index.mjs +1 -1
  75. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  76. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  77. package/dist/builtins/platform-probe/index.js +1 -1
  78. package/dist/builtins/platform-probe/index.mjs +1 -1
  79. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  80. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  81. package/dist/builtins/snapshot/index.js +1 -1
  82. package/dist/builtins/snapshot/index.mjs +1 -1
  83. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  84. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  85. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  86. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  87. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  88. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  89. package/dist/builtins/system-config/system-config.addon.js +1 -1
  90. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  91. package/dist/builtins/winston-logging/index.js +1 -1
  92. package/dist/builtins/winston-logging/index.mjs +1 -1
  93. package/dist/{child-cap-dispatch-CHTiHbsv.mjs → child-cap-dispatch-AAltfBUH.mjs} +1484 -1417
  94. package/dist/{child-cap-dispatch-Dyn_7ezH.js → child-cap-dispatch-DzcUd_ct.js} +1503 -1424
  95. package/dist/composition-sources-DYqQLRzF.js +122 -0
  96. package/dist/composition-sources-yonsRneP.mjs +111 -0
  97. package/dist/{dist-CkRwnhbo.js → dist-DVKv5i-e.js} +5396 -4331
  98. package/dist/{dist-Cj7pJmXq.mjs → dist-MzYJVCLE.mjs} +5330 -4331
  99. package/dist/index.js +137 -8
  100. package/dist/index.mjs +133 -8
  101. package/dist/kernel/capability-registry.d.ts +43 -1
  102. package/dist/kernel/index.d.ts +3 -1
  103. package/dist/kernel/moleculer/device-cap-proxy.d.ts +0 -10
  104. package/dist/kernel/status-overlay.d.ts +18 -0
  105. package/dist/kernel/transport/claimed-get-status.d.ts +15 -0
  106. package/dist/kernel/transport/index.d.ts +2 -0
  107. package/dist/kernel/transport/local-child-registry.d.ts +23 -0
  108. package/dist/kernel/transport/parent-unowned-call.d.ts +25 -0
  109. package/dist/{retired-settings-keys-C1duPikd.mjs → retired-settings-keys-8yY2sTc-.mjs} +1 -1
  110. package/dist/{retired-settings-keys-CGSuXhP8.js → retired-settings-keys-iY5I0nKM.js} +1 -1
  111. package/package.json +1 -1
@@ -1,4 +1,6 @@
1
1
  import { DeviceManagerSettings } from './device-meta-store.js';
2
+ import { OwnedCaps } from './field-ownership.js';
3
+ import { MigrationRefusedError } from './migration-refused.js';
2
4
  /** The narrow settings slice the swap needs — the seam the spec fakes. */
3
5
  export type HardwareStateStore = Pick<DeviceManagerSettings, 'readDeviceStore' | 'writeDeviceStore' | 'clearDeviceStore' | 'readDeviceRuntimeState' | 'writeDeviceRuntimeState'>;
4
6
  export interface SwappedHardwareState {
@@ -14,6 +16,59 @@ export interface SwappedHardwareState {
14
16
  readonly roleKeysKeptAtSource: readonly string[];
15
17
  readonly roleKeysKeptAtTarget: readonly string[];
16
18
  }
19
+ /** One claimed cap on a device that blocks its migration. */
20
+ export interface BlockingClaim {
21
+ readonly cap: string;
22
+ readonly owner: string;
23
+ readonly fields: readonly string[];
24
+ }
25
+ /** A device whose row carries claims, and what they are. */
26
+ export interface ClaimBlockedDevice {
27
+ readonly deviceId: number;
28
+ readonly claims: readonly BlockingClaim[];
29
+ }
30
+ /**
31
+ * The refusal of a migration over a device under customization. `blocked`
32
+ * carries the same facts as the message, per device, so the caller can log
33
+ * one line per device tagged with its id.
34
+ */
35
+ export declare class FieldClaimsBlockMigrationError extends Error {
36
+ readonly sourceId: number;
37
+ readonly targetId: number;
38
+ readonly blocked: readonly ClaimBlockedDevice[];
39
+ constructor(sourceId: number, targetId: number, blocked: readonly ClaimBlockedDevice[]);
40
+ }
41
+ /**
42
+ * A claim that landed while the migration was already running (D663 final
43
+ * review I-1). It passed the claim methods' migration gate just before the
44
+ * migration began, and its write-through had not reached the row when
45
+ * {@link assertNoFieldClaims} read it. Found after the drain, before any row
46
+ * moved — so it is a {@link MigrationRefusedError}: both cameras' switches are
47
+ * restored, and the claim stays on the number it names.
48
+ */
49
+ export declare class ClaimLandedDuringMigrationError extends MigrationRefusedError {
50
+ readonly sourceId: number;
51
+ readonly targetId: number;
52
+ readonly blocked: readonly ClaimBlockedDevice[];
53
+ constructor(sourceId: number, targetId: number, blocked: readonly ClaimBlockedDevice[]);
54
+ }
55
+ /**
56
+ * The second look, from MEMORY, once the mirror's writer is held and drained
57
+ * (I-1). A claim is in the mirror from the moment it is accepted, before its
58
+ * write-through lands, and the hold refuses any claim that reaches the mirror
59
+ * after it — so an empty answer here means no claim can land on either row
60
+ * until the swap is done.
61
+ */
62
+ export declare function assertNoClaimLandedDuringMigration(ownedCapsOf: (deviceId: number) => OwnedCaps, sourceId: number, targetId: number): void;
63
+ /**
64
+ * A claim names a NUMBER (`$owned` sits in `__device-state:<id>`); the block
65
+ * that owns it names a STABLE ID; `swapIds` moves the stable id to the other
66
+ * number. No swap of `$owned` keeps both keys right, so a device under
67
+ * customization is not migratable (D663). Read from the ROW — claims are
68
+ * written through, so the row is where one exists. A row that cannot be read
69
+ * THROWS: an unknown answer must refuse, never read as "no claim" (D49).
70
+ */
71
+ export declare function assertNoFieldClaims(settings: Pick<HardwareStateStore, 'readDeviceRuntimeState'>, sourceId: number, targetId: number): Promise<void>;
17
72
  /**
18
73
  * Swap the hardware-scoped halves of two devices' persisted state. Runs right
19
74
  * after `swapIds`, under the same meta write lock. THROWS on failure: a
@@ -0,0 +1,23 @@
1
+ /**
2
+ * A migration step that REFUSED with nothing moved.
3
+ *
4
+ * `migrateDevice` distinguishes two kinds of failure after the disable phase,
5
+ * and they must never be confused (D663 task 5b ruling I-1):
6
+ *
7
+ * - **refused, nothing moved** — a precheck of `swapIds` (a row missing, the
8
+ * temp id taken), the mirror drain that runs before the rows move, or a
9
+ * claim found in the mirror after that drain (it landed while the migration
10
+ * was starting). Both cameras are switched back to exactly what they were,
11
+ * because nothing about either device changed.
12
+ * - **failed while moving** — any other throw from `swapIds` (rows partially
13
+ * moved), or from `swapHardwareState` (rows swapped, state not). The two
14
+ * devices may wear each other's identity; switching them back on would run
15
+ * pipelines against crossed identities, so they stay off and the operator is
16
+ * told to intervene.
17
+ *
18
+ * Only a step that KNOWS it wrote nothing may throw this. An unknown error is
19
+ * read as "may have moved" — the safe reading.
20
+ */
21
+ export declare class MigrationRefusedError extends Error {
22
+ constructor(message: string, options?: ErrorOptions);
23
+ }
@@ -0,0 +1,138 @@
1
+ import { IScopedLogger } from '@camstack/types';
2
+ import { DeviceManagerSettings } from './device-meta-store.js';
3
+ import { MigrationRefusedError } from './migration-refused.js';
4
+ import { RuntimeStatePolicyLookup } from './runtime-state-persist-gate.js';
5
+ /** A device's row as it may reach disk: cap name → persistable slice (plus `$owned`). */
6
+ export type PersistableRow = Record<string, Record<string, unknown>>;
7
+ /**
8
+ * The drain before a migration could not vouch for a device's row. Nothing has
9
+ * moved yet, so it is a {@link MigrationRefusedError}: the migration refuses
10
+ * and restores both cameras' switches.
11
+ */
12
+ export declare class RuntimeStateDrainError extends MigrationRefusedError {
13
+ readonly deviceId: number;
14
+ constructor(deviceId: number, reason: string, options?: ErrorOptions);
15
+ }
16
+ /** A migration's hold on the debounced writer (`holdDiskWrites`). */
17
+ export interface MirrorWriteHold {
18
+ /** Let the writer back onto the rows; re-arms it so the row converges to memory. */
19
+ readonly release: () => void;
20
+ }
21
+ /** What the writer needs from the mirror that owns it. */
22
+ export interface MirrorRowWriterDeps {
23
+ readonly logger: IScopedLogger;
24
+ /** Per-cap durability + volatile-field policy (D4). */
25
+ readonly policyFor: RuntimeStatePolicyLookup;
26
+ /** The device's row as memory holds it NOW — taken when a write RUNS. */
27
+ readonly persistableFor: (deviceId: number) => PersistableRow;
28
+ }
29
+ export declare class MirrorRowWriter {
30
+ private readonly deps;
31
+ static readonly RUNTIME_STATE_DEBOUNCE_MS = 1000;
32
+ /**
33
+ * Per-device disk-write debouncer for runtime-state. `setCapSlice`
34
+ * updates the in-memory mirror synchronously and emits the change
35
+ * event immediately, but the disk write is coalesced.
36
+ */
37
+ private readonly runtimeStateDebounce;
38
+ /**
39
+ * Devices whose debounced writer is HELD off the row — a migration is
40
+ * swapping it (`holdDiskWrites`). A change while held stays in memory only.
41
+ */
42
+ private readonly writesHeld;
43
+ /** Held devices a durable change was scheduled for — what `release` re-arms. */
44
+ private readonly heldDirty;
45
+ /**
46
+ * What is believed to be ON DISK for each device — the persistable projection
47
+ * of the last blob actually written (or read at seed time). The
48
+ * effective-change gate compares against THIS, not against the previous
49
+ * mirror state: two clock-only ticks in a row must not add up to a write just
50
+ * because each was compared with its immediate predecessor.
51
+ */
52
+ private readonly lastPersisted;
53
+ /**
54
+ * Per-device count of writes the effective-change gate skipped since the last
55
+ * real one. Reported on the next write that DOES happen, so the log says how
56
+ * much churn the gate absorbed instead of saying nothing at all — a branch
57
+ * that drops work silently reads as "never happened".
58
+ */
59
+ private readonly skippedSinceWrite;
60
+ constructor(deps: MirrorRowWriterDeps);
61
+ /** What is believed to be on the device's row; undefined before any read or write. */
62
+ persisted(deviceId: number): PersistableRow | undefined;
63
+ /** Record what the row now holds — a write that landed, or a row just read. */
64
+ markPersisted(deviceId: number, row: PersistableRow): void;
65
+ /** A migration holds the writer off this device's row right now. */
66
+ isHeld(deviceId: number): boolean;
67
+ /**
68
+ * Chain a row write behind whatever is in flight for the device. Returns the
69
+ * write's own promise (it may reject, to its caller); the queue tail never
70
+ * rejects and clears itself once it is the last one out.
71
+ */
72
+ serialiseWrite<T>(deviceId: number, task: () => Promise<T>): Promise<T>;
73
+ private slotFor;
74
+ /** Arm the writer for a durable change, or remember it for `release` while a migration holds the device. */
75
+ armOrHold(deviceId: number, settings: DeviceManagerSettings): void;
76
+ /**
77
+ * Debounced disk writer, behind two gates.
78
+ *
79
+ * GATE — DURABILITY: a change to a `durability: 'session'` cap never even
80
+ * arms the timer. That is where the fleet's write rate goes: `audio-metrics`
81
+ * and `zone-analytics` are ~90 % of the offered 12–19 writes/s, they change
82
+ * genuinely every second, and their restored value is worthless.
83
+ *
84
+ * GATE — EFFECTIVE CHANGE (at flush): the persistable blob is compared with
85
+ * what is believed to be on disk, ignoring the clock fields each cap declared
86
+ * volatile. `battery` wrote its blob 526 times in 25 minutes without one
87
+ * percentage moving; this is the gate that turns those into one write.
88
+ *
89
+ * The blob is read from the live mirror at flush time, so the disk picture is
90
+ * always the latest state — no risk of writing a stale snapshot.
91
+ */
92
+ schedule(deviceId: number, settings: DeviceManagerSettings, changedCap: string): void;
93
+ /** Arm the debounce timer; at fire, queue a write of the live blob unless disk already has it. */
94
+ private armDiskWrite;
95
+ /** The debounced write itself: the blob as memory holds it NOW, behind the effective-change gate. Never rejects. */
96
+ private writeLiveBlob;
97
+ /**
98
+ * Hold the debounced writer off these devices' rows while a migration swaps
99
+ * them (D663 re-review N-6). Resolves once each device is DRAINED: a write
100
+ * still in its debounce window is written now (it is this hardware's latest
101
+ * state, and the swap then carries it to the right number), and a write
102
+ * already in flight has landed. Until `release`, nothing the writer does
103
+ * reaches these rows — a blob read before the swap and written after it would
104
+ * put the other box's state back under the number, and make it the baseline.
105
+ *
106
+ * Only what the writer already had pending is written — never memory
107
+ * wholesale, which for a row nobody read would overwrite it with a partial
108
+ * picture. `release` re-arms the writer for a device changed while held:
109
+ * after the migration's `replace` reseed memory equals the row and nothing
110
+ * is written; after a failed swap the row converges to memory.
111
+ *
112
+ * The migration must not proceed over a row that is not what memory says it
113
+ * is. So a drain write that FAILS, and equally a write that was in flight and
114
+ * FAILED (ruling I-2 — awaiting it is not the same as it landing), release
115
+ * the hold, log the device and reject with a {@link RuntimeStateDrainError};
116
+ * the owed write is re-armed on release.
117
+ *
118
+ * One hold per device at a time: `writesHeld` is a set, not a refcount, so a
119
+ * second overlapping hold would be released by the first. Safe because the
120
+ * only caller is `migrateDevice`, serialised by `migrationGuard` and the meta
121
+ * write lock; a new caller needs a refcount first.
122
+ */
123
+ holdDiskWrites(deviceIds: readonly number[], settings: DeviceManagerSettings): Promise<MirrorWriteHold>;
124
+ /** Cancel the timer, await the write in flight, then write (queued) what is not yet on disk. */
125
+ private drainDiskWrite;
126
+ /** A drain that cannot vouch for the row: still owed (re-armed on release), logged, refused. */
127
+ private refuseDrain;
128
+ /**
129
+ * True when `blob` differs from what is believed to be on disk only in fields
130
+ * the owning caps declared volatile. Counts the skip so the next real write
131
+ * can report how much churn was absorbed.
132
+ */
133
+ private isAlreadyPersisted;
134
+ /** Flush every pending debounced disk write (graceful shutdown). Clears the
135
+ * debounce slots after awaiting in-flight + scheduled writes so shutdown is
136
+ * lossless. */
137
+ flushPendingWrites(settings: DeviceManagerSettings | undefined): Promise<void>;
138
+ }
@@ -11,6 +11,10 @@ export type RuntimeStateBlob = Readonly<Record<string, Readonly<Record<string, u
11
11
  * direction is deliberate: the cost of losing a session value is a cold window
12
12
  * until the provider republishes; the cost of persisting an unasked-for one is
13
13
  * a commit per second on the fleet's busiest write path.
14
+ *
15
+ * `$owned` is not a cap: it is the device's claims record (D663) and is kept
16
+ * whatever the lookup says — a restart that forgot who owns a field would hand
17
+ * it back to the native provider.
14
18
  */
15
19
  export declare function persistableBlob(blob: RuntimeStateBlob, policyFor: RuntimeStatePolicyLookup): Record<string, Record<string, unknown>>;
16
20
  /**
@@ -84,6 +84,7 @@ export interface BindingEntryView {
84
84
  readonly capName: string;
85
85
  readonly kind: string;
86
86
  readonly providerAddonId: string;
87
+ readonly nativeAddonId?: string;
87
88
  }
88
89
  /**
89
90
  * Does this list of binding entries name `ownAddonId` as the ACTIVE WRAPPER
@@ -0,0 +1,59 @@
1
+ import { CompositionFieldsFeature, CompositionSourceRef, DeviceType, ICoreBlocksProvider, IScopedLogger } from '@camstack/types';
2
+ import { BindingEntryView } from './binding-mirror.js';
3
+ import { DoorbellSource } from './doorbell-settings.js';
4
+ export declare const DOORBELL_COMPOSITION_MIGRATION_STATE_KEY = "doorbell-source-composition-migration-v1";
5
+ export declare const DOORBELL_COMPOSITION_MIGRATION_CALLER: {
6
+ readonly userId: "system:doorbell-source-migration";
7
+ readonly isAdmin: true;
8
+ };
9
+ export interface DoorbellMigrationDevice {
10
+ readonly id: number;
11
+ readonly addonId: string;
12
+ readonly stableId: string;
13
+ readonly type: DeviceType;
14
+ }
15
+ export interface DoorbellMigrationBindingRow {
16
+ readonly deviceId: number;
17
+ readonly entries: readonly BindingEntryView[];
18
+ }
19
+ export interface DoorbellCompositionMigrationDeps {
20
+ readonly logger: IScopedLogger;
21
+ readonly listCameras: () => Promise<readonly DoorbellMigrationDevice[]>;
22
+ readonly listSourceDevices: (deviceIds: readonly number[]) => Promise<readonly DoorbellMigrationDevice[]>;
23
+ readonly readSources: (cameraId: number) => Promise<readonly DoorbellSource[]>;
24
+ readonly getBindingsBatch: (deviceIds: readonly number[]) => Promise<readonly DoorbellMigrationBindingRow[]>;
25
+ readonly coreBlocks: () => ICoreBlocksProvider | null;
26
+ readonly setWrapperActive: (input: {
27
+ readonly deviceId: number;
28
+ readonly capName: 'doorbell';
29
+ readonly wrapperAddonId: 'virtual-doorbell';
30
+ readonly active: false;
31
+ }) => Promise<void>;
32
+ readonly isComplete: () => Promise<boolean>;
33
+ readonly markComplete: () => Promise<void>;
34
+ }
35
+ interface DoorbellFeatureSource {
36
+ readonly source: CompositionSourceRef;
37
+ readonly cap: string;
38
+ readonly activeField: string;
39
+ readonly changedAtField: string;
40
+ }
41
+ export declare function buildDoorbellCompositionFeature(input: {
42
+ readonly camera: CompositionSourceRef;
43
+ readonly sources: readonly DoorbellFeatureSource[];
44
+ readonly enabled: boolean;
45
+ }): CompositionFieldsFeature;
46
+ export declare class DoorbellCompositionMigration {
47
+ private readonly deps;
48
+ private rerunRequested;
49
+ private inFlight;
50
+ private stopped;
51
+ constructor(deps: DoorbellCompositionMigrationDeps);
52
+ request(reason: string): void;
53
+ waitForIdle(): Promise<void>;
54
+ shutdown(): void;
55
+ private runUntilQuiet;
56
+ private runAttempt;
57
+ private warnCamera;
58
+ }
59
+ export {};
@@ -42,6 +42,8 @@ interface VirtualDoorbellAddonConfig {
42
42
  */
43
43
  export declare class VirtualDoorbellAddon extends BaseAddon<VirtualDoorbellAddonConfig> {
44
44
  private engine;
45
+ private compositionMigration;
46
+ private providerRegisteredUnsubscribe;
45
47
  /** Monotonic guard: assignments are reloaded lazily on trigger traffic. */
46
48
  private assignmentsLoadedAt;
47
49
  private assignmentsLoading;
@@ -53,6 +55,7 @@ export declare class VirtualDoorbellAddon extends BaseAddon<VirtualDoorbellAddon
53
55
  private readonly bindingMirror;
54
56
  constructor();
55
57
  protected onInitialize(): Promise<ProviderRegistration[]>;
58
+ protected onShutdown(): Promise<void>;
56
59
  /**
57
60
  * The `device-extension` provider: the only editor of a camera's trigger
58
61
  * devices since D655 (see `doorbell-extension-slot.ts`).