@camstack/system 1.2.157 → 1.2.160
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 +17 -16
- package/dist/addon-runner.mjs +2 -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/backup-orchestrator/backup-orchestrator.addon.d.ts +10 -7
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +50 -12
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +50 -12
- package/dist/builtins/backup-orchestrator/write-targets.d.ts +63 -0
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-bindings-store.d.ts +56 -10
- package/dist/builtins/device-manager/device-manager.addon.d.ts +11 -0
- package/dist/builtins/device-manager/device-manager.addon.js +216 -81
- package/dist/builtins/device-manager/device-manager.addon.mjs +216 -81
- package/dist/builtins/device-manager/device-meta-actions.d.ts +9 -3
- package/dist/builtins/device-manager/device-meta-store.d.ts +32 -5
- package/dist/builtins/device-manager/device-queries.d.ts +7 -3
- package/dist/builtins/device-manager/write-lock.d.ts +17 -0
- 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/filter-compiler.d.ts +41 -2
- package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +15 -7
- 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/vector-index-shared.d.ts +30 -5
- package/dist/builtins/sqlite-storage/vector-index-vec.d.ts +28 -6
- package/dist/builtins/storage-orchestrator/access-guards.d.ts +32 -0
- package/dist/builtins/storage-orchestrator/location-secrets.d.ts +33 -0
- package/dist/builtins/storage-orchestrator/storage-migration-coordinator.d.ts +74 -3
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +26 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +552 -15
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +552 -15
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +56 -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-CDgIzo82.js → dist-CcvXUhHK.js} +537 -17
- package/dist/{dist-LvKus-mT.mjs → dist-DKs---Vx.mjs} +520 -18
- package/dist/index.js +141 -139
- package/dist/index.mjs +3 -3
- package/dist/kernel/deps/manifest-system-deps.d.ts +15 -0
- package/dist/kernel/index.d.ts +4 -2
- package/dist/kernel/moleculer/cap-usage-registry.d.ts +132 -7
- package/dist/kernel/storage-location-registry.d.ts +16 -1
- package/dist/kernel/transport/local-child-registry.d.ts +57 -0
- package/dist/{manifest-python-deps-CD14Pv0-.mjs → manifest-system-deps-DnoPpLGp.mjs} +466 -83
- package/dist/{manifest-python-deps-DVODn-qc.js → manifest-system-deps-oJMkWSX-.js} +477 -82
- package/dist/{retired-settings-keys-CV4QVdXj.mjs → retired-settings-keys-C3eONTvV.mjs} +1 -1
- package/dist/{retired-settings-keys-BfAzWvPC.js → retired-settings-keys-DXZ2xe7C.js} +1 -1
- package/package.json +2 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as bareAddonId, Gt as createEvent, Mt as BaseAddon, Rt as DeviceType, U as doorbellCapability, _ as SOURCE_DEVICE_TYPES, et as isSameAddonId, g as SOURCE_CAPS, in as EventCategory, jt as errMsg, q as evaluateSensorEdge, tt as isSourceCap } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
//#region src/builtins/doorbell/trigger-engine.ts
|
|
3
3
|
/**
|
|
4
4
|
* Pure trigger logic for the virtual-doorbell builtin — edge detection over
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-CcvXUhHK.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 {
|
|
1
|
+
import { Mt as BaseAddon, ot as logDestinationCapability } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
import { t as formatLogLine } from "../../formatter-B7qW8bPJ.mjs";
|
|
3
3
|
//#region src/builtins/hub-forwarder/hub-forwarder-destination.ts
|
|
4
4
|
var DEFAULT_OUTBOUND_BUFFER_SIZE = 500;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-CcvXUhHK.js");
|
|
4
4
|
//#region src/builtins/liveness-monitor/liveness-checks.ts
|
|
5
5
|
var NO_DEVICES = "liveness:no-devices";
|
|
6
6
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Gt as createEvent, Mt as BaseAddon, in as EventCategory, jt as errMsg } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
//#region src/builtins/liveness-monitor/liveness-checks.ts
|
|
3
3
|
var NO_DEVICES = "liveness:no-devices";
|
|
4
4
|
var ALL_OFFLINE = "liveness:all-devices-offline";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-CcvXUhHK.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
9
9
|
let crypto$1 = require("crypto");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { C as UserRecordSchema, Mt as BaseAddon, O as authProviderCapability, Ot as userManagementCapability, b as ScopedTokenSchema, r as ApiKeyRecordSchema } from "../../dist-DKs---Vx.mjs";
|
|
3
3
|
import * as crypto$2 from "node:crypto";
|
|
4
4
|
import { createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
|
5
5
|
import nodeCrypto from "crypto";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
3
|
+
const require_dist = require("../../dist-CcvXUhHK.js");
|
|
4
4
|
const require_tls = require("../../tls-BxQlomxd.js");
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mt as BaseAddon, at as localNetworkCapability, in as EventCategory } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
import { S as readLanHttpState, _ as DEFAULT_LAN_HTTP_PORT, a as writeTlsMode, i as writeExtraSans, l as reissueTlsLeaf, n as readTlsAccessStatus, o as validateUploadedTls, r as readTlsMode, s as ensureTlsCert, t as readExtraSans, y as applyLanHttp } from "../../tls-DOTmtLCW.mjs";
|
|
3
3
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -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-CcvXUhHK.js");
|
|
7
7
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
8
8
|
/**
|
|
9
9
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mt as BaseAddon, ot as logDestinationCapability } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
//#region src/builtins/loki-logging/loki-payload.ts
|
|
3
3
|
/**
|
|
4
4
|
* Loki rejects a label name that is not a valid Prometheus label
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-CcvXUhHK.js");
|
|
7
7
|
let node_fs_promises = require("node:fs/promises");
|
|
8
8
|
let node_child_process = require("node:child_process");
|
|
9
9
|
let node_util = require("node:util");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Gt as createEvent, Mt as BaseAddon, X as foldSnapshotByFunction, gt as resolveBucketMs, ht as reducePoints, in as EventCategory, jt as errMsg, lt as metricsProviderCapability } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
import { readFile, readdir } from "node:fs/promises";
|
|
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-CcvXUhHK.js");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_util = require("node:util");
|
|
9
9
|
let node_fs = require("node:fs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { Mt as BaseAddon, W as enumerateInferenceDevices, in as EventCategory, jt as errMsg, pt as platformProbeCapability, qt as emitReadiness, xt as scoreRuntimes } from "../../dist-DKs---Vx.mjs";
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import * as fs from "node:fs";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-CcvXUhHK.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 {
|
|
1
|
+
import { Mt as BaseAddon, in as EventCategory } from "../../dist-DKs---Vx.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
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-CcvXUhHK.js");
|
|
7
7
|
let zod = require("zod");
|
|
8
8
|
let node_crypto = require("node:crypto");
|
|
9
9
|
let _camstack_types_node = require("@camstack/types/node");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as bareAddonId, Ct as snapshotCapability, Dt as streamQualityLabel, It as DeviceFeature, Mt as BaseAddon, Rt as DeviceType, Zt as nodePin, i as BatteryStatusSchema, in as EventCategory, j as batteryCapability, jt as errMsg, z as deriveBatteryPresence } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { signExpiringUrl, verifyExpiringUrl } from "@camstack/types/node";
|
|
@@ -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-CcvXUhHK.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs_promises = require("node:fs/promises");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Et as storageProviderCapability, Mt as BaseAddon, Y as filesystemBrowseCapability } from "../../dist-DKs---Vx.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -17,6 +17,22 @@
|
|
|
17
17
|
* was written against it. `mutate` mode refuses both, before any SQL exists.
|
|
18
18
|
* Emptying a collection is a legitimate thing to want, and it should be a
|
|
19
19
|
* differently-named operation rather than a filter that happens to be empty.
|
|
20
|
+
* `measure` mode sits between them — see {@link FilterMode}.
|
|
21
|
+
*
|
|
22
|
+
* ── NULL is a value the language can name ────────────────────────────────
|
|
23
|
+
*
|
|
24
|
+
* A `null` on the right-hand side of `where` / `whereNot` compiles to `IS NULL`
|
|
25
|
+
* / `IS NOT NULL`, and binds no parameter. It used to compile to `= ?` / `!= ?`
|
|
26
|
+
* with a NULL parameter, and SQL three-valued logic makes both of those NULL
|
|
27
|
+
* for every row: the filter matched NOTHING and the caller got a `0` that
|
|
28
|
+
* looked like an answer. `pipelineAnalytics.countUnstampedEventMedia` — the
|
|
29
|
+
* gate that decides whether 152 GB of event media may be repointed onto
|
|
30
|
+
* another disk — asks exactly "how many rows have no `locationId`", and `0` is
|
|
31
|
+
* the one value that opens it. `whereNot: { field: null }` was the same defect
|
|
32
|
+
* mirrored: `(expr IS NULL OR expr != NULL)` is TRUE for a NULL row and NULL
|
|
33
|
+
* for every other, so it selected precisely the rows it was asked to exclude.
|
|
34
|
+
*
|
|
35
|
+
* Nothing could ever have MEANT `= NULL`; there is no caller to preserve.
|
|
20
36
|
*/
|
|
21
37
|
/** The part of a declared collection this compiler needs. */
|
|
22
38
|
export interface CollectionShape {
|
|
@@ -29,10 +45,28 @@ export interface CollectionShape {
|
|
|
29
45
|
*/
|
|
30
46
|
readonly kvBlobColumn: string | null;
|
|
31
47
|
}
|
|
32
|
-
|
|
48
|
+
/**
|
|
49
|
+
* How forgiving this compilation is allowed to be.
|
|
50
|
+
*
|
|
51
|
+
* - `select` — a predicate naming a field the collection cannot express is
|
|
52
|
+
* SKIPPED. The caller receives the rows and can see for itself that the
|
|
53
|
+
* filter did not bite; the cost is extra rows, not a wrong belief.
|
|
54
|
+
* - `measure` — the same predicate is REFUSED. A count, an aggregate or a
|
|
55
|
+
* histogram hands back a NUMBER: the whole collection and the intended
|
|
56
|
+
* subset are the same shape, and the caller has no way to tell which one it
|
|
57
|
+
* was given. An empty filter is still fine — `COUNT(*)` over everything is a
|
|
58
|
+
* legitimate question, unlike `DELETE` over everything. This closes the
|
|
59
|
+
* asymmetry `aggregate` already applied to its FIELDS ("a dropped aggregate
|
|
60
|
+
* returns a number that is wrong and looks exactly like a real one") but not
|
|
61
|
+
* to its predicate.
|
|
62
|
+
* - `mutate` — refuses that AND an empty filter, which on a bulk write is the
|
|
63
|
+
* entire collection.
|
|
64
|
+
*/
|
|
65
|
+
export type FilterMode = 'select' | 'measure' | 'mutate';
|
|
33
66
|
/** Marshals a JS value to what the driver should bind. */
|
|
34
67
|
export type SerializeValue = (value: unknown) => unknown;
|
|
35
68
|
export interface QueryFilterInput {
|
|
69
|
+
/** Equality. A `null` value means `IS NULL` — see the file docblock. */
|
|
36
70
|
readonly where?: Readonly<Record<string, unknown>>;
|
|
37
71
|
readonly whereIn?: Readonly<Record<string, readonly unknown[]>>;
|
|
38
72
|
readonly whereBetween?: Readonly<Record<string, readonly [unknown, unknown]>>;
|
|
@@ -40,6 +74,10 @@ export interface QueryFilterInput {
|
|
|
40
74
|
* NULL-safe exclusion, compiled as `(expr IS NULL OR expr != ?)`. SQL `!=`
|
|
41
75
|
* alone drops NULL rows, and on a nullable column those are precisely the
|
|
42
76
|
* rows a "not this kind" filter must keep (they predate the column).
|
|
77
|
+
*
|
|
78
|
+
* A `null` VALUE means `IS NOT NULL` instead: "everything except the rows
|
|
79
|
+
* that carry no value" is the only reading of `whereNot: { f: null }` that
|
|
80
|
+
* is not a contradiction.
|
|
43
81
|
*/
|
|
44
82
|
readonly whereNot?: Readonly<Record<string, unknown>>;
|
|
45
83
|
}
|
|
@@ -53,7 +91,8 @@ export interface CompiledFilter {
|
|
|
53
91
|
readonly whereSql: string;
|
|
54
92
|
readonly params: readonly unknown[];
|
|
55
93
|
}
|
|
56
|
-
/** Thrown by `mutate` mode. Distinct type so a caller can map it
|
|
94
|
+
/** Thrown by `measure` and `mutate` mode. Distinct type so a caller can map it
|
|
95
|
+
* to a 400. */
|
|
57
96
|
export declare class UnsafeFilterError extends Error {
|
|
58
97
|
constructor(message: string);
|
|
59
98
|
}
|
|
@@ -210,8 +210,12 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
210
210
|
* (visibility, kind, device, time window, expiry) — so `total` counted rows
|
|
211
211
|
* the page could never show, including other users'.
|
|
212
212
|
*
|
|
213
|
-
* `
|
|
214
|
-
*
|
|
213
|
+
* `measure` mode. `count` compiles the same predicates `query` does — the
|
|
214
|
+
* two must never disagree about WHICH rows they are talking about — but it
|
|
215
|
+
* refuses a predicate it cannot express rather than skipping it. `query`
|
|
216
|
+
* hands back rows and a caller can see the filter did not bite; a count
|
|
217
|
+
* hands back a number, and the whole collection and the intended subset are
|
|
218
|
+
* the same shape.
|
|
215
219
|
*/
|
|
216
220
|
count({ namespace, collection, filter }: SettingsCountInput): Promise<number>;
|
|
217
221
|
/**
|
|
@@ -223,8 +227,10 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
223
227
|
* projection — an unresolvable field THROWS rather than being dropped, because
|
|
224
228
|
* a missing aggregate comes back as a number that looks real.
|
|
225
229
|
*
|
|
226
|
-
* `
|
|
227
|
-
* which rows it is talking about
|
|
230
|
+
* `measure` mode on the filter, so this agrees with `query` and `count`
|
|
231
|
+
* about which rows it is talking about — and, since 2026-08-30, refuses an
|
|
232
|
+
* unresolvable PREDICATE for the same reason it already refused an
|
|
233
|
+
* unresolvable FIELD. Both come back as a number that looks real.
|
|
228
234
|
*/
|
|
229
235
|
aggregate({ namespace, collection, fields, filter, }: SettingsAggregateInput): Promise<SettingsAggregateResult>;
|
|
230
236
|
histogram({ namespace, collection, field, bucketSize, origin, filter, }: SettingsHistogramInput): Promise<readonly HistogramBucket[]>;
|
|
@@ -305,15 +311,17 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
|
|
|
305
311
|
* connection whose `sqlite_stat1` visibility and temp schema match.
|
|
306
312
|
*
|
|
307
313
|
* `EXPLAIN QUERY PLAN` prepares and plans; it does not execute the statement,
|
|
308
|
-
* so it costs no page reads of its own.
|
|
309
|
-
* slow-call path, once per shape per window
|
|
314
|
+
* so it costs no page reads of its own. In production it is called only from
|
|
315
|
+
* the slow-call path, once per shape per window; it is public so a guard can
|
|
316
|
+
* assert that a declared index is the one the planner actually picks — "we
|
|
317
|
+
* added an index" and "the query uses it" are different claims.
|
|
310
318
|
*
|
|
311
319
|
* Never throws. A plan that cannot be taken (a statement the engine will no
|
|
312
320
|
* longer prepare, a closed handle mid-shutdown) is a missing ANSWER, not a
|
|
313
321
|
* failure worth turning a slow query into a crash — the profiler falls back
|
|
314
322
|
* to the line it would have printed anyway.
|
|
315
323
|
*/
|
|
316
|
-
|
|
324
|
+
explainQueryPlan(sql: string, params: readonly unknown[]): readonly string[];
|
|
317
325
|
/**
|
|
318
326
|
* Run one synchronous statement under the profiler.
|
|
319
327
|
*
|
|
Binary file
|
|
Binary file
|
|
@@ -8,6 +8,33 @@ export interface VectorMatchRow {
|
|
|
8
8
|
readonly score: number;
|
|
9
9
|
readonly metadata: VectorMetadata;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* What one ranked search returned, INCLUDING the `k` it really ran with.
|
|
13
|
+
*
|
|
14
|
+
* `effectiveTopK` is part of the answer rather than a log line for the reason
|
|
15
|
+
* `vector-store.cap.ts` records: a ceiling the caller learns about only from
|
|
16
|
+
* the provider's own log is a number truncated in silence.
|
|
17
|
+
*/
|
|
18
|
+
export interface VectorQueryOutcome {
|
|
19
|
+
readonly matches: VectorMatchRow[];
|
|
20
|
+
readonly scanned: number;
|
|
21
|
+
readonly truncated: boolean;
|
|
22
|
+
readonly effectiveTopK: number;
|
|
23
|
+
}
|
|
24
|
+
/** One row read back WITH its vector, base64 Float32LE. */
|
|
25
|
+
export interface VectorFetchRow {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly vector: string;
|
|
28
|
+
readonly metadata: VectorMetadata;
|
|
29
|
+
}
|
|
30
|
+
/** One page of an unranked walk. `nextCursor: null` means end of index. */
|
|
31
|
+
export interface VectorScanPage {
|
|
32
|
+
readonly items: Array<{
|
|
33
|
+
id: string;
|
|
34
|
+
metadata: VectorMetadata;
|
|
35
|
+
}>;
|
|
36
|
+
readonly nextCursor: number | null;
|
|
37
|
+
}
|
|
11
38
|
export interface VectorIndexStats {
|
|
12
39
|
readonly backend: string;
|
|
13
40
|
readonly count: number;
|
|
@@ -32,15 +59,13 @@ export interface VectorIndexBackend {
|
|
|
32
59
|
topK: number;
|
|
33
60
|
minScore?: number;
|
|
34
61
|
filter?: VectorFilter;
|
|
35
|
-
}): Promise<
|
|
36
|
-
matches: VectorMatchRow[];
|
|
37
|
-
scanned: number;
|
|
38
|
-
truncated: boolean;
|
|
39
|
-
}>;
|
|
62
|
+
}): Promise<VectorQueryOutcome>;
|
|
40
63
|
getByIds(index: string, ids: readonly string[]): Promise<Array<{
|
|
41
64
|
id: string;
|
|
42
65
|
metadata: VectorMetadata;
|
|
43
66
|
}>>;
|
|
67
|
+
fetchByIds(index: string, ids: readonly string[]): Promise<VectorFetchRow[]>;
|
|
68
|
+
scan(index: string, cursor: number, limit: number): Promise<VectorScanPage>;
|
|
44
69
|
deleteByIds(index: string, ids: readonly string[]): Promise<number>;
|
|
45
70
|
deleteByFilter(index: string, filter: VectorFilter): Promise<number>;
|
|
46
71
|
stats(index: string): Promise<VectorIndexStats>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IScopedLogger, VectorFilter, VectorMetadata, VectorMetric, encodeVectorBase64 } from '@camstack/types';
|
|
2
2
|
import { SqliteOpProfiler } from './query-profiler.js';
|
|
3
|
-
import { VectorIndexStats,
|
|
3
|
+
import { VectorFetchRow, VectorIndexStats, VectorQueryOutcome, VectorScanPage } from './vector-index-shared.js';
|
|
4
4
|
/** The raw statement surface this index needs from better-sqlite3. */
|
|
5
5
|
export interface RawSqliteDatabase {
|
|
6
6
|
exec(sql: string): unknown;
|
|
@@ -68,11 +68,33 @@ export declare class SqliteVecVectorIndex {
|
|
|
68
68
|
topK: number;
|
|
69
69
|
minScore?: number;
|
|
70
70
|
filter?: VectorFilter;
|
|
71
|
-
}): Promise<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
}): Promise<VectorQueryOutcome>;
|
|
72
|
+
/**
|
|
73
|
+
* Ids read back WITH their vectors.
|
|
74
|
+
*
|
|
75
|
+
* `vec0` returns a vector column as the packed Float32 blob it stores, which
|
|
76
|
+
* is byte-for-byte what `upsert` was given — so the round trip is a base64
|
|
77
|
+
* encode and nothing else. No re-normalisation, no float re-ordering: a
|
|
78
|
+
* gallery loaded through here ranks identically to one loaded from the JSON
|
|
79
|
+
* column it replaced.
|
|
80
|
+
*/
|
|
81
|
+
fetchByIds(index: string, ids: readonly string[]): Promise<VectorFetchRow[]>;
|
|
82
|
+
/**
|
|
83
|
+
* One page of the whole index, unranked.
|
|
84
|
+
*
|
|
85
|
+
* `LIMIT ? OFFSET ?` over the `vec0` table with NO `MATCH` clause: a plain
|
|
86
|
+
* scan, so the extension's KNN `k` ceiling ({@link VEC_KNN_MAX_K}) does not
|
|
87
|
+
* apply and no distance is computed at all. Ordered by `id` so a cursor means
|
|
88
|
+
* the same thing across calls — `rowid` order is not stable under the
|
|
89
|
+
* DELETE-then-INSERT upsert this class performs.
|
|
90
|
+
*
|
|
91
|
+
* The cursor is an OFFSET, so rows deleted behind the walk shift the window.
|
|
92
|
+
* That is acceptable and deliberate for the one caller: a reconcile that
|
|
93
|
+
* misses a row this pass sees it next pass, and the alternative — a keyset
|
|
94
|
+
* cursor on a virtual table whose ordering the extension owns — buys nothing
|
|
95
|
+
* for a walk that is idempotent by construction.
|
|
96
|
+
*/
|
|
97
|
+
scan(index: string, cursor: number, limit: number): Promise<VectorScanPage>;
|
|
76
98
|
getByIds(index: string, ids: readonly string[]): Promise<Array<{
|
|
77
99
|
id: string;
|
|
78
100
|
metadata: VectorMetadata;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { StorageLocation } from '@camstack/types';
|
|
2
|
+
/** What a provider answered about itself; `undefined` = not yet classified. */
|
|
3
|
+
export type ProviderLocality = boolean | undefined;
|
|
4
|
+
/**
|
|
5
|
+
* May `storage.resolve` return a path for this location?
|
|
6
|
+
*
|
|
7
|
+
* `resolve` is the only method on the cap whose result LEAVES the storage
|
|
8
|
+
* abstraction — every caller of it opens the string with `node:fs`. A remote
|
|
9
|
+
* provider answers with a path on the remote host, which on this node is
|
|
10
|
+
* either missing or an unrelated local directory of the same name. For the
|
|
11
|
+
* recorder, writing to the wrong place is not an error the operator sees; it
|
|
12
|
+
* is a black window in the footage.
|
|
13
|
+
*
|
|
14
|
+
* Only a POSITIVE `false` refuses. `undefined` is "not yet classified", not
|
|
15
|
+
* "remote" — and a conclusion drawn from a read that was never made is how a
|
|
16
|
+
* doorbell rang 0/3 (D49).
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRefusalFor(location: StorageLocation, locality: ProviderLocality): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Can this node's filesystem answer "does this location still hold anything?"
|
|
21
|
+
*
|
|
22
|
+
* Only for a provider it KNOWS to be node-local. Everything else is `unknown`,
|
|
23
|
+
* which refuses a delete.
|
|
24
|
+
*
|
|
25
|
+
* The direction that matters is the one this closes. A remote location carries
|
|
26
|
+
* a plain-looking `basePath` (`/backups/camstack`); `fs.readdir` of it on the
|
|
27
|
+
* hub answers `ENOENT`, and `ENOENT` is deliberately read as `empty` — because
|
|
28
|
+
* for a LOCAL location an absent root means there is nothing the delete can
|
|
29
|
+
* strand. Applied to a remote row, that same reasoning turns a full share into
|
|
30
|
+
* a vacant one and walks D293's delete guard straight past it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function canProbeOccupancyLocally(locality: ProviderLocality): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { StorageLocation } from '@camstack/types';
|
|
2
|
+
/** Shared empty set for providers that declare no secrets (or are unknown). */
|
|
3
|
+
export declare const EMPTY_SECRET_KEYS: ReadonlySet<string>;
|
|
4
|
+
/** Minimal shape needed off a provider's `getProviderInfo()` result. */
|
|
5
|
+
export interface ProviderInfoLike {
|
|
6
|
+
readonly providerId: string;
|
|
7
|
+
readonly configSchema: unknown;
|
|
8
|
+
}
|
|
9
|
+
/** Secret config keys a provider declares, derived from its own form schema. */
|
|
10
|
+
export declare function secretKeysOfProviderInfo(info: ProviderInfoLike): ReadonlySet<string>;
|
|
11
|
+
/**
|
|
12
|
+
* A copy of `config` with every declared secret replaced by the sentinel.
|
|
13
|
+
* Returns the SAME object when there is nothing to redact, so the common case
|
|
14
|
+
* (a filesystem location) allocates nothing.
|
|
15
|
+
*
|
|
16
|
+
* A key the provider declares secret but that is absent from the stored config
|
|
17
|
+
* is NOT invented — an absent password must keep reading as absent, or the
|
|
18
|
+
* wizard would show a credential where none is set and the write-back would
|
|
19
|
+
* then "keep" a value that never existed.
|
|
20
|
+
*/
|
|
21
|
+
export declare function redactLocationConfig(config: Readonly<Record<string, unknown>>, secretKeys: ReadonlySet<string>): Readonly<Record<string, unknown>>;
|
|
22
|
+
/** The same, applied to a whole location record. */
|
|
23
|
+
export declare function redactLocation(location: StorageLocation, secretKeys: ReadonlySet<string>): StorageLocation;
|
|
24
|
+
/**
|
|
25
|
+
* Undo the redaction on the way IN: any secret key whose incoming value is the
|
|
26
|
+
* sentinel is restored from what is already stored.
|
|
27
|
+
*
|
|
28
|
+
* A sentinel with NOTHING stored behind it is dropped rather than persisted —
|
|
29
|
+
* writing the literal `__camstack_redacted__` as a password is a
|
|
30
|
+
* lock-yourself-out bug that would only surface at the next connection
|
|
31
|
+
* attempt, hours later, as an authentication failure with no explanation.
|
|
32
|
+
*/
|
|
33
|
+
export declare function restoreRedactedSecrets(incoming: Readonly<Record<string, unknown>>, stored: Readonly<Record<string, unknown>> | undefined, secretKeys: ReadonlySet<string>): Readonly<Record<string, unknown>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RelocateFootageInput, RelocateJob, RelocateMediaInput, StorageMigrationFootageMoveInput, StorageMigrationInput, StorageMigrationJob, StorageMigrationMediaMoveInput, StorageMigrationPlan, UnstampedEventMediaCount } from '@camstack/types';
|
|
1
|
+
import { RelocatableMediaCount, RelocatableMediaCountInput, RelocateFootageInput, RelocateJob, RelocateMediaInput, RelocateResidue, RelocateResidueInput, StorageMigrationDrainInput, StorageMigrationFootageMoveInput, StorageMigrationInput, StorageMigrationJob, StorageMigrationMediaMoveInput, StorageMigrationMover, StorageMigrationPlan, StorageMigrationResidue, UnstampedEventMediaCount } from '@camstack/types';
|
|
2
2
|
import { StorageOrchestratorService } from './storage-orchestrator.service.js';
|
|
3
3
|
export interface MigrationState {
|
|
4
4
|
get(): Promise<StorageMigrationJob | null>;
|
|
@@ -24,6 +24,10 @@ export interface FootageMigrationParticipant extends LeaseParticipant {
|
|
|
24
24
|
getMove(jobId: string): Promise<RelocateJob | null>;
|
|
25
25
|
cancelMove(jobId: string): Promise<boolean>;
|
|
26
26
|
refresh(leaseId: string): Promise<void>;
|
|
27
|
+
/** Every mover this engine knows about — including ones no migration armed. */
|
|
28
|
+
listMovers(): Promise<readonly RelocateJob[]>;
|
|
29
|
+
/** What a source STILL holds, from the durable hour ledger (D295). */
|
|
30
|
+
residue(input: RelocateResidueInput): Promise<RelocateResidue>;
|
|
27
31
|
}
|
|
28
32
|
export interface MediaMigrationParticipant extends LeaseParticipant {
|
|
29
33
|
startMove(input: StorageMigrationMediaMoveInput): Promise<{
|
|
@@ -41,6 +45,11 @@ export interface MediaMigrationParticipant extends LeaseParticipant {
|
|
|
41
45
|
getMove(jobId: string): Promise<RelocateJob | null>;
|
|
42
46
|
cancelMove(jobId: string): Promise<boolean>;
|
|
43
47
|
refresh(leaseId: string): Promise<void>;
|
|
48
|
+
/** Every mover this engine knows about — including ones no migration armed. */
|
|
49
|
+
listMovers(): Promise<readonly RelocateJob[]>;
|
|
50
|
+
/** How many rows a pass would still act on — the media lane's residue, from
|
|
51
|
+
* the same derivation that produces its `filesTotal`. */
|
|
52
|
+
residue(input: RelocatableMediaCountInput): Promise<RelocatableMediaCount>;
|
|
44
53
|
}
|
|
45
54
|
export interface StorageMigrationParticipants {
|
|
46
55
|
readonly pipeline: LeaseParticipant;
|
|
@@ -103,6 +112,67 @@ export declare class StorageMigrationCoordinator {
|
|
|
103
112
|
start(input: StorageMigrationInput): Promise<string>;
|
|
104
113
|
status(jobId?: string): Promise<StorageMigrationJob | null>;
|
|
105
114
|
cancel(jobId: string): Promise<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* Every mover running right now, in both lanes — including the ones no
|
|
117
|
+
* migration armed.
|
|
118
|
+
*
|
|
119
|
+
* `status` already carries a migration's own progress (the coordinator folds
|
|
120
|
+
* it onto each move from the poll it is already doing). This exists for the
|
|
121
|
+
* other half: `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
|
|
122
|
+
* are operator-callable, and until {@link drain} existed that was the only way
|
|
123
|
+
* to run a drain at all. Such a mover has no job to fold into, so without this
|
|
124
|
+
* read a five-hour operation is invisible in the UI.
|
|
125
|
+
*
|
|
126
|
+
* `migrationJobId` is best-effort by construction: the coordinator keeps ONE
|
|
127
|
+
* durable job, so a mover armed by an older, since-overwritten migration
|
|
128
|
+
* reports `null`. That is the honest answer — nothing here can still claim it.
|
|
129
|
+
*/
|
|
130
|
+
movers(): Promise<readonly StorageMigrationMover[]>;
|
|
131
|
+
/**
|
|
132
|
+
* What every class's source STILL holds — the census behind a "drain
|
|
133
|
+
* remaining" action.
|
|
134
|
+
*
|
|
135
|
+
* A class appears here only when something is (or might be) left on a
|
|
136
|
+
* location that is not its default. An empty result therefore means exactly
|
|
137
|
+
* "there is nothing to drain", which is what lets the UI offer the action
|
|
138
|
+
* only when it is true, and what lets {@link drain} refuse rather than start
|
|
139
|
+
* a job that would move nothing and report `done` — the failure mode D295
|
|
140
|
+
* exists to end.
|
|
141
|
+
*
|
|
142
|
+
* `items: null` is "the archive could not be asked" and is still listed. A
|
|
143
|
+
* residue nobody could measure is the case an operator most needs to see;
|
|
144
|
+
* dropping it because the read failed would be the quiet success again.
|
|
145
|
+
*/
|
|
146
|
+
residue(): Promise<readonly StorageMigrationResidue[]>;
|
|
147
|
+
/**
|
|
148
|
+
* Run the DRAIN half alone, against classes whose default has already moved.
|
|
149
|
+
*
|
|
150
|
+
* ## Why this is a second verb rather than a looser `start`
|
|
151
|
+
*
|
|
152
|
+
* `start` refuses a destination that is already the class's default
|
|
153
|
+
* (`"recordingsLow:ssd" is already the "recordingsLow" default`). That refusal
|
|
154
|
+
* is correct and it is load-bearing: there is genuinely nothing left to
|
|
155
|
+
* repoint, and an operator must never be able to re-repoint a migrated class
|
|
156
|
+
* by accident. Making `start` idempotent — "an already-repointed class
|
|
157
|
+
* proceeds straight to draining" — would delete that protection AND make the
|
|
158
|
+
* verb mean two different things depending on state, so the confirmation an
|
|
159
|
+
* operator reads ("pauses the writers…") would be a lie half the time.
|
|
160
|
+
*
|
|
161
|
+
* `drain` instead cannot repoint AT ALL: it never touches
|
|
162
|
+
* `setDefaultLocations`, and its job starts in `draining` with `repointed`
|
|
163
|
+
* already true, so the `repointing` / `refreshing` / `resuming` blocks of
|
|
164
|
+
* {@link run} are behind it and unreachable. The two verbs are disjoint, and
|
|
165
|
+
* `start`'s refusal keeps meaning exactly what it meant.
|
|
166
|
+
*
|
|
167
|
+
* ## Why it re-derives the work instead of resuming the old job
|
|
168
|
+
*
|
|
169
|
+
* The finished job is the audit of what happened; re-opening it destroys
|
|
170
|
+
* that. And a drain is needed in cases where no migration job ever existed
|
|
171
|
+
* (a mover armed by hand, footage stranded on a location an operator added
|
|
172
|
+
* and then un-defaulted). One job = one operation, and the work list comes
|
|
173
|
+
* from {@link residue} — the archive — not from what a previous job believed.
|
|
174
|
+
*/
|
|
175
|
+
drain(input: StorageMigrationDrainInput): Promise<string>;
|
|
106
176
|
/** Boot recovery resumes a durable unfinished state. A missing in-memory
|
|
107
177
|
* child mover is recreated from the same copy-if-absent input. */
|
|
108
178
|
recover(): Promise<void>;
|
|
@@ -143,9 +213,10 @@ export declare class StorageMigrationCoordinator {
|
|
|
143
213
|
*
|
|
144
214
|
* The gate is the RE-COUNT, not the seal job's terminal state: a seal that
|
|
145
215
|
* failed some rows still finishes, and "finished" is not "there are none
|
|
146
|
-
* left". A
|
|
216
|
+
* left". A remaining row here fails the job while nothing has been paused
|
|
147
217
|
* and nothing has been repointed, which is the cheapest possible place to
|
|
148
|
-
* discover it.
|
|
218
|
+
* discover it. So does a re-count that could not be TAKEN: the gate opens on
|
|
219
|
+
* a measured absence, and only on that.
|
|
149
220
|
*
|
|
150
221
|
* The seal's own mover job id is deliberately NOT durable. It is idempotent
|
|
151
222
|
* and cheap, so a coordinator restart mid-seal simply re-runs the whole
|