@camstack/system 1.2.201 → 1.2.203
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/backup-orchestrator/backup-orchestrator.addon.d.ts +12 -0
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +37 -3
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +37 -3
- 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-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/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/drain-ratchet.d.ts +104 -0
- package/dist/builtins/storage-orchestrator/drain-state-store.d.ts +13 -0
- package/dist/builtins/storage-orchestrator/location-occupancy-slice.d.ts +47 -0
- package/dist/builtins/storage-orchestrator/location-protection.d.ts +20 -0
- package/dist/builtins/storage-orchestrator/location-root-collision.d.ts +44 -0
- package/dist/builtins/storage-orchestrator/location-store.d.ts +12 -3
- package/dist/builtins/storage-orchestrator/storage-migration-coordinator.d.ts +9 -9
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +47 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +865 -157
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +865 -157
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +75 -31
- package/dist/builtins/storage-orchestrator/storage-pressure-manager.d.ts +45 -0
- 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-CcV3k-hD.js → dist-BFvWv_bz.js} +933 -546
- package/dist/{dist-BmJNnb_s.mjs → dist-DcMUcdeq.mjs} +862 -547
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{retired-settings-keys-nLVKlhe-.mjs → retired-settings-keys-CJfgnUSb.mjs} +1 -1
- package/dist/{retired-settings-keys-Bd8fuwO1.js → retired-settings-keys-Dd7K3K3r.js} +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IScopedLogger, IStorageOccupancyProvider } from '@camstack/types';
|
|
2
|
+
/** What one location's owners hold there, merged. */
|
|
3
|
+
export interface LocationOwnedBytes {
|
|
4
|
+
readonly bytes: number;
|
|
5
|
+
/** The OLDEST contributing measurement — honest about the whole figure
|
|
6
|
+
* rather than about its freshest part. */
|
|
7
|
+
readonly measuredAtMs: number;
|
|
8
|
+
}
|
|
9
|
+
export interface OccupancySliceDeps {
|
|
10
|
+
readonly logger: IScopedLogger;
|
|
11
|
+
/** Registered `storage-occupancy` providers (cross-process proxies). */
|
|
12
|
+
readonly getProviders: () => readonly IStorageOccupancyProvider[];
|
|
13
|
+
/** Every location worth asking about, at refresh time. */
|
|
14
|
+
readonly locationIds: () => readonly string[];
|
|
15
|
+
readonly now: () => number;
|
|
16
|
+
/** How long a figure stays servable before a refresh is kicked. */
|
|
17
|
+
readonly staleAfterMs?: number;
|
|
18
|
+
}
|
|
19
|
+
export declare class LocationOccupancySlice {
|
|
20
|
+
private readonly deps;
|
|
21
|
+
private slice;
|
|
22
|
+
private refreshing;
|
|
23
|
+
private lastRefreshMs;
|
|
24
|
+
constructor(deps: OccupancySliceDeps);
|
|
25
|
+
/**
|
|
26
|
+
* What the last completed refresh saw for this location, or `undefined` when
|
|
27
|
+
* nothing has reported for it. There is deliberately no fallback: unknown is
|
|
28
|
+
* the truth about a location nobody owns bytes on.
|
|
29
|
+
*/
|
|
30
|
+
ownedFor(locationId: string): LocationOwnedBytes | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Serve-and-revalidate. Called by every read: it returns immediately, and
|
|
33
|
+
* kicks a background refresh when the slice is older than the window. A read
|
|
34
|
+
* never awaits, and never joins a refresh already in flight (D224).
|
|
35
|
+
*/
|
|
36
|
+
serveAndRevalidate(): void;
|
|
37
|
+
/**
|
|
38
|
+
* Ask every provider once and replace the slice.
|
|
39
|
+
*
|
|
40
|
+
* A provider that throws contributes NOTHING and is logged — it does not
|
|
41
|
+
* contribute a zero, and it does not take the other providers' answers down
|
|
42
|
+
* with it. A location that ends up with no contribution at all simply stays
|
|
43
|
+
* out of the slice, which is what makes it read back as unknown.
|
|
44
|
+
*/
|
|
45
|
+
refresh(): Promise<void>;
|
|
46
|
+
private runRefresh;
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** statfs of the location's volume, when this node could take it. */
|
|
2
|
+
export interface LocationVolumeCapacity {
|
|
3
|
+
readonly totalBytes: number;
|
|
4
|
+
readonly availableBytes: number;
|
|
5
|
+
}
|
|
6
|
+
/** One line, at most once per location per process. */
|
|
7
|
+
export type ProtectionNotice = (message: string, meta: Record<string, unknown>) => void;
|
|
8
|
+
/**
|
|
9
|
+
* The free-space percentage the guard enforces for one location.
|
|
10
|
+
*
|
|
11
|
+
* Order:
|
|
12
|
+
* 1. `config.minFreeGb`, converted against the volume total — the operator's
|
|
13
|
+
* criterion in the unit they set it in.
|
|
14
|
+
* 2. `config.minFreePercent` — the legacy per-location override, and the
|
|
15
|
+
* fallback when `minFreeGb` cannot be converted (no volume total: a
|
|
16
|
+
* remote-node or unstattable location). Falling back is the right
|
|
17
|
+
* direction — the alternative is a location with no guard at all.
|
|
18
|
+
* 3. the provider's declared default.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveMinFreePercent(config: Readonly<Record<string, unknown>>, providerDefaultPercent: number, capacity: LocationVolumeCapacity | null, notice: ProtectionNotice): number;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PhysicalRoot } from '@camstack/types/node';
|
|
2
|
+
/** The minimum a row must expose for this check. */
|
|
3
|
+
export interface RootCollisionCandidate {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly type: string;
|
|
6
|
+
/** Which node the path lives on. Two locations on DIFFERENT nodes can never
|
|
7
|
+
* collide, however identical their paths look. */
|
|
8
|
+
readonly nodeId?: string;
|
|
9
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
10
|
+
}
|
|
11
|
+
/** How this location's physical root was derived, or that it could not be. */
|
|
12
|
+
export type ResolveRoot = (basePath: string) => PhysicalRoot;
|
|
13
|
+
/** A same-type root collision, with everything the message needs to name it. */
|
|
14
|
+
export interface RootCollision {
|
|
15
|
+
readonly otherId: string;
|
|
16
|
+
readonly type: string;
|
|
17
|
+
readonly sharedRoot: string;
|
|
18
|
+
/** `equal` — the same directory. `nested` — one inside the other's tree. */
|
|
19
|
+
readonly kind: 'equal' | 'nested';
|
|
20
|
+
}
|
|
21
|
+
/** Why the check could not be made. Never a refusal — the caller warns. */
|
|
22
|
+
export interface RootCollisionUnknown {
|
|
23
|
+
readonly reason: 'no-base-path' | 'unresolvable';
|
|
24
|
+
readonly detail: string;
|
|
25
|
+
}
|
|
26
|
+
export type RootCollisionVerdict = {
|
|
27
|
+
readonly outcome: 'clear';
|
|
28
|
+
} | {
|
|
29
|
+
readonly outcome: 'collides';
|
|
30
|
+
readonly collision: RootCollision;
|
|
31
|
+
} | {
|
|
32
|
+
readonly outcome: 'unknown';
|
|
33
|
+
readonly unknown: RootCollisionUnknown;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Does `next` collide with anything already stored?
|
|
37
|
+
*
|
|
38
|
+
* `existing` is every other location; the row being EDITED is excluded by id,
|
|
39
|
+
* so re-saving a location without moving its root never refuses itself.
|
|
40
|
+
*/
|
|
41
|
+
export declare function findRootCollision(next: RootCollisionCandidate, existing: Iterable<RootCollisionCandidate>, resolveRoot: ResolveRoot, storedSelf?: RootCollisionCandidate): RootCollisionVerdict;
|
|
42
|
+
/** The operator-facing refusal. Names the location it collides with and the
|
|
43
|
+
* shared root — a refusal that says only "conflict" makes the operator hunt. */
|
|
44
|
+
export declare function rootCollisionMessage(id: string, collision: RootCollision): string;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { ISettingsStoreProvider, StorageLocation } from '@camstack/types';
|
|
1
|
+
import { ISettingsStoreProvider, LegacyStorageLocationDefault, StorageLocation } from '@camstack/types';
|
|
2
|
+
/**
|
|
3
|
+
* A location as it comes back OFF DISK: the live record plus the retired
|
|
4
|
+
* `isDefault` column, which exists only so the orchestrator's boot migration
|
|
5
|
+
* can turn an old default into the enabled write set and say which row it came
|
|
6
|
+
* from. No other reader may take this type.
|
|
7
|
+
*/
|
|
8
|
+
export type PersistedStorageLocation = StorageLocation & LegacyStorageLocationDefault;
|
|
2
9
|
/**
|
|
3
10
|
* Abstract storage interface for `StorageLocation` records. Methods are
|
|
4
11
|
* async because production-side implementations hit SQLite — but the
|
|
@@ -6,8 +13,10 @@ import { ISettingsStoreProvider, StorageLocation } from '@camstack/types';
|
|
|
6
13
|
* disk doesn't block the cap surface.
|
|
7
14
|
*/
|
|
8
15
|
export interface ILocationStore {
|
|
9
|
-
/** Read every persisted location. Called once at boot.
|
|
10
|
-
|
|
16
|
+
/** Read every persisted location. Called once at boot. Rows carry the
|
|
17
|
+
* RETIRED `isDefault` column when it was set, for the one reader allowed to
|
|
18
|
+
* see it: the orchestrator's boot migration onto the `enabled` write set. */
|
|
19
|
+
loadAll(): Promise<readonly PersistedStorageLocation[]>;
|
|
11
20
|
/** Insert or replace a single location by id. */
|
|
12
21
|
upsert(loc: StorageLocation): Promise<void>;
|
|
13
22
|
/** Remove a location by id. */
|
|
@@ -125,7 +125,7 @@ export declare class StorageMigrationCoordinator {
|
|
|
125
125
|
* remaining" action.
|
|
126
126
|
*
|
|
127
127
|
* A class appears here only when something is (or might be) left on a
|
|
128
|
-
* location
|
|
128
|
+
* location outside its write set. An empty result therefore means exactly
|
|
129
129
|
* "there is nothing to drain", which is what lets the UI offer the action
|
|
130
130
|
* only when it is true, and what lets {@link drain} refuse rather than start
|
|
131
131
|
* a job that would move nothing and report `done` — the failure mode D295
|
|
@@ -137,12 +137,12 @@ export declare class StorageMigrationCoordinator {
|
|
|
137
137
|
*/
|
|
138
138
|
residue(): Promise<readonly StorageMigrationResidue[]>;
|
|
139
139
|
/**
|
|
140
|
-
* Run the DRAIN half alone, against classes whose
|
|
140
|
+
* Run the DRAIN half alone, against classes whose write set has already moved.
|
|
141
141
|
*
|
|
142
142
|
* ## Why this is a second verb rather than a looser `start`
|
|
143
143
|
*
|
|
144
|
-
* `start` refuses a destination that is already the class's
|
|
145
|
-
* (`"recordingsLow:ssd" is already the "recordingsLow"
|
|
144
|
+
* `start` refuses a destination that is already the class's only write target
|
|
145
|
+
* (`"recordingsLow:ssd" is already the only write target for "recordingsLow"`). That refusal
|
|
146
146
|
* is correct and it is load-bearing: there is genuinely nothing left to
|
|
147
147
|
* repoint, and an operator must never be able to re-repoint a migrated class
|
|
148
148
|
* by accident. Making `start` idempotent — "an already-repointed class
|
|
@@ -151,7 +151,7 @@ export declare class StorageMigrationCoordinator {
|
|
|
151
151
|
* operator reads ("pauses the writers…") would be a lie half the time.
|
|
152
152
|
*
|
|
153
153
|
* `drain` instead cannot repoint AT ALL: it never touches
|
|
154
|
-
* `
|
|
154
|
+
* `setWriteTargets`, and its job starts in `draining` with `repointed`
|
|
155
155
|
* already true, so the `repointing` / `refreshing` / `resuming` blocks of
|
|
156
156
|
* {@link run} are behind it and unreachable. The two verbs are disjoint, and
|
|
157
157
|
* `start`'s refusal keeps meaning exactly what it meant.
|
|
@@ -161,7 +161,7 @@ export declare class StorageMigrationCoordinator {
|
|
|
161
161
|
* The finished job is the audit of what happened; re-opening it destroys
|
|
162
162
|
* that. And a drain is needed in cases where no migration job ever existed
|
|
163
163
|
* (a mover armed by hand, footage stranded on a location an operator added
|
|
164
|
-
* and then
|
|
164
|
+
* and then disabled again). One job = one operation, and the work list comes
|
|
165
165
|
* from {@link residue} — the archive — not from what a previous job believed.
|
|
166
166
|
*/
|
|
167
167
|
drain(input: StorageMigrationDrainInput): Promise<string>;
|
|
@@ -217,9 +217,9 @@ export declare class StorageMigrationCoordinator {
|
|
|
217
217
|
private waitForSeal;
|
|
218
218
|
private verifyMoves;
|
|
219
219
|
/**
|
|
220
|
-
* After a from→to copy whose source was NOT
|
|
221
|
-
*
|
|
222
|
-
* naming a
|
|
220
|
+
* After a from→to copy whose source was NOT a write target: stop new writes
|
|
221
|
+
* to `from`. The rest of the write set is unchanged (that is the whole point
|
|
222
|
+
* of naming a source outside it). A location that is already disabled is
|
|
223
223
|
* left alone.
|
|
224
224
|
*/
|
|
225
225
|
private freezeDrainedSources;
|
|
@@ -4,6 +4,15 @@ interface StorageOrchestratorConfig extends Record<string, unknown> {
|
|
|
4
4
|
export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestratorConfig> {
|
|
5
5
|
private service;
|
|
6
6
|
private pressureManager;
|
|
7
|
+
/**
|
|
8
|
+
* How much of each location CamStack itself holds (D388).
|
|
9
|
+
*
|
|
10
|
+
* Built on first use rather than in a field initializer: `this.ctx` does not
|
|
11
|
+
* exist until `initialize()` runs, and a field initializer touching it throws
|
|
12
|
+
* in the constructor.
|
|
13
|
+
*/
|
|
14
|
+
private occupancySlice;
|
|
15
|
+
private get occupancy();
|
|
7
16
|
private pressureTimer;
|
|
8
17
|
private pressureSweepInFlight;
|
|
9
18
|
private migration;
|
|
@@ -98,6 +107,25 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
|
|
|
98
107
|
* the recorder so disk-pressure is a single core concern.
|
|
99
108
|
*/
|
|
100
109
|
private resolveLocationThresholds;
|
|
110
|
+
/** Location ids that have already logged the `minFreeGb` / `minFreePercent`
|
|
111
|
+
* redundancy. The pressure sweep runs on a timer; the notice is worth
|
|
112
|
+
* exactly one line per location per process, not one per minute. */
|
|
113
|
+
private readonly minFreeRedundancyLogged;
|
|
114
|
+
private logMinFreeRedundancyOnce;
|
|
115
|
+
/**
|
|
116
|
+
* The draining locations and the ACTIVE peers of their class whose growth
|
|
117
|
+
* paces them (D386). Rebuilt every sweep from the live mode of every
|
|
118
|
+
* location, so this is also what STOPS a drain: a location the operator
|
|
119
|
+
* switched back simply stops appearing.
|
|
120
|
+
*/
|
|
121
|
+
private drainStore;
|
|
122
|
+
/** The drain ratchet's durable home, once a data-store engine exists. Same
|
|
123
|
+
* late-wiring window as the location table — on an agent it is never. */
|
|
124
|
+
private resolveDrainStore;
|
|
125
|
+
private drainPlanRefusalLogged;
|
|
126
|
+
private resolveDrainPlan;
|
|
127
|
+
/** Drain progress for every draining location, as of the last sweep. */
|
|
128
|
+
private drainProgress;
|
|
101
129
|
/**
|
|
102
130
|
* What the provider's own schema declared secret, or `undefined` when this
|
|
103
131
|
* node has never heard from that provider. The distinction is the whole
|
|
@@ -106,6 +134,25 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
|
|
|
106
134
|
private declaredSecretKeys;
|
|
107
135
|
/** Which keys of a given stored config must never leave this process. */
|
|
108
136
|
private secretKeysFor;
|
|
137
|
+
/**
|
|
138
|
+
* Ring the doorbell: the SET of storage locations changed (D387).
|
|
139
|
+
*
|
|
140
|
+
* A consumer that resolves locations at boot had no way to learn that the
|
|
141
|
+
* operator added, edited or removed one — the recorder kept writing to the
|
|
142
|
+
* disk set it resolved hours ago, and an operator who added a second
|
|
143
|
+
* recordings disk in the admin UI got nothing until something unrelated
|
|
144
|
+
* happened to re-resolve.
|
|
145
|
+
*
|
|
146
|
+
* Telemetry, and deliberately so (D8/D11): it carries no row, it is allowed
|
|
147
|
+
* to be dropped, and every consumer re-reads `listLocations` for itself and
|
|
148
|
+
* converges on its own periodic path anyway. A bulk edit rings it once per
|
|
149
|
+
* row — coalescing is the consumer's job, not this one's.
|
|
150
|
+
*
|
|
151
|
+
* Never throws into the mutation that produced it: the location IS saved,
|
|
152
|
+
* and a bus that will not take a telemetry event must not turn that into a
|
|
153
|
+
* failed upsert.
|
|
154
|
+
*/
|
|
155
|
+
private announceLocationsChanged;
|
|
109
156
|
/** A location with its provider's declared secrets replaced by the sentinel. */
|
|
110
157
|
private redacted;
|
|
111
158
|
/**
|