@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.
- package/dist/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/claim-gate.d.ts +32 -0
- package/dist/builtins/composer/composed-device-set.d.ts +38 -0
- package/dist/builtins/composer/composed-device.d.ts +4 -3
- package/dist/builtins/composer/composer-grafts.d.ts +54 -0
- package/dist/builtins/composer/composer-plan.d.ts +96 -0
- package/dist/builtins/composer/composer-verdict.d.ts +19 -0
- package/dist/builtins/composer/composer.addon.js +2717 -477
- package/dist/builtins/composer/composer.addon.mjs +2722 -482
- package/dist/builtins/composer/composer.d.ts +71 -52
- package/dist/builtins/composer/composition-runtime.d.ts +93 -9
- package/dist/builtins/composer/confirmed-seed.d.ts +32 -0
- package/dist/builtins/composer/existing-target.d.ts +96 -0
- package/dist/builtins/composer/field-record.d.ts +50 -0
- package/dist/builtins/composer/graft-host.d.ts +47 -0
- package/dist/builtins/composer/held-claims.d.ts +12 -0
- package/dist/builtins/composer/owned-fields-target.d.ts +55 -0
- package/dist/builtins/composer/slice-assembler.d.ts +10 -0
- package/dist/builtins/composer/source-readings.d.ts +122 -0
- package/dist/builtins/composer/source-tracker.d.ts +35 -42
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/composition-api.d.ts +7 -4
- package/dist/builtins/core-blocks/composition-peers.d.ts +9 -0
- package/dist/builtins/core-blocks/composition-sources.d.ts +16 -3
- package/dist/builtins/core-blocks/core-block-store.d.ts +8 -1
- package/dist/builtins/core-blocks/core-blocks.addon.d.ts +4 -3
- package/dist/builtins/core-blocks/core-blocks.addon.js +255 -79
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +255 -79
- package/dist/builtins/device-manager/claimed-status-overlay.d.ts +12 -0
- package/dist/builtins/device-manager/device-manager.addon.d.ts +16 -0
- package/dist/builtins/device-manager/device-manager.addon.js +1825 -317
- package/dist/builtins/device-manager/device-manager.addon.mjs +1825 -317
- package/dist/builtins/device-manager/device-provider-context.d.ts +24 -2
- package/dist/builtins/device-manager/device-row-store.d.ts +2 -0
- package/dist/builtins/device-manager/device-state-claim-views.d.ts +77 -0
- package/dist/builtins/device-manager/device-state-claims.d.ts +43 -0
- package/dist/builtins/device-manager/device-state-mirror.d.ts +145 -61
- package/dist/builtins/device-manager/field-claims-index.d.ts +69 -0
- package/dist/builtins/device-manager/field-ownership.d.ts +53 -0
- package/dist/builtins/device-manager/migrate-device.d.ts +27 -0
- package/dist/builtins/device-manager/migrate-guard.d.ts +7 -0
- package/dist/builtins/device-manager/migrate-hardware-state.d.ts +55 -0
- package/dist/builtins/device-manager/migration-refused.d.ts +23 -0
- package/dist/builtins/device-manager/mirror-row-writer.d.ts +138 -0
- package/dist/builtins/device-manager/runtime-state-persist-gate.d.ts +4 -0
- package/dist/builtins/doorbell/binding-mirror.d.ts +1 -0
- package/dist/builtins/doorbell/doorbell-composition-migration.d.ts +59 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.d.ts +3 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +315 -10
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +315 -10
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{child-cap-dispatch-CHTiHbsv.mjs → child-cap-dispatch-AAltfBUH.mjs} +1484 -1417
- package/dist/{child-cap-dispatch-Dyn_7ezH.js → child-cap-dispatch-DzcUd_ct.js} +1503 -1424
- package/dist/composition-sources-DYqQLRzF.js +122 -0
- package/dist/composition-sources-yonsRneP.mjs +111 -0
- package/dist/{dist-CkRwnhbo.js → dist-DVKv5i-e.js} +5396 -4331
- package/dist/{dist-Cj7pJmXq.mjs → dist-MzYJVCLE.mjs} +5330 -4331
- package/dist/index.js +137 -8
- package/dist/index.mjs +133 -8
- package/dist/kernel/capability-registry.d.ts +43 -1
- package/dist/kernel/index.d.ts +3 -1
- package/dist/kernel/moleculer/device-cap-proxy.d.ts +0 -10
- package/dist/kernel/status-overlay.d.ts +18 -0
- package/dist/kernel/transport/claimed-get-status.d.ts +15 -0
- package/dist/kernel/transport/index.d.ts +2 -0
- package/dist/kernel/transport/local-child-registry.d.ts +23 -0
- package/dist/kernel/transport/parent-unowned-call.d.ts +25 -0
- package/dist/{retired-settings-keys-C1duPikd.mjs → retired-settings-keys-8yY2sTc-.mjs} +1 -1
- package/dist/{retired-settings-keys-CGSuXhP8.js → retired-settings-keys-iY5I0nKM.js} +1 -1
- 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
|
/**
|
|
@@ -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`).
|