@camstack/system 1.2.30 → 1.2.31

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 (51) 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/backup-orchestrator/backup-orchestrator.addon.js +135 -92
  8. package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +135 -92
  9. package/dist/builtins/backup-orchestrator/destination-policy.d.ts +12 -14
  10. package/dist/builtins/backup-orchestrator/schedule-store.d.ts +6 -6
  11. package/dist/builtins/console-logging/index.js +1 -1
  12. package/dist/builtins/console-logging/index.mjs +1 -1
  13. package/dist/builtins/device-manager/device-manager.addon.js +1 -1
  14. package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
  15. package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
  16. package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
  17. package/dist/builtins/hub-forwarder/index.js +1 -1
  18. package/dist/builtins/hub-forwarder/index.mjs +1 -1
  19. package/dist/builtins/local-auth/local-auth.addon.js +1 -1
  20. package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
  21. package/dist/builtins/local-network/local-network.addon.js +1 -1
  22. package/dist/builtins/local-network/local-network.addon.mjs +1 -1
  23. package/dist/builtins/loki-logging/index.js +1 -1
  24. package/dist/builtins/loki-logging/index.mjs +1 -1
  25. package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
  26. package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
  27. package/dist/builtins/platform-probe/index.js +1 -1
  28. package/dist/builtins/platform-probe/index.mjs +1 -1
  29. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
  30. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
  31. package/dist/builtins/snapshot/index.js +1 -1
  32. package/dist/builtins/snapshot/index.mjs +1 -1
  33. package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
  34. package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
  35. package/dist/builtins/sqlite-storage/integration-registry.d.ts +3 -3
  36. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +6 -8
  37. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +3 -25
  38. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +3 -25
  39. package/dist/builtins/storage-orchestrator/location-store.d.ts +17 -24
  40. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +0 -6
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +130 -121
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +130 -121
  43. package/dist/builtins/system-config/system-config.addon.js +1 -1
  44. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  45. package/dist/builtins/winston-logging/index.js +1 -1
  46. package/dist/builtins/winston-logging/index.mjs +1 -1
  47. package/dist/{dist-VIwdvws5.js → dist-CDv5YTHQ.js} +12 -1
  48. package/dist/{dist-Dr_P0DOB.mjs → dist-CNmXITJ-.mjs} +12 -1
  49. package/dist/index.js +362 -224
  50. package/dist/index.mjs +362 -224
  51. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
- import { Integration, PersistedDevice, CreateIntegrationInput, CreateDeviceInput, IIntegrationRegistry, ISettingsBackend } from '@camstack/types';
1
+ import { Integration, PersistedDevice, CreateIntegrationInput, CreateDeviceInput, IIntegrationRegistry, ISettingsStoreProvider } from '@camstack/types';
2
2
  export declare class IntegrationRegistry implements IIntegrationRegistry {
3
- private readonly backend;
4
- constructor(backend: ISettingsBackend);
3
+ private readonly store;
4
+ constructor(backend: ISettingsStoreProvider);
5
5
  initialize(): Promise<void>;
6
6
  createIntegration(input: CreateIntegrationInput): Promise<Integration>;
7
7
  getIntegration(id: string): Promise<Integration | null>;
@@ -1,5 +1,5 @@
1
1
  import { default as Database } from 'better-sqlite3';
2
- import { DataStoreEngineInfo, ISettingsBackend, SettingsRecord, TableSchema, TableQueryOptions, SettingsGetInput, SettingsSetInput, SettingsQueryInput, SettingsInsertInput, SettingsUpdateInput, SettingsDeleteInput, SettingsCountInput, SettingsIsEmptyInput, SettingsHistogramInput, HistogramBucket, CollectionColumn, CollectionIndex } from '@camstack/types';
2
+ import { DataStoreEngineInfo, ISettingsBackend, SettingsRecord, SettingsGetInput, SettingsSetInput, SettingsQueryInput, SettingsInsertInput, SettingsUpdateInput, SettingsDeleteInput, SettingsCountInput, SettingsIsEmptyInput, SettingsHistogramInput, HistogramBucket, CollectionColumn, CollectionIndex } from '@camstack/types';
3
3
  import { MutationFilterInput } from './filter-compiler.js';
4
4
  /** Input for {@link SqliteSettingsBackend.deleteWhere}. */
5
5
  interface SettingsDeleteWhereInput {
@@ -139,13 +139,11 @@ export declare class SqliteSettingsBackend implements ISettingsBackend {
139
139
  }): Promise<void>;
140
140
  /** Serialise per-column values for SQL binding: objects → JSON, booleans → 0/1. */
141
141
  private serializeColumnValue;
142
- ensureTable(table: string, schema: TableSchema): Promise<void>;
143
- tableInsert(table: string, row: Record<string, unknown>): Promise<void>;
144
- tableUpdate(table: string, filter: Record<string, unknown>, updates: Record<string, unknown>): Promise<number>;
145
- tableDelete(table: string, filter: Record<string, unknown>): Promise<number>;
146
- tableQuery(table: string, options?: TableQueryOptions): Promise<readonly Record<string, unknown>[]>;
147
- tableGet(table: string, filter: Record<string, unknown>): Promise<Record<string, unknown> | null>;
148
- tableCount(table: string, filter?: Record<string, unknown>): Promise<number>;
142
+ private ensureTable;
143
+ private tableInsert;
144
+ private tableUpdate;
145
+ private tableDelete;
146
+ private tableCount;
149
147
  private buildWhere;
150
148
  }
