@camstack/types 1.2.45 → 1.2.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +11 -2
- package/dist/addon.mjs +11 -2
- package/dist/{fmp4-box-splitter-B53u9-Nu.mjs → canonical-hash-rO1sRmEK.mjs} +34 -34
- package/dist/capabilities/core-blocks.cap.d.ts +52 -0
- package/dist/capabilities/device-manager.cap.d.ts +11 -427
- package/dist/capabilities/index.d.ts +3 -3
- package/dist/capabilities/motion-detection.cap.d.ts +6 -1
- package/dist/capabilities/notification-output.cap.d.ts +4 -4
- package/dist/capabilities/notification-rules.cap.d.ts +24 -24
- package/dist/capabilities/oauth-integration.cap.d.ts +4 -0
- package/dist/capabilities/osd-manager.cap.d.ts +12 -12
- package/dist/capabilities/pipeline-analytics.cap.d.ts +7 -1
- package/dist/capabilities/sso-bridge.cap.d.ts +3 -0
- package/dist/capabilities/stream-broker.cap.d.ts +165 -1
- package/dist/capabilities/user-management.cap.d.ts +3 -1
- package/dist/capabilities/videoclips.cap.d.ts +5 -0
- package/dist/device/declared-device.d.ts +197 -0
- package/dist/device/device-binding.d.ts +15 -9
- package/dist/device/device-management.d.ts +1 -83
- package/dist/device/index.d.ts +20 -19
- package/dist/expression/binding-source.d.ts +85 -0
- package/dist/expression/{link-expression.d.ts → expression-source.d.ts} +22 -13
- package/dist/expression/index.d.ts +17 -14
- package/dist/expression/limits.d.ts +1 -1
- package/dist/generated/addon-api.d.ts +21 -7
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/device-proxy.d.ts +2 -2
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +2 -2
- package/dist/index.d.ts +9 -8
- package/dist/index.js +10224 -9819
- package/dist/index.mjs +10195 -9805
- package/dist/node.js +7 -7
- package/dist/node.mjs +1 -1
- package/dist/{sleep-Bf5fBs7u.mjs → sleep-7WqNZVcL.mjs} +1 -1
- package/dist/{sleep-BOI-sVEA.js → sleep-ocMLM2o5.js} +1 -1
- package/package.json +1 -1
- package/dist/device/device-link-transform.d.ts +0 -5
- package/dist/{fmp4-box-splitter-BkWH7O3L.js → canonical-hash-DNV8S5ET.js} +33 -33
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declared devices — the ownership primitive for a device an addon DECLARES
|
|
3
|
+
* rather than discovers.
|
|
4
|
+
*
|
|
5
|
+
* A provider adopts what a vendor reports. A *declaration* is the other shape:
|
|
6
|
+
* a device exists because an addon says so, in code, on every boot. The NC
|
|
7
|
+
* alarm panel is the live instance of it (device 1023) and, until this module,
|
|
8
|
+
* the only one — hand-rolled inline with eight injected dependencies.
|
|
9
|
+
*
|
|
10
|
+
* What this owns, and why each part is here rather than at the call site:
|
|
11
|
+
*
|
|
12
|
+
* - **Idempotence.** `reconcile` is `get-or-create` on `(addonId, stableId)`
|
|
13
|
+
* and is safe to run on every boot and on every convergence tick. Nothing
|
|
14
|
+
* here is one-shot.
|
|
15
|
+
* - **Reconcile, not one-shot (D49).** The destructive branch — "the index does
|
|
16
|
+
* not list it, so CREATE it with its declared meta" — re-stamps the declared
|
|
17
|
+
* name over an operator's rename. It therefore requires a SECOND read of the
|
|
18
|
+
* index to agree before it runs. A read that fails changes nothing at all:
|
|
19
|
+
* the pass aborts, and the next one converges.
|
|
20
|
+
* - **The name follows the authority.** A declared device's name is seeded ONCE,
|
|
21
|
+
* at create. Every later pass adopts without meta, because a rename belongs to
|
|
22
|
+
* the operator and re-stamping it every boot is the losing half of two writers
|
|
23
|
+
* (D62).
|
|
24
|
+
* - **`fixed` is re-asserted every pass.** The defect the hand-rolled version
|
|
25
|
+
* shipped with wrote `info.fixed` only on the create path, so an install that
|
|
26
|
+
* already had the integration never received the flag — the kernel kept
|
|
27
|
+
* allowing its deletion. Get-or-create is not enough for a flag; it has to be
|
|
28
|
+
* re-asserted.
|
|
29
|
+
* - **Withdrawal is cleanup.** A declaration removed from the code removes its
|
|
30
|
+
* device, bounded to the addon's own FIXED integration and logged per device.
|
|
31
|
+
* Nothing outside that integration is ever touched.
|
|
32
|
+
* - **One owner node.** An addon that runs on every node must not race to own a
|
|
33
|
+
* cluster singleton. The default placement is `'hub'`.
|
|
34
|
+
*
|
|
35
|
+
* What this deliberately does NOT own: writing a declared device's STATE. The
|
|
36
|
+
* authority for a cap slice is already `deviceState.setCapSlice` (via
|
|
37
|
+
* `BaseDevice.runtimeState` / `ctx.fetchDevice(id)`). A third alias for it would
|
|
38
|
+
* be a second writer of a value that already has one.
|
|
39
|
+
*/
|
|
40
|
+
import type { IScopedLogger } from '../interfaces/logging.js';
|
|
41
|
+
import type { IDevice } from './device.js';
|
|
42
|
+
import type { DeviceConstructor, DeviceManagerApi } from './device-context.js';
|
|
43
|
+
import type { DeviceType } from './device-type.js';
|
|
44
|
+
/** Marker written to a declared integration's `info`. */
|
|
45
|
+
export declare const DECLARED_INTEGRATION_FIXED_KEY = "fixed";
|
|
46
|
+
/** One row of the addon's own device index, as `deviceManager.listAll` reports it. */
|
|
47
|
+
export interface DeclaredDeviceRow {
|
|
48
|
+
readonly id: number;
|
|
49
|
+
readonly stableId: string;
|
|
50
|
+
readonly integrationId?: string | null;
|
|
51
|
+
}
|
|
52
|
+
/** An integration as the integrations store reports it. */
|
|
53
|
+
export interface DeclaredIntegrationRow {
|
|
54
|
+
readonly id: string;
|
|
55
|
+
readonly info?: Record<string, unknown> | null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The narrow surface `DeclaredDevices` drives.
|
|
59
|
+
*
|
|
60
|
+
* Ports rather than a whole `AddonContext` for one reason that has cost this
|
|
61
|
+
* repo twice: a fake of the whole context supplies values production forgets to
|
|
62
|
+
* fetch. Five named operations can be faked without inventing behaviour.
|
|
63
|
+
*/
|
|
64
|
+
export interface DeclaredDevicePorts {
|
|
65
|
+
readonly logger: IScopedLogger;
|
|
66
|
+
/** The declaring addon's id — the owner half of `(addonId, stableId)`. */
|
|
67
|
+
readonly addonId: string;
|
|
68
|
+
/** `ctx.kernel.devices`. */
|
|
69
|
+
readonly devices: DeviceManagerApi;
|
|
70
|
+
/** `ctx.kernel.localNodeId`. A forked child reads `<node>/<addon>`. */
|
|
71
|
+
readonly localNodeId?: string | undefined;
|
|
72
|
+
/** `integrations.getByAddonId` — null when this addon has none yet. */
|
|
73
|
+
readonly getIntegration: (addonId: string) => Promise<DeclaredIntegrationRow | null>;
|
|
74
|
+
readonly createIntegration: (input: {
|
|
75
|
+
addonId: string;
|
|
76
|
+
name: string;
|
|
77
|
+
info: Record<string, unknown>;
|
|
78
|
+
}) => Promise<{
|
|
79
|
+
readonly id: string;
|
|
80
|
+
}>;
|
|
81
|
+
/** Merge `info` onto an EXISTING integration. Used to re-assert `fixed`. */
|
|
82
|
+
readonly updateIntegration: (input: {
|
|
83
|
+
id: string;
|
|
84
|
+
info: Record<string, unknown>;
|
|
85
|
+
}) => Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Every device row this addon owns.
|
|
88
|
+
*
|
|
89
|
+
* FALLIBLE on purpose: this is the read the D49 discipline applies to. It may
|
|
90
|
+
* throw, and it may transiently answer `[]` while the registry is cold — which
|
|
91
|
+
* is indistinguishable from "the operator deleted everything".
|
|
92
|
+
*/
|
|
93
|
+
readonly listOwnDevices: () => Promise<readonly DeclaredDeviceRow[]>;
|
|
94
|
+
}
|
|
95
|
+
/** One device an addon declares. */
|
|
96
|
+
export interface DeviceDeclaration<T extends IDevice = IDevice> {
|
|
97
|
+
/** Stable within the addon, never derived from the name — a rename must not
|
|
98
|
+
* mint a second device. */
|
|
99
|
+
readonly stableId: string;
|
|
100
|
+
/** Seeded ONCE, at create. Never re-stamped (D62). */
|
|
101
|
+
readonly name: string;
|
|
102
|
+
readonly type: DeviceType;
|
|
103
|
+
readonly DeviceClass: DeviceConstructor<T>;
|
|
104
|
+
/** Initial config blob, written before the constructor runs. Create only. */
|
|
105
|
+
readonly config?: Record<string, unknown>;
|
|
106
|
+
/** Optional semantic role, stamped at create alongside the type. */
|
|
107
|
+
readonly role?: string;
|
|
108
|
+
}
|
|
109
|
+
/** Where the declaration is owned. */
|
|
110
|
+
export type DeclarationPlacement = 'hub' | 'this-node';
|
|
111
|
+
export interface DeclaredDevicesSpec {
|
|
112
|
+
/** Display name of the FIXED integration the devices hang from. */
|
|
113
|
+
readonly integrationName: string;
|
|
114
|
+
readonly devices: readonly DeviceDeclaration[];
|
|
115
|
+
/**
|
|
116
|
+
* Default `'hub'`: a declaration is cluster state and belongs to the node
|
|
117
|
+
* that is always there. `'this-node'` is for a declaration that is genuinely
|
|
118
|
+
* per-node.
|
|
119
|
+
*/
|
|
120
|
+
readonly placement?: DeclarationPlacement;
|
|
121
|
+
}
|
|
122
|
+
export interface DeclaredDeviceOutcome {
|
|
123
|
+
readonly stableId: string;
|
|
124
|
+
readonly deviceId: number;
|
|
125
|
+
readonly device: IDevice;
|
|
126
|
+
/** True only on the boot that created it — the operator sees one line, once. */
|
|
127
|
+
readonly created: boolean;
|
|
128
|
+
}
|
|
129
|
+
export interface DeclaredDevicesResult {
|
|
130
|
+
/** Null when this node does not own the declaration (see `placement`). */
|
|
131
|
+
readonly integrationId: string | null;
|
|
132
|
+
readonly devices: readonly DeclaredDeviceOutcome[];
|
|
133
|
+
/** Device ids removed because their declaration was withdrawn. */
|
|
134
|
+
readonly removed: readonly number[];
|
|
135
|
+
/** False when the placement gate skipped this node entirely. */
|
|
136
|
+
readonly owned: boolean;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Strip the `<node>/<addon>` suffix a forked child carries.
|
|
140
|
+
*
|
|
141
|
+
* Comparing `ctx.kernel.localNodeId` raw skipped EVERY node — including the one
|
|
142
|
+
* that was supposed to act — because on the hub it reads `hub/<addon>`.
|
|
143
|
+
*/
|
|
144
|
+
export declare function declarationOwnerNodeId(localNodeId: string | undefined): string;
|
|
145
|
+
/** Cap on how many rows one withdrawal pass may delete. */
|
|
146
|
+
export declare const DECLARED_DEVICE_SWEEP_LIMIT = 32;
|
|
147
|
+
/**
|
|
148
|
+
* The one way an addon owns a device it declares.
|
|
149
|
+
*
|
|
150
|
+
* Construct once with the addon's ports, then call {@link reconcile} on boot and
|
|
151
|
+
* on every convergence tick. There is no second get-or-create helper — a guard
|
|
152
|
+
* in `scripts/` enforces that.
|
|
153
|
+
*/
|
|
154
|
+
export declare class DeclaredDevices {
|
|
155
|
+
private readonly ports;
|
|
156
|
+
constructor(ports: DeclaredDevicePorts);
|
|
157
|
+
/**
|
|
158
|
+
* Converge the declared set. Idempotent, and safe to call repeatedly.
|
|
159
|
+
*
|
|
160
|
+
* Throws only what the ports throw on the FIRST index read; every other
|
|
161
|
+
* failure is per-device and logged, so one bad declaration never takes the
|
|
162
|
+
* others down.
|
|
163
|
+
*/
|
|
164
|
+
reconcile(spec: DeclaredDevicesSpec): Promise<DeclaredDevicesResult>;
|
|
165
|
+
/**
|
|
166
|
+
* Get-or-create the FIXED integration, and RE-ASSERT the flag every pass.
|
|
167
|
+
*
|
|
168
|
+
* The re-assertion is the fix for the defect the hand-rolled version shipped
|
|
169
|
+
* with: writing `info.fixed` only on the create path left every pre-existing
|
|
170
|
+
* install without it, and the kernel kept offering to delete an integration
|
|
171
|
+
* the addon owns.
|
|
172
|
+
*/
|
|
173
|
+
private ensureIntegration;
|
|
174
|
+
private readIndex;
|
|
175
|
+
/**
|
|
176
|
+
* One declaration: adopt what exists, create what does not.
|
|
177
|
+
*
|
|
178
|
+
* The create branch is the destructive one — it seeds `initialMeta`, and
|
|
179
|
+
* `initialMeta.name` lands as an unconditional `setName`. A transiently empty
|
|
180
|
+
* index therefore looks exactly like a first boot and would silently re-stamp
|
|
181
|
+
* the declared name over the operator's rename. D49: that branch needs a
|
|
182
|
+
* second read to agree.
|
|
183
|
+
*/
|
|
184
|
+
private applyDeclaration;
|
|
185
|
+
/**
|
|
186
|
+
* Remove rows under the addon's FIXED integration whose declaration is gone.
|
|
187
|
+
*
|
|
188
|
+
* Bounded to that integration: a declared integration has no operator
|
|
189
|
+
* add-flow, so every row under it got there by declaration. Devices this
|
|
190
|
+
* addon owns OUTSIDE it (a provider's adopted devices) are never candidates.
|
|
191
|
+
*
|
|
192
|
+
* Bounded in count, and every deletion is logged with its `deviceId` — a
|
|
193
|
+
* withdrawal that removes an operator-visible row silently is the failure
|
|
194
|
+
* mode, not the removal itself.
|
|
195
|
+
*/
|
|
196
|
+
private sweepWithdrawn;
|
|
197
|
+
}
|
|
@@ -6,19 +6,25 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export interface DeviceBindingEntry {
|
|
8
8
|
capName: string;
|
|
9
|
-
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Which shape of provider serves the cap. UI metadata only — consumers must
|
|
11
|
+
* never branch on it for routing (D13); resolve a `(cap, device)` pair
|
|
12
|
+
* through `getProviderForDevice`.
|
|
13
|
+
*
|
|
14
|
+
* There were three. `linked` denoted a cap SYNTHESIZED from device-links with
|
|
15
|
+
* no provider at all, and it existed so a link-only cap would still render a
|
|
16
|
+
* panel — a fifth binding kind and a virtual-binding path the kernel paid for
|
|
17
|
+
* one feature. Wiring was deleted on 2026-08-08 (0 links across 302 devices)
|
|
18
|
+
* and the kind went with it: a composed device's caps are native to it, so
|
|
19
|
+
* nothing needs to be synthesized out of nowhere.
|
|
20
|
+
*/
|
|
21
|
+
kind: 'native' | 'wrapped';
|
|
14
22
|
/** Currently-active provider. For kind='native', equal to `nativeAddonId`.
|
|
15
|
-
* For kind='wrapped', the wrapper addon id.
|
|
16
|
-
* device-manager addon id (the synthesize owner). */
|
|
23
|
+
* For kind='wrapped', the wrapper addon id. */
|
|
17
24
|
providerAddonId: string;
|
|
18
25
|
/** Node where the active provider runs. 'hub' for native; may vary for wrappers. */
|
|
19
26
|
providerNodeId: string;
|
|
20
|
-
/** Always the addon id of the underlying native provider for the device.
|
|
21
|
-
* Empty for kind='linked' (no native exists). */
|
|
27
|
+
/** Always the addon id of the underlying native provider for the device. */
|
|
22
28
|
nativeAddonId: string;
|
|
23
29
|
}
|
|
24
30
|
export interface DeviceBinding {
|
|
@@ -95,82 +95,6 @@ export interface ChildLayoutEntry {
|
|
|
95
95
|
readonly collapsed?: boolean;
|
|
96
96
|
}
|
|
97
97
|
export type ChildLayout = readonly ChildLayoutEntry[];
|
|
98
|
-
/** Field source: copy one field of a sibling accessory's cap status.
|
|
99
|
-
* Addressed by the source's re-sync-stable accessory `stableIdSuffix`
|
|
100
|
-
* (`sourceKey`) — NOT a raw numeric id — so the link survives a re-sync. The
|
|
101
|
-
* source must be a sibling accessory under the SAME parent container as the
|
|
102
|
-
* target device; resolution is `${parentStableId}-${sourceKey}`. For a source
|
|
103
|
-
* anywhere else in the cluster use `DeviceLinkGlobalSource`. */
|
|
104
|
-
export interface DeviceLinkFieldSource {
|
|
105
|
-
readonly kind?: 'field';
|
|
106
|
-
readonly sourceKey: string;
|
|
107
|
-
readonly cap: string;
|
|
108
|
-
readonly fieldPath: string;
|
|
109
|
-
}
|
|
110
|
-
/** Literal source: a per-device constant (e.g. `battery.binary = true`,
|
|
111
|
-
* a consumable item's `label`/`resettable`). No sibling is read. */
|
|
112
|
-
export interface DeviceLinkLiteralSource {
|
|
113
|
-
readonly kind: 'literal';
|
|
114
|
-
readonly value: string | number | boolean | null;
|
|
115
|
-
}
|
|
116
|
-
/** Global source (P2e): copy one field of ANY device's cap status, regardless
|
|
117
|
-
* of parent container. Addressed by the source device's FULL `stableId` —
|
|
118
|
-
* chosen over the numeric id because a re-sync (`resetToSource`) REALLOCATES
|
|
119
|
-
* numeric ids while stableIds are deterministic from the provider (the same
|
|
120
|
-
* property the sibling `sourceKey` mechanism relies on). stableId uniqueness
|
|
121
|
-
* is formally per-addon; the resolver matches the first meta row with that
|
|
122
|
-
* stableId (effective global uniqueness — an `addonId` disambiguator can be
|
|
123
|
-
* added later without a wire break). */
|
|
124
|
-
export interface DeviceLinkGlobalSource {
|
|
125
|
-
readonly kind: 'global';
|
|
126
|
-
readonly sourceStableId: string;
|
|
127
|
-
readonly cap: string;
|
|
128
|
-
readonly fieldPath: string;
|
|
129
|
-
}
|
|
130
|
-
/** The source kinds a single expression BINDING may use — a sibling FIELD, a
|
|
131
|
-
* per-device LITERAL, or a GLOBAL device field. Never another expression:
|
|
132
|
-
* bindings do not nest, so an expression cannot reference another expression. */
|
|
133
|
-
export type DeviceLinkExpressionBinding = DeviceLinkFieldSource | DeviceLinkLiteralSource | DeviceLinkGlobalSource;
|
|
134
|
-
/** Expression source (Stage X): compute the target field from N named source
|
|
135
|
-
* bindings via a safe, non-Turing-complete infix expression (see
|
|
136
|
-
* `packages/types/src/expression/`). Each binding resolves to an
|
|
137
|
-
* `ExpressionValue` and is exposed to the expression under its record key.
|
|
138
|
-
* `now` (epoch ms) is auto-injected and is a reserved binding name. The
|
|
139
|
-
* evaluated result flows through `transform` exactly like a scalar source. */
|
|
140
|
-
export interface DeviceLinkExpressionSource {
|
|
141
|
-
readonly kind: 'expression';
|
|
142
|
-
readonly expr: string;
|
|
143
|
-
readonly bindings: Readonly<Record<string, DeviceLinkExpressionBinding>>;
|
|
144
|
-
}
|
|
145
|
-
export type DeviceLinkSource = DeviceLinkFieldSource | DeviceLinkLiteralSource | DeviceLinkGlobalSource | DeviceLinkExpressionSource;
|
|
146
|
-
/** The target field a link writes: a dot-path into the target cap's status. */
|
|
147
|
-
export interface DeviceLinkTarget {
|
|
148
|
-
readonly cap: string;
|
|
149
|
-
readonly fieldPath: string;
|
|
150
|
-
/** Optional grouping key for array-of-items target caps (consumables); P2. */
|
|
151
|
-
readonly itemKey?: string;
|
|
152
|
-
}
|
|
153
|
-
/** Optional value transform applied to the resolved source value. */
|
|
154
|
-
export type DeviceLinkTransform = {
|
|
155
|
-
readonly kind: 'identity';
|
|
156
|
-
} | {
|
|
157
|
-
readonly kind: 'enum-map';
|
|
158
|
-
readonly mapping: Record<string, string | number | boolean>;
|
|
159
|
-
readonly fallback?: string | number | boolean;
|
|
160
|
-
} | {
|
|
161
|
-
readonly kind: 'linear';
|
|
162
|
-
readonly scale: number;
|
|
163
|
-
readonly offset: number;
|
|
164
|
-
readonly clamp?: readonly [number, number];
|
|
165
|
-
};
|
|
166
|
-
/** One operator-authored cross-device field wiring, persisted on the TARGET. */
|
|
167
|
-
export interface DeviceLink {
|
|
168
|
-
readonly id: string;
|
|
169
|
-
readonly source: DeviceLinkSource;
|
|
170
|
-
readonly target: DeviceLinkTarget;
|
|
171
|
-
readonly transform?: DeviceLinkTransform;
|
|
172
|
-
}
|
|
173
|
-
export type DeviceLinks = readonly DeviceLink[];
|
|
174
98
|
/** Per-cap display refinement inside a `DeviceDisplayOverride` — unit/precision
|
|
175
99
|
* only (icon/label/hidden are device-level). Keyed by cap name for devices
|
|
176
100
|
* carrying several numeric caps (e.g. power-meter). */
|
|
@@ -254,12 +178,8 @@ export interface DeviceMeta {
|
|
|
254
178
|
* same lifecycle as `primaryChildEntityId`. Absent ⇒ no layout declared (all
|
|
255
179
|
* children render in the plain Overview list). */
|
|
256
180
|
readonly childLayout?: ChildLayout;
|
|
257
|
-
/** Operator-authored cross-device field wirings (source field → this device's
|
|
258
|
-
* cap field). Same create/persist/project/restore lifecycle as `childLayout`.
|
|
259
|
-
* Absent ⇒ no links. Overlaid onto the target cap's `getStatus` at read time. */
|
|
260
|
-
readonly deviceLinks?: DeviceLinks;
|
|
261
181
|
/** Operator-authored per-device display override (icon/label/unit/precision/
|
|
262
|
-
* hidden). Same create/persist/project/restore lifecycle as `
|
|
182
|
+
* hidden). Same create/persist/project/restore lifecycle as `childLayout`.
|
|
263
183
|
* Absent ⇒ no override; the renderer falls back to role default → live slice
|
|
264
184
|
* → canonical unit. Applied at RENDER time only — storage stays in source
|
|
265
185
|
* units. */
|
|
@@ -292,8 +212,6 @@ export interface InitialDeviceMeta {
|
|
|
292
212
|
* across re-register/restore. Optional: only set for CONTAINER devices that
|
|
293
213
|
* declare a layout. */
|
|
294
214
|
readonly childLayout?: ChildLayout;
|
|
295
|
-
/** Cross-device field wirings set at create. Mirrors `DeviceMeta.deviceLinks`. */
|
|
296
|
-
readonly deviceLinks?: DeviceLinks;
|
|
297
215
|
/** Per-device display override set at create. Mirrors `DeviceMeta.display`. */
|
|
298
216
|
readonly display?: DeviceDisplayOverride;
|
|
299
217
|
}
|
package/dist/device/index.d.ts
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { AccessoryKind, ACCESSORY_LABEL, accessoryStableId, type AccessoryKindValue, } from './accessory.js';
|
|
3
|
-
export type { IBatteryOperated, IRebootable, INativeSnapshot, IDoorbellButton, ITwoWayAudio, IPanTiltZoom, } from './features.js';
|
|
4
|
-
export { DeviceConfig } from './device-config.js';
|
|
5
|
-
export type { DeviceContext, DeviceManagerApi, DeviceConstructor, IDeviceRegistryReader, IDeviceRegistry, } from './device-context.js';
|
|
6
|
-
export { BaseDevice } from './base-device.js';
|
|
1
|
+
export { ACCESSORY_LABEL, AccessoryKind, type AccessoryKindValue, accessoryStableId, } from './accessory.js';
|
|
7
2
|
export type { AccessoryChildSpec } from './base-device.js';
|
|
3
|
+
export { BaseDevice } from './base-device.js';
|
|
4
|
+
export type { DeviceSummary, DiscoveryCandidate, FieldProbeResult, ProviderStatus, } from './base-device-provider.js';
|
|
8
5
|
export { BaseDeviceProvider, toDeviceSummary } from './base-device-provider.js';
|
|
9
|
-
export type { DiscoveryCandidate, DeviceSummary, ProviderStatus, FieldProbeResult, } from './base-device-provider.js';
|
|
10
|
-
export type { IDevice } from './device.js';
|
|
11
|
-
export { DEVICE_PROFILES, BATTERY_DEVICE_PROFILE, deviceMatchesProfile, resolveDeviceProfile, } from './device-profile.js';
|
|
12
|
-
export type { DeviceProfile, DeviceProfileMatch, DeviceProfileDefaults, PipelinePhaseMode, } from './device-profile.js';
|
|
13
6
|
export type { ICameraDevice, StreamSourceEntry } from './camera-device.js';
|
|
14
|
-
export type {
|
|
15
|
-
export {
|
|
16
|
-
export type {
|
|
17
|
-
export {
|
|
18
|
-
export type {
|
|
7
|
+
export type { DeclarationPlacement, DeclaredDeviceOutcome, DeclaredDevicePorts, DeclaredDeviceRow, DeclaredDevicesResult, DeclaredDevicesSpec, DeclaredIntegrationRow, DeviceDeclaration, } from './declared-device.js';
|
|
8
|
+
export { DECLARED_DEVICE_SWEEP_LIMIT, DECLARED_INTEGRATION_FIXED_KEY, DeclaredDevices, declarationOwnerNodeId, } from './declared-device.js';
|
|
9
|
+
export type { IDevice } from './device.js';
|
|
10
|
+
export { DeviceConfig } from './device-config.js';
|
|
11
|
+
export type { DeviceConstructor, DeviceContext, DeviceManagerApi, IDeviceRegistry, IDeviceRegistryReader, } from './device-context.js';
|
|
12
|
+
export type { ChildLayout, ChildLayoutEntry, CreateDeviceSpec, DeviceDiscovery, DeviceManualCreation, DeviceMeta, DiscoveredDevice, InitialDeviceMeta, SavedDevice, } from './device-management.js';
|
|
13
|
+
export type { DeviceProfile, DeviceProfileDefaults, DeviceProfileMatch, PipelinePhaseMode, } from './device-profile.js';
|
|
14
|
+
export { BATTERY_DEVICE_PROFILE, DEVICE_PROFILES, deviceMatchesProfile, resolveDeviceProfile, } from './device-profile.js';
|
|
19
15
|
export type { IDeviceRuntimeState, Snapshot as RuntimeStateSnapshot, } from './device-runtime-state.js';
|
|
16
|
+
export { ChargingStatus, DeviceFeature, DeviceRole, DeviceType } from './device-type.js';
|
|
17
|
+
export type { IBatteryOperated, IDoorbellButton, INativeSnapshot, IPanTiltZoom, IRebootable, ITwoWayAudio, } from './features.js';
|
|
20
18
|
export { getByPath, setByPath } from './path-util.js';
|
|
21
|
-
export {
|
|
22
|
-
export {
|
|
19
|
+
export type { ReachabilityPollHandle, ReachabilityPollLogger, ReachabilityPollOptions, } from './reachability-poll.js';
|
|
20
|
+
export { REACHABILITY_FAILURES_TO_OFFLINE, REACHABILITY_POLL_INTERVAL_MS, REACHABILITY_PROBE_TIMEOUT_MS, startReachabilityPoll, } from './reachability-poll.js';
|
|
21
|
+
export type { RuntimeStateBridge } from './runtime-state-helpers.js';
|
|
22
|
+
export { createRuntimeStateBridge } from './runtime-state-helpers.js';
|
|
23
23
|
export type { WireableField } from './schema-fields.js';
|
|
24
|
-
export {
|
|
25
|
-
export type {
|
|
24
|
+
export { enumerateItemArrayFields, enumerateSchemaFields } from './schema-fields.js';
|
|
25
|
+
export type { DeviceConfigEntry } from './zod-to-config-ui.js';
|
|
26
|
+
export { zodEntriesToConfigUI } from './zod-to-config-ui.js';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an expression's named bindings READ from.
|
|
3
|
+
*
|
|
4
|
+
* Salvaged verbatim from the deleted device-link mechanism. Wiring's source
|
|
5
|
+
* kinds were the one part of it worth keeping: addressing a device field by
|
|
6
|
+
* re-sync-stable `stableId`, a per-device constant, and a sibling-accessory
|
|
7
|
+
* read are the vocabulary any cross-device derivation needs, and they were
|
|
8
|
+
* already correct. What wiring got wrong was the DESTINATION — a field on
|
|
9
|
+
* somebody else's device, with no identity — not the source.
|
|
10
|
+
*
|
|
11
|
+
* These shapes are therefore kept, re-homed next to the engine that consumes
|
|
12
|
+
* them, and are the binding type of a composition recipe (the source picker
|
|
13
|
+
* stays `deviceManager.getWireableFields`). They deliberately do NOT nest: a
|
|
14
|
+
* binding is a read, never another expression.
|
|
15
|
+
*
|
|
16
|
+
* Schemas are authoritative; every type is `z.infer` of one, so a wire shape and
|
|
17
|
+
* a TypeScript shape cannot drift apart (`scripts/check-schema-type-twins.ts`).
|
|
18
|
+
*/
|
|
19
|
+
import { z } from 'zod';
|
|
20
|
+
/** Read a sibling accessory's status field, addressed by the sibling's key.
|
|
21
|
+
* `kind` is optional for wire compatibility — absent means `'field'`. */
|
|
22
|
+
export declare const ExpressionFieldBindingSchema: z.ZodObject<{
|
|
23
|
+
kind: z.ZodOptional<z.ZodLiteral<"field">>;
|
|
24
|
+
sourceKey: z.ZodString;
|
|
25
|
+
cap: z.ZodString;
|
|
26
|
+
fieldPath: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
/** A constant. No device is read. */
|
|
29
|
+
export declare const ExpressionLiteralBindingSchema: z.ZodObject<{
|
|
30
|
+
kind: z.ZodLiteral<"literal">;
|
|
31
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
/** Read ANY device's status field, addressed by its re-sync-stable `stableId` —
|
|
34
|
+
* never by numeric id, which a re-adoption reissues. */
|
|
35
|
+
export declare const ExpressionGlobalBindingSchema: z.ZodObject<{
|
|
36
|
+
kind: z.ZodLiteral<"global">;
|
|
37
|
+
sourceStableId: z.ZodString;
|
|
38
|
+
cap: z.ZodString;
|
|
39
|
+
fieldPath: z.ZodString;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
export declare const ExpressionBindingSourceSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
42
|
+
kind: z.ZodOptional<z.ZodLiteral<"field">>;
|
|
43
|
+
sourceKey: z.ZodString;
|
|
44
|
+
cap: z.ZodString;
|
|
45
|
+
fieldPath: z.ZodString;
|
|
46
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
47
|
+
kind: z.ZodLiteral<"literal">;
|
|
48
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
|
|
49
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
50
|
+
kind: z.ZodLiteral<"global">;
|
|
51
|
+
sourceStableId: z.ZodString;
|
|
52
|
+
cap: z.ZodString;
|
|
53
|
+
fieldPath: z.ZodString;
|
|
54
|
+
}, z.core.$strip>]>;
|
|
55
|
+
/**
|
|
56
|
+
* An expression plus the bindings its free identifiers resolve against.
|
|
57
|
+
*
|
|
58
|
+
* The `superRefine` runs the SAME author-time validation as
|
|
59
|
+
* `validateExpressionSource` — compiles the expression, checks binding names,
|
|
60
|
+
* checks identifier coverage — so every boundary that parses one
|
|
61
|
+
* validates-at-write rather than discovering the problem at read time.
|
|
62
|
+
* Compiles are LRU-cached, so repeated validation of the same string is a hit.
|
|
63
|
+
*/
|
|
64
|
+
export declare const ExpressionSourceSchema: z.ZodObject<{
|
|
65
|
+
expr: z.ZodString;
|
|
66
|
+
bindings: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
67
|
+
kind: z.ZodOptional<z.ZodLiteral<"field">>;
|
|
68
|
+
sourceKey: z.ZodString;
|
|
69
|
+
cap: z.ZodString;
|
|
70
|
+
fieldPath: z.ZodString;
|
|
71
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
72
|
+
kind: z.ZodLiteral<"literal">;
|
|
73
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>;
|
|
74
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
75
|
+
kind: z.ZodLiteral<"global">;
|
|
76
|
+
sourceStableId: z.ZodString;
|
|
77
|
+
cap: z.ZodString;
|
|
78
|
+
fieldPath: z.ZodString;
|
|
79
|
+
}, z.core.$strip>]>>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type ExpressionFieldBinding = z.infer<typeof ExpressionFieldBindingSchema>;
|
|
82
|
+
export type ExpressionLiteralBinding = z.infer<typeof ExpressionLiteralBindingSchema>;
|
|
83
|
+
export type ExpressionGlobalBinding = z.infer<typeof ExpressionGlobalBindingSchema>;
|
|
84
|
+
export type ExpressionBindingSource = z.infer<typeof ExpressionBindingSourceSchema>;
|
|
85
|
+
export type ExpressionSource = z.infer<typeof ExpressionSourceSchema>;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Expression-source helpers — the single seam every consumer of the engine
|
|
3
|
+
* shares, so author-time validation and read-time evaluation cannot drift.
|
|
4
|
+
*
|
|
5
|
+
* These were written for the `expression` device-link source and outlived it:
|
|
6
|
+
* wiring was deleted on 2026-08-08 with zero live users, and the engine was
|
|
7
|
+
* kept on purpose. It is the derivation language of a composed device and the
|
|
8
|
+
* escape-hatch leaf of an automation's condition tree — one language, three
|
|
9
|
+
* positions (derive a value, gate a trigger, compute an argument).
|
|
10
|
+
*/
|
|
2
11
|
import type { ExpressionValue } from './ast.js';
|
|
12
|
+
import { type ExpressionEvalOptions } from './evaluator.js';
|
|
3
13
|
/** The `now` epoch-ms binding is auto-injected into every evaluation and is a
|
|
4
14
|
* reserved binding name (authors may not rebind it). */
|
|
5
15
|
export declare const EXPRESSION_INJECTED_NOW = "now";
|
|
@@ -10,8 +20,8 @@ export declare const EXPRESSION_INJECTED_NOW = "now";
|
|
|
10
20
|
* its binding-miss policy (→ `null`). `null` itself is a valid value.
|
|
11
21
|
*/
|
|
12
22
|
export declare function toExpressionValue(raw: unknown): ExpressionValue | undefined;
|
|
13
|
-
/** Shape the author-time validator accepts (structural subset of
|
|
14
|
-
*
|
|
23
|
+
/** Shape the author-time validator accepts (structural subset of an
|
|
24
|
+
* {@link ExpressionSource}). */
|
|
15
25
|
export interface ExpressionSourceInput {
|
|
16
26
|
readonly expr: string;
|
|
17
27
|
readonly bindings: Readonly<Record<string, unknown>>;
|
|
@@ -24,10 +34,9 @@ export interface ExpressionSourceInput {
|
|
|
24
34
|
* FREE identifier of the AST is covered by a binding or the injected `now`.
|
|
25
35
|
*/
|
|
26
36
|
export declare function validateExpressionSource(src: ExpressionSourceInput): string | null;
|
|
27
|
-
/** Result of
|
|
28
|
-
* branch carries a reason the
|
|
29
|
-
|
|
30
|
-
export type EvaluateLinkExpressionResult = {
|
|
37
|
+
/** Result of an expression evaluation — `ok` carries the value, the error
|
|
38
|
+
* branch carries a reason the caller can log while it skips the derivation. */
|
|
39
|
+
export type EvaluateExpressionSourceResult = {
|
|
31
40
|
readonly ok: true;
|
|
32
41
|
readonly value: ExpressionValue;
|
|
33
42
|
} | {
|
|
@@ -35,10 +44,10 @@ export type EvaluateLinkExpressionResult = {
|
|
|
35
44
|
readonly error: string;
|
|
36
45
|
};
|
|
37
46
|
/**
|
|
38
|
-
* Shared read-path evaluation
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* "
|
|
47
|
+
* Shared read-path evaluation. Builds a null-proto scope from `bindingValues`
|
|
48
|
+
* plus the injected `now` (supplied by the caller for determinism and
|
|
49
|
+
* testability), compiles via the LRU, and evaluates. Any failure (parse or
|
|
50
|
+
* eval) returns `{ ok: false }` — the caller treats that as "skip this
|
|
51
|
+
* derivation", never as a throw that takes the pass down.
|
|
43
52
|
*/
|
|
44
|
-
export declare function
|
|
53
|
+
export declare function evaluateExpressionSource(expr: string, bindingValues: Readonly<Record<string, ExpressionValue>>, now: number, opts?: ExpressionEvalOptions): EvaluateExpressionSourceResult;
|
|
@@ -2,24 +2,27 @@
|
|
|
2
2
|
* Safe expression engine — public surface. A non-Turing-complete infix
|
|
3
3
|
* mini-language (tokenizer → Pratt parser → whitelisted-AST interpreter) with
|
|
4
4
|
* NO eval / new Function / node:vm, NO member access, NO loops/lambdas, and
|
|
5
|
-
* hard resource bounds.
|
|
5
|
+
* hard resource bounds. It is the derivation language of a composed device and
|
|
6
|
+
* the escape-hatch leaf of an automation's condition tree.
|
|
6
7
|
*
|
|
7
8
|
* This is the module barrel; leaf files inside @camstack/types MUST import from
|
|
8
9
|
* the deep modules (e.g. `../expression/compile.js`), never through this file
|
|
9
10
|
* OR the root barrel (see biome-plugins/no-types-barrel-leaf-import.grit).
|
|
10
11
|
*/
|
|
11
|
-
export type {
|
|
12
|
-
export {
|
|
13
|
-
export {
|
|
14
|
-
export { tokenize } from './tokenizer.js';
|
|
15
|
-
export type { Token, Punctuator, Keyword } from './tokenizer.js';
|
|
16
|
-
export { parseExpression } from './parser.js';
|
|
17
|
-
export type { ParsedExpression } from './parser.js';
|
|
18
|
-
export { EXPRESSION_BUILTINS, EXPRESSION_BUILTIN_NAMES, } from './builtins.js';
|
|
12
|
+
export type { ExpressionBinaryNode, ExpressionBinaryOperator, ExpressionCallNode, ExpressionConditionalNode, ExpressionIdentifierNode, ExpressionLiteralNode, ExpressionLogicalNode, ExpressionLogicalOperator, ExpressionNode, ExpressionUnaryNode, ExpressionUnaryOperator, ExpressionValue, } from './ast.js';
|
|
13
|
+
export type { ExpressionBindingSource, ExpressionFieldBinding, ExpressionGlobalBinding, ExpressionLiteralBinding, ExpressionSource, } from './binding-source.js';
|
|
14
|
+
export { ExpressionBindingSourceSchema, ExpressionFieldBindingSchema, ExpressionGlobalBindingSchema, ExpressionLiteralBindingSchema, ExpressionSourceSchema, } from './binding-source.js';
|
|
19
15
|
export type { ExpressionBuiltin, ExpressionEvalHooks } from './builtins.js';
|
|
20
|
-
export {
|
|
21
|
-
export type { ExpressionEvalOptions } from './evaluator.js';
|
|
22
|
-
export { compileExpression, compileExpressionSafe } from './compile.js';
|
|
16
|
+
export { EXPRESSION_BUILTIN_NAMES, EXPRESSION_BUILTINS, } from './builtins.js';
|
|
23
17
|
export type { CompileResult } from './compile.js';
|
|
24
|
-
export {
|
|
25
|
-
export
|
|
18
|
+
export { compileExpression, compileExpressionSafe } from './compile.js';
|
|
19
|
+
export { ExpressionEvalError, ExpressionParseError } from './errors.js';
|
|
20
|
+
export type { ExpressionEvalOptions } from './evaluator.js';
|
|
21
|
+
export { createExpressionScope, evaluateAst } from './evaluator.js';
|
|
22
|
+
export type { EvaluateExpressionSourceResult, ExpressionSourceInput, } from './expression-source.js';
|
|
23
|
+
export { EXPRESSION_INJECTED_NOW, evaluateExpressionSource, toExpressionValue, validateExpressionSource, } from './expression-source.js';
|
|
24
|
+
export { EXPRESSION_COMPILE_CACHE_CAPACITY, EXPRESSION_IDENTIFIER_RE, MAX_EXPRESSION_AST_NODES, MAX_EXPRESSION_BINDINGS, MAX_EXPRESSION_CALL_ARGS, MAX_EXPRESSION_EVAL_STEPS, MAX_EXPRESSION_SOURCE_LENGTH, RESERVED_BINDING_NAMES, } from './limits.js';
|
|
25
|
+
export type { ParsedExpression } from './parser.js';
|
|
26
|
+
export { parseExpression } from './parser.js';
|
|
27
|
+
export type { Keyword, Punctuator, Token } from './tokenizer.js';
|
|
28
|
+
export { tokenize } from './tokenizer.js';
|
|
@@ -17,7 +17,7 @@ export declare const MAX_EXPRESSION_AST_NODES = 256;
|
|
|
17
17
|
* evaluation. The grammar guarantees O(nodeCount) walks, so this can only trip
|
|
18
18
|
* on a crafted maximum-size AST. */
|
|
19
19
|
export declare const MAX_EXPRESSION_EVAL_STEPS = 4096;
|
|
20
|
-
/** Max named bindings on one
|
|
20
|
+
/** Max named bindings on one {@link ExpressionSource}. */
|
|
21
21
|
export declare const MAX_EXPRESSION_BINDINGS = 32;
|
|
22
22
|
/** Max positional arguments to any builtin call. */
|
|
23
23
|
export declare const MAX_EXPRESSION_CALL_ARGS = 16;
|
|
@@ -1441,6 +1441,13 @@ export type AppRouter = TrpcCoreRouter<{
|
|
|
1441
1441
|
output: z.infer<typeof coreBlocksCapability.methods.setEnabled.output>;
|
|
1442
1442
|
meta: object;
|
|
1443
1443
|
}>;
|
|
1444
|
+
restart: TRPCMutationProcedure<{
|
|
1445
|
+
input: {
|
|
1446
|
+
[x: string]: unknown;
|
|
1447
|
+
} & z.input<typeof coreBlocksCapability.methods.restart.input>;
|
|
1448
|
+
output: z.infer<typeof coreBlocksCapability.methods.restart.output>;
|
|
1449
|
+
meta: object;
|
|
1450
|
+
}>;
|
|
1444
1451
|
compile: TRPCMutationProcedure<{
|
|
1445
1452
|
input: {
|
|
1446
1453
|
[x: string]: unknown;
|
|
@@ -2076,13 +2083,6 @@ export type AppRouter = TrpcCoreRouter<{
|
|
|
2076
2083
|
output: z.infer<typeof deviceManagerCapability.methods.setChildLayout.output>;
|
|
2077
2084
|
meta: object;
|
|
2078
2085
|
}>;
|
|
2079
|
-
setDeviceLinks: TRPCMutationProcedure<{
|
|
2080
|
-
input: {
|
|
2081
|
-
[x: string]: unknown;
|
|
2082
|
-
} & z.input<typeof deviceManagerCapability.methods.setDeviceLinks.input>;
|
|
2083
|
-
output: z.infer<typeof deviceManagerCapability.methods.setDeviceLinks.output>;
|
|
2084
|
-
meta: object;
|
|
2085
|
-
}>;
|
|
2086
2086
|
setDisplay: TRPCMutationProcedure<{
|
|
2087
2087
|
input: {
|
|
2088
2088
|
[x: string]: unknown;
|
|
@@ -6961,6 +6961,20 @@ export type AppRouter = TrpcCoreRouter<{
|
|
|
6961
6961
|
output: z.infer<typeof streamBrokerCapability.methods.renderPreBufferClip.output>;
|
|
6962
6962
|
meta: object;
|
|
6963
6963
|
}>;
|
|
6964
|
+
produceEventMedia: TRPCMutationProcedure<{
|
|
6965
|
+
input: {
|
|
6966
|
+
[x: string]: unknown;
|
|
6967
|
+
} & z.input<typeof streamBrokerCapability.methods.produceEventMedia.input>;
|
|
6968
|
+
output: z.infer<typeof streamBrokerCapability.methods.produceEventMedia.output>;
|
|
6969
|
+
meta: object;
|
|
6970
|
+
}>;
|
|
6971
|
+
fetchEventMedia: TRPCMutationProcedure<{
|
|
6972
|
+
input: {
|
|
6973
|
+
[x: string]: unknown;
|
|
6974
|
+
} & z.input<typeof streamBrokerCapability.methods.fetchEventMedia.input>;
|
|
6975
|
+
output: z.infer<typeof streamBrokerCapability.methods.fetchEventMedia.output>;
|
|
6976
|
+
meta: object;
|
|
6977
|
+
}>;
|
|
6964
6978
|
listAllCameraStreams: TRPCQueryProcedure<{
|
|
6965
6979
|
input: {
|
|
6966
6980
|
nodeId?: string | undefined;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Plain data, zero Zod: a forked runner must not import the schema barrel
|
|
12
12
|
* (~144MB RSS per runner — D28).
|
|
13
13
|
*
|
|
14
|
-
* Coverage: 23 caps,
|
|
14
|
+
* Coverage: 23 caps, 51 methods with defaults.
|
|
15
15
|
*/
|
|
16
16
|
export declare const CAP_INPUT_DEFAULTS: Readonly<Record<string, Readonly<Record<string, Readonly<Record<string, unknown>>>>>>;
|
|
17
17
|
/**
|