@camstack/system 1.2.328 → 1.2.329

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.
Files changed (69) hide show
  1. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
  2. package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
  3. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
  4. package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
  5. package/dist/builtins/alerts/alerts.addon.js +1 -1
  6. package/dist/builtins/alerts/alerts.addon.mjs +1 -1
  7. package/dist/builtins/autotrack/index.js +1 -1
  8. package/dist/builtins/autotrack/index.mjs +1 -1
  9. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.d.ts +7 -0
  10. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +294 -79
  11. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +294 -79
  12. package/dist/builtins/camera-grid/index.js +1 -1
  13. package/dist/builtins/camera-grid/index.mjs +1 -1
  14. package/dist/builtins/composer/composer.addon.js +2 -2
  15. package/dist/builtins/composer/composer.addon.mjs +2 -2
  16. package/dist/builtins/console-logging/index.js +1 -1
  17. package/dist/builtins/console-logging/index.mjs +1 -1
  18. package/dist/builtins/core-blocks/core-blocks.addon.js +2 -2
  19. package/dist/builtins/core-blocks/core-blocks.addon.mjs +2 -2
  20. package/dist/builtins/device-manager/device-manager.addon.js +2 -2
  21. package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
  22. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  23. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  24. package/dist/builtins/hub-forwarder/index.js +1 -1
  25. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  26. package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
  27. package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
  28. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  29. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  30. package/dist/builtins/local-network/local-network.addon.js +1 -1
  31. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  32. package/dist/builtins/loki-logging/index.js +1 -1
  33. package/dist/builtins/loki-logging/index.mjs +1 -1
  34. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  35. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  36. package/dist/builtins/platform-probe/index.js +1 -1
  37. package/dist/builtins/platform-probe/index.mjs +1 -1
  38. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  39. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  40. package/dist/builtins/snapshot/index.js +1 -1
  41. package/dist/builtins/snapshot/index.mjs +1 -1
  42. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  43. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  44. package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +4 -0
  45. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
  46. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
  47. package/dist/builtins/sqlite-storage/sqlite-snapshot-protocol.d.ts +49 -0
  48. package/dist/builtins/sqlite-storage/sqlite-snapshot-worker.d.ts +33 -0
  49. package/dist/builtins/sqlite-storage/sqlite-snapshotter.d.ts +98 -0
  50. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
  51. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
  52. package/dist/builtins/system-backup/system-backup.service.d.ts +74 -20
  53. package/dist/builtins/system-config/system-config.addon.js +1 -1
  54. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  55. package/dist/builtins/winston-logging/index.js +1 -1
  56. package/dist/builtins/winston-logging/index.mjs +1 -1
  57. package/dist/{composition-sources-GkgXF6B9.js → composition-sources-B7hfTiPS.js} +1 -1
  58. package/dist/{composition-sources-I8Rx7woY.mjs → composition-sources-BHPqf6So.mjs} +1 -1
  59. package/dist/{dist-BUejKKBm.js → dist-CSs0cFDN.js} +496 -337
  60. package/dist/{dist-DWq17vC8.mjs → dist-CV7IqaYq.mjs} +467 -338
  61. package/dist/index.js +1 -1
  62. package/dist/index.mjs +1 -1
  63. package/dist/{retired-settings-keys-DBPEzGop.js → retired-settings-keys-CGQnAoRx.js} +1 -1
  64. package/dist/{retired-settings-keys-BfHclEyd.mjs → retired-settings-keys-DSUs7U6l.mjs} +1 -1
  65. package/dist/sqlite-snapshot-protocol-2QaTouWV.mjs +34 -0
  66. package/dist/sqlite-snapshot-protocol-B9OndqZv.js +51 -0
  67. package/dist/sqlite-snapshot-worker.js +185 -0
  68. package/dist/sqlite-snapshot-worker.mjs +179 -0
  69. package/package.json +5 -2
@@ -1,5 +1,5 @@
1
1
  import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
