@camstack/system 1.2.60 → 1.2.62
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-aggregation.d.ts +49 -2
- package/dist/builtins/device-manager/device-manager.addon.js +60 -5
- package/dist/builtins/device-manager/device-manager.addon.mjs +60 -5
- 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/prefix-range.d.ts +50 -0
- package/dist/builtins/sqlite-storage/query-bounds.d.ts +116 -0
- package/dist/builtins/sqlite-storage/sqlite-pragmas.d.ts +12 -0
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +281 -19
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +281 -19
- 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-Cis_iV-u.js → dist-DgPUarcs.js} +636 -3
- package/dist/{dist-C5XpOjbI.mjs → dist-DnKpFaP1.mjs} +636 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +337 -12
- package/dist/index.mjs +328 -12
- package/dist/kernel/addon-residue.d.ts +49 -0
- package/dist/kernel/heap-watch.d.ts +48 -1
- package/dist/kernel/index.d.ts +2 -0
- 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-DgPUarcs.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ft as errMsg, m as addonPagesCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { ft as errMsg, m as addonPagesCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as fs from "node:fs";
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
const require_settle_sources = require("../../settle-sources-Bhsy57y-.js");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ft as errMsg, g as addonWidgetsCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { ft as errMsg, g as addonWidgetsCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { t as settleSourcesWithTimeout } from "../../settle-sources-CDtNC8ub.mjs";
|
|
3
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
4
|
import * as fs from "node:fs";
|
|
@@ -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-DgPUarcs.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 { Et as createEvent, _ as alertsCapability, ft as errMsg, n as AlertSchema, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { Et as createEvent, _ as alertsCapability, ft as errMsg, n as AlertSchema, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.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-DgPUarcs.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
let node_fs$1 = require_chunk.__toESM(node_fs, 1);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { b as backupCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { b as backupCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import Kt from "node:fs";
|
|
@@ -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-DgPUarcs.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 { G as logDestinationCapability, pt as BaseAddon } from "../../dist-
|
|
1
|
+
import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-DnKpFaP1.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-DgPUarcs.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { C as coreBlocksCapability, o as CoreBlockSchema, pt as BaseAddon } from "../../dist-
|
|
2
|
+
import { C as coreBlocksCapability, o as CoreBlockSchema, pt as BaseAddon } from "../../dist-DnKpFaP1.mjs";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
5
5
|
import * as fs from "node:fs/promises";
|
|
@@ -88,6 +88,36 @@ export declare function resolveNativeDeviceOwner(deps: AggregationDeps, deviceId
|
|
|
88
88
|
addonId: string;
|
|
89
89
|
nodeId: string;
|
|
90
90
|
} | null;
|
|
91
|
+
/**
|
|
92
|
+
* How long ONE cap's `getStatus` may take before the aggregate gives up on it
|
|
93
|
+
* and reports `null`.
|
|
94
|
+
*
|
|
95
|
+
* A cap call for a device owned by another node crosses the cluster, where the
|
|
96
|
+
* transport's own ceiling is `REMOTE_CALL_TIMEOUT_MS` — SIXTY seconds. That
|
|
97
|
+
* ceiling is right for a deliberate operator action; it is catastrophic here,
|
|
98
|
+
* because this aggregate is what a camera LIST asks for on every boot, and it
|
|
99
|
+
* has no answer to give until the slowest cap replies.
|
|
100
|
+
*
|
|
101
|
+
* The failure this bounds is not a node that is DOWN — `classifyCapRoute`
|
|
102
|
+
* rejects those immediately with `node-offline`, which the catch below turns
|
|
103
|
+
* into `null` in microseconds. It is a node that is FLAPPING: a crash-looping
|
|
104
|
+
* agent re-registers every few seconds, so the registry believes it is online,
|
|
105
|
+
* the call is routed, and the peer dies mid-flight. Then nothing throws and
|
|
106
|
+
* nothing resolves. On 2026-08-08 little-unraid did exactly that for 22
|
|
107
|
+
* minutes and the viewer showed a loading skeleton the whole time.
|
|
108
|
+
*
|
|
109
|
+
* 3s is chosen against what a HEALTHY remote cap costs (a same-LAN cap call is
|
|
110
|
+
* single-digit ms; the slowest observed live is ~300ms) — so this cannot cut
|
|
111
|
+
* off a working node — and against what a human waits for a camera list.
|
|
112
|
+
*/
|
|
113
|
+
export declare const STATUS_CALL_DEADLINE_MS = 3000;
|
|
114
|
+
/** Per-call bound for a status aggregation. Tests pass a small value; nothing
|
|
115
|
+
* in production overrides the default. */
|
|
116
|
+
export interface StatusAggregateOptions {
|
|
117
|
+
/** Deadline for a single cap's `getStatus`. Defaults to
|
|
118
|
+
* {@link STATUS_CALL_DEADLINE_MS}. */
|
|
119
|
+
readonly deadlineMs?: number;
|
|
120
|
+
}
|
|
91
121
|
/**
|
|
92
122
|
* Aggregate `status` across every registered cap for a device.
|
|
93
123
|
*
|
|
@@ -99,6 +129,10 @@ export declare function resolveNativeDeviceOwner(deps: AggregationDeps, deviceId
|
|
|
99
129
|
* overall aggregate stays usable — a single misbehaving provider
|
|
100
130
|
* must not blank out a device's entire status view.
|
|
101
131
|
*
|
|
132
|
+
* A provider that never answers is treated exactly like one that throws: it
|
|
133
|
+
* costs its own cap and nothing else. See {@link STATUS_CALL_DEADLINE_MS} for
|
|
134
|
+
* why a bound is needed at all when the transport already has one.
|
|
135
|
+
*
|
|
102
136
|
* Returned shape is `Record<capName, unknown | null>`; the client-
|
|
103
137
|
* side hook tightens this to `CapStatusTypeMap` via the generated
|
|
104
138
|
* `cap-status-types.ts`.
|
|
@@ -106,7 +140,7 @@ export declare function resolveNativeDeviceOwner(deps: AggregationDeps, deviceId
|
|
|
106
140
|
export declare function getDeviceStatusAggregate(deps: AggregationDeps, input: {
|
|
107
141
|
readonly deviceId: number;
|
|
108
142
|
readonly caps?: readonly string[];
|
|
109
|
-
}): Promise<Record<string, unknown | null>>;
|
|
143
|
+
}, options?: StatusAggregateOptions): Promise<Record<string, unknown | null>>;
|
|
110
144
|
/**
|
|
111
145
|
* `getDeviceStatusAggregate` for a LIST of devices, in one round-trip.
|
|
112
146
|
*
|
|
@@ -120,11 +154,24 @@ export declare function getDeviceStatusAggregate(deps: AggregationDeps, input: {
|
|
|
120
154
|
* A device whose whole aggregation fails still gets an ENTRY (all caps null)
|
|
121
155
|
* rather than being omitted: a caller that reads a missing key as "not
|
|
122
156
|
* answered yet" would keep the previous badge on screen forever.
|
|
157
|
+
*
|
|
158
|
+
* ## The response is BOUNDED, and that is load-bearing
|
|
159
|
+
*
|
|
160
|
+
* The windows are walked SEQUENTIALLY, so before the per-cap deadline existed a
|
|
161
|
+
* single unanswerable device did not merely cost its own window — it held every
|
|
162
|
+
* later window behind it, and the call never returned at all. That is the whole
|
|
163
|
+
* mechanism behind "one node offline and the app loads nothing": this is the
|
|
164
|
+
* one call a camera list makes for every card, the client has no timeout of its
|
|
165
|
+
* own, and a tRPC query that never settles is `isLoading` forever.
|
|
166
|
+
*
|
|
167
|
+
* The ceiling is now `ceil(devices / STATUS_BATCH_WINDOW) × STATUS_CALL_DEADLINE_MS`
|
|
168
|
+
* in the worst case (a dead device in every window), and unchanged when every
|
|
169
|
+
* node is healthy. It is never unbounded.
|
|
123
170
|
*/
|
|
124
171
|
export declare function getDeviceStatusAggregateBatch(deps: AggregationDeps, input: {
|
|
125
172
|
readonly deviceIds: readonly number[];
|
|
126
173
|
readonly caps?: readonly string[];
|
|
127
|
-
}): Promise<Record<string, Record<string, unknown | null>>>;
|
|
174
|
+
}, options?: StatusAggregateOptions): Promise<Record<string, Record<string, unknown | null>>>;
|
|
128
175
|
/**
|
|
129
176
|
* Return the driver-specific device-settings contribution. Hub-local
|
|
130
177
|
* devices call `getSettingsUISchema()` directly; forked-worker devices
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let _camstack_types_node = require("@camstack/types/node");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
@@ -1992,6 +1992,30 @@ function resolveNativeDeviceOwner(deps, deviceId) {
|
|
|
1992
1992
|
} : null;
|
|
1993
1993
|
}
|
|
1994
1994
|
/**
|
|
1995
|
+
* Race `work` against a deadline WITHOUT swallowing its rejection.
|
|
1996
|
+
*
|
|
1997
|
+
* `Promise.race` attaches a handler to every entrant, so a `work` that rejects
|
|
1998
|
+
* after the deadline already fired is still handled — no unhandled rejection,
|
|
1999
|
+
* and no `.catch(() => {})` that would also hide a genuine throw from the
|
|
2000
|
+
* caller's `try`. A rejection propagates out of here exactly as it would
|
|
2001
|
+
* without the race; only the TIMEOUT is turned into a value.
|
|
2002
|
+
*/
|
|
2003
|
+
async function withDeadline(work, deadlineMs) {
|
|
2004
|
+
let timer;
|
|
2005
|
+
const deadline = new Promise((resolve) => {
|
|
2006
|
+
timer = setTimeout(() => resolve({ kind: "timed-out" }), deadlineMs);
|
|
2007
|
+
timer.unref?.();
|
|
2008
|
+
});
|
|
2009
|
+
try {
|
|
2010
|
+
return await Promise.race([work.then((value) => ({
|
|
2011
|
+
kind: "value",
|
|
2012
|
+
value
|
|
2013
|
+
})), deadline]);
|
|
2014
|
+
} finally {
|
|
2015
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
1995
2019
|
* Aggregate `status` across every registered cap for a device.
|
|
1996
2020
|
*
|
|
1997
2021
|
* Walks the supplied cap list (or `CAP_NAMES_WITH_STATUS` when
|
|
@@ -2002,11 +2026,16 @@ function resolveNativeDeviceOwner(deps, deviceId) {
|
|
|
2002
2026
|
* overall aggregate stays usable — a single misbehaving provider
|
|
2003
2027
|
* must not blank out a device's entire status view.
|
|
2004
2028
|
*
|
|
2029
|
+
* A provider that never answers is treated exactly like one that throws: it
|
|
2030
|
+
* costs its own cap and nothing else. See {@link STATUS_CALL_DEADLINE_MS} for
|
|
2031
|
+
* why a bound is needed at all when the transport already has one.
|
|
2032
|
+
*
|
|
2005
2033
|
* Returned shape is `Record<capName, unknown | null>`; the client-
|
|
2006
2034
|
* side hook tightens this to `CapStatusTypeMap` via the generated
|
|
2007
2035
|
* `cap-status-types.ts`.
|
|
2008
2036
|
*/
|
|
2009
|
-
async function getDeviceStatusAggregate(deps, input) {
|
|
2037
|
+
async function getDeviceStatusAggregate(deps, input, options) {
|
|
2038
|
+
const deadlineMs = options?.deadlineMs ?? 3e3;
|
|
2010
2039
|
const capNames = input.caps ?? require_dist.CAP_NAMES_WITH_STATUS;
|
|
2011
2040
|
const registry = deps.capabilityRegistry;
|
|
2012
2041
|
const out = {};
|
|
@@ -2026,7 +2055,20 @@ async function getDeviceStatusAggregate(deps, input) {
|
|
|
2026
2055
|
out[capName] = null;
|
|
2027
2056
|
return;
|
|
2028
2057
|
}
|
|
2029
|
-
const
|
|
2058
|
+
const settled = await withDeadline(provider.getStatus({ deviceId: input.deviceId }), deadlineMs);
|
|
2059
|
+
if (settled.kind === "timed-out") {
|
|
2060
|
+
deps.ctx.logger.warn("getDeviceStatusAggregate: cap missed its deadline, dropping", {
|
|
2061
|
+
tags: { deviceId: input.deviceId },
|
|
2062
|
+
meta: {
|
|
2063
|
+
deviceId: input.deviceId,
|
|
2064
|
+
capName,
|
|
2065
|
+
deadlineMs
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
out[capName] = null;
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
const raw = settled.value;
|
|
2030
2072
|
if (raw == null) {
|
|
2031
2073
|
out[capName] = null;
|
|
2032
2074
|
return;
|
|
@@ -2080,8 +2122,21 @@ var STATUS_BATCH_WINDOW = 10;
|
|
|
2080
2122
|
* A device whose whole aggregation fails still gets an ENTRY (all caps null)
|
|
2081
2123
|
* rather than being omitted: a caller that reads a missing key as "not
|
|
2082
2124
|
* answered yet" would keep the previous badge on screen forever.
|
|
2125
|
+
*
|
|
2126
|
+
* ## The response is BOUNDED, and that is load-bearing
|
|
2127
|
+
*
|
|
2128
|
+
* The windows are walked SEQUENTIALLY, so before the per-cap deadline existed a
|
|
2129
|
+
* single unanswerable device did not merely cost its own window — it held every
|
|
2130
|
+
* later window behind it, and the call never returned at all. That is the whole
|
|
2131
|
+
* mechanism behind "one node offline and the app loads nothing": this is the
|
|
2132
|
+
* one call a camera list makes for every card, the client has no timeout of its
|
|
2133
|
+
* own, and a tRPC query that never settles is `isLoading` forever.
|
|
2134
|
+
*
|
|
2135
|
+
* The ceiling is now `ceil(devices / STATUS_BATCH_WINDOW) × STATUS_CALL_DEADLINE_MS`
|
|
2136
|
+
* in the worst case (a dead device in every window), and unchanged when every
|
|
2137
|
+
* node is healthy. It is never unbounded.
|
|
2083
2138
|
*/
|
|
2084
|
-
async function getDeviceStatusAggregateBatch(deps, input) {
|
|
2139
|
+
async function getDeviceStatusAggregateBatch(deps, input, options) {
|
|
2085
2140
|
const out = {};
|
|
2086
2141
|
const ids = [...new Set(input.deviceIds)];
|
|
2087
2142
|
for (let i = 0; i < ids.length; i += STATUS_BATCH_WINDOW) {
|
|
@@ -2090,7 +2145,7 @@ async function getDeviceStatusAggregateBatch(deps, input) {
|
|
|
2090
2145
|
try {
|
|
2091
2146
|
const single = { deviceId };
|
|
2092
2147
|
if (input.caps !== void 0) single.caps = input.caps;
|
|
2093
|
-
out[String(deviceId)] = await getDeviceStatusAggregate(deps, single);
|
|
2148
|
+
out[String(deviceId)] = await getDeviceStatusAggregate(deps, single, options);
|
|
2094
2149
|
} catch (err) {
|
|
2095
2150
|
deps.ctx.logger.warn("getDeviceStatusAggregateBatch: device aggregation threw", {
|
|
2096
2151
|
tags: { deviceId },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as deviceStateCapability, E as deviceManagerCapability, I as getByPath, M as enumerateSchemaFields, N as evaluateLinkExpression, O as deviceStatusCapability, Rt as sleep, S as buildStreamParamsConfigSchema, St as WELL_KNOWN_TAB_MAP, Y as normalizeUnit, Z as parseStreamParamsFormPatch, _t as DeviceFeature, a as CAP_NAMES_WITH_STATUS, ft as errMsg, j as enumerateItemArrayFields, jt as isDeviceConfigCap, lt as validateExpressionSource, pt as BaseAddon, s as DeviceStatusSchema, st as toExpressionValue, t as ALL_CAPABILITY_DEFINITIONS, tt as setByPath, u as STREAM_PROFILE_META, v as applyTransform, vt as DeviceRole, x as bareAddonId, yt as DeviceType, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { D as deviceStateCapability, E as deviceManagerCapability, I as getByPath, M as enumerateSchemaFields, N as evaluateLinkExpression, O as deviceStatusCapability, Rt as sleep, S as buildStreamParamsConfigSchema, St as WELL_KNOWN_TAB_MAP, Y as normalizeUnit, Z as parseStreamParamsFormPatch, _t as DeviceFeature, a as CAP_NAMES_WITH_STATUS, ft as errMsg, j as enumerateItemArrayFields, jt as isDeviceConfigCap, lt as validateExpressionSource, pt as BaseAddon, s as DeviceStatusSchema, st as toExpressionValue, t as ALL_CAPABILITY_DEFINITIONS, tt as setByPath, u as STREAM_PROFILE_META, v as applyTransform, vt as DeviceRole, x as bareAddonId, yt as DeviceType, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { canonicalDeviceFingerprint } from "@camstack/types/node";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -1987,6 +1987,30 @@ function resolveNativeDeviceOwner(deps, deviceId) {
|
|
|
1987
1987
|
} : null;
|
|
1988
1988
|
}
|
|
1989
1989
|
/**
|
|
1990
|
+
* Race `work` against a deadline WITHOUT swallowing its rejection.
|
|
1991
|
+
*
|
|
1992
|
+
* `Promise.race` attaches a handler to every entrant, so a `work` that rejects
|
|
1993
|
+
* after the deadline already fired is still handled — no unhandled rejection,
|
|
1994
|
+
* and no `.catch(() => {})` that would also hide a genuine throw from the
|
|
1995
|
+
* caller's `try`. A rejection propagates out of here exactly as it would
|
|
1996
|
+
* without the race; only the TIMEOUT is turned into a value.
|
|
1997
|
+
*/
|
|
1998
|
+
async function withDeadline(work, deadlineMs) {
|
|
1999
|
+
let timer;
|
|
2000
|
+
const deadline = new Promise((resolve) => {
|
|
2001
|
+
timer = setTimeout(() => resolve({ kind: "timed-out" }), deadlineMs);
|
|
2002
|
+
timer.unref?.();
|
|
2003
|
+
});
|
|
2004
|
+
try {
|
|
2005
|
+
return await Promise.race([work.then((value) => ({
|
|
2006
|
+
kind: "value",
|
|
2007
|
+
value
|
|
2008
|
+
})), deadline]);
|
|
2009
|
+
} finally {
|
|
2010
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
1990
2014
|
* Aggregate `status` across every registered cap for a device.
|
|
1991
2015
|
*
|
|
1992
2016
|
* Walks the supplied cap list (or `CAP_NAMES_WITH_STATUS` when
|
|
@@ -1997,11 +2021,16 @@ function resolveNativeDeviceOwner(deps, deviceId) {
|
|
|
1997
2021
|
* overall aggregate stays usable — a single misbehaving provider
|
|
1998
2022
|
* must not blank out a device's entire status view.
|
|
1999
2023
|
*
|
|
2024
|
+
* A provider that never answers is treated exactly like one that throws: it
|
|
2025
|
+
* costs its own cap and nothing else. See {@link STATUS_CALL_DEADLINE_MS} for
|
|
2026
|
+
* why a bound is needed at all when the transport already has one.
|
|
2027
|
+
*
|
|
2000
2028
|
* Returned shape is `Record<capName, unknown | null>`; the client-
|
|
2001
2029
|
* side hook tightens this to `CapStatusTypeMap` via the generated
|
|
2002
2030
|
* `cap-status-types.ts`.
|
|
2003
2031
|
*/
|
|
2004
|
-
async function getDeviceStatusAggregate(deps, input) {
|
|
2032
|
+
async function getDeviceStatusAggregate(deps, input, options) {
|
|
2033
|
+
const deadlineMs = options?.deadlineMs ?? 3e3;
|
|
2005
2034
|
const capNames = input.caps ?? CAP_NAMES_WITH_STATUS;
|
|
2006
2035
|
const registry = deps.capabilityRegistry;
|
|
2007
2036
|
const out = {};
|
|
@@ -2021,7 +2050,20 @@ async function getDeviceStatusAggregate(deps, input) {
|
|
|
2021
2050
|
out[capName] = null;
|
|
2022
2051
|
return;
|
|
2023
2052
|
}
|
|
2024
|
-
const
|
|
2053
|
+
const settled = await withDeadline(provider.getStatus({ deviceId: input.deviceId }), deadlineMs);
|
|
2054
|
+
if (settled.kind === "timed-out") {
|
|
2055
|
+
deps.ctx.logger.warn("getDeviceStatusAggregate: cap missed its deadline, dropping", {
|
|
2056
|
+
tags: { deviceId: input.deviceId },
|
|
2057
|
+
meta: {
|
|
2058
|
+
deviceId: input.deviceId,
|
|
2059
|
+
capName,
|
|
2060
|
+
deadlineMs
|
|
2061
|
+
}
|
|
2062
|
+
});
|
|
2063
|
+
out[capName] = null;
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
const raw = settled.value;
|
|
2025
2067
|
if (raw == null) {
|
|
2026
2068
|
out[capName] = null;
|
|
2027
2069
|
return;
|
|
@@ -2075,8 +2117,21 @@ var STATUS_BATCH_WINDOW = 10;
|
|
|
2075
2117
|
* A device whose whole aggregation fails still gets an ENTRY (all caps null)
|
|
2076
2118
|
* rather than being omitted: a caller that reads a missing key as "not
|
|
2077
2119
|
* answered yet" would keep the previous badge on screen forever.
|
|
2120
|
+
*
|
|
2121
|
+
* ## The response is BOUNDED, and that is load-bearing
|
|
2122
|
+
*
|
|
2123
|
+
* The windows are walked SEQUENTIALLY, so before the per-cap deadline existed a
|
|
2124
|
+
* single unanswerable device did not merely cost its own window — it held every
|
|
2125
|
+
* later window behind it, and the call never returned at all. That is the whole
|
|
2126
|
+
* mechanism behind "one node offline and the app loads nothing": this is the
|
|
2127
|
+
* one call a camera list makes for every card, the client has no timeout of its
|
|
2128
|
+
* own, and a tRPC query that never settles is `isLoading` forever.
|
|
2129
|
+
*
|
|
2130
|
+
* The ceiling is now `ceil(devices / STATUS_BATCH_WINDOW) × STATUS_CALL_DEADLINE_MS`
|
|
2131
|
+
* in the worst case (a dead device in every window), and unchanged when every
|
|
2132
|
+
* node is healthy. It is never unbounded.
|
|
2078
2133
|
*/
|
|
2079
|
-
async function getDeviceStatusAggregateBatch(deps, input) {
|
|
2134
|
+
async function getDeviceStatusAggregateBatch(deps, input, options) {
|
|
2080
2135
|
const out = {};
|
|
2081
2136
|
const ids = [...new Set(input.deviceIds)];
|
|
2082
2137
|
for (let i = 0; i < ids.length; i += STATUS_BATCH_WINDOW) {
|
|
@@ -2085,7 +2140,7 @@ async function getDeviceStatusAggregateBatch(deps, input) {
|
|
|
2085
2140
|
try {
|
|
2086
2141
|
const single = { deviceId };
|
|
2087
2142
|
if (input.caps !== void 0) single.caps = input.caps;
|
|
2088
|
-
out[String(deviceId)] = await getDeviceStatusAggregate(deps, single);
|
|
2143
|
+
out[String(deviceId)] = await getDeviceStatusAggregate(deps, single, options);
|
|
2089
2144
|
} catch (err) {
|
|
2090
2145
|
deps.ctx.logger.warn("getDeviceStatusAggregateBatch: device aggregation threw", {
|
|
2091
2146
|
tags: { deviceId },
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
//#region src/builtins/doorbell/trigger-engine.ts
|
|
8
8
|
/**
|
|
9
9
|
* Pure trigger logic for the virtual-doorbell builtin — edge detection over
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as isSameAddonId, Et as createEvent, ft as errMsg, k as doorbellCapability, pt as BaseAddon, x as bareAddonId, yt as DeviceType, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { B as isSameAddonId, Et as createEvent, ft as errMsg, k as doorbellCapability, pt as BaseAddon, x as bareAddonId, yt as DeviceType, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
//#region src/builtins/doorbell/trigger-engine.ts
|
|
3
3
|
/**
|
|
4
4
|
* Pure trigger logic for the virtual-doorbell builtin — edge detection over
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
//#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
|
|
9
9
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-
|
|
1
|
+
import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
|
|
4
4
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-DgPUarcs.js");
|
|
4
4
|
//#region src/builtins/liveness-monitor/liveness-checks.ts
|
|
5
5
|
var NO_DEVICES = "liveness:no-devices";
|
|
6
6
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Et as createEvent, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { Et as createEvent, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
//#region src/builtins/liveness-monitor/liveness-checks.ts
|
|
3
3
|
var NO_DEVICES = "liveness:no-devices";
|
|
4
4
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
9
9
|
let crypto$1 = require("crypto");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { ct as userManagementCapability, d as ScopedTokenSchema, p as UserRecordSchema, pt as BaseAddon, r as ApiKeyRecordSchema, y as authProviderCapability } from "../../dist-
|
|
2
|
+
import { ct as userManagementCapability, d as ScopedTokenSchema, p as UserRecordSchema, pt as BaseAddon, r as ApiKeyRecordSchema, y as authProviderCapability } from "../../dist-DnKpFaP1.mjs";
|
|
3
3
|
import * as crypto$1 from "node:crypto";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
5
|
import nodeCrypto from "crypto";
|
|
@@ -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-DgPUarcs.js");
|
|
4
4
|
let node_os = require("node:os");
|
|
5
5
|
node_os = require_chunk.__toESM(node_os);
|
|
6
6
|
//#region src/builtins/local-network/local-network.addon.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { W as localNetworkCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { W as localNetworkCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import * as os from "node:os";
|
|
3
3
|
//#region src/builtins/local-network/local-network.addon.ts
|
|
4
4
|
/**
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-DgPUarcs.js");
|
|
7
7
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
8
8
|
/**
|
|
9
9
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-
|
|
1
|
+
import { G as logDestinationCapability, pt as BaseAddon } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
3
3
|
/**
|
|
4
4
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
9
|
let node_child_process = require("node:child_process");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Et as createEvent, J as metricsProviderCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { Et as createEvent, J as metricsProviderCapability, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { execFile, execFileSync } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
9
|
let node_child_process = require("node:child_process");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { A as enumerateInferenceDevices, Ot as emitReadiness, Q as platformProbeCapability, et as scoreRuntimes, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
2
|
+
import { A as enumerateInferenceDevices, Ot as emitReadiness, Q as platformProbeCapability, et as scoreRuntimes, ft as errMsg, pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import { promisify } from "node:util";
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
8
8
|
/**
|
|
9
9
|
* Reconcile the durable `enabledProviders` set against authoritative
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { pt as BaseAddon, zt as EventCategory } from "../../dist-
|
|
1
|
+
import { pt as BaseAddon, zt as EventCategory } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
3
3
|
/**
|
|
4
4
|
* Reconcile the durable `enabledProviders` set against authoritative
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
//#region src/builtins/snapshot/snapshot-coalescing.ts
|
|
9
9
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mt as nodePin, _t as DeviceFeature, ft as errMsg, i as BatteryStatusSchema, ot as streamQualityLabel, pt as BaseAddon, rt as snapshotCapability, x as bareAddonId, yt as DeviceType } from "../../dist-
|
|
1
|
+
import { Mt as nodePin, _t as DeviceFeature, ft as errMsg, i as BatteryStatusSchema, ot as streamQualityLabel, pt as BaseAddon, rt as snapshotCapability, x as bareAddonId, yt as DeviceType } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { execFile, spawn } from "node:child_process";
|
|
3
3
|
//#region src/builtins/snapshot/snapshot-coalescing.ts
|
|
4
4
|
/**
|
|
@@ -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-DgPUarcs.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as filesystemBrowseCapability, at as storageProviderCapability, pt as BaseAddon } from "../../dist-
|
|
1
|
+
import { F as filesystemBrowseCapability, at as storageProviderCapability, pt as BaseAddon } from "../../dist-DnKpFaP1.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import * as path$1 from "node:path";
|