151
149
  export default SqliteSettingsBackend;
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CDv5YTHQ.js");
7
7
  let node_crypto = require("node:crypto");
8
8
  let better_sqlite3 = require("better-sqlite3");
9
9
  better_sqlite3 = require_chunk.__toESM(better_sqlite3);
@@ -536,7 +536,8 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
536
536
  type: c.type === "JSON" ? "TEXT" : c.type === "BOOLEAN" ? "INTEGER" : c.type,
537
537
  ...c.primaryKey !== void 0 ? { primaryKey: c.primaryKey } : {},
538
538
  ...c.notNull !== void 0 ? { notNull: c.notNull } : {},
539
- ...c.unique !== void 0 ? { unique: c.unique } : {}
539
+ ...c.unique !== void 0 ? { unique: c.unique } : {},
540
+ ...c.defaultValue !== void 0 ? { defaultValue: c.defaultValue } : {}
540
541
  })),
541
542
  ...input.indexes ? { indexes: input.indexes.map((i) => ({
542
543
  name: i.name,
@@ -621,29 +622,6 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
621
622
  const { whereSql, whereValues } = this.buildWhere(filter);
622
623
  return this.getDb().prepare(`DELETE FROM "${table}"${whereSql}`).run(...whereValues).changes;
623
624
  }
624
- async tableQuery(table, options) {
625
- let sql = `SELECT * FROM "${table}"`;
626
- const values = [];
627
- if (options?.where) {
628
- const { whereSql, whereValues } = this.buildWhere(options.where);
629
- sql += whereSql;
630
- values.push(...whereValues);
631
- }
632
- if (options?.orderBy) sql += ` ORDER BY "${options.orderBy.field}" ${options.orderBy.direction === "desc" ? "DESC" : "ASC"}`;
633
- if (options?.limit !== void 0) {
634
- sql += ` LIMIT ?`;
635
- values.push(options.limit);
636
- }
637
- if (options?.offset !== void 0) {
638
- sql += ` OFFSET ?`;
639
- values.push(options.offset);
640
- }
641
- return this.getDb().prepare(sql).all(...values).flatMap((r) => require_dist.asJsonObject(r) ? [require_dist.asJsonObject(r)] : []);
642
- }
643
- async tableGet(table, filter) {
644
- const { whereSql, whereValues } = this.buildWhere(filter);
645
- return require_dist.asJsonObject(this.getDb().prepare(`SELECT * FROM "${table}"${whereSql} LIMIT 1`).get(...whereValues));
646
- }
647
625
  async tableCount(table, filter) {
648
626
  let sql = `SELECT COUNT(*) as count FROM "${table}"`;
649
627
  const values = [];
@@ -1,4 +1,4 @@
1
- import { Dt as parseJsonUnknown, at as errMsg, c as RUNTIME_DEFAULTS, gt as asJsonObject, ot as BaseAddon, x as dataStoreProviderCapability } from "../../dist-Dr_P0DOB.mjs";
1
+ import { Dt as parseJsonUnknown, at as errMsg, c as RUNTIME_DEFAULTS, gt as asJsonObject, ot as BaseAddon, x as dataStoreProviderCapability } from "../../dist-CNmXITJ-.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import Database from "better-sqlite3";
4
4
  //#region src/builtins/sqlite-storage/filter-compiler.ts
@@ -530,7 +530,8 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
530
530
  type: c.type === "JSON" ? "TEXT" : c.type === "BOOLEAN" ? "INTEGER" : c.type,
531
531
  ...c.primaryKey !== void 0 ? { primaryKey: c.primaryKey } : {},
532
532
  ...c.notNull !== void 0 ? { notNull: c.notNull } : {},
533
- ...c.unique !== void 0 ? { unique: c.unique } : {}
533
+ ...c.unique !== void 0 ? { unique: c.unique } : {},
534
+ ...c.defaultValue !== void 0 ? { defaultValue: c.defaultValue } : {}
534
535
  })),
535
536
  ...input.indexes ? { indexes: input.indexes.map((i) => ({
536
537
  name: i.name,
@@ -615,29 +616,6 @@ var SqliteSettingsBackend = class SqliteSettingsBackend {
615
616
  const { whereSql, whereValues } = this.buildWhere(filter);
616
617
  return this.getDb().prepare(`DELETE FROM "${table}"${whereSql}`).run(...whereValues).changes;
617
618
  }
618
- async tableQuery(table, options) {
619
- let sql = `SELECT * FROM "${table}"`;
620
- const values = [];
621
- if (options?.where) {
622
- const { whereSql, whereValues } = this.buildWhere(options.where);
623
- sql += whereSql;
624
- values.push(...whereValues);
625
- }
626
- if (options?.orderBy) sql += ` ORDER BY "${options.orderBy.field}" ${options.orderBy.direction === "desc" ? "DESC" : "ASC"}`;
627
- if (options?.limit !== void 0) {
628
- sql += ` LIMIT ?`;
629
- values.push(options.limit);
630
- }
631
- if (options?.offset !== void 0) {
632
- sql += ` OFFSET ?`;
633
- values.push(options.offset);
634
- }
635
- return this.getDb().prepare(sql).all(...values).flatMap((r) => asJsonObject(r) ? [asJsonObject(r)] : []);
636
- }
637
- async tableGet(table, filter) {
638
- const { whereSql, whereValues } = this.buildWhere(filter);
639
- return asJsonObject(this.getDb().prepare(`SELECT * FROM "${table}"${whereSql} LIMIT 1`).get(...whereValues));
640
- }
641
619
  async tableCount(table, filter) {
642
620
  let sql = `SELECT COUNT(*) as count FROM "${table}"`;
643
621
  const values = [];
@@ -1,4 +1,4 @@
1
- import { ISettingsBackend, StorageLocation } from '@camstack/types';
1
+ import { ISettingsStoreProvider, StorageLocation } from '@camstack/types';
2
2
  /**
3
3
  * Abstract storage interface for `StorageLocation` records. Methods are
4
4
  * async because production-side implementations hit SQLite — but the
@@ -14,35 +14,28 @@ export interface ILocationStore {
14
14
  delete(id: string): Promise<void>;
15
15
  }
16
16
  /**
17
- * SQLite-backed implementation of `ILocationStore` — uses the
18
- * `settings-store` cap's structured-table surface (`ensureTable` /
19
- * `tableInsert` / `tableUpdate` / `tableDelete` / `tableQuery`). Wired
20
- * to the live `ISettingsBackend` provider obtained from
21
- * `kernel.capabilityRegistry.getSingleton('settings-store')`.
22
- *
23
- * The cap-router surface (`ctx.api.settingsStore.*`) does NOT expose
24
- * the `tableXxx` methods — they're only on the raw `ISettingsBackend`.
25
- * Same constraint that drives `IntegrationRegistry` to take a direct
26
- * provider reference instead of going through the cap.
17
+ * `ILocationStore` on the `settings-store` collection surface the same door
18
+ * every other consumer uses. It holds the `data-store-provider` engine object
19
+ * directly (not the `settings-store` singleton) because since D44 the
20
+ * storage-orchestrator IS that singleton, and asking the registry for it would
21
+ * route this table back through the orchestrator itself.
27
22
  */
28
23
  export declare class SqliteLocationStore implements ILocationStore {
29
- private readonly backend;
30
- private tableEnsured;
31
- constructor(backend: ISettingsBackend);
24
+ private readonly store;
25
+ private declared;
26
+ constructor(store: ISettingsStoreProvider);
32
27
  /**
33
- * Lazy `ensureTable` — keeps the constructor cheap and async-free.
34
- * Called from every mutation/read path; the backend's `ensureTable`
35
- * is itself idempotent (additive migration), so repeated calls are
36
- * effectively no-ops after the first.
28
+ * Lazy `declareCollection` — keeps the constructor cheap and async-free.
29
+ * Called from every mutation/read path; `declareCollection` is idempotent
30
+ * (and additive on shape changes), so repeated calls collapse to a no-op.
37
31
  */
38
- private ensureTable;
32
+ private ensureDeclared;
39
33
  loadAll(): Promise<readonly StorageLocation[]>;
40
34
  /**
41
- * Replace-or-insert. Implemented as `delete` + `insert` rather than
42
- * the conditional `tableUpdate` path because the orchestrator
43
- * serializes every mutation through the in-memory map before
44
- * dispatching to the store — there's no contention to lose, and the
45
- * delete-then-insert path is unconditionally simpler.
35
+ * Replace-or-insert. Implemented as delete + insert rather than a
36
+ * conditional update because the orchestrator serializes every mutation
37
+ * through the in-memory map before dispatching to the store — there's no
38
+ * contention to lose, and delete-then-insert is unconditionally simpler.
46
39
  */
47
40
  upsert(loc: StorageLocation): Promise<void>;
48
41
  delete(id: string): Promise<void>;
@@ -35,12 +35,6 @@ export declare class StorageOrchestratorAddon extends BaseAddon<StorageOrchestra
35
35
  * addon *is* that singleton, so asking the registry for it would hand
36
36
  * the orchestrator its own dispatcher and route its location table back
37
37
  * through itself.
38
- *
39
- * The `ensureTable` / `tableInsert` reach-around below is the B2 bypass,
40
- * still open: those six methods live on the concrete backend and on no
41
- * cap. Promoting them to the door is a separate task; until then this
42
- * holds the engine object directly, which only works because the engine
43
- * is in-process on this node.
44
38
  */
45
39
  private resolveLocationStore;
46
40
  /**
@@ -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-VIwdvws5.js");
6
+ const require_dist = require("../../dist-CDv5YTHQ.js");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_chunk.__toESM(node_path);
9
9
  let node_fs_promises = require("node:fs/promises");
@@ -535,7 +535,7 @@ var StorageOrchestratorService = class {
535
535
  };
536
536
  //#endregion
537
537
  //#region src/builtins/storage-orchestrator/location-store.ts
538
- var STORAGE_LOCATIONS_TABLE = "storage_locations";
538
+ var STORAGE_LOCATIONS = "storage_locations";
539
539
  /**
540
540
  * Reserved key under which the cluster `nodeId` (SP1) is persisted INSIDE
541
541
  * the `config` JSON blob. Storing it in the blob rather than a dedicated
@@ -548,125 +548,147 @@ var STORAGE_LOCATIONS_TABLE = "storage_locations";
548
548
  * API consumers and providers never see it.
549
549
  */
550
550
  var NODE_ID_CONFIG_KEY = "__nodeId";
551
- var STORAGE_LOCATIONS_SCHEMA = {
552
- columns: [
553
- {
554
- name: "id",
555
- type: "TEXT",
556
- primaryKey: true
557
- },
558
- {
559
- name: "type",
560
- type: "TEXT",
561
- notNull: true
562
- },
563
- {
564
- name: "display_name",
565
- type: "TEXT",
566
- notNull: true
567
- },
568
- {
569
- name: "provider_id",
570
- type: "TEXT",
571
- notNull: true
572
- },
573
- {
574
- name: "config",
575
- type: "TEXT",
576
- notNull: true,
577
- defaultValue: "{}"
578
- },
579
- {
580
- name: "is_default",
581
- type: "INTEGER",
582
- notNull: true,
583
- defaultValue: 0
584
- },
585
- {
586
- name: "is_system",
587
- type: "INTEGER",
588
- notNull: true,
589
- defaultValue: 0
590
- },
591
- {
592
- name: "created_at",
593
- type: "INTEGER",
594
- notNull: true
595
- },
596
- {
597
- name: "updated_at",
598
- type: "INTEGER",
599
- notNull: true
600
- }
601
- ],
602
- indexes: [{
603
- name: "idx_storage_locations_type",
604
- columns: ["type"]
605
- }]
606
- };
607
551
  /**
608
- * SQLite-backed implementation of `ILocationStore` uses the
609
- * `settings-store` cap's structured-table surface (`ensureTable` /
610
- * `tableInsert` / `tableUpdate` / `tableDelete` / `tableQuery`). Wired
611
- * to the live `ISettingsBackend` provider obtained from
612
- * `kernel.capabilityRegistry.getSingleton('settings-store')`.
613
- *
614
- * The cap-router surface (`ctx.api.settingsStore.*`) does NOT expose
615
- * the `tableXxx` methods — they're only on the raw `ISettingsBackend`.
616
- * Same constraint that drives `IntegrationRegistry` to take a direct
617
- * provider reference instead of going through the cap.
552
+ * `config` is declared `JSON`, so the store hands the surface an object and
553
+ * gets an object back. There is deliberately NO `node_id` column `nodeId`
554
+ * rides inside the blob under a reserved key (see {@link NODE_ID_CONFIG_KEY}),
555
+ * because a newly-declared column would put this table through
556
+ * `declareCollection`'s destructive rebuild and wipe operator customizations
557
+ * such as `recordings:default`'s `minFreePercent`.
558
+ */
559
+ var STORAGE_LOCATIONS_COLUMNS = [
560
+ {
561
+ name: "id",
562
+ type: "TEXT",
563
+ primaryKey: true
564
+ },
565
+ {
566
+ name: "type",
567
+ type: "TEXT",
568
+ notNull: true
569
+ },
570
+ {
571
+ name: "display_name",
572
+ type: "TEXT",
573
+ notNull: true
574
+ },
575
+ {
576
+ name: "provider_id",
577
+ type: "TEXT",
578
+ notNull: true
579
+ },
580
+ {
581
+ name: "config",
582
+ type: "JSON",
583
+ notNull: true,
584
+ defaultValue: "{}"
585
+ },
586
+ {
587
+ name: "is_default",
588
+ type: "INTEGER",
589
+ notNull: true,
590
+ defaultValue: 0
591
+ },
592
+ {
593
+ name: "is_system",
594
+ type: "INTEGER",
595
+ notNull: true,
596
+ defaultValue: 0
597
+ },
598
+ {
599
+ name: "created_at",
600
+ type: "INTEGER",
601
+ notNull: true
602
+ },
603
+ {
604
+ name: "updated_at",
605
+ type: "INTEGER",
606
+ notNull: true
607
+ }
608
+ ];
609
+ var STORAGE_LOCATIONS_INDEXES = [{
610
+ name: "idx_storage_locations_type",
611
+ columns: ["type"]
612
+ }];
613
+ /**
614
+ * `ILocationStore` on the `settings-store` collection surface — the same door
615
+ * every other consumer uses. It holds the `data-store-provider` engine object
616
+ * directly (not the `settings-store` singleton) because since D44 the
617
+ * storage-orchestrator IS that singleton, and asking the registry for it would
618
+ * route this table back through the orchestrator itself.
618
619
  */
619
620
  var SqliteLocationStore = class {
620
- backend;
621
- tableEnsured = false;
622
- constructor(backend) {
623
- this.backend = backend;
621
+ store;
622
+ declared = false;
623
+ constructor(store) {
624
+ this.store = store;
624
625
  }
625
626
  /**
626
- * Lazy `ensureTable` — keeps the constructor cheap and async-free.
627
- * Called from every mutation/read path; the backend's `ensureTable`
628
- * is itself idempotent (additive migration), so repeated calls are
629
- * effectively no-ops after the first.
627
+ * Lazy `declareCollection` — keeps the constructor cheap and async-free.
628
+ * Called from every mutation/read path; `declareCollection` is idempotent
629
+ * (and additive on shape changes), so repeated calls collapse to a no-op.
630
630
  */
631
- async ensureTable() {
632
- if (this.tableEnsured) return;
633
- if (!this.backend.ensureTable) throw new Error("SqliteLocationStore: settings backend does not implement ensureTable — expected the SQLite settings backend, got a backend without structured-table support");
634
- await this.backend.ensureTable(STORAGE_LOCATIONS_TABLE, STORAGE_LOCATIONS_SCHEMA);
635
- this.tableEnsured = true;
631
+ async ensureDeclared() {
632
+ if (this.declared) return;
633
+ await this.store.declareCollection({
634
+ collection: STORAGE_LOCATIONS,
635
+ columns: STORAGE_LOCATIONS_COLUMNS,
636
+ indexes: STORAGE_LOCATIONS_INDEXES
637
+ });
638
+ this.declared = true;
636
639
  }
637
640
  async loadAll() {
638
- await this.ensureTable();
639
- if (!this.backend.tableQuery) return [];
640
- const rows = await this.backend.tableQuery(STORAGE_LOCATIONS_TABLE, { orderBy: {
641
- field: "created_at",
642
- direction: "asc"
643
- } });
641
+ await this.ensureDeclared();
642
+ const rows = await this.store.query({
643
+ collection: STORAGE_LOCATIONS,
644
+ filter: { orderBy: {
645
+ field: "created_at",
646
+ direction: "asc"
647
+ } }
648
+ });
644
649
  const out = [];
645
650
  for (const row of rows) {
646
- const mapped = mapRowToLocation(row);
651
+ const mapped = mapRowToLocation(row.data);
647
652
  if (mapped) out.push(mapped);
648
653
  }
649
654
  return out;
650
655
  }
651
656
  /**
652
- * Replace-or-insert. Implemented as `delete` + `insert` rather than
653
- * the conditional `tableUpdate` path because the orchestrator
654
- * serializes every mutation through the in-memory map before
655
- * dispatching to the store — there's no contention to lose, and the
656
- * delete-then-insert path is unconditionally simpler.
657
+ * Replace-or-insert. Implemented as delete + insert rather than a
658
+ * conditional update because the orchestrator serializes every mutation
659
+ * through the in-memory map before dispatching to the store — there's no
660
+ * contention to lose, and delete-then-insert is unconditionally simpler.
657
661
  */
658
662
  async upsert(loc) {
659
- await this.ensureTable();
660
- if (!this.backend.tableDelete || !this.backend.tableInsert) throw new Error("SqliteLocationStore: backend missing tableDelete/tableInsert");
661
- await this.backend.tableDelete(STORAGE_LOCATIONS_TABLE, { id: loc.id });
662
- await this.backend.tableInsert(STORAGE_LOCATIONS_TABLE, mapLocationToRow(loc));
663
+ await this.ensureDeclared();
664
+ await this.store.deleteWhere({
665
+ collection: STORAGE_LOCATIONS,
666
+ filter: { where: { id: loc.id } }
667
+ });
668
+ await this.store.insert({
669
+ collection: STORAGE_LOCATIONS,
670
+ record: {
671
+ id: loc.id,
672
+ data: mapLocationToRow(loc)
673
+ }
674
+ });
663
675
  }
664
676
  async delete(id) {
665
- await this.ensureTable();
666
- if (!this.backend.tableDelete) throw new Error("SqliteLocationStore: backend missing tableDelete");
667
- await this.backend.tableDelete(STORAGE_LOCATIONS_TABLE, { id });
677
+ await this.ensureDeclared();
678
+ await this.store.deleteWhere({
679
+ collection: STORAGE_LOCATIONS,
680
+ filter: { where: { id } }
681
+ });
668
682
  }
669
683
  };
684
+ function isPlainObject(value) {
685
+ return value !== null && typeof value === "object" && !Array.isArray(value);
686
+ }
687
+ function readConfig(raw) {
688
+ if (isPlainObject(raw)) return { ...raw };
689
+ if (typeof raw === "string" && raw.length > 0) return require_dist.parseJsonObject(raw) ?? {};
690
+ return {};
691
+ }
670
692
  function mapLocationToRow(loc) {
671
693
  const storedConfig = loc.nodeId !== void 0 ? {
672
694
  ...loc.config,
@@ -677,7 +699,7 @@ function mapLocationToRow(loc) {
677
699
  type: loc.type,
678
700
  display_name: loc.displayName,
679
701
  provider_id: loc.providerId,
680
- config: JSON.stringify(storedConfig),
702
+ config: storedConfig,
681
703
  is_default: loc.isDefault ? 1 : 0,
682
704
  is_system: loc.isSystem ? 1 : 0,
683
705
  created_at: loc.createdAt,
@@ -685,11 +707,9 @@ function mapLocationToRow(loc) {
685
707
  };
686
708
  }
687
709
  /**
688
- * Decode a row read from the structured table. Returns `null` when the
689
- * row's `type` doesn't parse against the Zod enumthe caller skips
690
- * the row and logs. Defensive against schema drift across upgrades
691
- * (e.g. an older build wrote a since-removed `recordings-archive`
692
- * type and the new build no longer recognises it).
710
+ * Decode a row read from the collection. Returns `null` when the row's `type`
711
+ * fails the id-format schema or the row has no id a corrupt row (hand-edited,
712
+ * partial migration) is skipped rather than taking the boot read down with it.
693
713
  */
694
714
  function mapRowToLocation(row) {
695
715
  const typeRaw = row["type"];
@@ -698,8 +718,7 @@ function mapRowToLocation(row) {
698
718
  const type = parsedType.data;
699
719
  const id = String(row["id"] ?? "");
700
720
  if (!id) return null;
701
- const configRaw = row["config"];
702
- const storedConfig = typeof configRaw === "string" && configRaw.length > 0 ? require_dist.parseJsonObject(configRaw) ?? {} : {};
721
+ const storedConfig = readConfig(row["config"]);
703
722
  const nodeIdRaw = storedConfig[NODE_ID_CONFIG_KEY];
704
723
  const nodeId = typeof nodeIdRaw === "string" && nodeIdRaw.length > 0 ? nodeIdRaw : void 0;
705
724
  const config = { ...storedConfig };
@@ -1210,26 +1229,16 @@ var StorageOrchestratorAddon = class extends require_dist.BaseAddon {
1210
1229
  * addon *is* that singleton, so asking the registry for it would hand
1211
1230
  * the orchestrator its own dispatcher and route its location table back
1212
1231
  * through itself.
1213
- *
1214
- * The `ensureTable` / `tableInsert` reach-around below is the B2 bypass,
1215
- * still open: those six methods live on the concrete backend and on no
1216
- * cap. Promoting them to the door is a separate task; until then this
1217
- * holds the engine object directly, which only works because the engine
1218
- * is in-process on this node.
1219
1232
  */
1220
1233
  resolveLocationStore(quiet = false) {
1221
1234
  const reg = this.ctx.kernel.capabilityRegistry;
1222
1235
  if (!reg) return null;
1223
- const backend = reg.getCollection(DATA_STORE_PROVIDER_CAP_NAME)[0];
1224
- if (!backend) {
1236
+ const engine = reg.getCollection(DATA_STORE_PROVIDER_CAP_NAME)[0];
1237
+ if (!engine) {
1225
1238
  if (!quiet) this.ctx.logger.warn("storage-orchestrator: no data-store-provider engine yet — running in-memory until one registers");
1226
1239
  return null;
1227
1240
  }
1228
- if (!backend.ensureTable || !backend.tableInsert) {
1229
- if (!quiet) this.ctx.logger.warn("storage-orchestrator: settings backend lacks structured-table support — running in-memory only");
1230
- return null;
1231
- }
1232
- return new SqliteLocationStore(backend);
1241
+ return new SqliteLocationStore(engine);
1233
1242
  }
1234
1243
  /**
1235
1244
  * Late-wire the persistence store once a `data-store-provider` engine