@camstack/system 1.2.439 → 1.2.440
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon-runner.js +743 -79
- package/dist/addon-runner.mjs +743 -79
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-settings-store.d.ts +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +77 -11
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +77 -12
- package/dist/builtins/core-blocks/index.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +181 -59
- package/dist/builtins/device-manager/device-manager.addon.mjs +180 -60
- package/dist/builtins/device-manager/device-removal-hooks.d.ts +0 -11
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +29 -11
- package/dist/builtins/keychain/index.mjs +29 -11
- package/dist/builtins/keychain/keychain-service.d.ts +8 -0
- 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 +2 -2
- package/dist/builtins/local-auth/local-auth.addon.mjs +2 -2
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- 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/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +38 -7
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +69 -3
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +69 -3
- package/dist/builtins/sqlite-storage/storage-admin-provider.d.ts +31 -0
- package/dist/builtins/storage-orchestrator/hub-settings-forward.d.ts +3 -3
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +4 -4
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +4 -4
- package/dist/builtins/system-config/system-config.addon.js +2 -2
- package/dist/builtins/system-config/system-config.addon.mjs +283 -283
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-BGOSCGWa.mjs → composition-sources-CS0D7imL.mjs} +1 -1
- package/dist/{composition-sources-B4jC2FC7.js → composition-sources-cBG8fBTu.js} +1 -1
- package/dist/{constants-B92BjObl.mjs → constants-CIzPjw7L.mjs} +1 -1
- package/dist/{constants-BRsjQwi-.js → constants-CRI3QzT4.js} +1 -1
- package/dist/{device-ca-46BUsWD1.mjs → device-ca-DLKXYseS.mjs} +1 -1
- package/dist/{device-ca-DHG9OW2m.js → device-ca-DNHjts4_.js} +1 -1
- package/dist/{dist-yI6kF6r6.mjs → dist-9fcsrFU3.mjs} +237 -25
- package/dist/{dist-BOWdLjoK.js → dist-BKhlcYz6.js} +254 -24
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-D7S5AJRU.js → hub-mdns-ciao-3Zc9G9MS.js} +1 -1
- package/dist/{hub-mdns-ciao-DGwVc2uh.mjs → hub-mdns-ciao-Dy2YjRqC.mjs} +1 -1
- package/dist/index.js +336 -336
- package/dist/index.mjs +337 -337
- package/dist/kernel/addon-settings-store.d.ts +43 -0
- package/dist/kernel/config-manager.d.ts +0 -19
- package/dist/kernel/device-settings-store.d.ts +74 -0
- package/dist/kernel/legacy-blob-guard.d.ts +58 -0
- package/dist/kernel/moleculer/runner-addon-settings.d.ts +34 -0
- package/dist/kernel/moleculer/runner-device-settings.d.ts +39 -0
- package/dist/kernel/prefix-walk.d.ts +39 -0
- package/dist/kernel/sections-store.d.ts +20 -0
- package/dist/kernel/settings-door-view.d.ts +2 -2
- package/dist/kernel/settings-view-types.d.ts +5 -30
- package/dist/kernel/store-settings-view.d.ts +0 -3
- package/dist/prefix-range-D_KueXbQ.js +27 -0
- package/dist/prefix-range-Dl0znowN.mjs +22 -0
- package/dist/prefix-walk-DAOBSyH1.mjs +88 -0
- package/dist/prefix-walk-Dv6MLZlM.js +99 -0
- package/dist/{restore-archive-DiGiI6Jp.js → restore-archive-49aZwkp8.js} +1 -1
- package/dist/{restore-archive-CpdUO-uS.mjs → restore-archive-DgvDecXM.mjs} +82 -82
- package/dist/{retired-settings-keys-UsVtLOT0.mjs → retired-settings-keys-Bhwe4ymb.mjs} +2 -2
- package/dist/{retired-settings-keys-CyQ7uvta.js → retired-settings-keys-D_VwZiDt.js} +10 -4
- package/dist/{wal-maintenance-xsvdT5Go.mjs → wal-maintenance-BZGmF-3U.mjs} +0 -0
- package/dist/{wal-maintenance-DEL_jeG0.js → wal-maintenance-C041zn6V.js} +0 -0
- package/package.json +5 -2
- package/dist/{custom-action-registry-Bf_YnagS.js → custom-action-registry-BKmoaqG0.js} +1 -1
- package/dist/{custom-action-registry-BQnbN0DP.mjs → custom-action-registry-CyUuhATc.mjs} +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("./dist-
|
|
4
|
-
const require_device_ca = require("./device-ca-
|
|
3
|
+
const require_dist = require("./dist-BKhlcYz6.js");
|
|
4
|
+
const require_device_ca = require("./device-ca-DNHjts4_.js");
|
|
5
5
|
const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
|
|
6
6
|
require("./builtins/alerts/index.js");
|
|
7
7
|
const require_builtins_calendars_calendars_addon = require("./builtins/calendars/calendars.addon.js");
|
|
@@ -10,8 +10,11 @@ const require_builtins_composer_composer_addon = require("./builtins/composer/co
|
|
|
10
10
|
require("./builtins/composer/index.js");
|
|
11
11
|
const require_formatter = require("./formatter-DqAKDlvN.js");
|
|
12
12
|
const require_builtins_console_logging_index = require("./builtins/console-logging/index.js");
|
|
13
|
+
const require_prefix_range = require("./prefix-range-D_KueXbQ.js");
|
|
14
|
+
const require_prefix_walk = require("./prefix-walk-Dv6MLZlM.js");
|
|
13
15
|
const require_builtins_core_blocks_core_blocks_addon = require("./builtins/core-blocks/core-blocks.addon.js");
|
|
14
16
|
require("./builtins/core-blocks/index.js");
|
|
17
|
+
const require_retired_settings_keys = require("./retired-settings-keys-D_VwZiDt.js");
|
|
15
18
|
const require_builtins_device_manager_device_manager_addon = require("./builtins/device-manager/device-manager.addon.js");
|
|
16
19
|
require("./builtins/device-manager/index.js");
|
|
17
20
|
const require_builtins_hub_forwarder_index = require("./builtins/hub-forwarder/index.js");
|
|
@@ -22,21 +25,21 @@ require("./builtins/local-auth/index.js");
|
|
|
22
25
|
const require_builtins_loki_logging_index = require("./builtins/loki-logging/index.js");
|
|
23
26
|
const require_builtins_native_metrics_native_metrics_addon = require("./builtins/native-metrics/native-metrics.addon.js");
|
|
24
27
|
const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
|
|
25
|
-
const require_wal_maintenance = require("./wal-maintenance-
|
|
28
|
+
const require_wal_maintenance = require("./wal-maintenance-C041zn6V.js");
|
|
26
29
|
const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
|
|
27
30
|
const require_builtins_sqlite_storage_index = require("./builtins/sqlite-storage/index.js");
|
|
28
31
|
const require_builtins_storage_orchestrator_storage_orchestrator_addon = require("./builtins/storage-orchestrator/storage-orchestrator.addon.js");
|
|
29
32
|
require("./builtins/storage-orchestrator/index.js");
|
|
30
33
|
const require_agent_https = require("./agent-https-DlpTMDqA.js");
|
|
31
|
-
const require_constants = require("./constants-
|
|
34
|
+
const require_constants = require("./constants-CRI3QzT4.js");
|
|
32
35
|
const require_builtins_system_config_system_config_addon = require("./builtins/system-config/system-config.addon.js");
|
|
33
36
|
require("./builtins/system-config/index.js");
|
|
34
37
|
const require_builtins_winston_logging_index = require("./builtins/winston-logging/index.js");
|
|
35
|
-
const require_custom_action_registry = require("./custom-action-registry-
|
|
36
|
-
const require_hub_mdns_ciao = require("./hub-mdns-ciao-
|
|
38
|
+
const require_custom_action_registry = require("./custom-action-registry-BKmoaqG0.js");
|
|
39
|
+
const require_hub_mdns_ciao = require("./hub-mdns-ciao-3Zc9G9MS.js");
|
|
37
40
|
const require_platform_key_source = require("./platform-key-source-Di7HrlGR.js");
|
|
38
41
|
const require_resource_monitor = require("./resource-monitor-mio9dSCT.js");
|
|
39
|
-
const require_restore_archive = require("./restore-archive-
|
|
42
|
+
const require_restore_archive = require("./restore-archive-49aZwkp8.js");
|
|
40
43
|
let zod = require("zod");
|
|
41
44
|
let node_crypto = require("node:crypto");
|
|
42
45
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
@@ -48,13 +51,13 @@ node_path = require_chunk.__toESM(node_path);
|
|
|
48
51
|
let node_os = require("node:os");
|
|
49
52
|
node_os = require_chunk.__toESM(node_os);
|
|
50
53
|
let node_url = require("node:url");
|
|
54
|
+
let _camstack_types_addon = require("@camstack/types/addon");
|
|
51
55
|
let node_fs = require("node:fs");
|
|
52
56
|
node_fs = require_chunk.__toESM(node_fs);
|
|
53
57
|
let node_child_process = require("node:child_process");
|
|
54
58
|
let node_util = require("node:util");
|
|
55
59
|
node_util = require_chunk.__toESM(node_util);
|
|
56
60
|
let node_events = require("node:events");
|
|
57
|
-
let _camstack_types_addon = require("@camstack/types/addon");
|
|
58
61
|
let node_http = require("node:http");
|
|
59
62
|
let node_v8 = require("node:v8");
|
|
60
63
|
node_v8 = require_chunk.__toESM(node_v8);
|
|
@@ -1174,23 +1177,23 @@ function formatSocketPlane(window) {
|
|
|
1174
1177
|
}
|
|
1175
1178
|
//#endregion
|
|
1176
1179
|
//#region src/kernel/moleculer/mesh-queue-report.ts
|
|
1177
|
-
function isRecord$
|
|
1180
|
+
function isRecord$7(value) {
|
|
1178
1181
|
return typeof value === "object" && value !== null;
|
|
1179
1182
|
}
|
|
1180
1183
|
/** The shape `TcpWriter` keeps its sockets in. Anything else is not a mesh. */
|
|
1181
1184
|
function writerSocketsOf(broker) {
|
|
1182
|
-
if (!isRecord$
|
|
1185
|
+
if (!isRecord$7(broker)) return void 0;
|
|
1183
1186
|
const transit = broker["transit"];
|
|
1184
|
-
if (!isRecord$
|
|
1187
|
+
if (!isRecord$7(transit)) return void 0;
|
|
1185
1188
|
const transporter = transit["tx"];
|
|
1186
|
-
if (!isRecord$
|
|
1189
|
+
if (!isRecord$7(transporter)) return void 0;
|
|
1187
1190
|
const writer = transporter["writer"];
|
|
1188
|
-
if (!isRecord$
|
|
1191
|
+
if (!isRecord$7(writer)) return void 0;
|
|
1189
1192
|
const sockets = writer["sockets"];
|
|
1190
1193
|
return sockets instanceof Map ? sockets : void 0;
|
|
1191
1194
|
}
|
|
1192
1195
|
function readSocket(nodeId, socket) {
|
|
1193
|
-
if (typeof nodeId !== "string" || !isRecord$
|
|
1196
|
+
if (typeof nodeId !== "string" || !isRecord$7(socket)) return void 0;
|
|
1194
1197
|
const queuedBytes = socket["writableLength"];
|
|
1195
1198
|
const bytesWritten = socket["bytesWritten"];
|
|
1196
1199
|
if (typeof queuedBytes !== "number" || typeof bytesWritten !== "number") return void 0;
|
|
@@ -6310,7 +6313,7 @@ async function resolveUncached(options) {
|
|
|
6310
6313
|
}
|
|
6311
6314
|
//#endregion
|
|
6312
6315
|
//#region src/kernel/addon-class-resolver.ts
|
|
6313
|
-
function isRecord$
|
|
6316
|
+
function isRecord$6(value) {
|
|
6314
6317
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6315
6318
|
}
|
|
6316
6319
|
/**
|
|
@@ -6334,14 +6337,14 @@ function isRecord$5(value) {
|
|
|
6334
6337
|
function isAddonConstructor(fn) {
|
|
6335
6338
|
if (typeof fn !== "function") return false;
|
|
6336
6339
|
const proto = fn.prototype;
|
|
6337
|
-
if (!isRecord$
|
|
6340
|
+
if (!isRecord$6(proto)) return false;
|
|
6338
6341
|
return typeof proto["initialize"] === "function";
|
|
6339
6342
|
}
|
|
6340
6343
|
function resolveAddonClass(mod) {
|
|
6341
6344
|
let candidate = mod["default"];
|
|
6342
|
-
if (isRecord$
|
|
6345
|
+
if (isRecord$6(candidate) && "default" in candidate) candidate = candidate["default"];
|
|
6343
6346
|
if (isAddonConstructor(candidate)) return candidate;
|
|
6344
|
-
if (isRecord$
|
|
6347
|
+
if (isRecord$6(candidate)) {
|
|
6345
6348
|
const fn = Object.values(candidate).find(isAddonConstructor);
|
|
6346
6349
|
if (fn) return fn;
|
|
6347
6350
|
}
|
|
@@ -6350,7 +6353,7 @@ function resolveAddonClass(mod) {
|
|
|
6350
6353
|
}
|
|
6351
6354
|
//#endregion
|
|
6352
6355
|
//#region src/kernel/addon-loader.ts
|
|
6353
|
-
function isRecord$
|
|
6356
|
+
function isRecord$5(value) {
|
|
6354
6357
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6355
6358
|
}
|
|
6356
6359
|
/**
|
|
@@ -6448,9 +6451,9 @@ async function importAddonModuleFresh(entryPath) {
|
|
|
6448
6451
|
* AddonPackageManifest + AddonDeclaration.
|
|
6449
6452
|
*/
|
|
6450
6453
|
function toAddonPackageManifest(value) {
|
|
6451
|
-
if (!isRecord$
|
|
6454
|
+
if (!isRecord$5(value)) return void 0;
|
|
6452
6455
|
if (!Array.isArray(value.addons)) return void 0;
|
|
6453
|
-
if (!value.addons.every((a) => isRecord$
|
|
6456
|
+
if (!value.addons.every((a) => isRecord$5(a) && typeof a.id === "string")) return void 0;
|
|
6454
6457
|
return value;
|
|
6455
6458
|
}
|
|
6456
6459
|
/** Minimal no-op logger for default parameter */
|
|
@@ -6600,7 +6603,7 @@ var AddonLoader = class {
|
|
|
6600
6603
|
}
|
|
6601
6604
|
if (!node_fs.existsSync(entryPath)) throw new Error(`Entry not found: ${entryPath}`);
|
|
6602
6605
|
const modUnknown = await importAddonModuleFresh(entryPath);
|
|
6603
|
-
const mod = isRecord$
|
|
6606
|
+
const mod = isRecord$5(modUnknown) ? modUnknown : {};
|
|
6604
6607
|
const AddonClass = resolveAddonClass(mod);
|
|
6605
6608
|
if (!AddonClass) throw new Error(`No addon class in ${entryPath}`);
|
|
6606
6609
|
this.addons.set(declaration.id, {
|
|
@@ -6616,7 +6619,7 @@ var AddonLoader = class {
|
|
|
6616
6619
|
/** Load addon from a direct path (for development/testing) */
|
|
6617
6620
|
async loadFromPath(addonId, modulePath, packageName, declaration, packageVersion = "0.0.0") {
|
|
6618
6621
|
const modUnknown = await importAddonModuleFresh(modulePath);
|
|
6619
|
-
const mod = isRecord$
|
|
6622
|
+
const mod = isRecord$5(modUnknown) ? modUnknown : {};
|
|
6620
6623
|
const AddonClass = resolveAddonClass(mod);
|
|
6621
6624
|
if (!AddonClass) throw new Error(`Module ${modulePath} has no default export`);
|
|
6622
6625
|
this.addons.set(addonId, {
|
|
@@ -11536,92 +11539,11 @@ async function runHubAddonBoot(steps) {
|
|
|
11536
11539
|
}
|
|
11537
11540
|
//#endregion
|
|
11538
11541
|
//#region src/kernel/settings-door-view.ts
|
|
11539
|
-
function isRecord$
|
|
11542
|
+
function isRecord$4(value) {
|
|
11540
11543
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11541
11544
|
}
|
|
11542
11545
|
function asBlob(value) {
|
|
11543
|
-
return isRecord$
|
|
11544
|
-
}
|
|
11545
|
-
function unwrapValue(parsed) {
|
|
11546
|
-
if (!isRecord$3(parsed) || !("value" in parsed)) return parsed;
|
|
11547
|
-
return parsed.value;
|
|
11548
|
-
}
|
|
11549
|
-
/**
|
|
11550
|
-
* Rows one page of a prefix read asks for.
|
|
11551
|
-
*
|
|
11552
|
-
* Below `SETTINGS_QUERY_DEFAULT_ROW_CAP` (2 000, `query-bounds.ts`) on purpose:
|
|
11553
|
-
* this read now names its own bound, so the engine's defensive cap is never the
|
|
11554
|
-
* thing that decides how much of the answer the caller gets.
|
|
11555
|
-
*/
|
|
11556
|
-
var PREFIX_PAGE_ROWS = 1e3;
|
|
11557
|
-
/**
|
|
11558
|
-
* Pages one prefix read may take before it refuses.
|
|
11559
|
-
*
|
|
11560
|
-
* 512 pages is 512 000 rows — three orders of magnitude past the largest live
|
|
11561
|
-
* scope (`addon-device-settings` for one addon over a ~1 019-device fleet) and
|
|
11562
|
-
* still a bound, because a walk with no bound is the defect this file was
|
|
11563
|
-
* fixing, not one to reintroduce.
|
|
11564
|
-
*/
|
|
11565
|
-
var PREFIX_PAGE_BUDGET = 512;
|
|
11566
|
-
/**
|
|
11567
|
-
* Every row under `prefix`, or a throw — never the first 2 000 of them.
|
|
11568
|
-
*
|
|
11569
|
-
* **The defect.** This issued ONE `query` with no `limit`, and
|
|
11570
|
-
* `queryDeclared` resolves an absent limit to `SETTINGS_QUERY_DEFAULT_ROW_CAP`
|
|
11571
|
-
* and cuts the result there. On the live hub that fired 138 times in two hours
|
|
11572
|
-
* (`settings-store query truncated by a defensive row cap`, collection
|
|
11573
|
-
* `addon-device-settings`, `rowCap: 2000`, `requestedLimit: null`): a
|
|
11574
|
-
* ~1 019-device fleet with more than two stored keys per device puts one
|
|
11575
|
-
* addon's `<addonId>:` range past the cap. The engine warned; the door did not
|
|
11576
|
-
* notice, and handed its caller a short map that is **shape-identical to a
|
|
11577
|
-
* complete one**. Every device whose rows sort past the cap reads as "has no
|
|
11578
|
-
* stored settings", which is exactly what a device with none looks like.
|
|
11579
|
-
*
|
|
11580
|
-
* **The fix is a keyset walk, not a bigger limit.** The cursor is the last id
|
|
11581
|
-
* of the previous page, INCLUSIVE — so the page boundary re-reads exactly one
|
|
11582
|
-
* row (ids are unique, so a full page always advances) and no row can fall
|
|
11583
|
-
* between two pages the way an `OFFSET` walk drops rows under a concurrent
|
|
11584
|
-
* write (D295 rule 1). Re-reading is free here: the accumulator is keyed by
|
|
11585
|
-
* suffix, so the duplicate overwrites itself with its own value.
|
|
11586
|
-
*
|
|
11587
|
-
* `ORDER BY id` is not decoration — the cursor is only meaningful if the
|
|
11588
|
-
* engine returns the range in the order the cursor advances through. It is the
|
|
11589
|
-
* primary key, so the ordering is the range scan's own.
|
|
11590
|
-
*
|
|
11591
|
-
* @throws when the prefix cannot be expressed as a range and the collection is
|
|
11592
|
-
* larger than one page, or when the walk exceeds {@link PREFIX_PAGE_BUDGET}.
|
|
11593
|
-
* A throw is the point: a caller that cannot be answered completely must not
|
|
11594
|
-
* be handed something that looks like a complete answer.
|
|
11595
|
-
*/
|
|
11596
|
-
async function loadPrefixed(door, collection, prefix, decode = unwrapValue) {
|
|
11597
|
-
const range = require_wal_maintenance.prefixRange(prefix);
|
|
11598
|
-
const result = {};
|
|
11599
|
-
let cursor = range?.lo ?? null;
|
|
11600
|
-
for (let page = 0;; page++) {
|
|
11601
|
-
if (page >= PREFIX_PAGE_BUDGET) throw new Error(`settings door: reading "${prefix}" from "${collection}" exceeded ${PREFIX_PAGE_BUDGET} pages of ${PREFIX_PAGE_ROWS} rows. Refusing to return the part that was read — a partial scope read is indistinguishable from an empty one.`);
|
|
11602
|
-
const rows = await door.query({
|
|
11603
|
-
collection,
|
|
11604
|
-
filter: {
|
|
11605
|
-
...range !== null && cursor !== null ? { whereBetween: { id: [cursor, range.hi] } } : {},
|
|
11606
|
-
orderBy: {
|
|
11607
|
-
field: "id",
|
|
11608
|
-
direction: "asc"
|
|
11609
|
-
},
|
|
11610
|
-
limit: PREFIX_PAGE_ROWS
|
|
11611
|
-
}
|
|
11612
|
-
});
|
|
11613
|
-
for (const row of rows) {
|
|
11614
|
-
if (range !== null && (row.id < range.lo || row.id >= range.hi)) continue;
|
|
11615
|
-
if (!row.id.startsWith(prefix)) continue;
|
|
11616
|
-
result[row.id.slice(prefix.length)] = decode(row.data);
|
|
11617
|
-
}
|
|
11618
|
-
if (rows.length < PREFIX_PAGE_ROWS) return result;
|
|
11619
|
-
const last = rows[rows.length - 1];
|
|
11620
|
-
if (last === void 0) return result;
|
|
11621
|
-
if (range === null) throw new Error(`settings door: prefix "${prefix}" on "${collection}" cannot be expressed as an id range, and the unfiltered read filled a page — the answer would be partial. Use an ASCII, non-empty prefix.`);
|
|
11622
|
-
if (last.id === cursor) throw new Error(`settings door: reading "${prefix}" from "${collection}" made no progress at id "${last.id}" — ids under a prefix must be unique.`);
|
|
11623
|
-
cursor = last.id;
|
|
11624
|
-
}
|
|
11546
|
+
return isRecord$4(value) ? value : {};
|
|
11625
11547
|
}
|
|
11626
11548
|
/**
|
|
11627
11549
|
* Every `system-settings` row under `prefix`, keyed by the suffix — or a throw.
|
|
@@ -11638,7 +11560,7 @@ async function loadPrefixed(door, collection, prefix, decode = unwrapValue) {
|
|
|
11638
11560
|
* form opens, and the capability preferences once at boot.
|
|
11639
11561
|
*/
|
|
11640
11562
|
async function loadSystemSettingsPrefix(door, prefix, mayRead = () => true) {
|
|
11641
|
-
const listed = await loadPrefixed(door, "system-settings", prefix, () => null);
|
|
11563
|
+
const listed = await require_prefix_walk.loadPrefixed(door, "system-settings", prefix, () => null);
|
|
11642
11564
|
const result = {};
|
|
11643
11565
|
for (const suffix of Object.keys(listed).filter(mayRead)) {
|
|
11644
11566
|
const value = await door.get({
|
|
@@ -11649,106 +11571,34 @@ async function loadSystemSettingsPrefix(door, prefix, mayRead = () => true) {
|
|
|
11649
11571
|
}
|
|
11650
11572
|
return result;
|
|
11651
11573
|
}
|
|
11652
|
-
async function replacePrefixed(door, collection, prefix, values, wrap) {
|
|
11653
|
-
const range = require_wal_maintenance.prefixRange(prefix);
|
|
11654
|
-
if (range !== null) await door.deleteWhere({
|
|
11655
|
-
collection,
|
|
11656
|
-
filter: { whereBetween: { id: [range.lo, range.hi] } }
|
|
11657
|
-
});
|
|
11658
|
-
for (const [key, value] of Object.entries(values)) {
|
|
11659
|
-
if (value === void 0) continue;
|
|
11660
|
-
await door.set({
|
|
11661
|
-
collection,
|
|
11662
|
-
key: `${prefix}${key}`,
|
|
11663
|
-
value: wrap(key, value)
|
|
11664
|
-
});
|
|
11665
|
-
}
|
|
11666
|
-
}
|
|
11667
11574
|
function createDoorSettingsView(addonId, door, sections) {
|
|
11668
|
-
const
|
|
11575
|
+
const addonStore = require_builtins_core_blocks_core_blocks_addon.createAddonSettingsStore(door, addonId);
|
|
11576
|
+
const deviceStore = require_builtins_device_manager_device_manager_addon.createDeviceSettingsStore(door, addonId);
|
|
11669
11577
|
return {
|
|
11670
11578
|
async readAddonStore() {
|
|
11671
|
-
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
collection: "addon-settings",
|
|
11677
|
-
key: `${addonPrefix}${key}`
|
|
11678
|
-
});
|
|
11679
|
-
return row === void 0 ? { kind: "absent" } : {
|
|
11680
|
-
kind: "present",
|
|
11681
|
-
value: unwrapValue(row)
|
|
11682
|
-
};
|
|
11683
|
-
} catch (err) {
|
|
11684
|
-
return {
|
|
11685
|
-
kind: "unreadable",
|
|
11686
|
-
reason: "store-read-failed",
|
|
11687
|
-
error: err instanceof Error ? err.message : String(err)
|
|
11688
|
-
};
|
|
11579
|
+
const read = await addonStore.read();
|
|
11580
|
+
switch (read.kind) {
|
|
11581
|
+
case "present": return { ...read.value };
|
|
11582
|
+
case "absent": return {};
|
|
11583
|
+
case "unreadable": throw new Error(`settings of "${addonId}" could not be read: ${read.error}`);
|
|
11689
11584
|
}
|
|
11690
11585
|
},
|
|
11691
|
-
async
|
|
11692
|
-
|
|
11693
|
-
|
|
11694
|
-
|
|
11695
|
-
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
value
|
|
11699
|
-
}));
|
|
11586
|
+
async readAddonStoreKeyResult(key) {
|
|
11587
|
+
const read = await addonStore.readKey(key);
|
|
11588
|
+
return read.kind === "unreadable" ? {
|
|
11589
|
+
kind: "unreadable",
|
|
11590
|
+
reason: "store-read-failed",
|
|
11591
|
+
error: read.error
|
|
11592
|
+
} : read;
|
|
11700
11593
|
},
|
|
11594
|
+
writeAddonStore: (patch) => addonStore.merge(patch),
|
|
11701
11595
|
async readDeviceStore(deviceId) {
|
|
11702
|
-
return
|
|
11703
|
-
},
|
|
11704
|
-
async readDeviceStoreBatch(deviceIds) {
|
|
11705
|
-
const out = /* @__PURE__ */ new Map();
|
|
11706
|
-
if (deviceIds.length === 0) return out;
|
|
11707
|
-
for (const id of deviceIds) out.set(id, {});
|
|
11708
|
-
const wanted = new Set(deviceIds);
|
|
11709
|
-
const all = await loadPrefixed(door, "addon-device-settings", `${addonId}:`);
|
|
11710
|
-
for (const [suffix, value] of Object.entries(all)) {
|
|
11711
|
-
const dot = suffix.indexOf(".");
|
|
11712
|
-
if (dot <= 0) continue;
|
|
11713
|
-
const deviceId = Number(suffix.slice(0, dot));
|
|
11714
|
-
if (!Number.isInteger(deviceId) || !wanted.has(deviceId)) continue;
|
|
11715
|
-
const blob = out.get(deviceId);
|
|
11716
|
-
if (blob === void 0) continue;
|
|
11717
|
-
blob[suffix.slice(dot + 1)] = value;
|
|
11718
|
-
}
|
|
11719
|
-
return out;
|
|
11720
|
-
},
|
|
11721
|
-
async listDeviceStoreIds() {
|
|
11722
|
-
const all = await loadPrefixed(door, "addon-device-settings", `${addonId}:`, () => null);
|
|
11723
|
-
const ids = /* @__PURE__ */ new Set();
|
|
11724
|
-
for (const suffix of Object.keys(all)) {
|
|
11725
|
-
const dot = suffix.indexOf(".");
|
|
11726
|
-
if (dot <= 0) continue;
|
|
11727
|
-
const deviceId = Number(suffix.slice(0, dot));
|
|
11728
|
-
if (Number.isInteger(deviceId) && deviceId > 0) ids.add(deviceId);
|
|
11729
|
-
}
|
|
11730
|
-
return [...ids].toSorted((a, b) => a - b);
|
|
11731
|
-
},
|
|
11732
|
-
async writeDeviceStore(deviceId, patch) {
|
|
11733
|
-
const scope = `${addonId}:${String(deviceId)}.`;
|
|
11734
|
-
await replacePrefixed(door, "addon-device-settings", scope, {
|
|
11735
|
-
...await loadPrefixed(door, "addon-device-settings", scope),
|
|
11736
|
-
...patch
|
|
11737
|
-
}, (key, value) => ({
|
|
11738
|
-
addonId,
|
|
11739
|
-
deviceId: String(deviceId),
|
|
11740
|
-
key,
|
|
11741
|
-
value
|
|
11742
|
-
}));
|
|
11743
|
-
},
|
|
11744
|
-
async clearDeviceStore(deviceId) {
|
|
11745
|
-
const range = require_wal_maintenance.prefixRange(`${addonId}:${String(deviceId)}.`);
|
|
11746
|
-
if (range === null) return;
|
|
11747
|
-
await door.deleteWhere({
|
|
11748
|
-
collection: "addon-device-settings",
|
|
11749
|
-
filter: { whereBetween: { id: [range.lo, range.hi] } }
|
|
11750
|
-
});
|
|
11596
|
+
return require_builtins_device_manager_device_manager_addon.readDeviceOrThrow(deviceStore, deviceId);
|
|
11751
11597
|
},
|
|
11598
|
+
readDeviceStoreBatch: (deviceIds) => deviceStore.readBatch(deviceIds),
|
|
11599
|
+
listDeviceStoreIds: () => deviceStore.listIds(),
|
|
11600
|
+
writeDeviceStore: (deviceId, patch) => deviceStore.merge(deviceId, patch),
|
|
11601
|
+
clearDeviceStore: (deviceId) => deviceStore.clear(deviceId),
|
|
11752
11602
|
async readDeviceRuntimeState(deviceId) {
|
|
11753
11603
|
return asBlob(await door.get({
|
|
11754
11604
|
collection: "device-runtime-state",
|
|
@@ -14262,7 +14112,7 @@ import_js_yaml.default;
|
|
|
14262
14112
|
* so that file stays one class. The `console.*` calls are the documented
|
|
14263
14113
|
* residuals described on `ConfigManager` (no logger exists yet at boot).
|
|
14264
14114
|
*/
|
|
14265
|
-
function isRecord$
|
|
14115
|
+
function isRecord$3(value) {
|
|
14266
14116
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14267
14117
|
}
|
|
14268
14118
|
/** Sections that live in config.yaml. Everything else goes to the settings-store. */
|
|
@@ -14353,7 +14203,7 @@ function setNested(obj, configPath, value) {
|
|
|
14353
14203
|
[head]: value
|
|
14354
14204
|
};
|
|
14355
14205
|
const existing = obj[head];
|
|
14356
|
-
const child = isRecord$
|
|
14206
|
+
const child = isRecord$3(existing) ? existing : {};
|
|
14357
14207
|
return {
|
|
14358
14208
|
...obj,
|
|
14359
14209
|
[head]: setNested(child, rest.join("."), value)
|
|
@@ -14386,8 +14236,10 @@ function loadYaml(configPath) {
|
|
|
14386
14236
|
//#endregion
|
|
14387
14237
|
//#region src/kernel/store-settings-view.ts
|
|
14388
14238
|
function createStoreSettingsView(addonId, cm) {
|
|
14239
|
+
const refuseDeviceSettings = async () => {
|
|
14240
|
+
throw new Error(`device settings of addon "${addonId}" are served by the settings-store door only`);
|
|
14241
|
+
};
|
|
14389
14242
|
const viewAddonConfig = () => !cm.hasSyncStore() ? (0, _camstack_types_addon.asJsonObject)(cm.getBootstrap(`addons.${addonId}`)) ?? {} : cm.getAddonConfig(addonId);
|
|
14390
|
-
const viewAddonDevice = (deviceId) => !cm.hasSyncStore() ? {} : cm.getAddonDevice(addonId, deviceId);
|
|
14391
14243
|
return {
|
|
14392
14244
|
async readAddonStore() {
|
|
14393
14245
|
return viewAddonConfig();
|
|
@@ -14419,29 +14271,11 @@ function createStoreSettingsView(addonId, cm) {
|
|
|
14419
14271
|
...patch
|
|
14420
14272
|
});
|
|
14421
14273
|
},
|
|
14422
|
-
|
|
14423
|
-
|
|
14424
|
-
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
for (const deviceId of deviceIds) try {
|
|
14428
|
-
out.set(deviceId, viewAddonDevice(String(deviceId)));
|
|
14429
|
-
} catch {
|
|
14430
|
-
out.set(deviceId, {});
|
|
14431
|
-
}
|
|
14432
|
-
return out;
|
|
14433
|
-
},
|
|
14434
|
-
async writeDeviceStore(deviceId, patch) {
|
|
14435
|
-
const key = String(deviceId);
|
|
14436
|
-
const existing = viewAddonDevice(key);
|
|
14437
|
-
cm.setAddonDevice(addonId, key, {
|
|
14438
|
-
...existing,
|
|
14439
|
-
...patch
|
|
14440
|
-
});
|
|
14441
|
-
},
|
|
14442
|
-
async clearDeviceStore(deviceId) {
|
|
14443
|
-
cm.clearAddonDevice(addonId, String(deviceId));
|
|
14444
|
-
},
|
|
14274
|
+
readDeviceStore: refuseDeviceSettings,
|
|
14275
|
+
readDeviceStoreBatch: refuseDeviceSettings,
|
|
14276
|
+
listDeviceStoreIds: refuseDeviceSettings,
|
|
14277
|
+
writeDeviceStore: refuseDeviceSettings,
|
|
14278
|
+
clearDeviceStore: refuseDeviceSettings,
|
|
14445
14279
|
async readDeviceRuntimeState(deviceId) {
|
|
14446
14280
|
return cm.getDeviceRuntimeState(String(deviceId));
|
|
14447
14281
|
},
|
|
@@ -14626,7 +14460,7 @@ var SystemSettingsMirror = class {
|
|
|
14626
14460
|
};
|
|
14627
14461
|
//#endregion
|
|
14628
14462
|
//#region src/kernel/config-manager.ts
|
|
14629
|
-
function isRecord$
|
|
14463
|
+
function isRecord$2(value) {
|
|
14630
14464
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14631
14465
|
}
|
|
14632
14466
|
function errorText(err) {
|
|
@@ -14815,7 +14649,7 @@ var ConfigManager = class {
|
|
|
14815
14649
|
async readAddonDeviceStrict(addonId, deviceId) {
|
|
14816
14650
|
const door = this.settingsDoor;
|
|
14817
14651
|
switch (this.backendMode()) {
|
|
14818
|
-
case "store":
|
|
14652
|
+
case "store": break;
|
|
14819
14653
|
case "door":
|
|
14820
14654
|
if (door === null) break;
|
|
14821
14655
|
try {
|
|
@@ -14833,34 +14667,6 @@ var ConfigManager = class {
|
|
|
14833
14667
|
this.settingsStore.setAllAddon(addonId, config);
|
|
14834
14668
|
}
|
|
14835
14669
|
/**
|
|
14836
|
-
* Read per-device overrides for a specific addon. Returns a flat record
|
|
14837
|
-
* of field → value. Missing keys fall back to the addon-global value via
|
|
14838
|
-
* `getAddonConfig(addonId)` — that composition is the resolver service's
|
|
14839
|
-
* responsibility, not ConfigManager's.
|
|
14840
|
-
* SYNC store only — THROWS without one (D675); async: {@link readAddonDeviceStrict}.
|
|
14841
|
-
*/
|
|
14842
|
-
getAddonDevice(addonId, deviceId) {
|
|
14843
|
-
if (this.settingsStore === null) throw this.refuseSyncRead("getAddonDevice", `addon "${addonId}" device "${deviceId}"`);
|
|
14844
|
-
return this.settingsStore.getAddonDevice(addonId, deviceId);
|
|
14845
|
-
}
|
|
14846
|
-
/**
|
|
14847
|
-
* Write (bulk-replace) per-device overrides for a specific addon.
|
|
14848
|
-
* Throws if the settings store is not yet wired.
|
|
14849
|
-
*/
|
|
14850
|
-
setAddonDevice(addonId, deviceId, values) {
|
|
14851
|
-
if (this.settingsStore === null) throw new Error("[ConfigManager] SettingsStore not initialized -- call setSettingsStore() first");
|
|
14852
|
-
this.settingsStore.setAddonDevice(addonId, deviceId, values);
|
|
14853
|
-
}
|
|
14854
|
-
/**
|
|
14855
|
-
* Clear all per-device overrides for a specific addon/device pair.
|
|
14856
|
-
* Resolution falls back to the addon-global value after this call.
|
|
14857
|
-
* No-op if the settings store is not yet wired.
|
|
14858
|
-
*/
|
|
14859
|
-
clearAddonDevice(addonId, deviceId) {
|
|
14860
|
-
if (this.settingsStore === null) return;
|
|
14861
|
-
this.settingsStore.clearAddonDevice(addonId, deviceId);
|
|
14862
|
-
}
|
|
14863
|
-
/**
|
|
14864
14670
|
* The device's persisted runtime-state blob. THROWS before the store is
|
|
14865
14671
|
* wired: a store not asked is not an empty row (D677, D675).
|
|
14866
14672
|
*/
|
|
@@ -14937,11 +14743,7 @@ var ConfigManager = class {
|
|
|
14937
14743
|
writeAddonStore: (patch) => active().writeAddonStore(patch),
|
|
14938
14744
|
readDeviceStore: (deviceId) => active().readDeviceStore(deviceId),
|
|
14939
14745
|
readDeviceStoreBatch: (deviceIds) => active().readDeviceStoreBatch(deviceIds),
|
|
14940
|
-
listDeviceStoreIds:
|
|
14941
|
-
const list = active().listDeviceStoreIds;
|
|
14942
|
-
if (list === void 0) throw new Error("listDeviceStoreIds is not available on this settings view");
|
|
14943
|
-
return list.call(active());
|
|
14944
|
-
},
|
|
14746
|
+
listDeviceStoreIds: () => active().listDeviceStoreIds(),
|
|
14945
14747
|
writeDeviceStore: (deviceId, patch) => active().writeDeviceStore(deviceId, patch),
|
|
14946
14748
|
clearDeviceStore: (deviceId) => active().clearDeviceStore(deviceId),
|
|
14947
14749
|
getSection: (section) => active().getSection(section),
|
|
@@ -15017,7 +14819,7 @@ var ConfigManager = class {
|
|
|
15017
14819
|
const keys = configPath.split(".");
|
|
15018
14820
|
let current = this.bootstrapConfig;
|
|
15019
14821
|
for (const key of keys) {
|
|
15020
|
-
if (!isRecord$
|
|
14822
|
+
if (!isRecord$2(current)) return void 0;
|
|
15021
14823
|
current = current[key];
|
|
15022
14824
|
}
|
|
15023
14825
|
return current;
|
|
@@ -15868,27 +15670,6 @@ function asName(value) {
|
|
|
15868
15670
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
15869
15671
|
}
|
|
15870
15672
|
//#endregion
|
|
15871
|
-
//#region src/kernel/moleculer/settings-read-guard.ts
|
|
15872
|
-
/**
|
|
15873
|
-
* Merge `patch` over the current value and write it — unless the read failed,
|
|
15874
|
-
* in which case write NOTHING.
|
|
15875
|
-
*
|
|
15876
|
-
* Returns whether the write was attempted, so a caller can surface the refusal.
|
|
15877
|
-
*/
|
|
15878
|
-
async function readModifyWrite(input) {
|
|
15879
|
-
const current = await input.read();
|
|
15880
|
-
if (current.kind === "failed") {
|
|
15881
|
-
input.onRefused(current.error);
|
|
15882
|
-
return false;
|
|
15883
|
-
}
|
|
15884
|
-
const base = current.kind === "value" ? current.value : {};
|
|
15885
|
-
await input.write({
|
|
15886
|
-
...base,
|
|
15887
|
-
...input.patch
|
|
15888
|
-
});
|
|
15889
|
-
return true;
|
|
15890
|
-
}
|
|
15891
|
-
//#endregion
|
|
15892
15673
|
//#region src/kernel/moleculer/children-batch-loader.ts
|
|
15893
15674
|
/**
|
|
15894
15675
|
* Narrow an untyped cap answer to the fields the reconcile reads.
|
|
@@ -16023,7 +15804,7 @@ var RuntimeStateLoadError = class extends Error {
|
|
|
16023
15804
|
this.name = "RuntimeStateLoadError";
|
|
16024
15805
|
}
|
|
16025
15806
|
};
|
|
16026
|
-
function isRecord(value) {
|
|
15807
|
+
function isRecord$1(value) {
|
|
16027
15808
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16028
15809
|
}
|
|
16029
15810
|
function errorMessage(err) {
|
|
@@ -16054,7 +15835,7 @@ async function loadRuntimeStateOrRefuse(load, logger, target) {
|
|
|
16054
15835
|
} catch (err) {
|
|
16055
15836
|
return refuse(errorMessage(err), err);
|
|
16056
15837
|
}
|
|
16057
|
-
if (!isRecord(answer)) return refuse(`answered ${answer === null ? "null" : typeof answer}`);
|
|
15838
|
+
if (!isRecord$1(answer)) return refuse(`answered ${answer === null ? "null" : typeof answer}`);
|
|
16058
15839
|
return answer;
|
|
16059
15840
|
}
|
|
16060
15841
|
//#endregion
|
|
@@ -23145,6 +22926,261 @@ function createRemoteProviderProxy(broker, serviceName, capName, nodeId, methods
|
|
|
23145
22926
|
return dispatcher;
|
|
23146
22927
|
}
|
|
23147
22928
|
//#endregion
|
|
22929
|
+
//#region src/kernel/sections-store.ts
|
|
22930
|
+
function createSectionsStore(door) {
|
|
22931
|
+
return {
|
|
22932
|
+
read: (section) => loadSystemSettingsPrefix(door, `${section}.`, (key) => storeMayAnswer(`${section}.${key}`)),
|
|
22933
|
+
write: async (section, patch) => {
|
|
22934
|
+
for (const [key, value] of Object.entries(patch)) await door.set({
|
|
22935
|
+
collection: "system-settings",
|
|
22936
|
+
key: `${section}.${key}`,
|
|
22937
|
+
value
|
|
22938
|
+
});
|
|
22939
|
+
}
|
|
22940
|
+
};
|
|
22941
|
+
}
|
|
22942
|
+
//#endregion
|
|
22943
|
+
//#region src/kernel/moleculer/runner-addon-settings.ts
|
|
22944
|
+
/** @param client the hub's `settingsStore` client; `undefined` makes every call THROW. */
|
|
22945
|
+
function createRunnerAddonSettings(client, addonId, before = async () => {}) {
|
|
22946
|
+
if (client === void 0) {
|
|
22947
|
+
const refuse = async () => {
|
|
22948
|
+
throw new Error(`settings of "${addonId}": settings store not wired`);
|
|
22949
|
+
};
|
|
22950
|
+
return {
|
|
22951
|
+
readAddonStore: refuse,
|
|
22952
|
+
readAddonStoreResult: async () => ({
|
|
22953
|
+
kind: "failed",
|
|
22954
|
+
error: "settings store not wired"
|
|
22955
|
+
}),
|
|
22956
|
+
readAddonStoreKeyResult: async () => ({
|
|
22957
|
+
kind: "unreadable",
|
|
22958
|
+
reason: "store-read-failed",
|
|
22959
|
+
error: "settings store not wired"
|
|
22960
|
+
}),
|
|
22961
|
+
writeAddonStore: refuse,
|
|
22962
|
+
getSection: refuse,
|
|
22963
|
+
setSection: refuse
|
|
22964
|
+
};
|
|
22965
|
+
}
|
|
22966
|
+
const door = {
|
|
22967
|
+
query: async (input) => {
|
|
22968
|
+
await before();
|
|
22969
|
+
return client.query.query(input);
|
|
22970
|
+
},
|
|
22971
|
+
get: async (input) => {
|
|
22972
|
+
await before();
|
|
22973
|
+
return client.get.query(input);
|
|
22974
|
+
},
|
|
22975
|
+
set: async (input) => {
|
|
22976
|
+
await before();
|
|
22977
|
+
return client.set.mutate(input);
|
|
22978
|
+
},
|
|
22979
|
+
transact: async (input) => {
|
|
22980
|
+
await before();
|
|
22981
|
+
return client.transact.mutate(input);
|
|
22982
|
+
}
|
|
22983
|
+
};
|
|
22984
|
+
const store = require_builtins_core_blocks_core_blocks_addon.createAddonSettingsStore(door, addonId);
|
|
22985
|
+
const sections = createSectionsStore(door);
|
|
22986
|
+
return {
|
|
22987
|
+
readAddonStore: async () => {
|
|
22988
|
+
const read = await store.read();
|
|
22989
|
+
switch (read.kind) {
|
|
22990
|
+
case "present": return { ...read.value };
|
|
22991
|
+
case "absent": return {};
|
|
22992
|
+
case "unreadable": throw new Error(`settings of "${addonId}" could not be read: ${read.error}`);
|
|
22993
|
+
}
|
|
22994
|
+
},
|
|
22995
|
+
readAddonStoreResult: async () => {
|
|
22996
|
+
const read = await store.read();
|
|
22997
|
+
switch (read.kind) {
|
|
22998
|
+
case "present": return {
|
|
22999
|
+
kind: "value",
|
|
23000
|
+
value: { ...read.value }
|
|
23001
|
+
};
|
|
23002
|
+
case "absent": return { kind: "missing" };
|
|
23003
|
+
case "unreadable": return {
|
|
23004
|
+
kind: "failed",
|
|
23005
|
+
error: read.error
|
|
23006
|
+
};
|
|
23007
|
+
}
|
|
23008
|
+
},
|
|
23009
|
+
readAddonStoreKeyResult: async (key) => {
|
|
23010
|
+
const read = await store.readKey(key);
|
|
23011
|
+
return read.kind === "unreadable" ? {
|
|
23012
|
+
kind: "unreadable",
|
|
23013
|
+
reason: "store-read-failed",
|
|
23014
|
+
error: read.error
|
|
23015
|
+
} : read;
|
|
23016
|
+
},
|
|
23017
|
+
writeAddonStore: (patch) => store.merge(patch),
|
|
23018
|
+
getSection: (section) => sections.read(section),
|
|
23019
|
+
setSection: (section, patch) => sections.write(section, patch)
|
|
23020
|
+
};
|
|
23021
|
+
}
|
|
23022
|
+
//#endregion
|
|
23023
|
+
//#region src/kernel/legacy-blob-guard.ts
|
|
23024
|
+
/** Root keys retired long before D870: they never count as "settings still to migrate". */
|
|
23025
|
+
var OWN_RETIRED_ROOT_KEYS = new Set([
|
|
23026
|
+
"recordingConfigs",
|
|
23027
|
+
"recordingExports",
|
|
23028
|
+
"recordingOpsLog",
|
|
23029
|
+
"probeSnapshots",
|
|
23030
|
+
"rtspTokens",
|
|
23031
|
+
"profileMap"
|
|
23032
|
+
]);
|
|
23033
|
+
/** Is `key` of `addonId` a root key that was retired (and must not be migrated)? */
|
|
23034
|
+
function isRetiredRootKey(addonId, key) {
|
|
23035
|
+
if (OWN_RETIRED_ROOT_KEYS.has(key)) return true;
|
|
23036
|
+
return require_retired_settings_keys.RETIRED_SETTINGS_KEYS.some((spec) => spec.namespace === addonId && (spec.keys.includes(key) || spec.perNodeKeys.some((k) => key === k || key.startsWith(`${k}@`))));
|
|
23037
|
+
}
|
|
23038
|
+
var UnmigratedSettingsError = class extends Error {
|
|
23039
|
+
constructor(addonId, what) {
|
|
23040
|
+
super(`addon "${addonId}" refuses to start: ${what} - the settings it had are still in the per-addon tables and the standard store holds none. Run scripts/hub/migrate-device-store.ts --copy --apply (D870) before starting it.`);
|
|
23041
|
+
this.name = "UnmigratedSettingsError";
|
|
23042
|
+
}
|
|
23043
|
+
};
|
|
23044
|
+
async function countOf(client, input) {
|
|
23045
|
+
const n = await client.count.query(input);
|
|
23046
|
+
if (typeof n !== "number") throw new Error("settings store answered a non-number to a count");
|
|
23047
|
+
return n;
|
|
23048
|
+
}
|
|
23049
|
+
var LEGACY_PAGE = 500;
|
|
23050
|
+
var LEGACY_PAGE_BUDGET = 200;
|
|
23051
|
+
var ID_TAIL = "";
|
|
23052
|
+
function isRecord(value) {
|
|
23053
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23054
|
+
}
|
|
23055
|
+
/**
|
|
23056
|
+
* Does the addon's per-device blob table hold a row the copy would actually
|
|
23057
|
+
* migrate - a positive numeric id with a non-empty record? An addon whose rows
|
|
23058
|
+
* the copy skips anyway must not be refused for them.
|
|
23059
|
+
*/
|
|
23060
|
+
async function hasMigratableDeviceRow(client, addonId) {
|
|
23061
|
+
let cursor = "";
|
|
23062
|
+
for (let page = 0; page < LEGACY_PAGE_BUDGET; page++) {
|
|
23063
|
+
const rows = await client.query.query({
|
|
23064
|
+
namespace: addonId,
|
|
23065
|
+
collection: "addon-devices",
|
|
23066
|
+
filter: {
|
|
23067
|
+
whereBetween: { id: [cursor, ID_TAIL] },
|
|
23068
|
+
orderBy: {
|
|
23069
|
+
field: "id",
|
|
23070
|
+
direction: "asc"
|
|
23071
|
+
},
|
|
23072
|
+
limit: LEGACY_PAGE
|
|
23073
|
+
}
|
|
23074
|
+
});
|
|
23075
|
+
if (!Array.isArray(rows)) throw new Error("settings store answered a non-list to a query");
|
|
23076
|
+
let lastId = cursor;
|
|
23077
|
+
for (const row of rows) {
|
|
23078
|
+
if (!isRecord(row) || typeof row["id"] !== "string") continue;
|
|
23079
|
+
lastId = row["id"];
|
|
23080
|
+
const id = Number(row["id"]);
|
|
23081
|
+
const data = row["data"];
|
|
23082
|
+
if (Number.isInteger(id) && id > 0 && isRecord(data) && Object.keys(data).length > 0) return true;
|
|
23083
|
+
}
|
|
23084
|
+
if (rows.length < LEGACY_PAGE || lastId === cursor) return false;
|
|
23085
|
+
cursor = lastId;
|
|
23086
|
+
}
|
|
23087
|
+
throw new Error(`addon "${addonId}": more legacy blob pages than the guard will read`);
|
|
23088
|
+
}
|
|
23089
|
+
/**
|
|
23090
|
+
* THROWS when the addon has blob settings and the standard store has none of
|
|
23091
|
+
* its own. A store that cannot be asked also throws: it is not "no blobs".
|
|
23092
|
+
*/
|
|
23093
|
+
async function assertLegacyBlobsMigrated(client, addonId, logger) {
|
|
23094
|
+
const legacyDevices = await hasMigratableDeviceRow(client, addonId);
|
|
23095
|
+
const root = await client.get.query({
|
|
23096
|
+
namespace: addonId,
|
|
23097
|
+
collection: "addon-settings",
|
|
23098
|
+
key: "root"
|
|
23099
|
+
});
|
|
23100
|
+
const legacySettings = isRecord(root) && Object.keys(root).some((key) => !isRetiredRootKey(addonId, key));
|
|
23101
|
+
if (!legacyDevices && !legacySettings) return;
|
|
23102
|
+
const rangeOf = (prefix) => {
|
|
23103
|
+
const range = require_prefix_range.prefixRange(prefix);
|
|
23104
|
+
if (range === null) throw new Error(`no id range for addon "${addonId}"`);
|
|
23105
|
+
return { whereBetween: { id: [range.lo, range.hi] } };
|
|
23106
|
+
};
|
|
23107
|
+
const newDevices = await countOf(client, {
|
|
23108
|
+
collection: "addon-device-settings",
|
|
23109
|
+
filter: rangeOf(`${addonId}:`)
|
|
23110
|
+
});
|
|
23111
|
+
const newSettings = await countOf(client, {
|
|
23112
|
+
collection: "addon-settings",
|
|
23113
|
+
filter: rangeOf(`${addonId}.`)
|
|
23114
|
+
});
|
|
23115
|
+
const problems = [];
|
|
23116
|
+
if (legacyDevices && newDevices === 0) problems.push("per-device blobs");
|
|
23117
|
+
if (legacySettings && newSettings === 0) problems.push("an addon settings blob");
|
|
23118
|
+
if (problems.length === 0) return;
|
|
23119
|
+
const error = new UnmigratedSettingsError(addonId, problems.join(" and "));
|
|
23120
|
+
logger.error(error.message, { meta: {
|
|
23121
|
+
addonId,
|
|
23122
|
+
newDevices,
|
|
23123
|
+
newSettings
|
|
23124
|
+
} });
|
|
23125
|
+
throw error;
|
|
23126
|
+
}
|
|
23127
|
+
//#endregion
|
|
23128
|
+
//#region src/kernel/moleculer/runner-device-settings.ts
|
|
23129
|
+
/**
|
|
23130
|
+
* The forked runner's `ctx.settings` device methods (D870).
|
|
23131
|
+
*
|
|
23132
|
+
* A runner does not hold the settings door; it holds a tRPC client of the hub's
|
|
23133
|
+
* `settings-store` cap. This file adapts that client to the door slice the
|
|
23134
|
+
* per-device store needs and hands back the five view methods — the SAME module
|
|
23135
|
+
* hub-main's view uses, so a device's settings are one set of rows whichever
|
|
23136
|
+
* process asks. Before D870 the runner kept its own `<addonId>:addon-devices`
|
|
23137
|
+
* blob per device; a hub-side write to the rows was invisible to it.
|
|
23138
|
+
*/
|
|
23139
|
+
/** The door slice the per-device store needs, over a tRPC client. */
|
|
23140
|
+
function doorOverClient(client, before = async () => {}) {
|
|
23141
|
+
return {
|
|
23142
|
+
query: async (input) => {
|
|
23143
|
+
await before();
|
|
23144
|
+
return client.query.query(input);
|
|
23145
|
+
},
|
|
23146
|
+
transact: async (input) => {
|
|
23147
|
+
await before();
|
|
23148
|
+
return client.transact.mutate(input);
|
|
23149
|
+
},
|
|
23150
|
+
deleteWhere: async (input) => {
|
|
23151
|
+
await before();
|
|
23152
|
+
return client.deleteWhere.mutate(input);
|
|
23153
|
+
}
|
|
23154
|
+
};
|
|
23155
|
+
}
|
|
23156
|
+
/**
|
|
23157
|
+
* @param client the hub's `settingsStore` client, or `undefined` when the
|
|
23158
|
+
* runner has none — every call then THROWS: a store that could not be asked
|
|
23159
|
+
* is not an empty one.
|
|
23160
|
+
*/
|
|
23161
|
+
function createRunnerDeviceSettings(client, addonId, before = async () => {}) {
|
|
23162
|
+
if (client === void 0) {
|
|
23163
|
+
const refuse = () => {
|
|
23164
|
+
throw new Error(`device settings of "${addonId}": settings store not wired`);
|
|
23165
|
+
};
|
|
23166
|
+
return {
|
|
23167
|
+
readDeviceStore: async () => refuse(),
|
|
23168
|
+
readDeviceStoreBatch: async () => refuse(),
|
|
23169
|
+
listDeviceStoreIds: async () => refuse(),
|
|
23170
|
+
writeDeviceStore: async () => refuse(),
|
|
23171
|
+
clearDeviceStore: async () => refuse()
|
|
23172
|
+
};
|
|
23173
|
+
}
|
|
23174
|
+
const store = require_builtins_device_manager_device_manager_addon.createDeviceSettingsStore(doorOverClient(client, before), addonId);
|
|
23175
|
+
return {
|
|
23176
|
+
readDeviceStore: (deviceId) => require_builtins_device_manager_device_manager_addon.readDeviceOrThrow(store, deviceId),
|
|
23177
|
+
readDeviceStoreBatch: (ids) => store.readBatch(ids),
|
|
23178
|
+
listDeviceStoreIds: () => store.listIds(),
|
|
23179
|
+
writeDeviceStore: (deviceId, patch) => store.merge(deviceId, patch),
|
|
23180
|
+
clearDeviceStore: (deviceId) => store.clear(deviceId)
|
|
23181
|
+
};
|
|
23182
|
+
}
|
|
23183
|
+
//#endregion
|
|
23148
23184
|
//#region src/kernel/moleculer/addon-context-factory.ts
|
|
23149
23185
|
/** This runtime's node id, regardless of mode (broker.nodeID or the UDS child nodeId). */
|
|
23150
23186
|
function runtimeNodeId(runtime) {
|
|
@@ -23411,20 +23447,6 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
23411
23447
|
}
|
|
23412
23448
|
return r.kind === "value" ? r.value : {};
|
|
23413
23449
|
};
|
|
23414
|
-
/** Read-modify-write that REFUSES to write when the read failed. */
|
|
23415
|
-
const mergeBlob = async (coll, key, patch, namespace) => {
|
|
23416
|
-
await readModifyWrite({
|
|
23417
|
-
read: () => readBlobResult(coll, key, namespace),
|
|
23418
|
-
write: (next) => writeBlob(coll, key, next, namespace),
|
|
23419
|
-
patch,
|
|
23420
|
-
onRefused: (error) => {
|
|
23421
|
-
scopedLogger.error(`settings merge REFUSED (${coll}/${key}) — read failed, refusing to overwrite with a partial blob`, { meta: {
|
|
23422
|
-
error,
|
|
23423
|
-
patchKeys: Object.keys(patch)
|
|
23424
|
-
} });
|
|
23425
|
-
}
|
|
23426
|
-
});
|
|
23427
|
-
};
|
|
23428
23450
|
const writeBlob = async (coll, key, value, namespace) => {
|
|
23429
23451
|
if (!settingsApi) return;
|
|
23430
23452
|
try {
|
|
@@ -23447,38 +23469,24 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
23447
23469
|
} });
|
|
23448
23470
|
}
|
|
23449
23471
|
};
|
|
23450
|
-
const
|
|
23451
|
-
|
|
23452
|
-
|
|
23453
|
-
const
|
|
23454
|
-
|
|
23455
|
-
|
|
23456
|
-
|
|
23457
|
-
|
|
23458
|
-
|
|
23472
|
+
const deviceSettingsClient = Reflect.get(api, "settingsStore");
|
|
23473
|
+
let blobsChecked = null;
|
|
23474
|
+
const bootGuard = () => {
|
|
23475
|
+
const client = deviceSettingsClient;
|
|
23476
|
+
if (client === void 0) return Promise.resolve();
|
|
23477
|
+
blobsChecked ??= assertLegacyBlobsMigrated({
|
|
23478
|
+
get: { query: (input) => client.get.query(input) },
|
|
23479
|
+
query: { query: (input) => client.query.query(input) },
|
|
23480
|
+
count: { query: (input) => client.count.query(input) }
|
|
23481
|
+
}, addonId, scopedLogger).catch((err) => {
|
|
23482
|
+
blobsChecked = null;
|
|
23483
|
+
throw err;
|
|
23484
|
+
});
|
|
23485
|
+
return blobsChecked;
|
|
23459
23486
|
};
|
|
23460
23487
|
const settingsView = {
|
|
23461
|
-
|
|
23462
|
-
|
|
23463
|
-
},
|
|
23464
|
-
readAddonStoreResult: () => readBlobResult("addon-settings", "root", addonId),
|
|
23465
|
-
async writeAddonStore(patch) {
|
|
23466
|
-
return serializeRmw("addon-settings", "root", async () => {
|
|
23467
|
-
await mergeBlob("addon-settings", "root", patch, addonId);
|
|
23468
|
-
});
|
|
23469
|
-
},
|
|
23470
|
-
async readDeviceStore(deviceId) {
|
|
23471
|
-
return readBlob("addon-devices", String(deviceId), addonId);
|
|
23472
|
-
},
|
|
23473
|
-
async writeDeviceStore(deviceId, patch) {
|
|
23474
|
-
const key = String(deviceId);
|
|
23475
|
-
return serializeRmw("addon-devices", key, async () => {
|
|
23476
|
-
await mergeBlob("addon-devices", key, patch, addonId);
|
|
23477
|
-
});
|
|
23478
|
-
},
|
|
23479
|
-
async clearDeviceStore(deviceId) {
|
|
23480
|
-
await writeBlob("addon-devices", String(deviceId), {}, addonId);
|
|
23481
|
-
},
|
|
23488
|
+
...createRunnerAddonSettings(deviceSettingsClient, addonId, bootGuard),
|
|
23489
|
+
...createRunnerDeviceSettings(deviceSettingsClient, addonId, bootGuard),
|
|
23482
23490
|
async readDeviceRuntimeState(deviceId) {
|
|
23483
23491
|
return readBlob("device-runtime-state", String(deviceId));
|
|
23484
23492
|
},
|
|
@@ -23487,14 +23495,6 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
23487
23495
|
},
|
|
23488
23496
|
async clearDeviceRuntimeState(deviceId) {
|
|
23489
23497
|
await writeBlob("device-runtime-state", String(deviceId), {});
|
|
23490
|
-
},
|
|
23491
|
-
async getSection(section) {
|
|
23492
|
-
return readBlob("sections", section);
|
|
23493
|
-
},
|
|
23494
|
-
async setSection(section, patch) {
|
|
23495
|
-
return serializeRmw("sections", section, async () => {
|
|
23496
|
-
await mergeBlob("sections", section, patch);
|
|
23497
|
-
});
|
|
23498
23498
|
}
|
|
23499
23499
|
};
|
|
23500
23500
|
const readinessRegistry = () => runtime.mode === "broker" ? getOrInitReadinessRegistry(runtime.broker, scopedEventBus, scopedLogger) : getOrInitReadinessRegistryForClient(runtime.client, scopedEventBus, scopedLogger, nodeId);
|