@camstack/system 1.2.317 → 1.2.319
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +3 -77
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +3 -77
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer-apply-waits.d.ts +21 -0
- package/dist/builtins/composer/composer-live-set.d.ts +22 -0
- package/dist/builtins/composer/composer-runtime-factory.d.ts +22 -0
- package/dist/builtins/composer/composer-seeds.d.ts +22 -0
- package/dist/builtins/composer/composer.addon.js +1905 -1809
- package/dist/builtins/composer/composer.addon.mjs +1905 -1809
- package/dist/builtins/composer/composer.d.ts +3 -15
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/shm-lock-canary.d.ts +60 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +1 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/sqlite-storage/wal-checkpoint-policy.d.ts +115 -34
- package/dist/builtins/sqlite-storage/wal-checkpointer.d.ts +6 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-backup/system-backup.service.d.ts +2 -17
- 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/{composition-sources-Dep7nBwu.js → composition-sources-BjRPhWtU.js} +1 -1
- package/dist/{composition-sources-DVClu006.mjs → composition-sources-Cg2MP-qf.mjs} +1 -1
- package/dist/{dist-Dori4pf3.mjs → dist-CQ9vXSUh.mjs} +135 -2
- package/dist/{dist-a6p0V-IL.js → dist-Dcy0gVHu.js} +135 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{retired-settings-keys-DVcKRgzm.mjs → retired-settings-keys-DNIbyfTR.mjs} +1 -1
- package/dist/{retired-settings-keys-DC1uHWPZ.js → retired-settings-keys-pzlrLUbA.js} +1 -1
- package/dist/wal-checkpoint-policy-BahrTtyD.mjs +186 -0
- package/dist/wal-checkpoint-policy-d2z2qPqx.js +233 -0
- package/dist/wal-checkpoint-worker.js +63 -57
- package/dist/wal-checkpoint-worker.mjs +63 -57
- package/package.json +1 -1
- package/dist/wal-checkpoint-policy-CAcg63o-.mjs +0 -92
- package/dist/wal-checkpoint-policy-CCJZngds.js +0 -127
|
@@ -160,20 +160,6 @@ export declare class SystemBackupService {
|
|
|
160
160
|
signal?: AbortSignal;
|
|
161
161
|
onArchiveBytes?: (bytesWritten: number) => void;
|
|
162
162
|
}): Promise<CreateArchiveResult>;
|
|
163
|
-
/**
|
|
164
|
-
* Best-effort `wal_checkpoint(TRUNCATE)` on every live SQLite database
|
|
165
|
-
* under the locations about to be archived. Discovers a DB by its
|
|
166
|
-
* `-wal` sidecar (only WAL-mode DBs have one, and only those need
|
|
167
|
-
* folding), opens a short-lived second connection — SQLite permits
|
|
168
|
-
* concurrent connections, and a checkpoint is a supported concurrent
|
|
169
|
-
* operation against the hub's live writer — checkpoints, and closes.
|
|
170
|
-
*
|
|
171
|
-
* Every failure is swallowed: a `SQLITE_BUSY`, a locked file, a
|
|
172
|
-
* missing native binding, or a non-DB `.db` file must not fail the
|
|
173
|
-
* backup. A residual WAL still restores correctly; the checkpoint is a
|
|
174
|
-
* consistency *improvement*, not a precondition.
|
|
175
|
-
*/
|
|
176
|
-
private checkpointDatabasesUnder;
|
|
177
163
|
/**
|
|
178
164
|
* without extracting payload files. Streams the tar.gz, parses the
|
|
179
165
|
* one entry we care about, then aborts.
|
|
@@ -213,9 +199,8 @@ export declare class SystemBackupService {
|
|
|
213
199
|
private getRestoreMarkerPath;
|
|
214
200
|
/**
|
|
215
201
|
* Absolute-path form of {@link isExcludedFromBackup}, anchored on
|
|
216
|
-
* this service's dataDir. Shared by the manifest walk
|
|
217
|
-
* stats
|
|
218
|
-
* filter about what a backup contains.
|
|
202
|
+
* this service's dataDir. Shared by the manifest walk and the location
|
|
203
|
+
* stats so both agree with the tar filter about what a backup contains.
|
|
219
204
|
*/
|
|
220
205
|
private exclusionPredicate;
|
|
221
206
|
}
|
|
@@ -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-Dcy0gVHu.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 { Gn as BaseAddon, Wn as errMsg, ar as hydrateSchema } from "../../dist-
|
|
1
|
+
import { Gn as BaseAddon, Wn as errMsg, ar as hydrateSchema } from "../../dist-CQ9vXSUh.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-Dcy0gVHu.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 { Gn as BaseAddon, Rt as logDestinationCapability } from "../../dist-
|
|
1
|
+
import { Gn as BaseAddon, Rt as logDestinationCapability } from "../../dist-CQ9vXSUh.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";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { In as compositionSourceKey, Wn as errMsg } from "./dist-
|
|
1
|
+
import { In as compositionSourceKey, Wn as errMsg } from "./dist-CQ9vXSUh.mjs";
|
|
2
2
|
//#region src/builtins/core-blocks/composition-sources.ts
|
|
3
3
|
/**
|
|
4
4
|
* Describe what a composition reads and writes for SAVE-time validation
|
|
@@ -20903,6 +20903,102 @@ var oauthIntegrationCapability = {
|
|
|
20903
20903
|
getDescriptor: method(z.void(), OauthIntegrationDescriptorSchema, { auth: "admin" }) }
|
|
20904
20904
|
};
|
|
20905
20905
|
/**
|
|
20906
|
+
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
20907
|
+
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
20908
|
+
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
20909
|
+
*/
|
|
20910
|
+
var MotionTriggeredBySchema = z.discriminatedUnion("kind", [z.object({ kind: z.literal("camera") }), z.object({
|
|
20911
|
+
kind: z.literal("sensor"),
|
|
20912
|
+
deviceId: z.number().int().positive()
|
|
20913
|
+
})]);
|
|
20914
|
+
Object.freeze({ kind: "camera" });
|
|
20915
|
+
var CameraSensorRefSchema = z.object({
|
|
20916
|
+
addonId: z.string().min(1),
|
|
20917
|
+
stableId: z.string().min(1)
|
|
20918
|
+
});
|
|
20919
|
+
function cameraSensorRefKey(ref) {
|
|
20920
|
+
return `${ref.addonId}/${ref.stableId}`;
|
|
20921
|
+
}
|
|
20922
|
+
/** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
|
|
20923
|
+
var CameraSensorsModeSchema = z.enum(["in-addition"]);
|
|
20924
|
+
z.object({
|
|
20925
|
+
mode: CameraSensorsModeSchema,
|
|
20926
|
+
sensors: z.array(CameraSensorRefSchema).min(1).max(8)
|
|
20927
|
+
}).superRefine((value, ctx) => {
|
|
20928
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20929
|
+
value.sensors.forEach((ref, index) => {
|
|
20930
|
+
const key = cameraSensorRefKey(ref);
|
|
20931
|
+
if (seen.has(key)) ctx.addIssue({
|
|
20932
|
+
code: "custom",
|
|
20933
|
+
path: ["sensors", index],
|
|
20934
|
+
message: `duplicate camera sensor ${key}`
|
|
20935
|
+
});
|
|
20936
|
+
seen.add(key);
|
|
20937
|
+
});
|
|
20938
|
+
});
|
|
20939
|
+
var RefFields = {
|
|
20940
|
+
addonId: z.string(),
|
|
20941
|
+
stableId: z.string()
|
|
20942
|
+
};
|
|
20943
|
+
var CameraSensorAttachmentSchema = z.discriminatedUnion("state", [
|
|
20944
|
+
z.object({
|
|
20945
|
+
...RefFields,
|
|
20946
|
+
state: z.literal("ready"),
|
|
20947
|
+
deviceId: z.number(),
|
|
20948
|
+
name: z.string(),
|
|
20949
|
+
caps: z.array(z.string()).readonly()
|
|
20950
|
+
}),
|
|
20951
|
+
z.object({
|
|
20952
|
+
...RefFields,
|
|
20953
|
+
state: z.literal("offline"),
|
|
20954
|
+
deviceId: z.number(),
|
|
20955
|
+
name: z.string(),
|
|
20956
|
+
reason: z.enum(["offline", "disabled"])
|
|
20957
|
+
}),
|
|
20958
|
+
z.object({
|
|
20959
|
+
...RefFields,
|
|
20960
|
+
state: z.literal("missing"),
|
|
20961
|
+
reason: z.string()
|
|
20962
|
+
}),
|
|
20963
|
+
z.object({
|
|
20964
|
+
...RefFields,
|
|
20965
|
+
state: z.literal("unsupported"),
|
|
20966
|
+
deviceId: z.number(),
|
|
20967
|
+
name: z.string(),
|
|
20968
|
+
reason: z.string()
|
|
20969
|
+
}),
|
|
20970
|
+
z.object({
|
|
20971
|
+
...RefFields,
|
|
20972
|
+
state: z.literal("unreadable"),
|
|
20973
|
+
reason: z.string()
|
|
20974
|
+
})
|
|
20975
|
+
]);
|
|
20976
|
+
/**
|
|
20977
|
+
* Why a configured camera forwards NOTHING from its sensors, however ready they
|
|
20978
|
+
* are (D672): it does not list `onboard` in `motionSources` (attaching a sensor
|
|
20979
|
+
* does not add it), or it has no active detection config at all (the bridge
|
|
20980
|
+
* reads the active config). Absent = forwarded, or an older node that does not
|
|
20981
|
+
* say.
|
|
20982
|
+
*/
|
|
20983
|
+
var CameraSensorsNotForwardedSchema = z.enum(["onboard-not-listed", "no-active-detection"]);
|
|
20984
|
+
var CameraSensorsStatusSchema = z.discriminatedUnion("state", [
|
|
20985
|
+
z.object({ state: z.literal("not-configured") }),
|
|
20986
|
+
z.object({
|
|
20987
|
+
state: z.literal("unreadable"),
|
|
20988
|
+
reason: z.string()
|
|
20989
|
+
}),
|
|
20990
|
+
z.object({
|
|
20991
|
+
state: z.literal("configured"),
|
|
20992
|
+
mode: CameraSensorsModeSchema,
|
|
20993
|
+
sensors: z.array(CameraSensorAttachmentSchema).readonly(),
|
|
20994
|
+
notForwarded: CameraSensorsNotForwardedSchema.optional()
|
|
20995
|
+
})
|
|
20996
|
+
]);
|
|
20997
|
+
Object.freeze({
|
|
20998
|
+
state: "unreadable",
|
|
20999
|
+
reason: "camera sensor mappings not loaded"
|
|
21000
|
+
});
|
|
21001
|
+
/**
|
|
20906
21002
|
* A model choice has a SCOPE, and a scope change has a workflow.
|
|
20907
21003
|
*
|
|
20908
21004
|
* ## The two scopes
|
|
@@ -21651,6 +21747,12 @@ var MotionEventSchema = z.object({
|
|
|
21651
21747
|
/** Populated by B5 (recording playback URL for this event). */
|
|
21652
21748
|
mediaUrl: z.string().optional(),
|
|
21653
21749
|
/**
|
|
21750
|
+
* The FIRST trigger of an `onboard` episode, written once at open (D672).
|
|
21751
|
+
* Absent = not recorded (a row written before D672, or an analyzer /
|
|
21752
|
+
* device-activity episode) — never read as "the camera" (D393).
|
|
21753
|
+
*/
|
|
21754
|
+
triggeredBy: MotionTriggeredBySchema.optional(),
|
|
21755
|
+
/**
|
|
21654
21756
|
* One row per motion EPISODE, not one per push (D475). `null` while the
|
|
21655
21757
|
* episode is still open — a further rising edge extends it in place rather
|
|
21656
21758
|
* than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
|
|
@@ -24860,6 +24962,23 @@ var MotionSourceEnum = z.enum([
|
|
|
24860
24962
|
*/
|
|
24861
24963
|
var MotionSourcesSchema = z.array(MotionSourceEnum);
|
|
24862
24964
|
/**
|
|
24965
|
+
* The operator-facing name of each MOTION source (D672: `onboard` = the
|
|
24966
|
+
* camera's sensors — its own motion plus any sensor attached to it). The wire
|
|
24967
|
+
* value stays `onboard`; only the label changed. `detectionSources: 'onboard'`
|
|
24968
|
+
* ("Camera onboard AI") and the notification source filter keep their labels.
|
|
24969
|
+
*/
|
|
24970
|
+
var MOTION_SOURCE_LABELS = {
|
|
24971
|
+
analyzer: "Frame-diff Analyzer (motion addon)",
|
|
24972
|
+
onboard: "Camera sensors",
|
|
24973
|
+
"device-activity": "Device activity (the device says it is working)"
|
|
24974
|
+
};
|
|
24975
|
+
/** The order the motion-source picker offers them in (not the enum's order). */
|
|
24976
|
+
var MOTION_SOURCE_PICKER_ORDER = [
|
|
24977
|
+
"analyzer",
|
|
24978
|
+
"onboard",
|
|
24979
|
+
"device-activity"
|
|
24980
|
+
];
|
|
24981
|
+
/**
|
|
24863
24982
|
* Which root detector a camera runs — EXACTLY ONE.
|
|
24864
24983
|
*
|
|
24865
24984
|
* Deliberately the SAME vocabulary post-analysis already tags every detection
|
|
@@ -25206,7 +25325,14 @@ var RunnerCameraConfigSchema = z.object({
|
|
|
25206
25325
|
*/
|
|
25207
25326
|
inferenceDevices: z.array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
25208
25327
|
});
|
|
25209
|
-
|
|
25328
|
+
MOTION_SOURCE_PICKER_ORDER.map((value) => value === "onboard" ? {
|
|
25329
|
+
value,
|
|
25330
|
+
label: MOTION_SOURCE_LABELS[value],
|
|
25331
|
+
description: "The camera's own motion, plus any sensor attached to it"
|
|
25332
|
+
} : {
|
|
25333
|
+
value,
|
|
25334
|
+
label: MOTION_SOURCE_LABELS[value]
|
|
25335
|
+
}), motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
25210
25336
|
/**
|
|
25211
25337
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
25212
25338
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -26002,6 +26128,11 @@ var CameraStatusSchema = z.object({
|
|
|
26002
26128
|
*/
|
|
26003
26129
|
switchedOff: z.array(CameraSwitchIdSchema).readonly(),
|
|
26004
26130
|
/**
|
|
26131
|
+
* The camera's attached sensors (D672). Optional only for a status composed
|
|
26132
|
+
* by an orchestrator older than D672; this orchestrator always sets it.
|
|
26133
|
+
*/
|
|
26134
|
+
cameraSensors: CameraSensorsStatusSchema.optional(),
|
|
26135
|
+
/**
|
|
26005
26136
|
* Stages of the bounded fan-out that were CUT SHORT — a timeout or a
|
|
26006
26137
|
* rejection — and whose block is therefore `null` because we could not
|
|
26007
26138
|
* READ it, not because there is nothing there.
|
|
@@ -35248,7 +35379,9 @@ var MotionOnMotionChangedDataSchema = z.object({
|
|
|
35248
35379
|
detected: z.boolean(),
|
|
35249
35380
|
timestamp: z.number(),
|
|
35250
35381
|
source: MotionSourceEnum,
|
|
35251
|
-
regions: z.array(MotionRegionSchema).readonly().optional()
|
|
35382
|
+
regions: z.array(MotionRegionSchema).readonly().optional(),
|
|
35383
|
+
/** Who started it (D672). Absent = the camera itself. Only `onboard` carries `sensor`. */
|
|
35384
|
+
triggeredBy: MotionTriggeredBySchema.optional()
|
|
35252
35385
|
});
|
|
35253
35386
|
var motionCapability = {
|
|
35254
35387
|
name: "motion",
|
|
@@ -20903,6 +20903,102 @@ var oauthIntegrationCapability = {
|
|
|
20903
20903
|
getDescriptor: method(zod.z.void(), OauthIntegrationDescriptorSchema, { auth: "admin" }) }
|
|
20904
20904
|
};
|
|
20905
20905
|
/**
|
|
20906
|
+
* What started a camera's onboard motion (D672). `camera` is the camera's own
|
|
20907
|
+
* firmware; `sensor` is a device the operator ATTACHED to it. Absent on the
|
|
20908
|
+
* wire means the camera: only the camera-sensors bridge ever says `sensor`.
|
|
20909
|
+
*/
|
|
20910
|
+
var MotionTriggeredBySchema = zod.z.discriminatedUnion("kind", [zod.z.object({ kind: zod.z.literal("camera") }), zod.z.object({
|
|
20911
|
+
kind: zod.z.literal("sensor"),
|
|
20912
|
+
deviceId: zod.z.number().int().positive()
|
|
20913
|
+
})]);
|
|
20914
|
+
Object.freeze({ kind: "camera" });
|
|
20915
|
+
var CameraSensorRefSchema = zod.z.object({
|
|
20916
|
+
addonId: zod.z.string().min(1),
|
|
20917
|
+
stableId: zod.z.string().min(1)
|
|
20918
|
+
});
|
|
20919
|
+
function cameraSensorRefKey(ref) {
|
|
20920
|
+
return `${ref.addonId}/${ref.stableId}`;
|
|
20921
|
+
}
|
|
20922
|
+
/** CS-1 accepts exactly one mode; CS-2 (D673) widens it to confirm / replace. */
|
|
20923
|
+
var CameraSensorsModeSchema = zod.z.enum(["in-addition"]);
|
|
20924
|
+
zod.z.object({
|
|
20925
|
+
mode: CameraSensorsModeSchema,
|
|
20926
|
+
sensors: zod.z.array(CameraSensorRefSchema).min(1).max(8)
|
|
20927
|
+
}).superRefine((value, ctx) => {
|
|
20928
|
+
const seen = /* @__PURE__ */ new Set();
|
|
20929
|
+
value.sensors.forEach((ref, index) => {
|
|
20930
|
+
const key = cameraSensorRefKey(ref);
|
|
20931
|
+
if (seen.has(key)) ctx.addIssue({
|
|
20932
|
+
code: "custom",
|
|
20933
|
+
path: ["sensors", index],
|
|
20934
|
+
message: `duplicate camera sensor ${key}`
|
|
20935
|
+
});
|
|
20936
|
+
seen.add(key);
|
|
20937
|
+
});
|
|
20938
|
+
});
|
|
20939
|
+
var RefFields = {
|
|
20940
|
+
addonId: zod.z.string(),
|
|
20941
|
+
stableId: zod.z.string()
|
|
20942
|
+
};
|
|
20943
|
+
var CameraSensorAttachmentSchema = zod.z.discriminatedUnion("state", [
|
|
20944
|
+
zod.z.object({
|
|
20945
|
+
...RefFields,
|
|
20946
|
+
state: zod.z.literal("ready"),
|
|
20947
|
+
deviceId: zod.z.number(),
|
|
20948
|
+
name: zod.z.string(),
|
|
20949
|
+
caps: zod.z.array(zod.z.string()).readonly()
|
|
20950
|
+
}),
|
|
20951
|
+
zod.z.object({
|
|
20952
|
+
...RefFields,
|
|
20953
|
+
state: zod.z.literal("offline"),
|
|
20954
|
+
deviceId: zod.z.number(),
|
|
20955
|
+
name: zod.z.string(),
|
|
20956
|
+
reason: zod.z.enum(["offline", "disabled"])
|
|
20957
|
+
}),
|
|
20958
|
+
zod.z.object({
|
|
20959
|
+
...RefFields,
|
|
20960
|
+
state: zod.z.literal("missing"),
|
|
20961
|
+
reason: zod.z.string()
|
|
20962
|
+
}),
|
|
20963
|
+
zod.z.object({
|
|
20964
|
+
...RefFields,
|
|
20965
|
+
state: zod.z.literal("unsupported"),
|
|
20966
|
+
deviceId: zod.z.number(),
|
|
20967
|
+
name: zod.z.string(),
|
|
20968
|
+
reason: zod.z.string()
|
|
20969
|
+
}),
|
|
20970
|
+
zod.z.object({
|
|
20971
|
+
...RefFields,
|
|
20972
|
+
state: zod.z.literal("unreadable"),
|
|
20973
|
+
reason: zod.z.string()
|
|
20974
|
+
})
|
|
20975
|
+
]);
|
|
20976
|
+
/**
|
|
20977
|
+
* Why a configured camera forwards NOTHING from its sensors, however ready they
|
|
20978
|
+
* are (D672): it does not list `onboard` in `motionSources` (attaching a sensor
|
|
20979
|
+
* does not add it), or it has no active detection config at all (the bridge
|
|
20980
|
+
* reads the active config). Absent = forwarded, or an older node that does not
|
|
20981
|
+
* say.
|
|
20982
|
+
*/
|
|
20983
|
+
var CameraSensorsNotForwardedSchema = zod.z.enum(["onboard-not-listed", "no-active-detection"]);
|
|
20984
|
+
var CameraSensorsStatusSchema = zod.z.discriminatedUnion("state", [
|
|
20985
|
+
zod.z.object({ state: zod.z.literal("not-configured") }),
|
|
20986
|
+
zod.z.object({
|
|
20987
|
+
state: zod.z.literal("unreadable"),
|
|
20988
|
+
reason: zod.z.string()
|
|
20989
|
+
}),
|
|
20990
|
+
zod.z.object({
|
|
20991
|
+
state: zod.z.literal("configured"),
|
|
20992
|
+
mode: CameraSensorsModeSchema,
|
|
20993
|
+
sensors: zod.z.array(CameraSensorAttachmentSchema).readonly(),
|
|
20994
|
+
notForwarded: CameraSensorsNotForwardedSchema.optional()
|
|
20995
|
+
})
|
|
20996
|
+
]);
|
|
20997
|
+
Object.freeze({
|
|
20998
|
+
state: "unreadable",
|
|
20999
|
+
reason: "camera sensor mappings not loaded"
|
|
21000
|
+
});
|
|
21001
|
+
/**
|
|
20906
21002
|
* A model choice has a SCOPE, and a scope change has a workflow.
|
|
20907
21003
|
*
|
|
20908
21004
|
* ## The two scopes
|
|
@@ -21651,6 +21747,12 @@ var MotionEventSchema = zod.z.object({
|
|
|
21651
21747
|
/** Populated by B5 (recording playback URL for this event). */
|
|
21652
21748
|
mediaUrl: zod.z.string().optional(),
|
|
21653
21749
|
/**
|
|
21750
|
+
* The FIRST trigger of an `onboard` episode, written once at open (D672).
|
|
21751
|
+
* Absent = not recorded (a row written before D672, or an analyzer /
|
|
21752
|
+
* device-activity episode) — never read as "the camera" (D393).
|
|
21753
|
+
*/
|
|
21754
|
+
triggeredBy: MotionTriggeredBySchema.optional(),
|
|
21755
|
+
/**
|
|
21654
21756
|
* One row per motion EPISODE, not one per push (D475). `null` while the
|
|
21655
21757
|
* episode is still open — a further rising edge extends it in place rather
|
|
21656
21758
|
* than inserting a new row. Set once, at close, to `lastOnAt - startedAt`
|
|
@@ -24860,6 +24962,23 @@ var MotionSourceEnum = zod.z.enum([
|
|
|
24860
24962
|
*/
|
|
24861
24963
|
var MotionSourcesSchema = zod.z.array(MotionSourceEnum);
|
|
24862
24964
|
/**
|
|
24965
|
+
* The operator-facing name of each MOTION source (D672: `onboard` = the
|
|
24966
|
+
* camera's sensors — its own motion plus any sensor attached to it). The wire
|
|
24967
|
+
* value stays `onboard`; only the label changed. `detectionSources: 'onboard'`
|
|
24968
|
+
* ("Camera onboard AI") and the notification source filter keep their labels.
|
|
24969
|
+
*/
|
|
24970
|
+
var MOTION_SOURCE_LABELS = {
|
|
24971
|
+
analyzer: "Frame-diff Analyzer (motion addon)",
|
|
24972
|
+
onboard: "Camera sensors",
|
|
24973
|
+
"device-activity": "Device activity (the device says it is working)"
|
|
24974
|
+
};
|
|
24975
|
+
/** The order the motion-source picker offers them in (not the enum's order). */
|
|
24976
|
+
var MOTION_SOURCE_PICKER_ORDER = [
|
|
24977
|
+
"analyzer",
|
|
24978
|
+
"onboard",
|
|
24979
|
+
"device-activity"
|
|
24980
|
+
];
|
|
24981
|
+
/**
|
|
24863
24982
|
* Which root detector a camera runs — EXACTLY ONE.
|
|
24864
24983
|
*
|
|
24865
24984
|
* Deliberately the SAME vocabulary post-analysis already tags every detection
|
|
@@ -25206,7 +25325,14 @@ var RunnerCameraConfigSchema = zod.z.object({
|
|
|
25206
25325
|
*/
|
|
25207
25326
|
inferenceDevices: zod.z.array(RunnerInferenceDeviceSchema).readonly().optional()
|
|
25208
25327
|
});
|
|
25209
|
-
|
|
25328
|
+
MOTION_SOURCE_PICKER_ORDER.map((value) => value === "onboard" ? {
|
|
25329
|
+
value,
|
|
25330
|
+
label: MOTION_SOURCE_LABELS[value],
|
|
25331
|
+
description: "The camera's own motion, plus any sensor attached to it"
|
|
25332
|
+
} : {
|
|
25333
|
+
value,
|
|
25334
|
+
label: MOTION_SOURCE_LABELS[value]
|
|
25335
|
+
}), motionFpsField.min, motionFpsField.max, motionFpsField.step, motionFpsField.default, detectionFpsField.min, detectionFpsField.max, detectionFpsField.step, detectionFpsField.default, motionCooldownMsField.min, motionCooldownMsField.max, motionCooldownMsField.step, motionCooldownMsField.default, maxSessionHoldMsField.min, maxSessionHoldMsField.max, maxSessionHoldMsField.step, maxSessionHoldMsField.default, audioMotionWindowMsField.min, audioMotionWindowMsField.max, audioMotionWindowMsField.step, audioMotionWindowMsField.default, preRollInferenceFramesField.min, preRollInferenceFramesField.max, preRollInferenceFramesField.step, preRollInferenceFramesField.default, occupancyRecheckSecField.min, occupancyRecheckSecField.max, occupancyRecheckSecField.step, occupancyRecheckSecField.default, occupancyRecheckFramesField.min, occupancyRecheckFramesField.max, occupancyRecheckFramesField.step, occupancyRecheckFramesField.default;
|
|
25210
25336
|
/**
|
|
25211
25337
|
* Runtime load summary returned by `getLocalLoad`. Used by the orchestrator's
|
|
25212
25338
|
* load-balancing levels (L2 capacity-based, L3 hardware-aware) to decide
|
|
@@ -26002,6 +26128,11 @@ var CameraStatusSchema = zod.z.object({
|
|
|
26002
26128
|
*/
|
|
26003
26129
|
switchedOff: zod.z.array(CameraSwitchIdSchema).readonly(),
|
|
26004
26130
|
/**
|
|
26131
|
+
* The camera's attached sensors (D672). Optional only for a status composed
|
|
26132
|
+
* by an orchestrator older than D672; this orchestrator always sets it.
|
|
26133
|
+
*/
|
|
26134
|
+
cameraSensors: CameraSensorsStatusSchema.optional(),
|
|
26135
|
+
/**
|
|
26005
26136
|
* Stages of the bounded fan-out that were CUT SHORT — a timeout or a
|
|
26006
26137
|
* rejection — and whose block is therefore `null` because we could not
|
|
26007
26138
|
* READ it, not because there is nothing there.
|
|
@@ -35248,7 +35379,9 @@ var MotionOnMotionChangedDataSchema = zod.z.object({
|
|
|
35248
35379
|
detected: zod.z.boolean(),
|
|
35249
35380
|
timestamp: zod.z.number(),
|
|
35250
35381
|
source: MotionSourceEnum,
|
|
35251
|
-
regions: zod.z.array(MotionRegionSchema).readonly().optional()
|
|
35382
|
+
regions: zod.z.array(MotionRegionSchema).readonly().optional(),
|
|
35383
|
+
/** Who started it (D672). Absent = the camera itself. Only `onboard` carries `sensor`. */
|
|
35384
|
+
triggeredBy: MotionTriggeredBySchema.optional()
|
|
35252
35385
|
});
|
|
35253
35386
|
var motionCapability = {
|
|
35254
35387
|
name: "motion",
|
package/dist/index.js
CHANGED
|
@@ -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-Dcy0gVHu.js");
|
|
4
4
|
const require_builtins_alerts_alerts_addon = require("./builtins/alerts/alerts.addon.js");
|
|
5
5
|
require("./builtins/alerts/index.js");
|
|
6
6
|
const require_builtins_composer_composer_addon = require("./builtins/composer/composer.addon.js");
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { $n as asNumber, Bt as looseSchema, C as METHOD_ACCESS_MAP, Dt as isCollectionArrayMethod, Et as isArrayOutputSchema, Ft as lifecycleJobSchema, Jn as DEVICE_STATUS_METHOD, Jt as objectInputDeclaresRequiredDeviceId, Kn as DATAPLANE_SECRET_HEADER$1, Mt as kebabToCamel, O as RUNTIME_DEFAULTS, Qn as asJsonObject$1, Qt as procedureAuthKey, Wn as errMsg$1, Xn as ReadinessTimeoutError, Yn as ReadinessRegistry, b as LOG_LEVEL_RANK, dr as readinessKey, er as asString$1, fr as resolveCapMount, hr as EventCategory$1, in as resolveMethodAuth, ir as expandCapMethods, jt as isVoidInput, kt as isObjectInput, lr as parseJsonObject, nr as emitDownForOwnedCaps, on as scopesAllowAddon, pr as scopeKey, qn as DEVICE_SETTINGS_CONTRIBUTION_METHODS, qt as objectInputDeclaresAddonId, sn as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tr as createEvent, ur as parseJsonUnknown, xt as extractNestedAddonId, z as addonSettingsCapability, zt as logLevelAtMost } from "./dist-
|
|
2
|
+
import { $n as asNumber, Bt as looseSchema, C as METHOD_ACCESS_MAP, Dt as isCollectionArrayMethod, Et as isArrayOutputSchema, Ft as lifecycleJobSchema, Jn as DEVICE_STATUS_METHOD, Jt as objectInputDeclaresRequiredDeviceId, Kn as DATAPLANE_SECRET_HEADER$1, Mt as kebabToCamel, O as RUNTIME_DEFAULTS, Qn as asJsonObject$1, Qt as procedureAuthKey, Wn as errMsg$1, Xn as ReadinessTimeoutError, Yn as ReadinessRegistry, b as LOG_LEVEL_RANK, dr as readinessKey, er as asString$1, fr as resolveCapMount, hr as EventCategory$1, in as resolveMethodAuth, ir as expandCapMethods, jt as isVoidInput, kt as isObjectInput, lr as parseJsonObject, nr as emitDownForOwnedCaps, on as scopesAllowAddon, pr as scopeKey, qn as DEVICE_SETTINGS_CONTRIBUTION_METHODS, qt as objectInputDeclaresAddonId, sn as scopesAllowDeviceCap, t as ALL_CAPABILITY_DEFINITIONS, tr as createEvent, ur as parseJsonUnknown, xt as extractNestedAddonId, z as addonSettingsCapability, zt as logLevelAtMost } from "./dist-CQ9vXSUh.mjs";
|
|
3
3
|
import { AlertCenterAddon } from "./builtins/alerts/alerts.addon.mjs";
|
|
4
4
|
import "./builtins/alerts/index.mjs";
|
|
5
5
|
import { ComposerAddon } from "./builtins/composer/composer.addon.mjs";
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
//#region src/builtins/sqlite-storage/wal-checkpoint-policy.ts
|
|
2
|
+
/**
|
|
3
|
+
* When the checkpoint worker folds the WAL — the judgement, pure.
|
|
4
|
+
*
|
|
5
|
+
* ## Why a worker checkpoints at all
|
|
6
|
+
*
|
|
7
|
+
* `hub/sqlite-settings` is one JS thread with a synchronous binding, and every
|
|
8
|
+
* configuration read in the cluster queues behind whatever statement it is
|
|
9
|
+
* inside. With `journal_mode = WAL` and the build default `synchronous =
|
|
10
|
+
* NORMAL`, an ordinary commit is an append to the WAL with NO fsync. The
|
|
11
|
+
* fsyncs — the only thing in a one-row write that can take seconds — happen
|
|
12
|
+
* at the CHECKPOINT: the WAL is synced before the pages are copied, the
|
|
13
|
+
* database file after. And SQLite runs the auto-checkpoint (1000 pages)
|
|
14
|
+
* INSIDE the commit of whichever writer crosses the threshold.
|
|
15
|
+
*
|
|
16
|
+
* Measured on the hub, 2026-09-11 10:45–10:55 CEST, from outside the process
|
|
17
|
+
* (`/proc/<pid>/syscall` at 10 Hz + the wal-index header at 1 Hz, D452): the
|
|
18
|
+
* WAL refilled at ~54 frames/s, the auto-checkpoint fired every 12–25 s (42
|
|
19
|
+
* in 9.5 min), and every D-state run on the settings thread that sat in a
|
|
20
|
+
* syscall sat in `fsync` (74), each within ±2 s of a checkpoint event —
|
|
21
|
+
* 12.6 s of the 569 s window, all of it inside writers' commits. In the two
|
|
22
|
+
* saturated bursts earlier that morning (08:35–08:40, 09:30–09:39) the same
|
|
23
|
+
* fsync took 1–10 s, once per checkpoint, and the profiler filed each as a
|
|
24
|
+
* slow `set`. `synchronous` was never the lever: it was already NORMAL, and
|
|
25
|
+
* `sqlite-pragmas.ts` had said so since 2026-08-08.
|
|
26
|
+
*
|
|
27
|
+
* So the checkpoint runs on a worker thread, over its own connection. The
|
|
28
|
+
* settings thread's commits stay appends. What stays on the settings thread
|
|
29
|
+
* is the WAL-header sync SQLite performs when a WAL is reused after a
|
|
30
|
+
* completed checkpoint — one small fsync per cycle — and that residual is
|
|
31
|
+
* named here so nobody reads "no fsync on the settings thread" into this.
|
|
32
|
+
*
|
|
33
|
+
* ## The rule this file reproduces
|
|
34
|
+
*
|
|
35
|
+
* The auto-checkpoint's own rule: fold when 1000 frames are pending. The
|
|
36
|
+
* point of moving the checkpoint is to change WHO waits on the disk, not how
|
|
37
|
+
* often the disk is asked to sync — a worker that folded every second would
|
|
38
|
+
* multiply the fsync count on a drive the media plane already saturates.
|
|
39
|
+
*
|
|
40
|
+
* ## How the worker knows how much is pending — and how it must NOT learn it
|
|
41
|
+
*
|
|
42
|
+
* Until 2026-09-29 the worker read the wal-index header straight out of the
|
|
43
|
+
* `-shm` file, once a second, with `openSync` / `readSync` / `closeSync`. That
|
|
44
|
+
* `closeSync` was the SIGBUS of D468 (corrected by D679): on POSIX, closing
|
|
45
|
+
* ANY descriptor of a file drops EVERY fcntl lock the process holds on that
|
|
46
|
+
* file — including the shared lock on `-shm` byte 128 (the "DMS" lock) that
|
|
47
|
+
* SQLite takes for the life of a connection and that tells every OTHER
|
|
48
|
+
* process "a live connection is using this wal-index, do not rebuild it". With
|
|
49
|
+
* the lock gone, the next process to open the database believed it was
|
|
50
|
+
* first, truncated and re-initialised the `-shm` under the runner's mappings,
|
|
51
|
+
* and the runner's next write past a 32 KiB wal-index region hit a page
|
|
52
|
+
* beyond end-of-file.
|
|
53
|
+
*
|
|
54
|
+
* So the worker never touches a SQLite file except through its connection.
|
|
55
|
+
* What it knows about the WAL it learns from the checkpoint it just ran:
|
|
56
|
+
* `PRAGMA wal_checkpoint(PASSIVE)` answers `log` (frames in the WAL) and
|
|
57
|
+
* `checkpointed` (frames now backfilled). Two consecutive answers give the
|
|
58
|
+
* frames appended between them, hence a rate, hence WHEN the next 1000 frames
|
|
59
|
+
* will have accumulated. The next checkpoint is scheduled for then —
|
|
60
|
+
* {@link nextCheckpointPlan} — clamped between the worker's poll interval and
|
|
61
|
+
* {@link WAL_CHECKPOINT_MAX_DELAY_MS}. A PASSIVE with nothing to fold performs
|
|
62
|
+
* no fsync (the copy and both syncs sit behind `nBackfill < mxFrame` in
|
|
63
|
+
* `wal.c`), so the clamp costs a quiet database nothing, and it bounds how
|
|
64
|
+
* long a burst that follows a quiet spell goes unseen.
|
|
65
|
+
*/
|
|
66
|
+
/** The engine's own `wal_autocheckpoint` default, in frames. Reproduced, not changed. */
|
|
67
|
+
var WAL_CHECKPOINT_THRESHOLD_FRAMES = 1e3;
|
|
68
|
+
/**
|
|
69
|
+
* Longest the worker waits between two checkpoints, in ms, whatever the
|
|
70
|
+
* measured rate says. It is the bound on how late a burst after a steady or
|
|
71
|
+
* quiet spell is noticed, and it is sized against the engine's own fallback
|
|
72
|
+
* (`SQLITE_WAL_AUTOCHECKPOINT_BOUND_PAGES` = 10 000 frames), which runs INSIDE
|
|
73
|
+
* a settings-thread commit — the stall D452 moved off that thread. No burst
|
|
74
|
+
* rate has been measured on the hub (D452's 54 frames/s is the steady rate),
|
|
75
|
+
* so the design point is 1 000 frames/s, ~18× steady: 8 s of it is 8 000
|
|
76
|
+
* frames, under the fallback, so the worker looks before the engine has to.
|
|
77
|
+
* A pass that finds more than {@link WAL_CHECKPOINT_LARGE_PASS_FRAMES} is
|
|
78
|
+
* reported so this number can be tuned from the field.
|
|
79
|
+
*
|
|
80
|
+
* The cost, stated against D452: at the steady 54 frames/s the threshold
|
|
81
|
+
* would fold every ~18.5 s; this clamp folds every 8 s, ~2.3× the fsync pairs
|
|
82
|
+
* (≈ 432 frames per fold instead of 1 000). They are paid on the WORKER, a
|
|
83
|
+
* thread nobody waits on — the settings thread's commits stay appends. A pass
|
|
84
|
+
* with nothing pending does no fsync at all.
|
|
85
|
+
*/
|
|
86
|
+
var WAL_CHECKPOINT_MAX_DELAY_MS = 8e3;
|
|
87
|
+
/**
|
|
88
|
+
* A pass that folded more than this many appended frames is reported (WARN,
|
|
89
|
+
* sampled: the first of each summary window, the rest counted). Half the
|
|
90
|
+
* engine's fallback bound: a gap that let this much accumulate was within 2×
|
|
91
|
+
* of handing the checkpoint back to the settings thread.
|
|
92
|
+
*/
|
|
93
|
+
var WAL_CHECKPOINT_LARGE_PASS_FRAMES = 5e3;
|
|
94
|
+
/** Shortest gap between two checkpoints, in ms — the worker's poll interval. */
|
|
95
|
+
var WAL_CHECKPOINT_POLL_MS = 1e3;
|
|
96
|
+
/**
|
|
97
|
+
* A checkpoint that took this long is reported at WARN with its numbers.
|
|
98
|
+
* Matched to `SQLITE_SLOW_CALL_MS`: it is the same "the disk held us" event
|
|
99
|
+
* the profiler used to file as a slow `set`, now on the thread that paid it.
|
|
100
|
+
*/
|
|
101
|
+
var WAL_CHECKPOINT_SLOW_MS = 1e3;
|
|
102
|
+
/**
|
|
103
|
+
* Frames appended to the WAL between two checkpoints.
|
|
104
|
+
*
|
|
105
|
+
* When the previous checkpoint backfilled EVERYTHING, the next writer restarts
|
|
106
|
+
* the WAL from frame 1, so every frame now in it is new. When it did not (a
|
|
107
|
+
* reader pinned a snapshot), the WAL kept growing and the difference is what
|
|
108
|
+
* was added — unless it shrank, which only a restart can do.
|
|
109
|
+
*
|
|
110
|
+
* An IDLE WAL is the exception to "complete means restarted": the WAL restarts
|
|
111
|
+
* only at the next write, so a fully checkpointed WAL nobody writes answers
|
|
112
|
+
* the same `log == checkpointed` pass after pass. Identical answers after a
|
|
113
|
+
* complete checkpoint are therefore 0 appended, not the whole WAL again —
|
|
114
|
+
* otherwise an idle node would be scheduled at the poll interval forever and
|
|
115
|
+
* every summary would count the same frames once per pass. (A restart that
|
|
116
|
+
* appended exactly as many frames as the previous cycle is read as idle too;
|
|
117
|
+
* it is scheduled at the max delay, which is the bounded case anyway.)
|
|
118
|
+
*
|
|
119
|
+
* The one remaining ambiguity — a complete checkpoint followed by writes the
|
|
120
|
+
* writer could NOT restart the WAL for — counts old frames as new, which
|
|
121
|
+
* over-estimates the rate and checkpoints EARLY: the safe direction. `-1`
|
|
122
|
+
* (not a WAL database, or a busy pass) reads as 0.
|
|
123
|
+
*/
|
|
124
|
+
function framesAppendedBetween(previous, current) {
|
|
125
|
+
const prevWal = Math.max(0, previous.walFrames);
|
|
126
|
+
const prevDone = Math.max(0, previous.checkpointedFrames);
|
|
127
|
+
const curWal = Math.max(0, current.walFrames);
|
|
128
|
+
const curDone = Math.max(0, current.checkpointedFrames);
|
|
129
|
+
if (prevDone >= prevWal) {
|
|
130
|
+
if (curWal === prevWal && curDone === prevDone) return 0;
|
|
131
|
+
return curWal;
|
|
132
|
+
}
|
|
133
|
+
return curWal >= prevWal ? curWal - prevWal : curWal;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* What the worker "saw" before its first checkpoint: nothing, at the moment it
|
|
137
|
+
* started. Against it, the first checkpoint counts every frame then in the WAL
|
|
138
|
+
* as appended since the start — which over-estimates the rate when the WAL
|
|
139
|
+
* held older frames, and so schedules the second checkpoint EARLY: the safe
|
|
140
|
+
* direction, and the second one measures the real rate.
|
|
141
|
+
*/
|
|
142
|
+
function workerStartObservation(at) {
|
|
143
|
+
return {
|
|
144
|
+
at,
|
|
145
|
+
walFrames: 0,
|
|
146
|
+
checkpointedFrames: 0
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* When to run the next checkpoint: the moment the measured rate says
|
|
151
|
+
* {@link WAL_CHECKPOINT_THRESHOLD_FRAMES} more frames will be pending,
|
|
152
|
+
* clamped to `[minDelayMs, maxDelayMs]`. Nothing appended since the last one
|
|
153
|
+
* is `maxDelayMs`.
|
|
154
|
+
*/
|
|
155
|
+
function nextCheckpointPlan(input) {
|
|
156
|
+
const { previous, current, minDelayMs, maxDelayMs } = input;
|
|
157
|
+
const appendedFrames = framesAppendedBetween(previous, current);
|
|
158
|
+
const elapsedMs = Math.max(1, current.at - previous.at);
|
|
159
|
+
if (appendedFrames === 0) return {
|
|
160
|
+
appendedFrames,
|
|
161
|
+
nextDelayMs: maxDelayMs
|
|
162
|
+
};
|
|
163
|
+
const untilThresholdMs = WAL_CHECKPOINT_THRESHOLD_FRAMES / (appendedFrames / elapsedMs);
|
|
164
|
+
return {
|
|
165
|
+
appendedFrames,
|
|
166
|
+
nextDelayMs: Math.round(Math.min(maxDelayMs, Math.max(minDelayMs, untilThresholdMs)))
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function isRecord(value) {
|
|
170
|
+
return typeof value === "object" && value !== null;
|
|
171
|
+
}
|
|
172
|
+
/** Narrow a message off the worker port without a cast. */
|
|
173
|
+
function isWorkerMessage(value) {
|
|
174
|
+
if (!isRecord(value)) return false;
|
|
175
|
+
switch (value["kind"]) {
|
|
176
|
+
case "ready": return true;
|
|
177
|
+
case "failed": return typeof value["error"] === "string";
|
|
178
|
+
case "checkpointed": return (value["reason"] === "first" || value["reason"] === "scheduled") && typeof value["appendedFrames"] === "number" && typeof value["nextDelayMs"] === "number" && typeof value["walFrames"] === "number" && typeof value["checkpointedFrames"] === "number" && typeof value["busy"] === "boolean" && typeof value["ms"] === "number";
|
|
179
|
+
default: return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function isWorkerStop(value) {
|
|
183
|
+
return isRecord(value) && value["kind"] === "stop";
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
export { isWorkerMessage as a, workerStartObservation as c, WAL_CHECKPOINT_SLOW_MS as i, WAL_CHECKPOINT_MAX_DELAY_MS as n, isWorkerStop as o, WAL_CHECKPOINT_POLL_MS as r, nextCheckpointPlan as s, WAL_CHECKPOINT_LARGE_PASS_FRAMES as t };
|