@camstack/system 1.1.52 → 1.1.53
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 +2 -2
- package/dist/addon-runner.mjs +2 -2
- 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/device-manager/device-linked-devices.d.ts +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +1595 -1585
- package/dist/builtins/device-manager/device-manager.addon.mjs +1595 -1585
- 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/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/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +11 -3
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +11 -3
- 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-BduBhv6N.js → dist-BNro5CRu.js} +92 -16
- package/dist/{dist-B_PSvr-m.mjs → dist-D7_69kDt.mjs} +92 -16
- package/dist/index.js +22 -5
- package/dist/index.mjs +22 -6
- package/dist/kernel/index.d.ts +1 -1
- package/dist/kernel/moleculer/readiness-context.d.ts +12 -8
- package/dist/kernel/moleculer/readiness-service.d.ts +16 -1
- package/dist/{manifest-python-deps-CPAMH5gV.js → manifest-python-deps-DszE7bYn.js} +35 -9
- package/dist/{manifest-python-deps-CIyvuHAB.mjs → manifest-python-deps-EM2H-jjS.mjs} +35 -9
- package/package.json +1 -1
|
@@ -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-BNro5CRu.js");
|
|
7
7
|
//#region src/builtins/doorbell/trigger-engine.ts
|
|
8
8
|
var DOORBELL_TRIGGERS = [
|
|
9
9
|
"contact-open",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as doorbellCapability, _t as createEvent, it as BaseAddon, kt as EventCategory, rt as errMsg, ut as DeviceType } from "../../dist-
|
|
1
|
+
import { C as doorbellCapability, _t as createEvent, it as BaseAddon, kt as EventCategory, rt as errMsg, ut as DeviceType } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
//#region src/builtins/doorbell/trigger-engine.ts
|
|
3
3
|
var DOORBELL_TRIGGERS = [
|
|
4
4
|
"contact-open",
|
|
@@ -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-BNro5CRu.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 { L as logDestinationCapability, it as BaseAddon } from "../../dist-
|
|
1
|
+
import { L as logDestinationCapability, it as BaseAddon } from "../../dist-D7_69kDt.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;
|
|
@@ -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-BNro5CRu.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,4 +1,4 @@
|
|
|
1
|
-
import { _ as authProviderCapability, d as UserRecordSchema, it as BaseAddon, l as ScopedTokenSchema, r as ApiKeyRecordSchema, tt as userManagementCapability } from "../../dist-
|
|
1
|
+
import { _ as authProviderCapability, d as UserRecordSchema, it as BaseAddon, l as ScopedTokenSchema, r as ApiKeyRecordSchema, tt as userManagementCapability } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as crypto$1 from "node:crypto";
|
|
4
4
|
import { randomUUID } from "node: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-BNro5CRu.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 { I as localNetworkCapability, it as BaseAddon, kt as EventCategory } from "../../dist-
|
|
1
|
+
import { I as localNetworkCapability, it as BaseAddon, kt as EventCategory } from "../../dist-D7_69kDt.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
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BNro5CRu.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 { _t as createEvent, it as BaseAddon, kt as EventCategory, z as metricsProviderCapability } from "../../dist-
|
|
1
|
+
import { _t as createEvent, it as BaseAddon, kt as EventCategory, z as metricsProviderCapability } from "../../dist-D7_69kDt.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-BNro5CRu.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 { W as platformProbeCapability, it as BaseAddon, kt as EventCategory, q as scoreRuntimes, rt as errMsg, yt as emitReadiness } from "../../dist-
|
|
1
|
+
import { W as platformProbeCapability, it as BaseAddon, kt as EventCategory, q as scoreRuntimes, rt as errMsg, yt as emitReadiness } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { execFile } 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
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BNro5CRu.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 { it as BaseAddon, kt as EventCategory } from "../../dist-
|
|
1
|
+
import { it as BaseAddon, kt as EventCategory } from "../../dist-D7_69kDt.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-BNro5CRu.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 { $ as streamQualityLabel, B as nodePin, X as snapshotCapability, ct as DeviceFeature, it as BaseAddon, rt as errMsg, ut as DeviceType } from "../../dist-
|
|
1
|
+
import { $ as streamQualityLabel, B as nodePin, X as snapshotCapability, ct as DeviceFeature, it as BaseAddon, rt as errMsg, ut as DeviceType } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import { execFile } 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-BNro5CRu.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { O as filesystemBrowseCapability, Q as storageProviderCapability, it as BaseAddon } from "../../dist-
|
|
1
|
+
import { O as filesystemBrowseCapability, Q as storageProviderCapability, it as BaseAddon } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-BNro5CRu.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let better_sqlite3 = require("better-sqlite3");
|
|
9
9
|
better_sqlite3 = require_chunk.__toESM(better_sqlite3);
|
|
@@ -510,10 +510,18 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
|
|
|
510
510
|
if (existingCols.length > 0) {
|
|
511
511
|
const existingNames = new Set(existingCols.map((c) => c.name));
|
|
512
512
|
const declaredNames = new Set(schema.columns.map((c) => c.name));
|
|
513
|
-
const
|
|
514
|
-
|
|
513
|
+
const missingCols = schema.columns.filter((c) => !existingNames.has(c.name));
|
|
514
|
+
const isLegacyKv = existingCols.length <= 2 && existingNames.has("data") && !declaredNames.has("data");
|
|
515
|
+
const addableInPlace = (col) => !col.primaryKey && !col.unique && (!col.notNull || col.defaultValue !== void 0);
|
|
516
|
+
if (isLegacyKv || missingCols.some((c) => !addableInPlace(c))) try {
|
|
515
517
|
this.getDb().exec(`DROP TABLE "${table}"`);
|
|
516
518
|
} catch {}
|
|
519
|
+
else if (missingCols.length > 0) for (const col of missingCols) {
|
|
520
|
+
const parts = [`"${col.name}" ${col.type}`];
|
|
521
|
+
if (col.notNull) parts.push("NOT NULL");
|
|
522
|
+
if (col.defaultValue !== void 0) parts.push(`DEFAULT ${typeof col.defaultValue === "string" ? `'${col.defaultValue}'` : col.defaultValue === null ? "NULL" : String(col.defaultValue)}`);
|
|
523
|
+
this.getDb().exec(`ALTER TABLE "${table}" ADD COLUMN ${parts.join(" ")}`);
|
|
524
|
+
}
|
|
517
525
|
}
|
|
518
526
|
this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (${colDefs.join(", ")})`);
|
|
519
527
|
if (schema.indexes) for (const idx of schema.indexes) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Y as settingsStoreCapability, it as BaseAddon, mt as asJsonObject, rt as errMsg, s as RUNTIME_DEFAULTS, wt as parseJsonUnknown } from "../../dist-
|
|
1
|
+
import { Y as settingsStoreCapability, it as BaseAddon, mt as asJsonObject, rt as errMsg, s as RUNTIME_DEFAULTS, wt as parseJsonUnknown } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
4
|
//#region src/builtins/sqlite-storage/sqlite-settings-backend.ts
|
|
@@ -504,10 +504,18 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
|
|
|
504
504
|
if (existingCols.length > 0) {
|
|
505
505
|
const existingNames = new Set(existingCols.map((c) => c.name));
|
|
506
506
|
const declaredNames = new Set(schema.columns.map((c) => c.name));
|
|
507
|
-
const
|
|
508
|
-
|
|
507
|
+
const missingCols = schema.columns.filter((c) => !existingNames.has(c.name));
|
|
508
|
+
const isLegacyKv = existingCols.length <= 2 && existingNames.has("data") && !declaredNames.has("data");
|
|
509
|
+
const addableInPlace = (col) => !col.primaryKey && !col.unique && (!col.notNull || col.defaultValue !== void 0);
|
|
510
|
+
if (isLegacyKv || missingCols.some((c) => !addableInPlace(c))) try {
|
|
509
511
|
this.getDb().exec(`DROP TABLE "${table}"`);
|
|
510
512
|
} catch {}
|
|
513
|
+
else if (missingCols.length > 0) for (const col of missingCols) {
|
|
514
|
+
const parts = [`"${col.name}" ${col.type}`];
|
|
515
|
+
if (col.notNull) parts.push("NOT NULL");
|
|
516
|
+
if (col.defaultValue !== void 0) parts.push(`DEFAULT ${typeof col.defaultValue === "string" ? `'${col.defaultValue}'` : col.defaultValue === null ? "NULL" : String(col.defaultValue)}`);
|
|
517
|
+
this.getDb().exec(`ALTER TABLE "${table}" ADD COLUMN ${parts.join(" ")}`);
|
|
518
|
+
}
|
|
511
519
|
}
|
|
512
520
|
this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (${colDefs.join(", ")})`);
|
|
513
521
|
if (schema.indexes) for (const idx of schema.indexes) {
|
|
@@ -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-BNro5CRu.js");
|
|
7
7
|
let node_path = require("node:path");
|
|
8
8
|
node_path = require_chunk.__toESM(node_path);
|
|
9
9
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as parseJsonObject, Z as storageCapability, it as BaseAddon, u as StorageLocationTypeSchema } from "../../dist-
|
|
1
|
+
import { Ct as parseJsonObject, Z as storageCapability, it as BaseAddon, u as StorageLocationTypeSchema } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import * as path$1 from "node:path";
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import { buildStorageLocationRegistry } from "@camstack/system";
|
|
@@ -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-BNro5CRu.js");
|
|
7
7
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
8
8
|
/**
|
|
9
9
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { it as BaseAddon, rt as errMsg, xt as hydrateSchema } from "../../dist-
|
|
1
|
+
import { it as BaseAddon, rt as errMsg, xt as hydrateSchema } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
//#region src/builtins/system-config/system-config.addon.ts
|
|
3
3
|
/**
|
|
4
4
|
* Built-in `system-config` addon — Phase 4 of the settings redesign.
|
|
@@ -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-BNro5CRu.js");
|
|
7
7
|
const require_formatter = require("../../formatter-DqAKDlvN.js");
|
|
8
8
|
let node_path = require("node:path");
|
|
9
9
|
node_path = require_chunk.__toESM(node_path);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as logDestinationCapability, it as BaseAddon } from "../../dist-
|
|
1
|
+
import { L as logDestinationCapability, it as BaseAddon } from "../../dist-D7_69kDt.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
4
|
import path from "node:path";
|
|
@@ -1802,6 +1802,19 @@ function scopeKey(scope) {
|
|
|
1802
1802
|
case "device": return `device:${scope.deviceId}`;
|
|
1803
1803
|
}
|
|
1804
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Producer transition order within ONE generation: `starting → ready →
|
|
1807
|
+
* down`. Used by `hydrate` to accept only forward moves for
|
|
1808
|
+
* same-generation snapshot records (a snapshot can be ahead of a lost
|
|
1809
|
+
* delta, never behind it, within one producer generation).
|
|
1810
|
+
*/
|
|
1811
|
+
function stateRank(state) {
|
|
1812
|
+
switch (state) {
|
|
1813
|
+
case "starting": return 0;
|
|
1814
|
+
case "ready": return 1;
|
|
1815
|
+
case "down": return 2;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1805
1818
|
function scopesEqual(a, b) {
|
|
1806
1819
|
if (a.type !== b.type) return false;
|
|
1807
1820
|
if (a.type === "global" || b.type === "global") return true;
|
|
@@ -1852,43 +1865,70 @@ var ReadinessRegistry = class {
|
|
|
1852
1865
|
return Array.from(this.snapshot.values());
|
|
1853
1866
|
}
|
|
1854
1867
|
/**
|
|
1855
|
-
* Hydrate the snapshot from an authoritative source
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
1860
|
-
*
|
|
1868
|
+
* Hydrate the snapshot from an authoritative source — a RECONCILE, not
|
|
1869
|
+
* an add-only merge (D8: readiness events are telemetry and may be
|
|
1870
|
+
* lost; the on-reconnect/periodic snapshot re-pull is the repair path,
|
|
1871
|
+
* so it MUST be able to advance a stale record — the 2026-07-17
|
|
1872
|
+
* "still awaiting platform-probe forever" wedge was this method
|
|
1873
|
+
* skipping every already-seen key).
|
|
1874
|
+
*
|
|
1875
|
+
* - **Unseen keys** are added (epoch 1 — mirrors the value the
|
|
1876
|
+
* consumer would have derived from the first observed `ready`).
|
|
1877
|
+
* - **Existing keys** are UPDATED when the authoritative record
|
|
1878
|
+
* carries a different generation (producer restart / synthetic-down
|
|
1879
|
+
* superseded — epoch bumps on a new-generation `ready`, mirroring
|
|
1880
|
+
* `ingest`), or a FORWARD state move within the same generation
|
|
1881
|
+
* (`starting → ready → down`; within one producer generation a
|
|
1882
|
+
* snapshot can only be ahead of a lost delta, never behind it).
|
|
1883
|
+
* - **Never a same-generation regress** — a snapshot pulled just
|
|
1884
|
+
* before a live delta landed must not un-ready a record (the next
|
|
1885
|
+
* reconcile round converges it anyway).
|
|
1886
|
+
* - **Never a record this process is authoritative for** — node-scoped
|
|
1887
|
+
* records for our own nodeId, and device/global records we emitted,
|
|
1888
|
+
* keep local truth (an authority's stale copy of OUR record must
|
|
1889
|
+
* not regress us).
|
|
1861
1890
|
*
|
|
1862
|
-
*
|
|
1863
|
-
*
|
|
1864
|
-
*
|
|
1865
|
-
* first `ready` event directly.
|
|
1891
|
+
* Every applied entry dispatches a one-shot transition to matching
|
|
1892
|
+
* subscriptions so pending `awaitReady` callers unblock without having
|
|
1893
|
+
* to wait for a fresh event.
|
|
1866
1894
|
*/
|
|
1867
1895
|
hydrate(records) {
|
|
1868
1896
|
const now = this.now();
|
|
1869
1897
|
for (const record of records) {
|
|
1870
1898
|
const key = readinessKey(record.capName, record.scope);
|
|
1871
|
-
|
|
1899
|
+
const prev = this.snapshot.get(key) ?? null;
|
|
1900
|
+
let epoch;
|
|
1901
|
+
let durationInPrevState;
|
|
1902
|
+
if (prev !== null) {
|
|
1903
|
+
if (this.isLocallyAuthoritative(prev)) continue;
|
|
1904
|
+
const sameGeneration = prev.generation === record.generation;
|
|
1905
|
+
if (sameGeneration && stateRank(record.state) <= stateRank(prev.state)) continue;
|
|
1906
|
+
epoch = !sameGeneration && record.state === "ready" ? prev.epoch + 1 : prev.epoch;
|
|
1907
|
+
durationInPrevState = Math.max(0, now - prev.lastChange);
|
|
1908
|
+
} else {
|
|
1909
|
+
epoch = 1;
|
|
1910
|
+
durationInPrevState = 0;
|
|
1911
|
+
}
|
|
1872
1912
|
const hydrated = {
|
|
1873
1913
|
capName: record.capName,
|
|
1874
1914
|
scope: record.scope,
|
|
1875
1915
|
state: record.state,
|
|
1876
1916
|
generation: record.generation,
|
|
1877
|
-
epoch
|
|
1917
|
+
epoch,
|
|
1878
1918
|
lastChange: now,
|
|
1879
1919
|
sourceNodeId: record.sourceNodeId
|
|
1880
1920
|
};
|
|
1881
1921
|
this.snapshot.set(key, hydrated);
|
|
1882
|
-
if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`);
|
|
1922
|
+
if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
|
|
1883
1923
|
const transition = {
|
|
1884
1924
|
capName: record.capName,
|
|
1885
1925
|
scope: record.scope,
|
|
1886
1926
|
state: record.state,
|
|
1887
|
-
epoch
|
|
1927
|
+
epoch,
|
|
1888
1928
|
generation: record.generation,
|
|
1889
1929
|
sourceNodeId: "hydrated",
|
|
1890
1930
|
ts: now,
|
|
1891
|
-
durationInPrevState
|
|
1931
|
+
durationInPrevState
|
|
1892
1932
|
};
|
|
1893
1933
|
for (const sub of this.subscriptions) {
|
|
1894
1934
|
if (sub.capName !== record.capName) continue;
|
|
@@ -1901,6 +1941,17 @@ var ReadinessRegistry = class {
|
|
|
1901
1941
|
}
|
|
1902
1942
|
}
|
|
1903
1943
|
}
|
|
1944
|
+
/**
|
|
1945
|
+
* True when THIS process is the origin authority for `record` — a
|
|
1946
|
+
* snapshot pulled from elsewhere must never overwrite it:
|
|
1947
|
+
* - node-scoped records whose `scope.nodeId` is our own node id
|
|
1948
|
+
* (this process — or a child bridged onto our bus — emits them);
|
|
1949
|
+
* - device/global-scoped records whose latest transition WE emitted.
|
|
1950
|
+
*/
|
|
1951
|
+
isLocallyAuthoritative(record) {
|
|
1952
|
+
if (record.scope.type === "node") return record.scope.nodeId === this.sourceNodeId;
|
|
1953
|
+
return record.sourceNodeId === this.sourceNodeId;
|
|
1954
|
+
}
|
|
1904
1955
|
/** Shallow copy of the full snapshot — mainly for diagnostics/tests. */
|
|
1905
1956
|
getAll() {
|
|
1906
1957
|
return new Map(this.snapshot);
|
|
@@ -3204,6 +3255,24 @@ var RecordingRetentionSchema = zod.z.object({
|
|
|
3204
3255
|
maxSizeGb: zod.z.number().min(0).optional()
|
|
3205
3256
|
});
|
|
3206
3257
|
/**
|
|
3258
|
+
* Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
|
|
3259
|
+
* sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
|
|
3260
|
+
* previews at. Five graduated steps; absent on a config = `standard` (the
|
|
3261
|
+
* shipped default, matching `sheet-geometry`/`sheet-composer`).
|
|
3262
|
+
*
|
|
3263
|
+
* Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
|
|
3264
|
+
* Each window's index sidecar carries its own tile dims, so a camera whose
|
|
3265
|
+
* preset changed over time renders every historical window at the dims it was
|
|
3266
|
+
* written with.
|
|
3267
|
+
*/
|
|
3268
|
+
var ScrubThumbnailPresetSchema = zod.z.enum([
|
|
3269
|
+
"minimal",
|
|
3270
|
+
"low",
|
|
3271
|
+
"standard",
|
|
3272
|
+
"high",
|
|
3273
|
+
"max"
|
|
3274
|
+
]);
|
|
3275
|
+
/**
|
|
3207
3276
|
* The full per-camera recording intent — the wire shape of a RecordingTarget.
|
|
3208
3277
|
*
|
|
3209
3278
|
* `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
|
|
@@ -3240,7 +3309,14 @@ var RecordingConfigSchema = zod.z.object({
|
|
|
3240
3309
|
* derived into bands once via `migrateConfigToBands`.
|
|
3241
3310
|
*/
|
|
3242
3311
|
bands: zod.z.array(RecordingBandSchema).optional(),
|
|
3243
|
-
retention: RecordingRetentionSchema.optional()
|
|
3312
|
+
retention: RecordingRetentionSchema.optional(),
|
|
3313
|
+
/**
|
|
3314
|
+
* Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
|
|
3315
|
+
* timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
|
|
3316
|
+
* windows only — existing sheets are immutable, and each window's index
|
|
3317
|
+
* carries its own tile dims so mixed-preset history renders correctly.
|
|
3318
|
+
*/
|
|
3319
|
+
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
3244
3320
|
});
|
|
3245
3321
|
/**
|
|
3246
3322
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -1802,6 +1802,19 @@ function scopeKey(scope) {
|
|
|
1802
1802
|
case "device": return `device:${scope.deviceId}`;
|
|
1803
1803
|
}
|
|
1804
1804
|
}
|
|
1805
|
+
/**
|
|
1806
|
+
* Producer transition order within ONE generation: `starting → ready →
|
|
1807
|
+
* down`. Used by `hydrate` to accept only forward moves for
|
|
1808
|
+
* same-generation snapshot records (a snapshot can be ahead of a lost
|
|
1809
|
+
* delta, never behind it, within one producer generation).
|
|
1810
|
+
*/
|
|
1811
|
+
function stateRank(state) {
|
|
1812
|
+
switch (state) {
|
|
1813
|
+
case "starting": return 0;
|
|
1814
|
+
case "ready": return 1;
|
|
1815
|
+
case "down": return 2;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1805
1818
|
function scopesEqual(a, b) {
|
|
1806
1819
|
if (a.type !== b.type) return false;
|
|
1807
1820
|
if (a.type === "global" || b.type === "global") return true;
|
|
@@ -1852,43 +1865,70 @@ var ReadinessRegistry = class {
|
|
|
1852
1865
|
return Array.from(this.snapshot.values());
|
|
1853
1866
|
}
|
|
1854
1867
|
/**
|
|
1855
|
-
* Hydrate the snapshot from an authoritative source
|
|
1856
|
-
*
|
|
1857
|
-
*
|
|
1858
|
-
*
|
|
1859
|
-
*
|
|
1860
|
-
*
|
|
1868
|
+
* Hydrate the snapshot from an authoritative source — a RECONCILE, not
|
|
1869
|
+
* an add-only merge (D8: readiness events are telemetry and may be
|
|
1870
|
+
* lost; the on-reconnect/periodic snapshot re-pull is the repair path,
|
|
1871
|
+
* so it MUST be able to advance a stale record — the 2026-07-17
|
|
1872
|
+
* "still awaiting platform-probe forever" wedge was this method
|
|
1873
|
+
* skipping every already-seen key).
|
|
1874
|
+
*
|
|
1875
|
+
* - **Unseen keys** are added (epoch 1 — mirrors the value the
|
|
1876
|
+
* consumer would have derived from the first observed `ready`).
|
|
1877
|
+
* - **Existing keys** are UPDATED when the authoritative record
|
|
1878
|
+
* carries a different generation (producer restart / synthetic-down
|
|
1879
|
+
* superseded — epoch bumps on a new-generation `ready`, mirroring
|
|
1880
|
+
* `ingest`), or a FORWARD state move within the same generation
|
|
1881
|
+
* (`starting → ready → down`; within one producer generation a
|
|
1882
|
+
* snapshot can only be ahead of a lost delta, never behind it).
|
|
1883
|
+
* - **Never a same-generation regress** — a snapshot pulled just
|
|
1884
|
+
* before a live delta landed must not un-ready a record (the next
|
|
1885
|
+
* reconcile round converges it anyway).
|
|
1886
|
+
* - **Never a record this process is authoritative for** — node-scoped
|
|
1887
|
+
* records for our own nodeId, and device/global records we emitted,
|
|
1888
|
+
* keep local truth (an authority's stale copy of OUR record must
|
|
1889
|
+
* not regress us).
|
|
1861
1890
|
*
|
|
1862
|
-
*
|
|
1863
|
-
*
|
|
1864
|
-
*
|
|
1865
|
-
* first `ready` event directly.
|
|
1891
|
+
* Every applied entry dispatches a one-shot transition to matching
|
|
1892
|
+
* subscriptions so pending `awaitReady` callers unblock without having
|
|
1893
|
+
* to wait for a fresh event.
|
|
1866
1894
|
*/
|
|
1867
1895
|
hydrate(records) {
|
|
1868
1896
|
const now = this.now();
|
|
1869
1897
|
for (const record of records) {
|
|
1870
1898
|
const key = readinessKey(record.capName, record.scope);
|
|
1871
|
-
|
|
1899
|
+
const prev = this.snapshot.get(key) ?? null;
|
|
1900
|
+
let epoch;
|
|
1901
|
+
let durationInPrevState;
|
|
1902
|
+
if (prev !== null) {
|
|
1903
|
+
if (this.isLocallyAuthoritative(prev)) continue;
|
|
1904
|
+
const sameGeneration = prev.generation === record.generation;
|
|
1905
|
+
if (sameGeneration && stateRank(record.state) <= stateRank(prev.state)) continue;
|
|
1906
|
+
epoch = !sameGeneration && record.state === "ready" ? prev.epoch + 1 : prev.epoch;
|
|
1907
|
+
durationInPrevState = Math.max(0, now - prev.lastChange);
|
|
1908
|
+
} else {
|
|
1909
|
+
epoch = 1;
|
|
1910
|
+
durationInPrevState = 0;
|
|
1911
|
+
}
|
|
1872
1912
|
const hydrated = {
|
|
1873
1913
|
capName: record.capName,
|
|
1874
1914
|
scope: record.scope,
|
|
1875
1915
|
state: record.state,
|
|
1876
1916
|
generation: record.generation,
|
|
1877
|
-
epoch
|
|
1917
|
+
epoch,
|
|
1878
1918
|
lastChange: now,
|
|
1879
1919
|
sourceNodeId: record.sourceNodeId
|
|
1880
1920
|
};
|
|
1881
1921
|
this.snapshot.set(key, hydrated);
|
|
1882
|
-
if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated, gen=${record.generation.slice(0, 6)})`);
|
|
1922
|
+
if (this.logger) this.logger.debug(`readiness: ${record.capName} (${scopeKey(record.scope)}) → ${record.state} (hydrated${prev !== null ? " update" : ""}, gen=${record.generation.slice(0, 6)})`);
|
|
1883
1923
|
const transition = {
|
|
1884
1924
|
capName: record.capName,
|
|
1885
1925
|
scope: record.scope,
|
|
1886
1926
|
state: record.state,
|
|
1887
|
-
epoch
|
|
1927
|
+
epoch,
|
|
1888
1928
|
generation: record.generation,
|
|
1889
1929
|
sourceNodeId: "hydrated",
|
|
1890
1930
|
ts: now,
|
|
1891
|
-
durationInPrevState
|
|
1931
|
+
durationInPrevState
|
|
1892
1932
|
};
|
|
1893
1933
|
for (const sub of this.subscriptions) {
|
|
1894
1934
|
if (sub.capName !== record.capName) continue;
|
|
@@ -1901,6 +1941,17 @@ var ReadinessRegistry = class {
|
|
|
1901
1941
|
}
|
|
1902
1942
|
}
|
|
1903
1943
|
}
|
|
1944
|
+
/**
|
|
1945
|
+
* True when THIS process is the origin authority for `record` — a
|
|
1946
|
+
* snapshot pulled from elsewhere must never overwrite it:
|
|
1947
|
+
* - node-scoped records whose `scope.nodeId` is our own node id
|
|
1948
|
+
* (this process — or a child bridged onto our bus — emits them);
|
|
1949
|
+
* - device/global-scoped records whose latest transition WE emitted.
|
|
1950
|
+
*/
|
|
1951
|
+
isLocallyAuthoritative(record) {
|
|
1952
|
+
if (record.scope.type === "node") return record.scope.nodeId === this.sourceNodeId;
|
|
1953
|
+
return record.sourceNodeId === this.sourceNodeId;
|
|
1954
|
+
}
|
|
1904
1955
|
/** Shallow copy of the full snapshot — mainly for diagnostics/tests. */
|
|
1905
1956
|
getAll() {
|
|
1906
1957
|
return new Map(this.snapshot);
|
|
@@ -3204,6 +3255,24 @@ var RecordingRetentionSchema = z.object({
|
|
|
3204
3255
|
maxSizeGb: z.number().min(0).optional()
|
|
3205
3256
|
});
|
|
3206
3257
|
/**
|
|
3258
|
+
* Scrub-thumbnail fidelity preset — the single per-camera selector bundling the
|
|
3259
|
+
* sprite tile RESOLUTION + JPEG QUALITY the recorder packs timeline-scrub
|
|
3260
|
+
* previews at. Five graduated steps; absent on a config = `standard` (the
|
|
3261
|
+
* shipped default, matching `sheet-geometry`/`sheet-composer`).
|
|
3262
|
+
*
|
|
3263
|
+
* Existing sheets are IMMUTABLE — a changed preset applies to NEW windows only.
|
|
3264
|
+
* Each window's index sidecar carries its own tile dims, so a camera whose
|
|
3265
|
+
* preset changed over time renders every historical window at the dims it was
|
|
3266
|
+
* written with.
|
|
3267
|
+
*/
|
|
3268
|
+
var ScrubThumbnailPresetSchema = z.enum([
|
|
3269
|
+
"minimal",
|
|
3270
|
+
"low",
|
|
3271
|
+
"standard",
|
|
3272
|
+
"high",
|
|
3273
|
+
"max"
|
|
3274
|
+
]);
|
|
3275
|
+
/**
|
|
3207
3276
|
* The full per-camera recording intent — the wire shape of a RecordingTarget.
|
|
3208
3277
|
*
|
|
3209
3278
|
* `mode` is the authoritative storage choice; `schedule`/`triggers`/`pre`/`post`
|
|
@@ -3240,7 +3309,14 @@ var RecordingConfigSchema = z.object({
|
|
|
3240
3309
|
* derived into bands once via `migrateConfigToBands`.
|
|
3241
3310
|
*/
|
|
3242
3311
|
bands: z.array(RecordingBandSchema).optional(),
|
|
3243
|
-
retention: RecordingRetentionSchema.optional()
|
|
3312
|
+
retention: RecordingRetentionSchema.optional(),
|
|
3313
|
+
/**
|
|
3314
|
+
* Per-camera scrub-thumbnail fidelity preset (resolution + JPEG quality for
|
|
3315
|
+
* timeline-scrub sprite previews). Absent = `standard`. Applies to NEW
|
|
3316
|
+
* windows only — existing sheets are immutable, and each window's index
|
|
3317
|
+
* carries its own tile dims so mixed-preset history renders correctly.
|
|
3318
|
+
*/
|
|
3319
|
+
scrubThumbnails: ScrubThumbnailPresetSchema.optional()
|
|
3244
3320
|
});
|
|
3245
3321
|
/**
|
|
3246
3322
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|