2
- import { H as authProviderCapability, L as UserRecordSchema, M as ScopedTokenSchema, Sn as userManagementCapability, qn as BaseAddon, r as ApiKeyRecordSchema } from "../../dist-DWq17vC8.mjs";
2
+ import { B as UserRecordSchema, En as userManagementCapability, F as ScopedTokenSchema, G as authProviderCapability, Zn as BaseAddon, r as ApiKeyRecordSchema } from "../../dist-CV7IqaYq.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-BUejKKBm.js");
3
+ const require_dist = require("../../dist-CSs0cFDN.js");
4
4
  const require_tls = require("../../tls-D_wUeOTD.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 { _r as EventCategory, qn as BaseAddon, zt as localNetworkCapability } from "../../dist-DWq17vC8.mjs";
1
+ import { Sr as EventCategory, Ut as localNetworkCapability, Zn as BaseAddon } from "../../dist-CV7IqaYq.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-IM-v7JV4.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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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 { Vt as logDestinationCapability, qn as BaseAddon } from "../../dist-DWq17vC8.mjs";
1
+ import { Gt as logDestinationCapability, Zn as BaseAddon } from "../../dist-CV7IqaYq.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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.js");
7
7
  const require_proc_stat = require("../../proc-stat-DL-WUOhl.js");
8
8
  let node_fs_promises = require("node:fs/promises");
9
9
  let node_fs = require("node:fs");
@@ -1,4 +1,4 @@
1
- import { Et as foldSnapshotByFunction, Kn as errMsg, _r as EventCategory, in as reducePoints, on as resolveBucketMs, qn as BaseAddon, qt as metricsProviderCapability, rr as createEvent } from "../../dist-DWq17vC8.mjs";
1
+ import { At as foldSnapshotByFunction, Sr as EventCategory, Xn as errMsg, Zn as BaseAddon, Zt as metricsProviderCapability, cn as reducePoints, sr as createEvent, un as resolveBucketMs } from "../../dist-CV7IqaYq.mjs";
2
2
  import { a as parseProcStat, i as nodeProcReader, n as elapsedSecSince, o as parseProcStatusRss, r as lifetimeCpuPercent, t as commandFromCmdline } from "../../proc-stat-CbC55Kqi.mjs";
3
3
  import { access, readFile, readdir } from "node:fs/promises";
4
4
  import * as fs from "node:fs";
@@ -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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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,5 +1,5 @@
1
1
  import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
2
- import { Kn as errMsg, _r as EventCategory, ar as emitReadiness, fn as scoreRuntimes, qn as BaseAddon, tn as platformProbeCapability, vt as enumerateInferenceDevices } from "../../dist-DWq17vC8.mjs";
2
+ import { Sr as EventCategory, St as enumerateInferenceDevices, Xn as errMsg, Zn as BaseAddon, an as platformProbeCapability, gn as scoreRuntimes, lr as emitReadiness } from "../../dist-CV7IqaYq.mjs";
3
3
  import * as fs from "node:fs";
4
4
  import { execFile } from "node:child_process";
5
5
  import * as os from "node:os";
