camstack 1.2.34 → 1.2.35
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/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ async function runServe(args) {
|
|
|
38
38
|
...typeof values.data === "string" ? { data: values.data } : {}
|
|
39
39
|
};
|
|
40
40
|
Object.assign(process.env, buildServeEnv(opts));
|
|
41
|
-
await import("./launcher-
|
|
41
|
+
await import("./launcher-IU5FZCJX.js");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/commands/agent.ts
|
|
@@ -83,7 +83,7 @@ async function runAgent(args) {
|
|
|
83
83
|
...typeof values.port === "string" ? { port: values.port } : {}
|
|
84
84
|
};
|
|
85
85
|
Object.assign(process.env, buildAgentEnv(opts));
|
|
86
|
-
await import("./launcher-
|
|
86
|
+
await import("./launcher-IU5FZCJX.js");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// src/commands/setup.ts
|
|
@@ -60548,7 +60548,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
60548
60548
|
}
|
|
60549
60549
|
async function listPersistedByAddon(pctx, input) {
|
|
60550
60550
|
const { addonId } = input;
|
|
60551
|
-
const
|
|
60551
|
+
const { index, meta } = await pctx.metaStore.readAll();
|
|
60552
60552
|
const stableIds = index[addonId] ?? [];
|
|
60553
60553
|
const byStableId = /* @__PURE__ */ new Map();
|
|
60554
60554
|
for (const m of Object.values(meta)) if (m.addonId === addonId) byStableId.set(m.stableId, m);
|
|
@@ -60573,8 +60573,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
60573
60573
|
const camerasOnly = input.isCamera === true;
|
|
60574
60574
|
const results = [];
|
|
60575
60575
|
const seen = /* @__PURE__ */ new Set();
|
|
60576
|
-
const meta = await pctx.metaStore.
|
|
60577
|
-
const metadataMap = await pctx.metaStore.readMetadataMap();
|
|
60576
|
+
const { meta, metadata: metadataMap, index } = await pctx.metaStore.readAll();
|
|
60578
60577
|
if (pctx.registry) {
|
|
60579
60578
|
const liveEntries = addonId ? pctx.registry.getAllForAddon(addonId).map((device) => ({
|
|
60580
60579
|
addonId,
|
|
@@ -60592,7 +60591,6 @@ var require_device_manager_addon = __commonJS({
|
|
|
60592
60591
|
} : info);
|
|
60593
60592
|
}
|
|
60594
60593
|
}
|
|
60595
|
-
const index = await pctx.metaStore.readIndex();
|
|
60596
60594
|
const metaByAddonStable = /* @__PURE__ */ new Map();
|
|
60597
60595
|
for (const m of Object.values(meta)) metaByAddonStable.set(`${m.addonId}\0${m.stableId}`, m);
|
|
60598
60596
|
const targetAddons = addonId ? [addonId] : Object.keys(index);
|
|
@@ -60690,11 +60688,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
60690
60688
|
}
|
|
60691
60689
|
const results = [];
|
|
60692
60690
|
const seen = /* @__PURE__ */ new Set();
|
|
60693
|
-
const
|
|
60694
|
-
pctx.metaStore.readIndex(),
|
|
60695
|
-
pctx.metaStore.readMeta(),
|
|
60696
|
-
pctx.metaStore.readMetadataMap()
|
|
60697
|
-
]);
|
|
60691
|
+
const { index, meta, metadata: metadataMap } = await pctx.metaStore.readAll();
|
|
60698
60692
|
if (pctx.registry) {
|
|
60699
60693
|
const liveChildren = pctx.registry.getChildren(parentDeviceId);
|
|
60700
60694
|
for (const device of liveChildren) {
|
|
@@ -62863,8 +62857,61 @@ var require_device_manager_addon = __commonJS({
|
|
|
62863
62857
|
this.settings = settings;
|
|
62864
62858
|
this.registry = registry;
|
|
62865
62859
|
}
|
|
62860
|
+
/** The read currently in flight, or null. Never a settled value — see
|
|
62861
|
+
* {@link readStore}. */
|
|
62862
|
+
inFlightRead = null;
|
|
62863
|
+
/**
|
|
62864
|
+
* The whole persisted addon store.
|
|
62865
|
+
*
|
|
62866
|
+
* **Concurrent callers join the read already in flight.** This is not a
|
|
62867
|
+
* cache and nothing survives settlement: a caller that awaited the running
|
|
62868
|
+
* promise could not have observed anything older than its result, so the
|
|
62869
|
+
* only thing that changes is cost. What that cost was, measured on the
|
|
62870
|
+
* 2026-08-19 boot: `readAddonStore` lands in `SqliteSettingsBackend
|
|
62871
|
+
* .getAllAddon`, which reads and `JSON.parse`s this addon's rows —
|
|
62872
|
+
* 625 KB on the live hub (`deviceMeta` 467 KB + `deviceMetadata` 87 KB +
|
|
62873
|
+
* `deviceIndex` 70 KB) — synchronously, on the hub's event loop. A V8
|
|
62874
|
+
* profile of that boot had hub-main's JS thread 99.9% busy with 64% of it
|
|
62875
|
+
* inside `getAllAddon`, ~89% of that entered here, and every runner's first
|
|
62876
|
+
* store read queued behind it (the notification centre's six parallel reads
|
|
62877
|
+
* all resolved together at t+44.7 s).
|
|
62878
|
+
*
|
|
62879
|
+
* A rejection is NOT latched: the slot is cleared before the promise
|
|
62880
|
+
* settles either way, so a failed read costs the joiners that one failure
|
|
62881
|
+
* and the next caller reaches the store again.
|
|
62882
|
+
*/
|
|
62866
62883
|
readStore = async () => {
|
|
62867
|
-
|
|
62884
|
+
const existing = this.inFlightRead;
|
|
62885
|
+
if (existing !== null) return existing;
|
|
62886
|
+
const read = (async () => {
|
|
62887
|
+
try {
|
|
62888
|
+
return await this.settings.readAddonStore();
|
|
62889
|
+
} finally {
|
|
62890
|
+
this.inFlightRead = null;
|
|
62891
|
+
}
|
|
62892
|
+
})();
|
|
62893
|
+
this.inFlightRead = read;
|
|
62894
|
+
return read;
|
|
62895
|
+
};
|
|
62896
|
+
/**
|
|
62897
|
+
* The three fleet projections from ONE read.
|
|
62898
|
+
*
|
|
62899
|
+
* `listAll` asked for `deviceMeta`, then `deviceMetadata`, then
|
|
62900
|
+
* `deviceIndex` — three SEQUENTIAL awaits, which {@link readStore}'s
|
|
62901
|
+
* in-flight join cannot collapse because each starts after the previous one
|
|
62902
|
+
* settled. Three full 625 KB parses per call, on a call made once per device
|
|
62903
|
+
* lifecycle event during a 974-device boot: 23% of hub-main's CPU.
|
|
62904
|
+
*
|
|
62905
|
+
* It is also ONE snapshot. Three separate reads could straddle a write and
|
|
62906
|
+
* hand back an index that names a device the meta map no longer has.
|
|
62907
|
+
*/
|
|
62908
|
+
readAll = async () => {
|
|
62909
|
+
const store = await this.readStore();
|
|
62910
|
+
return {
|
|
62911
|
+
index: store.deviceIndex ?? {},
|
|
62912
|
+
meta: store.deviceMeta ?? {},
|
|
62913
|
+
metadata: store.deviceMetadata ?? {}
|
|
62914
|
+
};
|
|
62868
62915
|
};
|
|
62869
62916
|
readIndex = async () => {
|
|
62870
62917
|
return (await this.readStore()).deviceIndex ?? {};
|
|
@@ -74542,6 +74589,20 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74542
74589
|
params
|
|
74543
74590
|
};
|
|
74544
74591
|
}
|
|
74592
|
+
var MAX_ASCII = 127;
|
|
74593
|
+
function prefixRange(prefix) {
|
|
74594
|
+
if (prefix.length === 0) return null;
|
|
74595
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
74596
|
+
const code = prefix.charCodeAt(i);
|
|
74597
|
+
if (code === 0 || code > MAX_ASCII) return null;
|
|
74598
|
+
}
|
|
74599
|
+
const last = prefix.charCodeAt(prefix.length - 1);
|
|
74600
|
+
if (last >= MAX_ASCII) return null;
|
|
74601
|
+
return {
|
|
74602
|
+
lo: prefix,
|
|
74603
|
+
hi: prefix.slice(0, -1) + String.fromCharCode(last + 1)
|
|
74604
|
+
};
|
|
74605
|
+
}
|
|
74545
74606
|
var SETTINGS_QUERY_DEFAULT_ROW_CAP = 2e3;
|
|
74546
74607
|
var SETTINGS_QUERY_HARD_ROW_CAP = 2e4;
|
|
74547
74608
|
function resolveRowBound(requested) {
|
|
@@ -74562,20 +74623,6 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74562
74623
|
function isImposedBound(source) {
|
|
74563
74624
|
return source !== "caller";
|
|
74564
74625
|
}
|
|
74565
|
-
var MAX_ASCII = 127;
|
|
74566
|
-
function prefixRange(prefix) {
|
|
74567
|
-
if (prefix.length === 0) return null;
|
|
74568
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
74569
|
-
const code = prefix.charCodeAt(i);
|
|
74570
|
-
if (code === 0 || code > MAX_ASCII) return null;
|
|
74571
|
-
}
|
|
74572
|
-
const last = prefix.charCodeAt(prefix.length - 1);
|
|
74573
|
-
if (last >= MAX_ASCII) return null;
|
|
74574
|
-
return {
|
|
74575
|
-
lo: prefix,
|
|
74576
|
-
hi: prefix.slice(0, -1) + String.fromCharCode(last + 1)
|
|
74577
|
-
};
|
|
74578
|
-
}
|
|
74579
74626
|
function parseRowData(raw) {
|
|
74580
74627
|
return require_dist10.asJsonObject(require_dist10.parseJsonUnknown(raw)) ?? {};
|
|
74581
74628
|
}
|
|
@@ -74958,10 +75005,33 @@ var require_sqlite_settings_addon = __commonJS({
|
|
|
74958
75005
|
const rows = this.getDb().prepare('SELECT id, data FROM "system-settings"').all();
|
|
74959
75006
|
return Object.fromEntries(rows.map((r) => [r.id, JSON.parse(r.data)]));
|
|
74960
75007
|
}
|
|
74961
|
-
/**
|
|
75008
|
+
/**
|
|
75009
|
+
* Get all settings for an addon.
|
|
75010
|
+
*
|
|
75011
|
+
* Selected by the SAME key range {@link setAllAddon} deletes and re-inserts
|
|
75012
|
+
* — `prefixWhere("<addonId>.")` — and for the same reason `getAllScoped`
|
|
75013
|
+
* uses it: `"<addonId>.<key>"` is the PRIMARY KEY, so the range is a
|
|
75014
|
+
* `SEARCH … USING INDEX` while anything else is a full `SCAN`.
|
|
75015
|
+
*
|
|
75016
|
+
* It used to select on `json_extract(data, '$.addonId')`, which is a second
|
|
75017
|
+
* authority for "this addon's rows" and disagreed with the writer in both
|
|
75018
|
+
* directions: a row inside the JSON's idea of the addon but outside the key
|
|
75019
|
+
* range was READ and never DELETED (a config key that could not be cleared),
|
|
75020
|
+
* and `json_extract` was evaluated on EVERY row — so a four-key addon paid
|
|
75021
|
+
* for the whole table and one unparseable neighbour aborted the statement,
|
|
75022
|
+
* taking out every addon's config read at once.
|
|
75023
|
+
*
|
|
75024
|
+
* The cost was not theoretical. On the live hub `addon-settings` is 627 KB
|
|
75025
|
+
* in 24 rows, 625 KB of it device-manager's three fleet blobs; a V8 profile
|
|
75026
|
+
* of a boot (2026-08-19) had hub-main's JS thread 99.9% busy with **64% of
|
|
75027
|
+
* it inside this method**, which is what put a ~45 s queue in front of every
|
|
75028
|
+
* runner's first store read. Measured on that table: 0.947 ms → ~0.02 ms for
|
|
75029
|
+
* a four-key addon, 2.90 ms → 2.13 ms for device-manager's own.
|
|
75030
|
+
*/
|
|
74962
75031
|
getAllAddon(addonId) {
|
|
74963
75032
|
this.requireDeclared("addon-settings");
|
|
74964
|
-
const
|
|
75033
|
+
const where = this.prefixWhere(`${addonId}.`);
|
|
75034
|
+
const rows = this.getDb().prepare(`SELECT id, data FROM "addon-settings" WHERE ${where.sql}`).all(...where.params);
|
|
74965
75035
|
if (rows.length === 0) return {};
|
|
74966
75036
|
const result = {};
|
|
74967
75037
|
for (const row of rows) {
|
|
@@ -401908,6 +401978,58 @@ var require_runner_spawn_fanout = __commonJS({
|
|
|
401908
401978
|
}
|
|
401909
401979
|
});
|
|
401910
401980
|
|
|
401981
|
+
// ../../server/backend/dist/core/addon/single-flight-refresh.js
|
|
401982
|
+
var require_single_flight_refresh = __commonJS({
|
|
401983
|
+
"../../server/backend/dist/core/addon/single-flight-refresh.js"(exports) {
|
|
401984
|
+
"use strict";
|
|
401985
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
401986
|
+
exports.createSingleFlightRefresh = createSingleFlightRefresh;
|
|
401987
|
+
function createSingleFlightRefresh(run) {
|
|
401988
|
+
let inFlight = null;
|
|
401989
|
+
let pending = false;
|
|
401990
|
+
let pendingPromise = null;
|
|
401991
|
+
let releasePending = () => {
|
|
401992
|
+
};
|
|
401993
|
+
const start = () => {
|
|
401994
|
+
const active = (async () => {
|
|
401995
|
+
try {
|
|
401996
|
+
await run();
|
|
401997
|
+
} finally {
|
|
401998
|
+
inFlight = null;
|
|
401999
|
+
if (pending) {
|
|
402000
|
+
pending = false;
|
|
402001
|
+
const release = releasePending;
|
|
402002
|
+
releasePending = () => {
|
|
402003
|
+
};
|
|
402004
|
+
pendingPromise = null;
|
|
402005
|
+
void start().finally(release);
|
|
402006
|
+
}
|
|
402007
|
+
}
|
|
402008
|
+
})();
|
|
402009
|
+
inFlight = active;
|
|
402010
|
+
return active;
|
|
402011
|
+
};
|
|
402012
|
+
return {
|
|
402013
|
+
request: async () => {
|
|
402014
|
+
if (inFlight === null) {
|
|
402015
|
+
await start();
|
|
402016
|
+
return;
|
|
402017
|
+
}
|
|
402018
|
+
if (!pending) {
|
|
402019
|
+
pending = true;
|
|
402020
|
+
pendingPromise = new Promise((resolve) => {
|
|
402021
|
+
releasePending = resolve;
|
|
402022
|
+
});
|
|
402023
|
+
}
|
|
402024
|
+
const wait = pendingPromise;
|
|
402025
|
+
if (wait !== null)
|
|
402026
|
+
await wait;
|
|
402027
|
+
}
|
|
402028
|
+
};
|
|
402029
|
+
}
|
|
402030
|
+
}
|
|
402031
|
+
});
|
|
402032
|
+
|
|
401911
402033
|
// ../../server/backend/dist/core/addon/addon-registry.service.js
|
|
401912
402034
|
var require_addon_registry_service = __commonJS({
|
|
401913
402035
|
"../../server/backend/dist/core/addon/addon-registry.service.js"(exports) {
|
|
@@ -401970,6 +402092,7 @@ var require_addon_registry_service = __commonJS({
|
|
|
401970
402092
|
var require_cache_js_1 = require_require_cache();
|
|
401971
402093
|
var runner_convergence_1 = require_runner_convergence();
|
|
401972
402094
|
var runner_spawn_fanout_js_1 = require_runner_spawn_fanout();
|
|
402095
|
+
var single_flight_refresh_js_1 = require_single_flight_refresh();
|
|
401973
402096
|
function shouldEvictMissingOnDisk(entry, id, onDiskIds) {
|
|
401974
402097
|
return entry.source === "installed" && entry.packageName !== "@camstack/system" && !onDiskIds.has(id);
|
|
401975
402098
|
}
|
|
@@ -402765,11 +402888,18 @@ var require_addon_registry_service = __commonJS({
|
|
|
402765
402888
|
}
|
|
402766
402889
|
return out;
|
|
402767
402890
|
};
|
|
402891
|
+
/** One full-fleet rebuild in flight, at most one queued behind it. Every
|
|
402892
|
+
* event below asks for the SAME total rebuild, so running it once per event
|
|
402893
|
+
* was ~1 000 rebuilds of a 974-row mirror on a boot — each one a 625 KB read
|
|
402894
|
+
* and parse on hub-main's event loop. No timer: the first event still
|
|
402895
|
+
* refreshes immediately, because this mirror backs scope enforcement.
|
|
402896
|
+
* See `single-flight-refresh.ts`. */
|
|
402897
|
+
deviceMirrorGate = (0, single_flight_refresh_js_1.createSingleFlightRefresh)(() => this.refreshDeviceParentMirror());
|
|
402768
402898
|
/** Subscribe the mirror to every device-meta lifecycle event, and warm it once
|
|
402769
402899
|
* the addon set (device-manager included) is up. */
|
|
402770
402900
|
wireDeviceParentMirror() {
|
|
402771
402901
|
const refresh = () => {
|
|
402772
|
-
void this.
|
|
402902
|
+
void this.deviceMirrorGate.request();
|
|
402773
402903
|
};
|
|
402774
402904
|
for (const category of [
|
|
402775
402905
|
types_1.EventCategory.DeviceMetaChanged,
|