@camstack/system 1.2.29 → 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 (53) 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/filter-compiler.d.ts +61 -0
  36. package/dist/builtins/sqlite-storage/integration-registry.d.ts +3 -3
  37. package/dist/builtins/sqlite-storage/sqlite-settings-backend.d.ts +49 -8
  38. package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +117 -85
  39. package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +117 -85
  40. package/dist/builtins/storage-orchestrator/location-store.d.ts +17 -24
  41. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +0 -6
  42. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +132 -121
  43. package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +132 -121
  44. package/dist/builtins/system-config/system-config.addon.js +1 -1
  45. package/dist/builtins/system-config/system-config.addon.mjs +1 -1
  46. package/dist/builtins/winston-logging/index.js +1 -1
  47. package/dist/builtins/winston-logging/index.mjs +1 -1
  48. package/dist/{dist-CPmFYLqq.js → dist-CDv5YTHQ.js} +93 -1
  49. package/dist/{dist-B7hOpr5i.mjs → dist-CNmXITJ-.mjs} +93 -1
  50. package/dist/index.js +406 -224
  51. package/dist/index.mjs +406 -224
  52. package/dist/kernel/addon-installer.d.ts +38 -0
  53. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { $ as storageCapability, Et as parseJsonObject, Z as settingsStoreCapability, d as StorageLocationTypeSchema, ot as BaseAddon } from "../../dist-B7hOpr5i.mjs";
1
+ import { $ as storageCapability, Et as parseJsonObject, Z as settingsStoreCapability, d as StorageLocationTypeSchema, ot as BaseAddon } from "../../dist-CNmXITJ-.mjs";
2
2
  import * as path$1 from "node:path";
3
3
  import * as fs from "node:fs/promises";
4
4
  import { buildStorageLocationRegistry } from "@camstack/system";
@@ -528,7 +528,7 @@ var StorageOrchestratorService = class {
528
528
  };
529
529
  //#endregion
530
530
  //#region src/builtins/storage-orchestrator/location-store.ts
531
- var STORAGE_LOCATIONS_TABLE = "storage_locations";
531
+ var STORAGE_LOCATIONS = "storage_locations";
532
532
  /**
533
533
  * Reserved key under which the cluster `nodeId` (SP1) is persisted INSIDE
534
534
  * the `config` JSON blob. Storing it in the blob rather than a dedicated
@@ -541,125 +541,147 @@ var STORAGE_LOCATIONS_TABLE = "storage_locations";
541
541
  * API consumers and providers never see it.
542
542
  */
543
543
  var NODE_ID_CONFIG_KEY = "__nodeId";
544
- var STORAGE_LOCATIONS_SCHEMA = {
545
- columns: [
546
- {
547
- name: "id",
548
- type: "TEXT",
549
- primaryKey: true
550
- },
551
- {
552
- name: "type",
553
- type: "TEXT",
554
- notNull: true
555
- },
556
- {
557
- name: "display_name",
558
- type: "TEXT",
559
- notNull: true
560
- },
561
- {
562
- name: "provider_id",
563
- type: "TEXT",
564
- notNull: true
565
- },
566
- {
567
- name: "config",
568
- type: "TEXT",
569
- notNull: true,
570
- defaultValue: "{}"
571
- },
572
- {
573
- name: "is_default",
574
- type: "INTEGER",
575
- notNull: true,
576
- defaultValue: 0
577
- },
578
- {
579
- name: "is_system",
580
- type: "INTEGER",
581
- notNull: true,
582
- defaultValue: 0
583
- },
584
- {
585
- name: "created_at",
586
- type: "INTEGER",
587
- notNull: true
588
- },
589
- {
590
- name: "updated_at",
591
- type: "INTEGER",
592
- notNull: true
593
- }
594
- ],
595
- indexes: [{
596
- name: "idx_storage_locations_type",
597
- columns: ["type"]
598
- }]
599
- };
600
544
  /**
601
- * SQLite-backed implementation of `ILocationStore` uses the
602
- * `settings-store` cap's structured-table surface (`ensureTable` /
603
- * `tableInsert` / `tableUpdate` / `tableDelete` / `tableQuery`). Wired
604
- * to the live `ISettingsBackend` provider obtained from
605
- * `kernel.capabilityRegistry.getSingleton('settings-store')`.
606
- *
607
- * The cap-router surface (`ctx.api.settingsStore.*`) does NOT expose
608
- * the `tableXxx` methods — they're only on the raw `ISettingsBackend`.
609
- * Same constraint that drives `IntegrationRegistry` to take a direct
610
- * provider reference instead of going through the cap.
545
+ * `config` is declared `JSON`, so the store hands the surface an object and
546
+ * gets an object back. There is deliberately NO `node_id` column `nodeId`
547
+ * rides inside the blob under a reserved key (see {@link NODE_ID_CONFIG_KEY}),
548
+ * because a newly-declared column would put this table through
549
+ * `declareCollection`'s destructive rebuild and wipe operator customizations
550
+ * such as `recordings:default`'s `minFreePercent`.
551
+ */
552
+ var STORAGE_LOCATIONS_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: "JSON",
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
+ var STORAGE_LOCATIONS_INDEXES = [{
603
+ name: "idx_storage_locations_type",
604
+ columns: ["type"]
605
+ }];
606
+ /**
607
+ * `ILocationStore` on the `settings-store` collection surface — the same door
608
+ * every other consumer uses. It holds the `data-store-provider` engine object
609
+ * directly (not the `settings-store` singleton) because since D44 the
610
+ * storage-orchestrator IS that singleton, and asking the registry for it would
611
+ * route this table back through the orchestrator itself.
611
612
  */