@@ -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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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 { _r as EventCategory, qn as BaseAddon } from "../../dist-DWq17vC8.mjs";
1
+ import { Sr as EventCategory, Zn as BaseAddon } from "../../dist-CV7IqaYq.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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.js");
7
7
  const require_snapshot_courtesy = require("../../snapshot-courtesy-Cqs7GkOV.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { G as batteryCapability, Gn as DeviceType, Kn as errMsg, Kt as memoizePinnedFfmpeg, Un as DeviceFeature, W as bareAddonId, _r as EventCategory, a as BatteryStatusSchema, hn as snapshotCapability, qn as BaseAddon, ur as nodePin, ut as deriveBatteryPresence, xn as streamQualityLabel } from "../../dist-DWq17vC8.mjs";
1
+ import { J as batteryCapability, Sr as EventCategory, Tn as streamQualityLabel, Xn as errMsg, Xt as memoizePinnedFfmpeg, Yn as DeviceType, Zn as BaseAddon, a as BatteryStatusSchema, mr as nodePin, mt as deriveBatteryPresence, q as bareAddonId, qn as DeviceFeature, yn as snapshotCapability } from "../../dist-CV7IqaYq.mjs";
2
2
  import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CZPvteq2.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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 { Tt as filesystemBrowseCapability, qn as BaseAddon, yn as storageProviderCapability } from "../../dist-DWq17vC8.mjs";
1
+ import { Cn as storageProviderCapability, Zn as BaseAddon, kt as filesystemBrowseCapability } from "../../dist-CV7IqaYq.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";
@@ -11,6 +11,10 @@ import { SqliteVecVectorIndex } from './vector-index-vec.js';
11
11
  * engine only through the door — which is the point, because it is what
12
12
  * makes the engine replaceable.
13
13
  *
14
+ * It also serves `database-snapshot` (singleton, internal): the verified copy
15
+ * of its database that the nightly backup archives in place of the live files
16
+ * (D693).
17
+ *
14
18
  * It ALSO serves `vector-store` (singleton, internal), from the same database
15
19
  * file and the same open handle. That capability first shipped as its own addon
16
20
  * and the separation cost two round trips per write — caller → door → engine —
@@ -0,0 +1,49 @@
1
+ import { DatabaseSnapshotTable } from '@camstack/types';
2
+ /** `workerData` of one snapshot thread. The `kind` tells it apart from any other worker. */
3
+ export interface SnapshotWorkerData {
4
+ readonly kind: 'sqlite-snapshot';
5
+ /** The live database — opened read-only, through this thread's own connection. */
6
+ readonly livePath: string;
7
+ /** Where the copy is written. Lives in a work directory nothing else uses. */
8
+ readonly copyPath: string;
9
+ /** Pages per backup step. Every step reads the same pinned transaction. */
10
+ readonly pagesPerStep: number;
11
+ }
12
+ /** What a copy that passed every check holds. */
13
+ export interface VerifiedCopy {
14
+ readonly takenAt: number;
15
+ readonly tables: readonly DatabaseSnapshotTable[];
16
+ readonly pageCount: number;
17
+ readonly pageSize: number;
18
+ readonly copyMs: number;
19
+ readonly verifyMs: number;
20
+ }
21
+ /**
22
+ * Why a copy was refused:
23
+ * - `integrity` — `PRAGMA integrity_check` on the copy answered anything but `ok`;
24
+ * - `row-count` — a table of the copy holds a different number of rows than the
25
+ * pinned source did, or the table sets differ;
26
+ * - `failed` — the copy could not be taken at all (open, I/O, busy, deadline);
27
+ * - `no-space` — the live database's disk cannot take the copy and keep its
28
+ * reserve, or its free space could not be measured (decided before the
29
+ * thread starts, by the snapshotter).
30
+ */
31
+ export type SnapshotRefusalReason = 'integrity' | 'row-count' | 'failed' | 'no-space';
32
+ export interface SnapshotDone {
33
+ readonly kind: 'done';
34
+ readonly copy: VerifiedCopy;
35
+ }
36
+ export interface SnapshotRefused {
37
+ readonly kind: 'refused';
38
+ readonly reason: SnapshotRefusalReason;
39
+ readonly detail: string;
40
+ }
41
+ export type SnapshotWorkerMessage = SnapshotDone | SnapshotRefused;
42
+ /** Thrown inside the worker by a check that refuses the copy. */
43
+ export declare class SnapshotRefusedError extends Error {
44
+ readonly reason: SnapshotRefusalReason;
45
+ readonly detail: string;
46
+ constructor(reason: SnapshotRefusalReason, detail: string);
47
+ }
48
+ export declare function isSnapshotWorkerData(value: unknown): value is SnapshotWorkerData;
49
+ export declare function isSnapshotWorkerMessage(value: unknown): value is SnapshotWorkerMessage;
@@ -0,0 +1,33 @@
1
+ import { DatabaseSnapshotTable } from '@camstack/types';
2
+ import { default as Database } from 'better-sqlite3';
3
+ import { SnapshotWorkerData, VerifiedCopy } from './sqlite-snapshot-protocol.js';
4
+ /** The pinned half: when it was taken, what it holds, how long it took. */
5
+ export interface PinnedCopy {
6
+ readonly takenAt: number;
7
+ readonly tables: readonly DatabaseSnapshotTable[];
8
+ readonly copyMs: number;
9
+ }
10
+ /** The checked half. */
11
+ export interface CopyCheck {
12
+ readonly pageCount: number;
13
+ readonly pageSize: number;
14
+ readonly verifyMs: number;
15
+ }
16
+ /**
17
+ * Copy `source` into `copyPath` from ONE read transaction. `onStep` observes
18
+ * each backup step (a spec interleaves writes there).
19
+ */
20
+ export declare function copyPinned(source: Database.Database, copyPath: string, pagesPerStep: number, onStep?: () => void): Promise<PinnedCopy>;
21
+ /**
22
+ * The answer of `PRAGMA journal_mode = DELETE` must be `delete`. SQLite answers
23
+ * the mode it is IN, so a switch it could not make comes back as `wal` with no
24
+ * error — and a copy left in WAL needs sidecars that will not travel with it.
25
+ */
26
+ export declare function requireRollbackJournal(answer: unknown): void;
27
+ /**
28
+ * Check a copy against what the pinned source held. Makes the copy one
29
+ * self-contained file first. Throws {@link SnapshotRefusedError}.
30
+ */
31
+ export declare function verifyCopy(copyPath: string, expected: readonly DatabaseSnapshotTable[]): CopyCheck;
32
+ /** The whole job: open the live database read-only, copy it pinned, check the copy. */
33
+ export declare function takeVerifiedCopy(data: SnapshotWorkerData): Promise<VerifiedCopy>;
@@ -0,0 +1,98 @@
1
+ import { DatabaseSnapshotResult, IScopedLogger } from '@camstack/types';
2
+ import { SnapshotWorkerData } from './sqlite-snapshot-protocol.js';
3
+ /**
4
+ * Pages per backup step. Every step reads the same pinned transaction, so the
5
+ * size only trades yields against per-step overhead: 1024 pages is 4 MiB at the
6
+ * default page size.
7
+ */
8
+ export declare const SNAPSHOT_PAGES_PER_STEP = 1024;
9
+ /**
10
+ * How long the snapshot thread may run before it is terminated. A minute under
11
+ * the capability's own timeout, so the runner gives up — and says why — before
12
+ * the caller's call does.
13
+ */
14
+ export declare const SNAPSHOT_DEADLINE_MS: number;
15
+ /** The least headroom left on the disk AFTER the copy, whatever the database's size. */
16
+ export declare const SNAPSHOT_MIN_RESERVE_BYTES: number;
17
+ /**
18
+ * Free bytes a snapshot needs before it may start: the copy (at most the
19
+ * database plus its WAL), then a reserve the size of the database (at least
20
+ * {@link SNAPSHOT_MIN_RESERVE_BYTES}) for the live writes and the WAL growth
21
+ * the pinned read causes while the copy runs.
22
+ */
23
+ export declare function snapshotSpaceNeeded(databaseBytes: number, walBytes: number): number;
24
+ /** The `statfs` fields the space check reads. */
25
+ export interface DiskSpace {
26
+ readonly bavail: number | bigint;
27
+ readonly bsize: number | bigint;
28
+ }
29
+ export type StatfsFn = (dir: string) => Promise<DiskSpace>;
30
+ /**
31
+ * Where the built worker entry lives: `dist/sqlite-snapshot-worker.js` at the
32
+ * package root, next to `wal-checkpoint-worker.js` (see `wal-checkpointer.ts`
33
+ * for why it resolves through the package's own `package.json`).
34
+ */
35
+ export declare function defaultSnapshotWorkerPath(): string;
36
+ /**
37
+ * The members of a snapshot thread this class uses. Structural, so a spec can
38
+ * hand in a thread whose `terminate()` never settles — the stall a native call
39
+ * that never returns would cause.
40
+ */
41
+ export interface SnapshotWorkerHandle {
42
+ onMessage(listener: (message: unknown) => void): void;
43
+ onError(listener: (err: Error) => void): void;
44
+ onExit(listener: (code: number) => void): void;
45
+ terminate(): Promise<unknown>;
46
+ }
47
+ export type SnapshotWorkerFactory = (data: SnapshotWorkerData) => SnapshotWorkerHandle;
48
+ export interface SqliteSnapshotterOptions {
49
+ /** The live database this process owns. */
50
+ readonly livePath: string;
51
+ readonly logger: IScopedLogger;
52
+ readonly pagesPerStep?: number;
53
+ /** Overrides the built entry — a spec points this at the TypeScript source. */
54
+ readonly workerPath?: string;
55
+ /** Extra `execArgv` for the thread — a spec passes a loader here. */
56
+ readonly workerExecArgv?: readonly string[];
57
+ /** Overrides {@link SNAPSHOT_DEADLINE_MS}. */
58
+ readonly deadlineMs?: number;
59
+ /** Overrides `fs.statfs` — a spec answers the space question here. */
60
+ readonly statfs?: StatfsFn;
61
+ /** Overrides the thread itself — a spec hands in one that never terminates. */
62
+ readonly spawnWorker?: SnapshotWorkerFactory;
63
+ /** Overrides the directory fsync — a spec makes it fail. */
64
+ readonly syncDirectory?: (dir: string) => Promise<void>;
65
+ }
66
+ export declare class SqliteSnapshotter {
67
+ private readonly livePath;
68
+ private readonly logger;
69
+ private readonly pagesPerStep;
70
+ private readonly deadlineMs;
71
+ private readonly statfs;
72
+ private readonly spawnWorker;
73
+ private readonly syncDirectory;
74
+ private inFlight;
75
+ /**
76
+ * Set while a thread the deadline gave up on is still being terminated. A
77
+ * native SQLite call cannot be interrupted, so `terminate()` may take as long
78
+ * as that call — or never settle. Until it does, a new snapshot is refused
79
+ * at once (never joined, never started beside a thread that may still hold
80
+ * a pinned read), and the work directory stays until nobody writes into it.
81
+ */
82
+ private draining;
83
+ constructor(options: SqliteSnapshotterOptions);
84
+ /** The database this snapshotter copies, absolute. */
85
+ get database(): string;
86
+ /** Write `<db>.backup-snapshot`. Joins a snapshot already running. */
87
+ write(): Promise<DatabaseSnapshotResult>;
88
+ /**
89
+ * Remove a snapshot nobody collected and a work directory a crash left.
90
+ * Called at boot; {@link write} does the same before it starts.
91
+ */
92
+ sweep(): Promise<void>;
93
+ private refuse;
94
+ /** Refuses with `no-space` unless the disk can take the copy and keep its reserve. */
95
+ private checkSpace;
96
+ private run;
97
+ private runWorker;
98
+ }
@@ -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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.js");
7
7
  const require_hub_cap_forward = require("../../hub-cap-forward-DmHNjbB0.js");
8
8
  let zod = require("zod");
9
9
  let node_crypto = require("node:crypto");
@@ -1,4 +1,4 @@
1
- import { D as REDACTED_SECRET, Dn as withLocationMode, F as StorageLocationTypeSchema, Gt as mayWriteToLocation, I as StorageMigrationJobSchema, Jt as modeMayWrite, Lt as legacyModeOf, N as StorageCleanupJobSchema, Ot as gbToBytes, P as StorageLocationModeSchema, St as evictionPolicyOfLocation, Wt as mayReadLocation, X as collectSecretConfigKeys, _n as storageMigrationCapability, _r as EventCategory, dr as parseJsonObject, gn as storageCapability, jt as isLocationEnabled, pn as settingsStoreCapability, qn as BaseAddon, sn as resolveLocationMode, x as LegacyStorageLocationDefaultSchema } from "../../dist-DWq17vC8.mjs";
1
+ import { $ as collectSecretConfigKeys, A as REDACTED_SECRET, Et as evictionPolicyOfLocation, Ft as isLocationEnabled, I as StorageCleanupJobSchema, Jt as mayReadLocation, L as StorageLocationModeSchema, Mt as gbToBytes, Qt as modeMayWrite, R as StorageLocationTypeSchema, Sr as EventCategory, Vt as legacyModeOf, Yt as mayWriteToLocation, Zn as BaseAddon, _n as settingsStoreCapability, bn as storageCapability, dn as resolveLocationMode, hr as parseJsonObject, jn as withLocationMode, w as LegacyStorageLocationDefaultSchema, xn as storageMigrationCapability, z as StorageMigrationJobSchema } from "../../dist-CV7IqaYq.mjs";
2
2
  import { t as HUB_CAP_FWD_ACTION } from "../../hub-cap-forward-BdT320dq.mjs";
3
3
  import { z } from "zod";
4
4
  import { randomUUID } from "node:crypto";
@@ -1,4 +1,4 @@
1
- import { IScopedLogger } from '@camstack/types';
1
+ import { ArchiveSqliteSnapshot, DatabaseSnapshotResult, IScopedLogger } from '@camstack/types';
2
2
  /**
3
3
  * Thrown when an archive build (or a backup run driving one) is
4
4
  * stopped via its `AbortSignal`. Callers use `instanceof` to tell a
@@ -8,6 +8,15 @@ import { IScopedLogger } from '@camstack/types';
8
8
  export declare class BackupCancelledError extends Error {
9
9
  constructor(message?: string);
10
10
  }
11
+ /**
12
+ * Thrown when an archive would carry a live database and its owner could not
13
+ * produce a verified snapshot of it (D693). Nothing was built: a run that
14
+ * "succeeded" without the database — or with a torn copy of it — would push
15
+ * the oldest good archive out of every destination's retention.
16
+ */
17
+ export declare class BackupSnapshotRefusedError extends Error {
18
+ constructor(reason: string);
19
+ }
11
20
  /**
12
21
  * Default set of paths considered part of a "system backup". Includes
13
22
  * everything that isn't pure backend code — config, sqlite, addons
@@ -42,6 +51,16 @@ export declare const DEFAULT_BACKUP_EXCLUDES: readonly ["addons-data/backup-orch
42
51
  * trailing `/`) and backslash separators.
43
52
  */
44
53
  export declare function isExcludedFromBackup(relPath: string): boolean;
54
+ /**
55
+ * The OWNER of the live databases in a data root (D693): it says which files
56
+ * it snapshots, and writes the snapshot. `backup-orchestrator` answers it with
57
+ * the `database-snapshot` capability, pinned to its own node.
58
+ */
59
+ export interface DatabaseOwner {
60
+ /** Absolute paths of the databases the owner snapshots. */
61
+ listDatabases(): Promise<readonly string[]>;
62
+ writeSnapshot(): Promise<DatabaseSnapshotResult>;
63
+ }
45
64
  /** Path of the manifest file embedded inside every archive. */
46
65
  export declare const ARCHIVE_MANIFEST_NAME = ".camstack-backup-manifest.json";
47
66
  export type ArchiveEntryKind = 'file' | 'dir' | 'symlink';
@@ -69,6 +88,11 @@ export interface ArchiveManifest {
69
88
  /** Sum of file `sizeBytes` (uncompressed). */
70
89
  totalBytes: number;
71
90
  totalFiles: number;
91
+ /**
92
+ * Live databases carried as their owner's verified snapshot instead of as
93
+ * files (D693). Paths are archive-relative.
94
+ */
95
+ sqliteSnapshots?: ArchiveSqliteSnapshot[];
72
96
  }
73
97
  export interface CreateArchiveResult {
74
98
  archivePath: string;
@@ -159,33 +183,37 @@ export declare class SystemBackupService {
159
183
  locations?: readonly string[];
160
184
  signal?: AbortSignal;
161
185
  onArchiveBytes?: (bytesWritten: number) => void;
186
+ /**
187
+ * The OWNER of the data root's live databases (D693). Asked only when the
188
+ * walk finds a SQLite file: which databases it owns (authoritative — a
189
+ * cleanly closed database has no sidecar and is just as owned), then for
190
+ * one verified snapshot. Each owned database in scope is archived as that
191
+ * snapshot in place of its `.db` / `-wal` / `-shm` / `-journal`, and the
192
+ * snapshot is deleted once the archive is built.
193
+ *
194
+ * Nothing is built — {@link BackupSnapshotRefusedError} — when the owner
195
+ * fails, or when a live database (one with a sidecar) has no owner: an
196
+ * archive without the settings database must never reach a destination,
197
+ * because its upload prunes an older archive that HAS it.
198
+ */
199
+ owner?: DatabaseOwner;
162
200
  }): Promise<CreateArchiveResult>;
201
+ /**
202
+ * Tar `present` (filtered by `excluded`) plus the staged manifest into
203
+ * `archivePath`.
204
+ */
205
+ private writeArchive;
163
206
  /**
164
207
  * without extracting payload files. Streams the tar.gz, parses the
165
208
  * one entry we care about, then aborts.
166
209
  */
167
210
  readArchiveManifest(archivePath: string): Promise<ArchiveManifest | null>;
168
- /**
169
- * Extract a previously-created archive over `targetDir`. Used at boot
170
- * by the restore hook — never call from a running server (would
171
- * corrupt sqlite mid-run).
172
- *
173
- * `locations` is an optional whitelist: only entries whose path
174
- * starts with one of the locations get extracted. Used by the
175
- * partial-restore flow where the operator picks a subset of the
176
- * archive contents to apply (e.g. "restore my devices but keep
177
- * my current TLS certs").
178
- */
179
- extractArchive(opts: {
180
- archivePath: string;
181
- targetDir: string;
182
- locations?: readonly string[];
183
- }): Promise<void>;
184
211
  /**
185
212
  * Schedule a restore for the next boot by writing a marker file.
186
- * `locations` (optional) carries through to the boot-time
187
- * extractArchive call so the operator's "restore only X and Y"
188
- * choice survives the process restart.
213
+ * `locations` (optional) carries through to the launcher's boot-time
214
+ * restore (`server/backend/src/launcher.ts`, the only restore there is)
215
+ * so the operator's "restore only X and Y" choice survives the process
216
+ * restart.
189
217
  */
190
218
  scheduleRestoreMarker(opts: {
191
219
  archivePath: string;
@@ -196,6 +224,32 @@ export declare class SystemBackupService {
196
224
  readPendingMarker(): RestoreMarker | null;
197
225
  /** Drop the marker after a successful boot-time restore. */
198
226
  clearPendingMarker(): void;
227
+ /**
228
+ * The databases the owner declares, archive-relative, that this walk found.
229
+ * One outside this data root is not in scope. An owner that cannot answer
230
+ * stops the archive: without the answer, nothing says which file is live.
231
+ */
232
+ private ownedDatabasesInScope;
233
+ /**
234
+ * Ask the owner for its snapshot, and place it in this archive. The snapshot
235
+ * must sit on THIS data root — the call is pinned to this node, so a path
236
+ * elsewhere means it reached another host, and that file is not ours to
237
+ * read — beside an owned database in scope, and it must cover EVERY owned
238
+ * database in scope. Anything else is refused.
239
+ *
240
+ * Only ever DELETED here: `<owned db>.backup-snapshot` under this data root,
241
+ * a name this process derives itself. A path the owner ANSWERS is never
242
+ * deleted on its word — answering the live database as the snapshot would
243
+ * otherwise delete the live database. A file at any other path is left to
244
+ * the owner's own sweep.
245
+ *
246
+ * A cancel is honoured while the owner is still copying; the copy it may
247
+ * finish afterwards is removed when that call settles.
248
+ */
249
+ private takeOwnerSnapshot;
250
+ private removeSnapshot;
251
+ /** `abs` relative to the data root, or null when it lies outside it. */
252
+ private archiveRelative;
199
253
  private getRestoreMarkerPath;
200
254
  /**
201
255
  * Absolute-path form of {@link isExcludedFromBackup}, anchored on
@@ -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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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 { Kn as errMsg, qn as BaseAddon, sr as hydrateSchema } from "../../dist-DWq17vC8.mjs";
1
+ import { Xn as errMsg, Zn as BaseAddon, dr as hydrateSchema } from "../../dist-CV7IqaYq.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-BUejKKBm.js");
6
+ const require_dist = require("../../dist-CSs0cFDN.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 { Vt as logDestinationCapability, qn as BaseAddon } from "../../dist-DWq17vC8.mjs";
1
+ import { Gt as logDestinationCapability, Zn as BaseAddon } from "../../dist-CV7IqaYq.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
- const require_dist = require("./dist-BUejKKBm.js");
1
+ const require_dist = require("./dist-CSs0cFDN.js");
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
@@ -1,4 +1,4 @@
1
- import { Kn as errMsg, Rn as compositionSourceKey } from "./dist-DWq17vC8.mjs";
1
+ import { Hn as compositionSourceKey, Xn as errMsg } from "./dist-CV7IqaYq.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