@camstack/system 1.2.135 → 1.2.137
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/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/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-bindings-store.d.ts +10 -4
- package/dist/builtins/device-manager/device-manager.addon.js +136 -27
- package/dist/builtins/device-manager/device-manager.addon.mjs +136 -27
- package/dist/builtins/device-manager/device-row-store.d.ts +65 -0
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- 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/{dist-Bh25kEUH.mjs → dist-DMOTtKH8.mjs} +310 -63
- package/dist/{dist-D-R0SuP-.js → dist-mQ3BkrJd.js} +310 -63
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{retired-settings-keys-BZbOXqch.mjs → retired-settings-keys-CPRvyt-E.mjs} +1 -1
- package/dist/{retired-settings-keys-CqD6rSQx.js → retired-settings-keys-Dvkcu_zf.js} +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.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 { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { Gt as EventCategory, v as addonPagesCapability, vt as errMsg, yt as BaseAddon } from "../../dist-DMOTtKH8.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-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.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 { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { Gt as EventCategory, b as addonWidgetsCapability, vt as errMsg, yt as BaseAddon } from "../../dist-DMOTtKH8.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-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.js");
|
|
7
7
|
//#region src/builtins/alerts/prune-policy.ts
|
|
8
8
|
/**
|
|
9
9
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { Gt as EventCategory, Mt as createEvent, n as AlertSchema, vt as errMsg, x as alertsCapability, yt as BaseAddon } from "../../dist-DMOTtKH8.mjs";
|
|
2
2
|
//#region src/builtins/alerts/prune-policy.ts
|
|
3
3
|
/**
|
|
4
4
|
* Ids older than `cutoffMs`, oldest first.
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let buffer = require("buffer");
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { C as backupCapability, Gt as EventCategory, yt as BaseAddon } from "../../dist-DMOTtKH8.mjs";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { Buffer as Buffer$1 } from "buffer";
|
|
4
4
|
import * as fsp from "node:fs/promises";
|
|
@@ -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-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/console-logging/console-destination.ts
|
|
9
9
|
var LEVEL_RANK = {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { X as logDestinationCapability, yt as BaseAddon } from "../../dist-DMOTtKH8.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/console-logging/console-destination.ts
|
|
4
4
|
var LEVEL_RANK = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-mQ3BkrJd.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
node_fs_promises = require_chunk.__toESM(node_fs_promises);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-
|
|
1
|
+
import { D as coreBlockAddonId, O as coreBlockIdFromAddonId, c as CoreBlockSchema, k as coreBlocksCapability, l as DeclaredDevices, o as CORE_BLOCKS_ADDON_ID, s as CORE_BLOCK_ADDON_PREFIX, vt as errMsg, yt as BaseAddon } from "../../dist-DMOTtKH8.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -137,10 +137,16 @@ export declare function getBindings(deps: BindingsDeps, input: {
|
|
|
137
137
|
* caller needing seventy-five paid either 75 round-trips (1211 ms / 118 KB) or
|
|
138
138
|
* `getAllBindings` (511 ms / 679 KB of mostly-unwanted rows).
|
|
139
139
|
*
|
|
140
|
-
* Sequential on purpose, like `getAllBindings`: `
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
140
|
+
* Sequential on purpose, like `getAllBindings`: `resolveBindingsForDevice`'s
|
|
141
|
+
* remaining per-device work (the bindings-store read, the routing rules) runs
|
|
142
|
+
* on this process's own event loop, so concurrency buys nothing there and a
|
|
143
|
+
* fan-out of `Promise.all` over hundreds of ids only makes the queue less fair
|
|
144
|
+
* to everything else on it. The one part that DOES amortise across the whole
|
|
145
|
+
* set — the device row lookup — is fetched ONCE, ahead of the loop
|
|
146
|
+
* ({@link DeviceRowStore.getMany}), rather than once per iteration: measured
|
|
147
|
+
* on the live hub 2026-08-25, `get device-manager:devices` ran 1 656
|
|
148
|
+
* calls/min steady-state, one row per call, which is exactly what looping
|
|
149
|
+
* `getBindings`'s own `rows.get(deviceId)` here produced.
|
|
144
150
|
*/
|
|
145
151
|
export declare function getBindingsBatch(deps: BindingsDeps, input: {
|
|
146
152
|
deviceIds: readonly number[];
|
|
@@ -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-
|
|
7
|
-
const require_retired_settings_keys = require("../../retired-settings-keys-
|
|
6
|
+
const require_dist = require("../../dist-mQ3BkrJd.js");
|
|
7
|
+
const require_retired_settings_keys = require("../../retired-settings-keys-Dvkcu_zf.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let _camstack_types_node = require("@camstack/types/node");
|
|
10
10
|
//#region src/builtins/device-manager/adoption-job-engine.ts
|
|
@@ -594,16 +594,33 @@ function capAppliesToDeviceType(def, deviceType) {
|
|
|
594
594
|
return declared.some((t) => t === deviceType);
|
|
595
595
|
}
|
|
596
596
|
async function getBindings(deps, input) {
|
|
597
|
-
const storeKey = String(input.deviceId);
|
|
598
|
-
const perDevice = (await readBindingsStore(deps)).deviceBindings[storeKey] ?? {};
|
|
599
597
|
const row = await deps.rows.get(input.deviceId);
|
|
600
|
-
|
|
601
|
-
|
|
598
|
+
return resolveBindingsForDevice(deps, input.deviceId, row);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* The `getBindings` resolver, taking the device's row as a PARAMETER instead
|
|
602
|
+
* of fetching it.
|
|
603
|
+
*
|
|
604
|
+
* `getBindings` (one device) and `getBindingsBatch` / `getAllBindings` (many)
|
|
605
|
+
* share this — the routing rules and the per-device shape stay identical by
|
|
606
|
+
* construction. The split exists so the batch callers can resolve every row
|
|
607
|
+
* they need in ONE store round trip ({@link DeviceRowStore.getMany} /
|
|
608
|
+
* {@link DeviceRowStore.listAll}) instead of looping this function's own
|
|
609
|
+
* `deps.rows.get(deviceId)`, which is the primary-key-lookup-per-loop-
|
|
610
|
+
* iteration shape measured on the live hub 2026-08-25: `get
|
|
611
|
+
* device-manager:devices` at 1 656 calls/min steady-state and 17 687 during
|
|
612
|
+
* boot, one row per call.
|
|
613
|
+
*/
|
|
614
|
+
async function resolveBindingsForDevice(deps, deviceId, row) {
|
|
615
|
+
const storeKey = String(deviceId);
|
|
616
|
+
const perDevice = (await readBindingsStore(deps)).deviceBindings[storeKey] ?? {};
|
|
617
|
+
const deviceType = resolveDeviceType(deps, row, deviceId);
|
|
618
|
+
const presence = await resolveDevicePresence(deps, row, deviceId);
|
|
602
619
|
const entries = [];
|
|
603
620
|
const seenCaps = /* @__PURE__ */ new Set();
|
|
604
|
-
const resolveRemote = (capName) => deps.remoteNativeCaps.get(
|
|
621
|
+
const resolveRemote = (capName) => deps.remoteNativeCaps.get(deviceId)?.get(capName) ?? resolveRemoteNativeCapFromRegistry(deps, capName, deviceId);
|
|
605
622
|
for (const [capName, { wrapperAddonId }] of Object.entries(perDevice)) {
|
|
606
|
-
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName,
|
|
623
|
+
const hubLocalNative = deps.capabilityRegistry?.getNativeAddonId(capName, deviceId) ?? null;
|
|
607
624
|
const remoteNative = resolveRemote(capName);
|
|
608
625
|
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
609
626
|
const nativeNodeId = hubLocalNative ? deps.ctx.kernel.localNodeId ?? "hub" : remoteNative?.nodeId ?? deps.ctx.kernel.localNodeId ?? "hub";
|
|
@@ -620,7 +637,7 @@ async function getBindings(deps, input) {
|
|
|
620
637
|
});
|
|
621
638
|
seenCaps.add(capName);
|
|
622
639
|
}
|
|
623
|
-
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device — returning none", { tags: { deviceId
|
|
640
|
+
if (presence === "absent") deps.ctx.logger.debug("bindings requested for absent device — returning none", { tags: { deviceId } });
|
|
624
641
|
else if (deps.capabilityRegistry) {
|
|
625
642
|
const skippedForType = [];
|
|
626
643
|
for (const capName of deps.capabilityRegistry.getCapsWithDefaultWrapper()) {
|
|
@@ -631,7 +648,7 @@ async function getBindings(deps, input) {
|
|
|
631
648
|
}
|
|
632
649
|
const defaultWrapperAddonId = deps.capabilityRegistry.getDefaultWrapperForCap(capName);
|
|
633
650
|
if (!defaultWrapperAddonId) continue;
|
|
634
|
-
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName,
|
|
651
|
+
const hubLocalNative = deps.capabilityRegistry.getNativeAddonId(capName, deviceId) ?? null;
|
|
635
652
|
const remoteNative = resolveRemote(capName);
|
|
636
653
|
const nativeAddonId = hubLocalNative ?? remoteNative?.addonId ?? "";
|
|
637
654
|
entries.push({
|
|
@@ -644,16 +661,16 @@ async function getBindings(deps, input) {
|
|
|
644
661
|
seenCaps.add(capName);
|
|
645
662
|
}
|
|
646
663
|
if (skippedForType.length > 0) deps.ctx.logger.debug("getBindings: default wrappers skipped — deviceTypes mismatch", {
|
|
647
|
-
tags: { deviceId
|
|
664
|
+
tags: { deviceId },
|
|
648
665
|
meta: {
|
|
649
666
|
deviceType,
|
|
650
667
|
skipped: skippedForType
|
|
651
668
|
}
|
|
652
669
|
});
|
|
653
670
|
}
|
|
654
|
-
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(
|
|
671
|
+
if (deps.capabilityRegistry) for (const capName of deps.capabilityRegistry.getNativeCapsForDevice(deviceId)) {
|
|
655
672
|
if (seenCaps.has(capName)) continue;
|
|
656
|
-
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName,
|
|
673
|
+
const nativeAddonId = deps.capabilityRegistry.getNativeAddonId(capName, deviceId) ?? "";
|
|
657
674
|
entries.push({
|
|
658
675
|
capName,
|
|
659
676
|
kind: "native",
|
|
@@ -663,7 +680,7 @@ async function getBindings(deps, input) {
|
|
|
663
680
|
});
|
|
664
681
|
seenCaps.add(capName);
|
|
665
682
|
}
|
|
666
|
-
const pushFed = deps.remoteNativeCaps.get(
|
|
683
|
+
const pushFed = deps.remoteNativeCaps.get(deviceId);
|
|
667
684
|
if (pushFed) for (const [capName, info] of pushFed) {
|
|
668
685
|
if (seenCaps.has(capName)) continue;
|
|
669
686
|
entries.push({
|
|
@@ -675,9 +692,9 @@ async function getBindings(deps, input) {
|
|
|
675
692
|
});
|
|
676
693
|
seenCaps.add(capName);
|
|
677
694
|
}
|
|
678
|
-
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(
|
|
695
|
+
const clusterCaps = deps.ctx.kernel.listClusterNativeCapsForDevice?.(deviceId) ?? deps.ctx.kernel.listClusterNativeCaps?.();
|
|
679
696
|
if (clusterCaps) for (const entry of clusterCaps) {
|
|
680
|
-
if (entry.deviceId !==
|
|
697
|
+
if (entry.deviceId !== deviceId) continue;
|
|
681
698
|
if (seenCaps.has(entry.capName)) continue;
|
|
682
699
|
if (!entry.addonId) continue;
|
|
683
700
|
const localNodeId = deps.ctx.kernel.localNodeId ?? "hub";
|
|
@@ -692,7 +709,7 @@ async function getBindings(deps, input) {
|
|
|
692
709
|
seenCaps.add(entry.capName);
|
|
693
710
|
}
|
|
694
711
|
return {
|
|
695
|
-
deviceId
|
|
712
|
+
deviceId,
|
|
696
713
|
entries
|
|
697
714
|
};
|
|
698
715
|
}
|
|
@@ -705,14 +722,22 @@ async function getBindings(deps, input) {
|
|
|
705
722
|
* caller needing seventy-five paid either 75 round-trips (1211 ms / 118 KB) or
|
|
706
723
|
* `getAllBindings` (511 ms / 679 KB of mostly-unwanted rows).
|
|
707
724
|
*
|
|
708
|
-
* Sequential on purpose, like `getAllBindings`: `
|
|
709
|
-
*
|
|
710
|
-
*
|
|
711
|
-
*
|
|
725
|
+
* Sequential on purpose, like `getAllBindings`: `resolveBindingsForDevice`'s
|
|
726
|
+
* remaining per-device work (the bindings-store read, the routing rules) runs
|
|
727
|
+
* on this process's own event loop, so concurrency buys nothing there and a
|
|
728
|
+
* fan-out of `Promise.all` over hundreds of ids only makes the queue less fair
|
|
729
|
+
* to everything else on it. The one part that DOES amortise across the whole
|
|
730
|
+
* set — the device row lookup — is fetched ONCE, ahead of the loop
|
|
731
|
+
* ({@link DeviceRowStore.getMany}), rather than once per iteration: measured
|
|
732
|
+
* on the live hub 2026-08-25, `get device-manager:devices` ran 1 656
|
|
733
|
+
* calls/min steady-state, one row per call, which is exactly what looping
|
|
734
|
+
* `getBindings`'s own `rows.get(deviceId)` here produced.
|
|
712
735
|
*/
|
|
713
736
|
async function getBindingsBatch(deps, input) {
|
|
737
|
+
const ids = [...new Set(input.deviceIds)];
|
|
738
|
+
const rows = await deps.rows.getMany(ids);
|
|
714
739
|
const out = [];
|
|
715
|
-
for (const deviceId of
|
|
740
|
+
for (const deviceId of ids) out.push(await resolveBindingsForDevice(deps, deviceId, rows.get(deviceId) ?? null));
|
|
716
741
|
return out;
|
|
717
742
|
}
|
|
718
743
|
/**
|
|
@@ -727,8 +752,13 @@ async function getBindingsBatch(deps, input) {
|
|
|
727
752
|
* payload periodically.
|
|
728
753
|
*/
|
|
729
754
|
async function getAllBindings(deps) {
|
|
755
|
+
const fleet = await deps.rows.listAll();
|
|
756
|
+
const rowById = /* @__PURE__ */ new Map();
|
|
730
757
|
const ids = /* @__PURE__ */ new Set();
|
|
731
|
-
for (const row of
|
|
758
|
+
for (const row of fleet) {
|
|
759
|
+
rowById.set(row.meta.id, row);
|
|
760
|
+
ids.add(row.meta.id);
|
|
761
|
+
}
|
|
732
762
|
const registered = deps.ctx.kernel?.deviceRegistry?.getAll() ?? [];
|
|
733
763
|
for (const device of registered) ids.add(device.id);
|
|
734
764
|
if (ids.size === 0) {
|
|
@@ -736,7 +766,7 @@ async function getAllBindings(deps) {
|
|
|
736
766
|
return [];
|
|
737
767
|
}
|
|
738
768
|
const out = [];
|
|
739
|
-
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await
|
|
769
|
+
for (const deviceId of [...ids].sort((a, b) => a - b)) out.push(await resolveBindingsForDevice(deps, deviceId, rowById.get(deviceId) ?? null));
|
|
740
770
|
return out;
|
|
741
771
|
}
|
|
742
772
|
/**
|
|
@@ -1315,7 +1345,10 @@ async function listAll(pctx, input) {
|
|
|
1315
1345
|
const idFilter = input.deviceIds === void 0 ? null : new Set(input.deviceIds);
|
|
1316
1346
|
const results = [];
|
|
1317
1347
|
const seen = /* @__PURE__ */ new Set();
|
|
1318
|
-
const fleet = addonId ? await pctx.metaStore.rows.listByAddon(addonId) : await pctx.metaStore.rows.
|
|
1348
|
+
const fleet = addonId ? await pctx.metaStore.rows.listByAddon(addonId) : await pctx.metaStore.rows.listFleet({
|
|
1349
|
+
...input.deviceIds !== void 0 ? { deviceIds: input.deviceIds } : {},
|
|
1350
|
+
slim
|
|
1351
|
+
});
|
|
1319
1352
|
const rowById = /* @__PURE__ */ new Map();
|
|
1320
1353
|
for (const row of fleet) rowById.set(row.meta.id, row);
|
|
1321
1354
|
if (pctx.registry) {
|
|
@@ -4051,6 +4084,36 @@ var DEVICE_ROWS_COLUMNS = [
|
|
|
4051
4084
|
type: "JSON"
|
|
4052
4085
|
}
|
|
4053
4086
|
];
|
|
4087
|
+
/**
|
|
4088
|
+
* The columns a SLIM fleet read selects: every declared column except the
|
|
4089
|
+
* primary key (always returned) and `metadata`.
|
|
4090
|
+
*
|
|
4091
|
+
* ## Why this is a DENYLIST and must stay one
|
|
4092
|
+
*
|
|
4093
|
+
* `decodeDeviceRow` returns `null` when `deviceId`, `addonId`, `stableId`,
|
|
4094
|
+
* `type` or `name` is missing, and `list()` then SKIPS the row with a warn. A
|
|
4095
|
+
* hand-written allowlist that forgot one of the five would not make the fleet
|
|
4096
|
+
* slower — it would make devices vanish from every listing, quietly, and the
|
|
4097
|
+
* only trace would be a log line nobody is reading at the time. Deriving the
|
|
4098
|
+
* list by subtraction means a column added tomorrow is carried by default and
|
|
4099
|
+
* the only way to lose one is to name it here.
|
|
4100
|
+
*
|
|
4101
|
+
* `metadata` is the one slim provably discards: `device-queries.ts#listAll`
|
|
4102
|
+
* sets `metadata: null` on every slim row. It is also the largest JSON column
|
|
4103
|
+
* on the row, which is why this is worth doing at all.
|
|
4104
|
+
*
|
|
4105
|
+
* ## What is NOT here, and will not be
|
|
4106
|
+
*
|
|
4107
|
+
* There is no `where: { type: 'camera' }` companion to this. `isCameraDevice`
|
|
4108
|
+
* is a method-presence check (`getStreamSources`), not the persisted `type`, so
|
|
4109
|
+
* a doorbell with a camera answers YES while its row says `doorbell`. Filtering
|
|
4110
|
+
* the rows by type would strip exactly those devices of their row — and
|
|
4111
|
+
* `toDeviceInfo` reads `name` / `location` / `disabled` from the ROW precisely
|
|
4112
|
+
* because the live device's copies are stale by construction. The fleet would
|
|
4113
|
+
* keep working and start reporting pre-rename names on doorbells. The
|
|
4114
|
+
* `isCamera` filter stays in JS.
|
|
4115
|
+
*/
|
|
4116
|
+
var DEVICE_ROWS_SLIM_COLUMNS = DEVICE_ROWS_COLUMNS.map((c) => c.name).filter((name) => name !== "id" && name !== "metadata");
|
|
4054
4117
|
var DEVICE_ROWS_INDEXES = [
|
|
4055
4118
|
{
|
|
4056
4119
|
name: "idx_dm_devices_addon_stable",
|
|
@@ -4269,6 +4332,51 @@ var DeviceRowStore = class {
|
|
|
4269
4332
|
});
|
|
4270
4333
|
}
|
|
4271
4334
|
/**
|
|
4335
|
+
* The fleet, read as narrowly as the question allows.
|
|
4336
|
+
*
|
|
4337
|
+
* `listAll` is a full-table `SELECT … ORDER BY "deviceId"` — ~1 017 rows a
|
|
4338
|
+
* call on the live hub, 14–54 calls a minute — and its callers then filtered
|
|
4339
|
+
* the result in JS. The two filters that CAN be pushed down are here:
|
|
4340
|
+
*
|
|
4341
|
+
* - `deviceIds` becomes a `whereIn`. The viewer's linked-devices panel asks
|
|
4342
|
+
* about ~8 named ids; it was answered by reading the whole ledger. An
|
|
4343
|
+
* EMPTY set reads nothing at all rather than degrading to "everything" —
|
|
4344
|
+
* a request for no devices is a request, not an omission.
|
|
4345
|
+
* - `slim` becomes a column projection, {@link DEVICE_ROWS_SLIM_COLUMNS}.
|
|
4346
|
+
*
|
|
4347
|
+
* `isCamera` is deliberately NOT one of them — see that constant's note and
|
|
4348
|
+
* `device-row-store-filtered.spec.ts`.
|
|
4349
|
+
*/
|
|
4350
|
+
async listFleet(opts) {
|
|
4351
|
+
if (opts.deviceIds !== void 0 && opts.deviceIds.length === 0) return [];
|
|
4352
|
+
return this.list({
|
|
4353
|
+
...opts.deviceIds !== void 0 ? { whereIn: { deviceId: [...new Set(opts.deviceIds)] } } : {},
|
|
4354
|
+
orderBy: {
|
|
4355
|
+
field: "deviceId",
|
|
4356
|
+
direction: "asc"
|
|
4357
|
+
},
|
|
4358
|
+
limit: DEVICE_ROWS_FLEET_LIMIT
|
|
4359
|
+
}, opts.slim === true ? DEVICE_ROWS_SLIM_COLUMNS : void 0);
|
|
4360
|
+
}
|
|
4361
|
+
/**
|
|
4362
|
+
* Many devices, by numeric id, in ONE query.
|
|
4363
|
+
*
|
|
4364
|
+
* `get(id)` called once per id in a loop is the primary-key lookup this
|
|
4365
|
+
* store exists to make cheap, repeated until it isn't — measured on the
|
|
4366
|
+
* live hub 2026-08-25: `get device-manager:devices` at 1 656 calls/min
|
|
4367
|
+
* steady-state and 17 687 during boot, exactly one row per call. This
|
|
4368
|
+
* collapses that into the same `whereIn` {@link listFleet} already does for
|
|
4369
|
+
* a fleet read, keyed by id.
|
|
4370
|
+
*
|
|
4371
|
+
* A requested id the fleet does not know is simply ABSENT from the map —
|
|
4372
|
+
* never a fabricated row, matching {@link get}'s `null`. An empty `ids`
|
|
4373
|
+
* reads nothing at all, same as `listFleet({ deviceIds: [] })`.
|
|
4374
|
+
*/
|
|
4375
|
+
async getMany(ids) {
|
|
4376
|
+
const rows = await this.listFleet({ deviceIds: ids });
|
|
4377
|
+
return new Map(rows.map((row) => [row.meta.id, row]));
|
|
4378
|
+
}
|
|
4379
|
+
/**
|
|
4272
4380
|
* The device an addon knows as `stableId`, or `null`.
|
|
4273
4381
|
*
|
|
4274
4382
|
* `(addonId, stableId)` is the addon-facing identity — unique by
|
|
@@ -4339,11 +4447,12 @@ var DeviceRowStore = class {
|
|
|
4339
4447
|
await this.declare();
|
|
4340
4448
|
return this.backend.count({ collection: DEVICE_ROWS_COLLECTION });
|
|
4341
4449
|
}
|
|
4342
|
-
async list(filter) {
|
|
4450
|
+
async list(filter, columns) {
|
|
4343
4451
|
await this.declare();
|
|
4344
4452
|
const records = await this.backend.query({
|
|
4345
4453
|
collection: DEVICE_ROWS_COLLECTION,
|
|
4346
|
-
filter
|
|
4454
|
+
filter,
|
|
4455
|
+
...columns === void 0 ? {} : { columns }
|
|
4347
4456
|
});
|
|
4348
4457
|
const out = [];
|
|
4349
4458
|
for (const record of records) {
|