612
613
  var SqliteLocationStore = class {
613
- backend;
614
- tableEnsured = false;
615
- constructor(backend) {
616
- this.backend = backend;
614
+ store;
615
+ declared = false;
616
+ constructor(store) {
617
+ this.store = store;
617
618
  }
618
619
  /**
619
- * Lazy `ensureTable` — keeps the constructor cheap and async-free.
620
- * Called from every mutation/read path; the backend's `ensureTable`
621
- * is itself idempotent (additive migration), so repeated calls are
622
- * effectively no-ops after the first.
620
+ * Lazy `declareCollection` — keeps the constructor cheap and async-free.
621
+ * Called from every mutation/read path; `declareCollection` is idempotent
622
+ * (and additive on shape changes), so repeated calls collapse to a no-op.
623
623
  */
624
- async ensureTable() {
625
- if (this.tableEnsured) return;
626
- 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");
627
- await this.backend.ensureTable(STORAGE_LOCATIONS_TABLE, STORAGE_LOCATIONS_SCHEMA);
628
- this.tableEnsured = true;
624
+ async ensureDeclared() {
625
+ if (this.declared) return;
626
+ await this.store.declareCollection({
627
+ collection: STORAGE_LOCATIONS,
628
+ columns: STORAGE_LOCATIONS_COLUMNS,
629
+ indexes: STORAGE_LOCATIONS_INDEXES
630
+ });
631
+ this.declared = true;
629
632
  }
630
633
  async loadAll() {
631
- await this.ensureTable();
632
- if (!this.backend.tableQuery) return [];
633
- const rows = await this.backend.tableQuery(STORAGE_LOCATIONS_TABLE, { orderBy: {
634
- field: "created_at",
635
- direction: "asc"
636
- } });
634
+ await this.ensureDeclared();
635
+ const rows = await this.store.query({
636
+ collection: STORAGE_LOCATIONS,
637
+ filter: { orderBy: {
638
+ field: "created_at",
639
+ direction: "asc"
640
+ } }
641
+ });
637
642
  const out = [];
638
643
  for (const row of rows) {
639
- const mapped = mapRowToLocation(row);
644
+ const mapped = mapRowToLocation(row.data);
640
645
  if (mapped) out.push(mapped);
641
646
  }
642
647
  return out;
643
648
  }
644
649
  /**
645
- * Replace-or-insert. Implemented as `delete` + `insert` rather than
646
- * the conditional `tableUpdate` path because the orchestrator
647
- * serializes every mutation through the in-memory map before
648
- * dispatching to the store — there's no contention to lose, and the
649
- * delete-then-insert path is unconditionally simpler.
650
+ * Replace-or-insert. Implemented as delete + insert rather than a
651
+ * conditional update because the orchestrator serializes every mutation
652
+ * through the in-memory map before dispatching to the store — there's no
653
+ * contention to lose, and delete-then-insert is unconditionally simpler.
650
654
  */
651
655
  async upsert(loc) {
652
- await this.ensureTable();
653
- if (!this.backend.tableDelete || !this.backend.tableInsert) throw new Error("SqliteLocationStore: backend missing tableDelete/tableInsert");
654
- await this.backend.tableDelete(STORAGE_LOCATIONS_TABLE, { id: loc.id });
655
- await this.backend.tableInsert(STORAGE_LOCATIONS_TABLE, mapLocationToRow(loc));
656
+ await this.ensureDeclared();
657
+ await this.store.deleteWhere({
658
+ collection: STORAGE_LOCATIONS,
659
+ filter: { where: { id: loc.id } }
660
+ });
661
+ await this.store.insert({
662
+ collection: STORAGE_LOCATIONS,
663
+ record: {
664
+ id: loc.id,
665
+ data: mapLocationToRow(loc)
666
+ }
667
+ });
656
668
  }
657
669
  async delete(id) {
658
- await this.ensureTable();
659
- if (!this.backend.tableDelete) throw new Error("SqliteLocationStore: backend missing tableDelete");
660
- await this.backend.tableDelete(STORAGE_LOCATIONS_TABLE, { id });
670
+ await this.ensureDeclared();
671
+ await this.store.deleteWhere({
672
+ collection: STORAGE_LOCATIONS,
673
+ filter: { where: { id } }
674
+ });
661
675
  }
662
676
  };
677
+ function isPlainObject(value) {
678
+ return value !== null && typeof value === "object" && !Array.isArray(value);
679
+ }
680
+ function readConfig(raw) {
681
+ if (isPlainObject(raw)) return { ...raw };
682
+ if (typeof raw === "string" && raw.length > 0) return parseJsonObject(raw) ?? {};
683
+ return {};
684
+ }
663
685
  function mapLocationToRow(loc) {
664
686
  const storedConfig = loc.nodeId !== void 0 ? {
665
687
  ...loc.config,
@@ -670,7 +692,7 @@ function mapLocationToRow(loc) {
670
692
  type: loc.type,
671
693
  display_name: loc.displayName,
672
694
  provider_id: loc.providerId,
673
- config: JSON.stringify(storedConfig),
695
+ config: storedConfig,
674
696
  is_default: loc.isDefault ? 1 : 0,
675
697
  is_system: loc.isSystem ? 1 : 0,
676
698
  created_at: loc.createdAt,
@@ -678,11 +700,9 @@ function mapLocationToRow(loc) {
678
700
  };
679
701
  }
680
702
  /**
681
- * Decode a row read from the structured table. Returns `null` when the
682
- * row's `type` doesn't parse against the Zod enumthe caller skips
683
- * the row and logs. Defensive against schema drift across upgrades
684
- * (e.g. an older build wrote a since-removed `recordings-archive`
685
- * type and the new build no longer recognises it).
703
+ * Decode a row read from the collection. Returns `null` when the row's `type`
704
+ * fails the id-format schema or the row has no id a corrupt row (hand-edited,
705
+ * partial migration) is skipped rather than taking the boot read down with it.
686
706
  */
687
707
  function mapRowToLocation(row) {
688
708
  const typeRaw = row["type"];
@@ -691,8 +711,7 @@ function mapRowToLocation(row) {
691
711
  const type = parsedType.data;
692
712
  const id = String(row["id"] ?? "");
693
713
  if (!id) return null;
694
- const configRaw = row["config"];
695
- const storedConfig = typeof configRaw === "string" && configRaw.length > 0 ? parseJsonObject(configRaw) ?? {} : {};
714
+ const storedConfig = readConfig(row["config"]);
696
715
  const nodeIdRaw = storedConfig[NODE_ID_CONFIG_KEY];
697
716
  const nodeId = typeof nodeIdRaw === "string" && nodeIdRaw.length > 0 ? nodeIdRaw : void 0;
698
717
  const config = { ...storedConfig };
@@ -763,6 +782,8 @@ function createDataStoreDispatch(getEngines) {
763
782
  insert: async (input) => (await engine()).insert(input),
764
783
  update: async (input) => (await engine()).update(input),
765
784
  delete: async (input) => (await engine()).delete(input),
785
+ deleteWhere: async (input) => (await engine()).deleteWhere(input),
786
+ updateWhere: async (input) => (await engine()).updateWhere(input),
766
787
  count: async (input) => (await engine()).count(input),
767
788
  histogram: async (input) => (await engine()).histogram(input),
768
789
  isEmpty: async (input) => (await engine()).isEmpty(input),
@@ -1201,26 +1222,16 @@ var StorageOrchestratorAddon = class extends BaseAddon {
1201
1222
  * addon *is* that singleton, so asking the registry for it would hand
1202
1223
  * the orchestrator its own dispatcher and route its location table back
1203
1224
  * through itself.
1204
- *
1205
- * The `ensureTable` / `tableInsert` reach-around below is the B2 bypass,
1206
- * still open: those six methods live on the concrete backend and on no
1207
- * cap. Promoting them to the door is a separate task; until then this
1208
- * holds the engine object directly, which only works because the engine
1209
- * is in-process on this node.
1210
1225
  */
1211
1226
  resolveLocationStore(quiet = false) {
1212
1227
  const reg = this.ctx.kernel.capabilityRegistry;
1213
1228
  if (!reg) return null;
1214
- const backend = reg.getCollection(DATA_STORE_PROVIDER_CAP_NAME)[0];
1215
- if (!backend) {
1229
+ const engine = reg.getCollection(DATA_STORE_PROVIDER_CAP_NAME)[0];
1230
+ if (!engine) {
1216
1231
  if (!quiet) this.ctx.logger.warn("storage-orchestrator: no data-store-provider engine yet — running in-memory until one registers");
1217
1232
  return null;
1218
1233
  }
1219
- if (!backend.ensureTable || !backend.tableInsert) {
1220
- if (!quiet) this.ctx.logger.warn("storage-orchestrator: settings backend lacks structured-table support — running in-memory only");
1221
- return null;
1222
- }
1223
- return new SqliteLocationStore(backend);
1234
+ return new SqliteLocationStore(engine);
1224
1235
  }
1225
1236
  /**
1226
1237
  * Late-wire the persistence store once a `data-store-provider` engine
@@ -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-CPmFYLqq.js");
6
+ const require_dist = require("../../dist-CDv5YTHQ.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 { Ct as hydrateSchema, at as errMsg, ot as BaseAddon } from "../../dist-B7hOpr5i.mjs";
1
+ import { Ct as hydrateSchema, at as errMsg, ot as BaseAddon } from "../../dist-CNmXITJ-.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-CPmFYLqq.js");
6
+ const require_dist = require("../../dist-CDv5YTHQ.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 { B as logDestinationCapability, ot as BaseAddon } from "../../dist-B7hOpr5i.mjs";
1
+ import { B as logDestinationCapability, ot as BaseAddon } from "../../dist-CNmXITJ-.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";
@@ -14544,6 +14544,20 @@ var QueryFilterSchema = zod.z.object({
14544
14544
  limit: zod.z.number().optional(),
14545
14545
  offset: zod.z.number().optional()
14546
14546
  });
14547
+ /**
14548
+ * The predicate half of a filter, for BULK MUTATIONS.
14549
+ *
14550
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
14551
+ * meaning for a statement that rewrites a set, and accepting them would invite
14552
+ * a caller to believe `limit` bounds the damage. Every field is optional here
14553
+ * only so the shape stays composable — the implementation REJECTS a filter
14554
+ * that compiles to no predicate, because that is the whole collection.
14555
+ */
14556
+ var MutationFilterSchema = zod.z.object({
14557
+ where: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
14558
+ whereIn: zod.z.record(zod.z.string(), zod.z.array(zod.z.unknown())).optional(),
14559
+ whereBetween: zod.z.record(zod.z.string(), zod.z.tuple([zod.z.unknown(), zod.z.unknown()])).optional()
14560
+ });
14547
14561
  /** A single stored record: `{ id, data }`. */
14548
14562
  var SettingsRecordSchema = zod.z.object({
14549
14563
  id: zod.z.string(),
@@ -14569,7 +14583,18 @@ var CollectionColumnSchema = zod.z.object({
14569
14583
  ]),
14570
14584
  primaryKey: zod.z.boolean().optional(),
14571
14585
  notNull: zod.z.boolean().optional(),
14572
- unique: zod.z.boolean().optional()
14586
+ unique: zod.z.boolean().optional(),
14587
+ /**
14588
+ * Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
14589
+ * columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
14590
+ * collection surface simply cannot express their existing tables — which is
14591
+ * why they were still on the uncapped `table*` API.
14592
+ */
14593
+ defaultValue: zod.z.union([
14594
+ zod.z.string(),
14595
+ zod.z.number(),
14596
+ zod.z.boolean()
14597
+ ]).optional()
14573
14598
  });
14574
14599
  var CollectionIndexSchema = zod.z.object({
14575
14600
  name: zod.z.string(),
@@ -14649,6 +14674,36 @@ var settingsStoreCapability = {
14649
14674
  collection: zod.z.string(),
14650
14675
  key: zod.z.string()
14651
14676
  }), zod.z.void(), { kind: "mutation" }),
14677
+ /**
14678
+ * Delete every record matching `filter`, in ONE statement, returning how
14679
+ * many rows went. This exists because its absence made every retention
14680
+ * path in the system an N+1 drain loop: `delete` takes a key, so a sweep
14681
+ * had to SELECT a page of full rows — every column, including the fat
14682
+ * ones — purely to learn their ids, then issue one call per row.
14683
+ *
14684
+ * **The filter is required and must resolve.** A predicate naming
14685
+ * something the collection cannot express is an ERROR here, not a
14686
+ * widening as it is on `query`, and a filter with no predicates is an
14687
+ * error rather than "every row". Deleting a whole collection is a
14688
+ * legitimate intent, but it must be asked for by name — not reached by
14689
+ * an empty object.
14690
+ */
14691
+ deleteWhere: method(zod.z.object({
14692
+ namespace: zod.z.string().optional(),
14693
+ collection: zod.z.string(),
14694
+ filter: MutationFilterSchema
14695
+ }), zod.z.object({ deleted: zod.z.number().int() }), { kind: "mutation" }),
14696
+ /**
14697
+ * Apply `data` to every record matching `filter`, in one statement,
14698
+ * returning how many rows changed. Same filter contract as
14699
+ * {@link deleteWhere} — an unresolvable predicate is an error.
14700
+ */
14701
+ updateWhere: method(zod.z.object({
14702
+ namespace: zod.z.string().optional(),
14703
+ collection: zod.z.string(),
14704
+ filter: MutationFilterSchema,
14705
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
14706
+ }), zod.z.object({ updated: zod.z.number().int() }), { kind: "mutation" }),
14652
14707
  /** Count entries in a collection, optionally filtered. */
14653
14708
  count: method(zod.z.object({
14654
14709
  namespace: zod.z.string().optional(),
@@ -14784,6 +14839,19 @@ var dataStoreProviderCapability = {
14784
14839
  collection: zod.z.string(),
14785
14840
  key: zod.z.string()
14786
14841
  }), zod.z.void(), { kind: "mutation" }),
14842
+ /** Delete every record matching `filter`, in one statement. */
14843
+ deleteWhere: method(zod.z.object({
14844
+ namespace: zod.z.string().optional(),
14845
+ collection: zod.z.string(),
14846
+ filter: MutationFilterSchema
14847
+ }), zod.z.object({ deleted: zod.z.number().int() }), { kind: "mutation" }),
14848
+ /** Apply `data` to every record matching `filter`, in one statement. */
14849
+ updateWhere: method(zod.z.object({
14850
+ namespace: zod.z.string().optional(),
14851
+ collection: zod.z.string(),
14852
+ filter: MutationFilterSchema,
14853
+ data: zod.z.record(zod.z.string(), zod.z.unknown())
14854
+ }), zod.z.object({ updated: zod.z.number().int() }), { kind: "mutation" }),
14787
14855
  /** Count entries in a collection, optionally filtered. */
14788
14856
  count: method(zod.z.object({
14789
14857
  namespace: zod.z.string().optional(),
@@ -24963,6 +25031,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
24963
25031
  addonId: null,
24964
25032
  access: "delete"
24965
25033
  },
25034
+ "dataStoreProvider.deleteWhere": {
25035
+ capName: "data-store-provider",
25036
+ capScope: "system",
25037
+ addonId: null,
25038
+ access: "delete"
25039
+ },
24966
25040
  "dataStoreProvider.get": {
24967
25041
  capName: "data-store-provider",
24968
25042
  capScope: "system",
@@ -25011,6 +25085,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
25011
25085
  addonId: null,
25012
25086
  access: "create"
25013
25087
  },
25088
+ "dataStoreProvider.updateWhere": {
25089
+ capName: "data-store-provider",
25090
+ capScope: "system",
25091
+ addonId: null,
25092
+ access: "create"
25093
+ },
25014
25094
  "dayNight.getOptions": {
25015
25095
  capName: "day-night",
25016
25096
  capScope: "device",
@@ -28089,6 +28169,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28089
28169
  addonId: null,
28090
28170
  access: "delete"
28091
28171
  },
28172
+ "settingsStore.deleteWhere": {
28173
+ capName: "settings-store",
28174
+ capScope: "system",
28175
+ addonId: null,
28176
+ access: "delete"
28177
+ },
28092
28178
  "settingsStore.get": {
28093
28179
  capName: "settings-store",
28094
28180
  capScope: "system",
@@ -28131,6 +28217,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28131
28217
  addonId: null,
28132
28218
  access: "create"
28133
28219
  },
28220
+ "settingsStore.updateWhere": {
28221
+ capName: "settings-store",
28222
+ capScope: "system",
28223
+ addonId: null,
28224
+ access: "create"
28225
+ },
28134
28226
  "smtpProvider.getStatus": {
28135
28227
  capName: "smtp-provider",
28136
28228
  capScope: "system",
@@ -14544,6 +14544,20 @@ var QueryFilterSchema = z.object({
14544
14544
  limit: z.number().optional(),
14545
14545
  offset: z.number().optional()
14546
14546
  });
14547
+ /**
14548
+ * The predicate half of a filter, for BULK MUTATIONS.
14549
+ *
14550
+ * Deliberately not `QueryFilterSchema`: `orderBy` / `limit` / `offset` have no
14551
+ * meaning for a statement that rewrites a set, and accepting them would invite
14552
+ * a caller to believe `limit` bounds the damage. Every field is optional here
14553
+ * only so the shape stays composable — the implementation REJECTS a filter
14554
+ * that compiles to no predicate, because that is the whole collection.
14555
+ */
14556
+ var MutationFilterSchema = z.object({
14557
+ where: z.record(z.string(), z.unknown()).optional(),
14558
+ whereIn: z.record(z.string(), z.array(z.unknown())).optional(),
14559
+ whereBetween: z.record(z.string(), z.tuple([z.unknown(), z.unknown()])).optional()
14560
+ });
14547
14561
  /** A single stored record: `{ id, data }`. */
14548
14562
  var SettingsRecordSchema = z.object({
14549
14563
  id: z.string(),
@@ -14569,7 +14583,18 @@ var CollectionColumnSchema = z.object({
14569
14583
  ]),
14570
14584
  primaryKey: z.boolean().optional(),
14571
14585
  notNull: z.boolean().optional(),
14572
- unique: z.boolean().optional()
14586
+ unique: z.boolean().optional(),
14587
+ /**
14588
+ * Column DEFAULT. Required for B2: the four `ensureTable` consumers declare
14589
+ * columns like `enabled INTEGER NOT NULL DEFAULT 1`, and without this the
14590
+ * collection surface simply cannot express their existing tables — which is
14591
+ * why they were still on the uncapped `table*` API.
14592
+ */
14593
+ defaultValue: z.union([
14594
+ z.string(),
14595
+ z.number(),
14596
+ z.boolean()
14597
+ ]).optional()
14573
14598
  });
14574
14599
  var CollectionIndexSchema = z.object({
14575
14600
  name: z.string(),
@@ -14649,6 +14674,36 @@ var settingsStoreCapability = {
14649
14674
  collection: z.string(),
14650
14675
  key: z.string()
14651
14676
  }), z.void(), { kind: "mutation" }),
14677
+ /**
14678
+ * Delete every record matching `filter`, in ONE statement, returning how
14679
+ * many rows went. This exists because its absence made every retention
14680
+ * path in the system an N+1 drain loop: `delete` takes a key, so a sweep
14681
+ * had to SELECT a page of full rows — every column, including the fat
14682
+ * ones — purely to learn their ids, then issue one call per row.
14683
+ *
14684
+ * **The filter is required and must resolve.** A predicate naming
14685
+ * something the collection cannot express is an ERROR here, not a
14686
+ * widening as it is on `query`, and a filter with no predicates is an
14687
+ * error rather than "every row". Deleting a whole collection is a
14688
+ * legitimate intent, but it must be asked for by name — not reached by
14689
+ * an empty object.
14690
+ */
14691
+ deleteWhere: method(z.object({
14692
+ namespace: z.string().optional(),
14693
+ collection: z.string(),
14694
+ filter: MutationFilterSchema
14695
+ }), z.object({ deleted: z.number().int() }), { kind: "mutation" }),
14696
+ /**
14697
+ * Apply `data` to every record matching `filter`, in one statement,
14698
+ * returning how many rows changed. Same filter contract as
14699
+ * {@link deleteWhere} — an unresolvable predicate is an error.
14700
+ */
14701
+ updateWhere: method(z.object({
14702
+ namespace: z.string().optional(),
14703
+ collection: z.string(),
14704
+ filter: MutationFilterSchema,
14705
+ data: z.record(z.string(), z.unknown())
14706
+ }), z.object({ updated: z.number().int() }), { kind: "mutation" }),
14652
14707
  /** Count entries in a collection, optionally filtered. */
14653
14708
  count: method(z.object({
14654
14709
  namespace: z.string().optional(),
@@ -14784,6 +14839,19 @@ var dataStoreProviderCapability = {
14784
14839
  collection: z.string(),
14785
14840
  key: z.string()
14786
14841
  }), z.void(), { kind: "mutation" }),
14842
+ /** Delete every record matching `filter`, in one statement. */
14843
+ deleteWhere: method(z.object({
14844
+ namespace: z.string().optional(),
14845
+ collection: z.string(),
14846
+ filter: MutationFilterSchema
14847
+ }), z.object({ deleted: z.number().int() }), { kind: "mutation" }),
14848
+ /** Apply `data` to every record matching `filter`, in one statement. */
14849
+ updateWhere: method(z.object({
14850
+ namespace: z.string().optional(),
14851
+ collection: z.string(),
14852
+ filter: MutationFilterSchema,
14853
+ data: z.record(z.string(), z.unknown())
14854
+ }), z.object({ updated: z.number().int() }), { kind: "mutation" }),
14787
14855
  /** Count entries in a collection, optionally filtered. */
14788
14856
  count: method(z.object({
14789
14857
  namespace: z.string().optional(),
@@ -24963,6 +25031,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
24963
25031
  addonId: null,
24964
25032
  access: "delete"
24965
25033
  },
25034
+ "dataStoreProvider.deleteWhere": {
25035
+ capName: "data-store-provider",
25036
+ capScope: "system",
25037
+ addonId: null,
25038
+ access: "delete"
25039
+ },
24966
25040
  "dataStoreProvider.get": {
24967
25041
  capName: "data-store-provider",
24968
25042
  capScope: "system",
@@ -25011,6 +25085,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
25011
25085
  addonId: null,
25012
25086
  access: "create"
25013
25087
  },
25088
+ "dataStoreProvider.updateWhere": {
25089
+ capName: "data-store-provider",
25090
+ capScope: "system",
25091
+ addonId: null,
25092
+ access: "create"
25093
+ },
25014
25094
  "dayNight.getOptions": {
25015
25095
  capName: "day-night",
25016
25096
  capScope: "device",
@@ -28089,6 +28169,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28089
28169
  addonId: null,
28090
28170
  access: "delete"
28091
28171
  },
28172
+ "settingsStore.deleteWhere": {
28173
+ capName: "settings-store",
28174
+ capScope: "system",
28175
+ addonId: null,
28176
+ access: "delete"
28177
+ },
28092
28178
  "settingsStore.get": {
28093
28179
  capName: "settings-store",
28094
28180
  capScope: "system",
@@ -28131,6 +28217,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
28131
28217
  addonId: null,
28132
28218
  access: "create"
28133
28219
  },
28220
+ "settingsStore.updateWhere": {
28221
+ capName: "settings-store",
28222
+ capScope: "system",
28223
+ addonId: null,
28224
+ access: "create"
28225
+ },
28134
28226
  "smtpProvider.getStatus": {
28135
28227
  capName: "smtp-provider",
28136
28228
  capScope: "system",