@camstack/system 1.2.156 → 1.2.157
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/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/index.js +64 -12
- package/dist/index.mjs +64 -12
- package/dist/kernel/config-manager.d.ts +31 -0
- package/dist/kernel/settings-door-view.d.ts +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9612,20 +9612,63 @@ var ConfigManager = class ConfigManager {
|
|
|
9612
9612
|
if (this.settingsStore === null) return;
|
|
9613
9613
|
this.settingsStore.clearDeviceRuntimeState(deviceId);
|
|
9614
9614
|
}
|
|
9615
|
+
/**
|
|
9616
|
+
* The addon's `ctx.settings`.
|
|
9617
|
+
*
|
|
9618
|
+
* The backend is chosen on EVERY call, never frozen when the view is built.
|
|
9619
|
+
* That is not a nicety: `settings-store` is owned by `storage-orchestrator`,
|
|
9620
|
+
* so the door is wired by `registerProvider` STRICTLY AFTER that addon's
|
|
9621
|
+
* context — and therefore its settings view — already exists. A view bound at
|
|
9622
|
+
* construction handed the door's own owner the sync-store backend, whose
|
|
9623
|
+
* writes throw for the whole life of the process once `sqlite-settings` runs
|
|
9624
|
+
* isolated (D181: no sync `ISettingsStore` reaches hub-main, only the async
|
|
9625
|
+
* door). Reads were worse than the writes: they answered `{}` in silence.
|
|
9626
|
+
* That is what made `storageMigration.start` fail on its first durable write
|
|
9627
|
+
* while every other addon's persistence worked. See D294.
|
|
9628
|
+
*/
|
|
9615
9629
|
createSettingsView(addonId) {
|
|
9616
|
-
|
|
9630
|
+
const storeBacked = this.createStoreSettingsView(addonId);
|
|
9631
|
+
let doorBacked = null;
|
|
9632
|
+
let doorBackedSource = null;
|
|
9633
|
+
const active = () => {
|
|
9634
|
+
if (this.settingsStore !== null) return storeBacked;
|
|
9617
9635
|
const door = this.settingsDoor;
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9636
|
+
if (door === null) return storeBacked;
|
|
9637
|
+
if (doorBacked === null || doorBackedSource !== door) {
|
|
9638
|
+
doorBackedSource = door;
|
|
9639
|
+
doorBacked = this.createDoorBackedSettingsView(addonId, door);
|
|
9640
|
+
}
|
|
9641
|
+
return doorBacked;
|
|
9642
|
+
};
|
|
9643
|
+
return {
|
|
9644
|
+
readAddonStore: () => active().readAddonStore(),
|
|
9645
|
+
writeAddonStore: (patch) => active().writeAddonStore(patch),
|
|
9646
|
+
readDeviceStore: (deviceId) => active().readDeviceStore(deviceId),
|
|
9647
|
+
readDeviceStoreBatch: (deviceIds) => active().readDeviceStoreBatch(deviceIds),
|
|
9648
|
+
writeDeviceStore: (deviceId, patch) => active().writeDeviceStore(deviceId, patch),
|
|
9649
|
+
clearDeviceStore: (deviceId) => active().clearDeviceStore(deviceId),
|
|
9650
|
+
getSection: (section) => active().getSection(section),
|
|
9651
|
+
setSection: (section, patch) => active().setSection(section, patch),
|
|
9652
|
+
readDeviceRuntimeState: (deviceId) => active().readDeviceRuntimeState(deviceId),
|
|
9653
|
+
writeDeviceRuntimeState: (deviceId, data) => active().writeDeviceRuntimeState(deviceId, data),
|
|
9654
|
+
clearDeviceRuntimeState: (deviceId) => active().clearDeviceRuntimeState(deviceId)
|
|
9655
|
+
};
|
|
9656
|
+
}
|
|
9657
|
+
/** The async-door backend of {@link createSettingsView}. */
|
|
9658
|
+
createDoorBackedSettingsView(addonId, door) {
|
|
9659
|
+
return createDoorSettingsView(addonId, door, {
|
|
9660
|
+
getSection: (section) => this.getSection(section),
|
|
9661
|
+
setSection: async (section, patch) => {
|
|
9662
|
+
for (const [key, value] of Object.entries(patch)) await door.set({
|
|
9663
|
+
collection: "system-settings",
|
|
9664
|
+
key: `${section}.${key}`,
|
|
9665
|
+
value
|
|
9666
|
+
});
|
|
9667
|
+
}
|
|
9668
|
+
});
|
|
9669
|
+
}
|
|
9670
|
+
/** The sync-`ISettingsStore` backend of {@link createSettingsView}. */
|
|
9671
|
+
createStoreSettingsView(addonId) {
|
|
9629
9672
|
const cm = this;
|
|
9630
9673
|
return {
|
|
9631
9674
|
async readAddonStore() {
|
|
@@ -9641,6 +9684,15 @@ var ConfigManager = class ConfigManager {
|
|
|
9641
9684
|
async readDeviceStore(deviceId) {
|
|
9642
9685
|
return cm.getAddonDevice(addonId, String(deviceId));
|
|
9643
9686
|
},
|
|
9687
|
+
async readDeviceStoreBatch(deviceIds) {
|
|
9688
|
+
const out = /* @__PURE__ */ new Map();
|
|
9689
|
+
for (const deviceId of deviceIds) try {
|
|
9690
|
+
out.set(deviceId, cm.getAddonDevice(addonId, String(deviceId)));
|
|
9691
|
+
} catch {
|
|
9692
|
+
out.set(deviceId, {});
|
|
9693
|
+
}
|
|
9694
|
+
return out;
|
|
9695
|
+
},
|
|
9644
9696
|
async writeDeviceStore(deviceId, patch) {
|
|
9645
9697
|
const key = String(deviceId);
|
|
9646
9698
|
const existing = cm.getAddonDevice(addonId, key);
|
package/dist/index.mjs
CHANGED
|
@@ -9605,20 +9605,63 @@ var ConfigManager = class ConfigManager {
|
|
|
9605
9605
|
if (this.settingsStore === null) return;
|
|
9606
9606
|
this.settingsStore.clearDeviceRuntimeState(deviceId);
|
|
9607
9607
|
}
|
|
9608
|
+
/**
|
|
9609
|
+
* The addon's `ctx.settings`.
|
|
9610
|
+
*
|
|
9611
|
+
* The backend is chosen on EVERY call, never frozen when the view is built.
|
|
9612
|
+
* That is not a nicety: `settings-store` is owned by `storage-orchestrator`,
|
|
9613
|
+
* so the door is wired by `registerProvider` STRICTLY AFTER that addon's
|
|
9614
|
+
* context — and therefore its settings view — already exists. A view bound at
|
|
9615
|
+
* construction handed the door's own owner the sync-store backend, whose
|
|
9616
|
+
* writes throw for the whole life of the process once `sqlite-settings` runs
|
|
9617
|
+
* isolated (D181: no sync `ISettingsStore` reaches hub-main, only the async
|
|
9618
|
+
* door). Reads were worse than the writes: they answered `{}` in silence.
|
|
9619
|
+
* That is what made `storageMigration.start` fail on its first durable write
|
|
9620
|
+
* while every other addon's persistence worked. See D294.
|
|
9621
|
+
*/
|
|
9608
9622
|
createSettingsView(addonId) {
|
|
9609
|
-
|
|
9623
|
+
const storeBacked = this.createStoreSettingsView(addonId);
|
|
9624
|
+
let doorBacked = null;
|
|
9625
|
+
let doorBackedSource = null;
|
|
9626
|
+
const active = () => {
|
|
9627
|
+
if (this.settingsStore !== null) return storeBacked;
|
|
9610
9628
|
const door = this.settingsDoor;
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9629
|
+
if (door === null) return storeBacked;
|
|
9630
|
+
if (doorBacked === null || doorBackedSource !== door) {
|
|
9631
|
+
doorBackedSource = door;
|
|
9632
|
+
doorBacked = this.createDoorBackedSettingsView(addonId, door);
|
|
9633
|
+
}
|
|
9634
|
+
return doorBacked;
|
|
9635
|
+
};
|
|
9636
|
+
return {
|
|
9637
|
+
readAddonStore: () => active().readAddonStore(),
|
|
9638
|
+
writeAddonStore: (patch) => active().writeAddonStore(patch),
|
|
9639
|
+
readDeviceStore: (deviceId) => active().readDeviceStore(deviceId),
|
|
9640
|
+
readDeviceStoreBatch: (deviceIds) => active().readDeviceStoreBatch(deviceIds),
|
|
9641
|
+
writeDeviceStore: (deviceId, patch) => active().writeDeviceStore(deviceId, patch),
|
|
9642
|
+
clearDeviceStore: (deviceId) => active().clearDeviceStore(deviceId),
|
|
9643
|
+
getSection: (section) => active().getSection(section),
|
|
9644
|
+
setSection: (section, patch) => active().setSection(section, patch),
|
|
9645
|
+
readDeviceRuntimeState: (deviceId) => active().readDeviceRuntimeState(deviceId),
|
|
9646
|
+
writeDeviceRuntimeState: (deviceId, data) => active().writeDeviceRuntimeState(deviceId, data),
|
|
9647
|
+
clearDeviceRuntimeState: (deviceId) => active().clearDeviceRuntimeState(deviceId)
|
|
9648
|
+
};
|
|
9649
|
+
}
|
|
9650
|
+
/** The async-door backend of {@link createSettingsView}. */
|
|
9651
|
+
createDoorBackedSettingsView(addonId, door) {
|
|
9652
|
+
return createDoorSettingsView(addonId, door, {
|
|
9653
|
+
getSection: (section) => this.getSection(section),
|
|
9654
|
+
setSection: async (section, patch) => {
|
|
9655
|
+
for (const [key, value] of Object.entries(patch)) await door.set({
|
|
9656
|
+
collection: "system-settings",
|
|
9657
|
+
key: `${section}.${key}`,
|
|
9658
|
+
value
|
|
9659
|
+
});
|
|
9660
|
+
}
|
|
9661
|
+
});
|
|
9662
|
+
}
|
|
9663
|
+
/** The sync-`ISettingsStore` backend of {@link createSettingsView}. */
|
|
9664
|
+
createStoreSettingsView(addonId) {
|
|
9622
9665
|
const cm = this;
|
|
9623
9666
|
return {
|
|
9624
9667
|
async readAddonStore() {
|
|
@@ -9634,6 +9677,15 @@ var ConfigManager = class ConfigManager {
|
|
|
9634
9677
|
async readDeviceStore(deviceId) {
|
|
9635
9678
|
return cm.getAddonDevice(addonId, String(deviceId));
|
|
9636
9679
|
},
|
|
9680
|
+
async readDeviceStoreBatch(deviceIds) {
|
|
9681
|
+
const out = /* @__PURE__ */ new Map();
|
|
9682
|
+
for (const deviceId of deviceIds) try {
|
|
9683
|
+
out.set(deviceId, cm.getAddonDevice(addonId, String(deviceId)));
|
|
9684
|
+
} catch {
|
|
9685
|
+
out.set(deviceId, {});
|
|
9686
|
+
}
|
|
9687
|
+
return out;
|
|
9688
|
+
},
|
|
9637
9689
|
async writeDeviceStore(deviceId, patch) {
|
|
9638
9690
|
const key = String(deviceId);
|
|
9639
9691
|
const existing = cm.getAddonDevice(addonId, key);
|
|
@@ -31,6 +31,19 @@ export interface AddonSettingsView {
|
|
|
31
31
|
writeDeviceRuntimeState(deviceId: number, data: Record<string, unknown>): Promise<void>;
|
|
32
32
|
clearDeviceRuntimeState(deviceId: number): Promise<void>;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* A CONCRETE backend behind {@link ConfigManager.createSettingsView} — the
|
|
36
|
+
* sync-`ISettingsStore` one or the async-door one.
|
|
37
|
+
*
|
|
38
|
+
* `readDeviceStoreBatch` is required here and optional on
|
|
39
|
+
* {@link AddonSettingsView} because both of these can answer it, so the view
|
|
40
|
+
* the kernel hands an addon never has to decide whether the method exists.
|
|
41
|
+
* The optionality on the public interface stays for the implementation that
|
|
42
|
+
* genuinely cannot batch: a forked runner's UDS-backed view.
|
|
43
|
+
*/
|
|
44
|
+
export interface SettingsViewBackend extends AddonSettingsView {
|
|
45
|
+
readDeviceStoreBatch(deviceIds: readonly number[]): Promise<ReadonlyMap<number, Record<string, unknown>>>;
|
|
46
|
+
}
|
|
34
47
|
export interface ISettingsStore {
|
|
35
48
|
getSystem(key: string): unknown;
|
|
36
49
|
setSystem(key: string, value: unknown): void;
|
|
@@ -181,7 +194,25 @@ export declare class ConfigManager {
|
|
|
181
194
|
setDeviceRuntimeState(deviceId: string, blob: Record<string, unknown>): void;
|
|
182
195
|
/** Forget the device's runtime state. No-op before the store is wired. */
|
|
183
196
|
clearDeviceRuntimeState(deviceId: string): void;
|
|
197
|
+
/**
|
|
198
|
+
* The addon's `ctx.settings`.
|
|
199
|
+
*
|
|
200
|
+
* The backend is chosen on EVERY call, never frozen when the view is built.
|
|
201
|
+
* That is not a nicety: `settings-store` is owned by `storage-orchestrator`,
|
|
202
|
+
* so the door is wired by `registerProvider` STRICTLY AFTER that addon's
|
|
203
|
+
* context — and therefore its settings view — already exists. A view bound at
|
|
204
|
+
* construction handed the door's own owner the sync-store backend, whose
|
|
205
|
+
* writes throw for the whole life of the process once `sqlite-settings` runs
|
|
206
|
+
* isolated (D181: no sync `ISettingsStore` reaches hub-main, only the async
|
|
207
|
+
* door). Reads were worse than the writes: they answered `{}` in silence.
|
|
208
|
+
* That is what made `storageMigration.start` fail on its first durable write
|
|
209
|
+
* while every other addon's persistence worked. See D294.
|
|
210
|
+
*/
|
|
184
211
|
createSettingsView(addonId: string): AddonSettingsView;
|
|
212
|
+
/** The async-door backend of {@link createSettingsView}. */
|
|
213
|
+
private createDoorBackedSettingsView;
|
|
214
|
+
/** The sync-`ISettingsStore` backend of {@link createSettingsView}. */
|
|
215
|
+
private createStoreSettingsView;
|
|
185
216
|
/** Get all system-wide activation overrides. */
|
|
186
217
|
getSystemActivation(): Readonly<Record<string, boolean>>;
|
|
187
218
|
/** Set system-wide activation for a capability/addon pair. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ISettingsStoreProvider } from '@camstack/types';
|
|
2
|
-
import {
|
|
2
|
+
import { SettingsViewBackend } from './config-manager.js';
|
|
3
3
|
export interface DoorSectionAccess {
|
|
4
4
|
getSection(section: string): Record<string, unknown>;
|
|
5
5
|
setSection(section: string, patch: Record<string, unknown>): void | Promise<void>;
|
|
6
6
|
}
|
|
7
|
-
export declare function createDoorSettingsView(addonId: string, door: ISettingsStoreProvider, sections: DoorSectionAccess):
|
|
7
|
+
export declare function createDoorSettingsView(addonId: string, door: ISettingsStoreProvider, sections: DoorSectionAccess): SettingsViewBackend;
|