@camstack/addon-provider-rtsp 1.2.151 → 1.2.152
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/CHANGELOG.md +6 -0
- package/dist/addon.js +765 -600
- package/dist/addon.mjs +765 -600
- package/package.json +2 -2
package/dist/addon.js
CHANGED
|
@@ -6005,7 +6005,7 @@ var ZodIssueCode = {
|
|
|
6005
6005
|
var ZodFirstPartyTypeKind;
|
|
6006
6006
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6007
6007
|
//#endregion
|
|
6008
|
-
//#region ../types/dist/sleep-
|
|
6008
|
+
//#region ../types/dist/sleep-BX_MqzUe.mjs
|
|
6009
6009
|
/**
|
|
6010
6010
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6011
6011
|
* window to float samples (D455).
|
|
@@ -6814,6 +6814,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6814
6814
|
}
|
|
6815
6815
|
return out;
|
|
6816
6816
|
}
|
|
6817
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6818
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6819
|
+
"cluster",
|
|
6820
|
+
"device-defaults",
|
|
6821
|
+
"node"
|
|
6822
|
+
];
|
|
6817
6823
|
/**
|
|
6818
6824
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6819
6825
|
*
|
|
@@ -7628,6 +7634,28 @@ var SECRET_RETRY_MAX_MS = 5 * 6e4;
|
|
|
7628
7634
|
/** Coalesces a burst of `keychain.changed` events into one re-resolve. */
|
|
7629
7635
|
var SECRET_CHANGE_DEBOUNCE_MS = 250;
|
|
7630
7636
|
/**
|
|
7637
|
+
* Pair a catalogue with the function that builds its ports FROM THE MIGRATION API. The one
|
|
7638
|
+
* place a catalogue is bound: a migration reaches nothing a ports binder cannot reach
|
|
7639
|
+
* through `api` (guard `check-migration-ports-surface`).
|
|
7640
|
+
*/
|
|
7641
|
+
function migrationSet(catalogue, bindPorts) {
|
|
7642
|
+
return { bind: (api) => defineAddonMigrations(catalogue, bindPorts(api)) };
|
|
7643
|
+
}
|
|
7644
|
+
/** A mixed array is a mistake: the other kind would be dropped without a word. */
|
|
7645
|
+
function refuseMixed(form, kept, dropped) {
|
|
7646
|
+
if (dropped > 0) throw new Error(`runMigrations(${form}): ${dropped} of ${kept + dropped} entries are of the other kind and would be dropped; pass one kind per call`);
|
|
7647
|
+
}
|
|
7648
|
+
function catalogueOf(list) {
|
|
7649
|
+
const kept = list.filter((entry) => !("execute" in entry));
|
|
7650
|
+
refuseMixed("catalogue, (api) => ports", kept.length, list.length - kept.length);
|
|
7651
|
+
return kept;
|
|
7652
|
+
}
|
|
7653
|
+
function boundOf(list) {
|
|
7654
|
+
const kept = list.filter((entry) => "execute" in entry);
|
|
7655
|
+
refuseMixed("bound migrations", kept.length, list.length - kept.length);
|
|
7656
|
+
return kept;
|
|
7657
|
+
}
|
|
7658
|
+
/**
|
|
7631
7659
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
7632
7660
|
*
|
|
7633
7661
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
@@ -7705,6 +7733,13 @@ var BaseAddon = class {
|
|
|
7705
7733
|
*/
|
|
7706
7734
|
secretStatePaths = [];
|
|
7707
7735
|
_addonSecretTree = null;
|
|
7736
|
+
/**
|
|
7737
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7738
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7739
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7740
|
+
*/
|
|
7741
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7742
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7708
7743
|
_deviceSecretTree = null;
|
|
7709
7744
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7710
7745
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7774,6 +7809,7 @@ var BaseAddon = class {
|
|
|
7774
7809
|
this._ctx = context;
|
|
7775
7810
|
this._secretSettings = null;
|
|
7776
7811
|
}
|
|
7812
|
+
await this.loadScopedSchemas();
|
|
7777
7813
|
await this.resolveConfig();
|
|
7778
7814
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7779
7815
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7783,25 +7819,24 @@ var BaseAddon = class {
|
|
|
7783
7819
|
const normalized = normalizeAddonInitResult(result);
|
|
7784
7820
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7785
7821
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7822
|
+
await this.loadScopedSchemas();
|
|
7786
7823
|
return normalized;
|
|
7787
7824
|
}
|
|
7825
|
+
async runMigrations(first, second, third) {
|
|
7826
|
+
if (typeof second === "function") return this.runMigrationSets([migrationSet(catalogueOf(first), second)], third);
|
|
7827
|
+
return this.runBound(boundOf(first), second);
|
|
7828
|
+
}
|
|
7788
7829
|
/**
|
|
7789
|
-
*
|
|
7790
|
-
*
|
|
7791
|
-
*
|
|
7792
|
-
* restored devices — with the catalogue bound to the addon's ports
|
|
7793
|
-
* (`defineAddonMigrations(ADDON_MIGRATIONS, ports)`).
|
|
7794
|
-
*
|
|
7795
|
-
* An engine that cannot be reached or refuses the catalogue is logged and runs
|
|
7796
|
-
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7797
|
-
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7798
|
-
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7799
|
-
*
|
|
7800
|
-
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7801
|
-
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7802
|
-
* omitted applied ids "newer").
|
|
7830
|
+
* Several catalogues of ONE owner in one declaration (the engine replaces an owner's
|
|
7831
|
+
* catalogue on every declare, so two calls would each judge the other's ids "newer").
|
|
7832
|
+
* Build each entry with `migrationSet(catalogue, (api) => ports)`.
|
|
7803
7833
|
*/
|
|
7804
|
-
async
|
|
7834
|
+
async runMigrationSets(sets, options) {
|
|
7835
|
+
const api = this.ctx.migrationApi;
|
|
7836
|
+
if (api === void 0) throw new Error(`${this.ctx.id}: the migration API is not available on this context`);
|
|
7837
|
+
return this.runBound(sets.flatMap((set) => set.bind(api)), options);
|
|
7838
|
+
}
|
|
7839
|
+
async runBound(migrations, options) {
|
|
7805
7840
|
const api = this.ctx.api.migrations;
|
|
7806
7841
|
const report = await runAddonMigrations({
|
|
7807
7842
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
@@ -7871,6 +7906,16 @@ var BaseAddon = class {
|
|
|
7871
7906
|
schema(schema) {
|
|
7872
7907
|
return schema;
|
|
7873
7908
|
}
|
|
7909
|
+
/**
|
|
7910
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7911
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7912
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7913
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7914
|
+
* once known.
|
|
7915
|
+
*/
|
|
7916
|
+
scopedSettingsCapabilities() {
|
|
7917
|
+
return [];
|
|
7918
|
+
}
|
|
7874
7919
|
/** Override to provide global-level settings UI schema. */
|
|
7875
7920
|
globalSettingsSchema(_cap) {
|
|
7876
7921
|
return null;
|
|
@@ -7914,7 +7959,8 @@ var BaseAddon = class {
|
|
|
7914
7959
|
return [];
|
|
7915
7960
|
}
|
|
7916
7961
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7917
|
-
|
|
7962
|
+
await this.loadScopedSchemas(cap);
|
|
7963
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7918
7964
|
if (!schema) return { sections: [] };
|
|
7919
7965
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7920
7966
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8045,6 +8091,7 @@ var BaseAddon = class {
|
|
|
8045
8091
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8046
8092
|
*/
|
|
8047
8093
|
async resolveGlobalStore(nodeId, cap) {
|
|
8094
|
+
await this.loadScopedSchemas(cap);
|
|
8048
8095
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8049
8096
|
return this.projectForNode(raw, nodeId, cap);
|
|
8050
8097
|
}
|
|
@@ -8055,6 +8102,7 @@ var BaseAddon = class {
|
|
|
8055
8102
|
}
|
|
8056
8103
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8057
8104
|
async readRawProjectedStore(nodeId, cap) {
|
|
8105
|
+
await this.loadScopedSchemas(cap);
|
|
8058
8106
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8059
8107
|
return this.projectForNode(raw, nodeId, cap);
|
|
8060
8108
|
}
|
|
@@ -8157,6 +8205,7 @@ var BaseAddon = class {
|
|
|
8157
8205
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8158
8206
|
}
|
|
8159
8207
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8208
|
+
await this.loadScopedSchemas();
|
|
8160
8209
|
const keys = this.perNodeKeys();
|
|
8161
8210
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8162
8211
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8170,7 +8219,8 @@ var BaseAddon = class {
|
|
|
8170
8219
|
await this.resolveConfig();
|
|
8171
8220
|
await this.onConfigChanged();
|
|
8172
8221
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8173
|
-
|
|
8222
|
+
const legacy = this.globalSettingsSchema();
|
|
8223
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8174
8224
|
}
|
|
8175
8225
|
/**
|
|
8176
8226
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8178,14 +8228,67 @@ var BaseAddon = class {
|
|
|
8178
8228
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8179
8229
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8180
8230
|
*/
|
|
8181
|
-
|
|
8231
|
+
/**
|
|
8232
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8233
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8234
|
+
* derived from the previous set. Idempotent.
|
|
8235
|
+
*/
|
|
8236
|
+
async loadScopedSchemas(extraCap) {
|
|
8237
|
+
const caps = new Set([
|
|
8238
|
+
...this.scopedSettingsCapabilities(),
|
|
8239
|
+
...this._registeredCapNames,
|
|
8240
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8241
|
+
]);
|
|
8242
|
+
let added = false;
|
|
8243
|
+
for (const cap of caps) {
|
|
8244
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8245
|
+
const loaded = [];
|
|
8246
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8247
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8248
|
+
if (schema !== null) loaded.push({
|
|
8249
|
+
scope,
|
|
8250
|
+
schema
|
|
8251
|
+
});
|
|
8252
|
+
}
|
|
8253
|
+
this._scopedByCap.set(cap, loaded);
|
|
8254
|
+
added = true;
|
|
8255
|
+
}
|
|
8256
|
+
if (!added) return;
|
|
8257
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8258
|
+
this._addonSecretTree = null;
|
|
8259
|
+
}
|
|
8260
|
+
/**
|
|
8261
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8262
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8263
|
+
*/
|
|
8264
|
+
scopedFormSchema(cap) {
|
|
8265
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8266
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8267
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8268
|
+
}
|
|
8269
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8270
|
+
loadedScopedSchemas(cap) {
|
|
8271
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8272
|
+
return [...this._scopedByCap.values()].flat();
|
|
8273
|
+
}
|
|
8274
|
+
/**
|
|
8275
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8276
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8277
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8278
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8279
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8280
|
+
*/
|
|
8182
8281
|
perNodeKeys(cap) {
|
|
8183
8282
|
const cacheKey = cap ?? "";
|
|
8184
|
-
const cached = this.
|
|
8283
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8185
8284
|
if (cached) return cached;
|
|
8186
|
-
const
|
|
8187
|
-
const keys = new Set(
|
|
8188
|
-
this.
|
|
8285
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8286
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8287
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8288
|
+
if (scope !== "node") continue;
|
|
8289
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8290
|
+
}
|
|
8291
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8189
8292
|
return keys;
|
|
8190
8293
|
}
|
|
8191
8294
|
/**
|
|
@@ -8195,10 +8298,9 @@ var BaseAddon = class {
|
|
|
8195
8298
|
* time to return its response before the addon is torn down and
|
|
8196
8299
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8197
8300
|
*/
|
|
8198
|
-
maybeAutoRestart(patchKeys,
|
|
8199
|
-
if (!schema) return;
|
|
8301
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8200
8302
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8201
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8303
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8202
8304
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8203
8305
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8204
8306
|
}
|
|
@@ -8394,12 +8496,14 @@ var BaseAddon = class {
|
|
|
8394
8496
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8395
8497
|
*/
|
|
8396
8498
|
async getDeclaredSettingsKeys() {
|
|
8499
|
+
await this.loadScopedSchemas();
|
|
8397
8500
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8398
8501
|
}
|
|
8399
8502
|
declaredKeySet() {
|
|
8400
8503
|
const keys = new Set(Object.keys(this.defaults));
|
|
8401
8504
|
const schema = this.globalSettingsSchema();
|
|
8402
8505
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8506
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8403
8507
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8404
8508
|
for (const path of this.secretStatePaths) {
|
|
8405
8509
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8495,7 +8599,11 @@ var BaseAddon = class {
|
|
|
8495
8599
|
}
|
|
8496
8600
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8497
8601
|
addonSecretTree() {
|
|
8498
|
-
this._addonSecretTree
|
|
8602
|
+
if (this._addonSecretTree === null) {
|
|
8603
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8604
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8605
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8606
|
+
}
|
|
8499
8607
|
return this._addonSecretTree;
|
|
8500
8608
|
}
|
|
8501
8609
|
deviceSecretTree() {
|
|
@@ -8745,6 +8853,23 @@ function dropPerNodeFields(fields) {
|
|
|
8745
8853
|
}
|
|
8746
8854
|
return kept;
|
|
8747
8855
|
}
|
|
8856
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8857
|
+
function collectValueFieldKeys(fields) {
|
|
8858
|
+
const collected = [];
|
|
8859
|
+
for (const field of fields) {
|
|
8860
|
+
if (field.type === "group") {
|
|
8861
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8862
|
+
continue;
|
|
8863
|
+
}
|
|
8864
|
+
if (field.type === "sub-tabs") {
|
|
8865
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8866
|
+
continue;
|
|
8867
|
+
}
|
|
8868
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8869
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8870
|
+
}
|
|
8871
|
+
return collected;
|
|
8872
|
+
}
|
|
8748
8873
|
function collectFieldKeys(fields) {
|
|
8749
8874
|
const collected = [];
|
|
8750
8875
|
for (const field of fields) {
|
|
@@ -13527,7 +13652,7 @@ var RecordIntegrationInstances = class {
|
|
|
13527
13652
|
return record === null ? null : this.viewOf(record);
|
|
13528
13653
|
};
|
|
13529
13654
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13530
|
-
createInstance = async ({ id, name, kind }) => {
|
|
13655
|
+
createInstance = async ({ id, name, kind, values }) => {
|
|
13531
13656
|
const minted = id ?? this.hooks.mintId?.({
|
|
13532
13657
|
name,
|
|
13533
13658
|
...kind !== void 0 ? { kind } : {}
|
|
@@ -13537,10 +13662,20 @@ var RecordIntegrationInstances = class {
|
|
|
13537
13662
|
id: minted,
|
|
13538
13663
|
data: this.hooks.initialBody(name, kind)
|
|
13539
13664
|
}, { ifRev: 0 });
|
|
13665
|
+
const patch = this.patchOf(values ?? {});
|
|
13666
|
+
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
13540
13667
|
await this.hooks.onChanged?.(minted, "created");
|
|
13541
13668
|
return { id: minted };
|
|
13542
13669
|
};
|
|
13543
13670
|
updateInstance = async ({ id, name, values }) => {
|
|
13671
|
+
const patch = this.patchOf(values);
|
|
13672
|
+
if (name !== void 0) patch[this.nameField] = name;
|
|
13673
|
+
await this.handle.patch(id, patch);
|
|
13674
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13675
|
+
return { ok: true };
|
|
13676
|
+
};
|
|
13677
|
+
/** The record patch a form `values` object means: secrets kept, cleared or replaced. */
|
|
13678
|
+
patchOf(values) {
|
|
13544
13679
|
const patch = {};
|
|
13545
13680
|
for (const [key, value] of Object.entries(values)) {
|
|
13546
13681
|
if (value === void 0) continue;
|
|
@@ -13551,11 +13686,8 @@ var RecordIntegrationInstances = class {
|
|
|
13551
13686
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13552
13687
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13553
13688
|
}
|
|
13554
|
-
|
|
13555
|
-
|
|
13556
|
-
await this.hooks.onChanged?.(id, "updated");
|
|
13557
|
-
return { ok: true };
|
|
13558
|
-
};
|
|
13689
|
+
return patch;
|
|
13690
|
+
}
|
|
13559
13691
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13560
13692
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13561
13693
|
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
@@ -14858,13 +14990,13 @@ async function claimDevicesToDefaultIntegration(ports, spec) {
|
|
|
14858
14990
|
};
|
|
14859
14991
|
if (orphans.some((d) => (d.parentDeviceId ?? null) === null) && fallback === null) throw new Error(`${orphans.length} device(s) of '${spec.addonId}' carry no integration and the addon has no integration to claim them to`);
|
|
14860
14992
|
for (const device of orphans) if (resolve(device, new Set([device.id])) === null) unresolved.add(device.id);
|
|
14861
|
-
for (const id of unresolved) ports.warnDevice(`device ${id} of '${spec.addonId}' carries no integration and its owner chain does not resolve to one: left unstamped`, id);
|
|
14993
|
+
for (const id of unresolved) await ports.warnDevice(`device ${id} of '${spec.addonId}' carries no integration and its owner chain does not resolve to one: left unstamped`, id);
|
|
14862
14994
|
for (const [deviceId, integrationId] of plan) await ports.setIntegrationId(deviceId, integrationId);
|
|
14863
14995
|
const wrong = [];
|
|
14864
14996
|
for (const [deviceId, integrationId] of plan) if (await ports.readIntegrationId(deviceId) !== integrationId) wrong.push(deviceId);
|
|
14865
14997
|
if (wrong.length > 0) throw new Error(`device(s) ${wrong.join(", ")} did not read back their integration after the claim`);
|
|
14866
14998
|
const claimed = plan.size;
|
|
14867
|
-
ports.info(`${claimed} device(s) of '${spec.addonId}' claimed to their owner's integration or the default; ${unresolved.size} unresolved, ${reservations.size} reservation(s) skipped`);
|
|
14999
|
+
await ports.info(`${claimed} device(s) of '${spec.addonId}' claimed to their owner's integration or the default; ${unresolved.size} unresolved, ${reservations.size} reservation(s) skipped`);
|
|
14868
15000
|
return {
|
|
14869
15001
|
outcome: claimed > 0 ? "done" : "already-done",
|
|
14870
15002
|
summary: {
|
|
@@ -14990,25 +15122,6 @@ function defaultIntegrationMigrations(spec) {
|
|
|
14990
15122
|
...spec.eligible !== void 0 ? { eligible: spec.eligible } : {}
|
|
14991
15123
|
})];
|
|
14992
15124
|
}
|
|
14993
|
-
function defaultIntegrationApiOver(api) {
|
|
14994
|
-
return {
|
|
14995
|
-
list: () => api.integrations.list.query(),
|
|
14996
|
-
readLegacyIntegrationRows: (input) => api.migrations.readLegacyIntegrationRows.query(input),
|
|
14997
|
-
clearLegacyIntegrationRows: (input) => api.migrations.clearLegacyIntegrationRows.mutate(input),
|
|
14998
|
-
listDevices: (addonId) => api.deviceManager.listAll.query({
|
|
14999
|
-
addonId,
|
|
15000
|
-
projection: "slim"
|
|
15001
|
-
}),
|
|
15002
|
-
setDeviceIntegrationId: async (deviceId, integrationId) => {
|
|
15003
|
-
await api.deviceManager.setIntegrationId.mutate({
|
|
15004
|
-
deviceId,
|
|
15005
|
-
integrationId
|
|
15006
|
-
});
|
|
15007
|
-
},
|
|
15008
|
-
readDeviceHasConfig: async (deviceId) => Object.keys((await api.deviceManager.getDevice.query({ deviceId }))?.config ?? {}).length > 0,
|
|
15009
|
-
readDeviceIntegrationId: async (deviceId) => (await api.deviceManager.getDevice.query({ deviceId }))?.integrationId ?? null
|
|
15010
|
-
};
|
|
15011
|
-
}
|
|
15012
15125
|
/**
|
|
15013
15126
|
* The trainer contract — what the hub hands the cloud trainer and what it
|
|
15014
15127
|
* gets back (cloud training, D805+).
|
|
@@ -16129,6 +16242,39 @@ var UnstampedEventMediaCountSchema = object({
|
|
|
16129
16242
|
/** Sum across both, or `null` when either lane could not be counted. */
|
|
16130
16243
|
total: number().int().nonnegative().nullable()
|
|
16131
16244
|
}).nullable();
|
|
16245
|
+
/**
|
|
16246
|
+
* Where the stored event media sits, per storage location: how many media rows
|
|
16247
|
+
* and how many bytes carry each location id, how many carry none (legacy,
|
|
16248
|
+
* resolved to the default `eventMedia` location), and how many name a location
|
|
16249
|
+
* that was not asked about (a deleted or never-listed one).
|
|
16250
|
+
*
|
|
16251
|
+
* One covering-index aggregate per asked location plus the NULL one, and one
|
|
16252
|
+
* unfiltered total that scans the covering index: O(rows), not a walk of the
|
|
16253
|
+
* rows themselves. `unlisted` is total minus the ASKED locations minus NULL, so
|
|
16254
|
+
* it is a deleted location only when every location id was asked.
|
|
16255
|
+
* `null` is "could not be measured", never zero.
|
|
16256
|
+
*/
|
|
16257
|
+
var MediaLocationCensusInputSchema = object({ locationIds: array(string().min(1)).max(50).readonly() });
|
|
16258
|
+
var MediaLocationCensusSchema = object({
|
|
16259
|
+
locations: array(object({
|
|
16260
|
+
locationId: string(),
|
|
16261
|
+
rows: number().int().nonnegative(),
|
|
16262
|
+
bytes: number().nonnegative()
|
|
16263
|
+
})).readonly(),
|
|
16264
|
+
unstamped: object({
|
|
16265
|
+
rows: number().int().nonnegative(),
|
|
16266
|
+
bytes: number().nonnegative()
|
|
16267
|
+
}),
|
|
16268
|
+
/** Rows stamped with a location id that was not in the asked list. */
|
|
16269
|
+
unlisted: object({
|
|
16270
|
+
rows: number().int().nonnegative(),
|
|
16271
|
+
bytes: number().nonnegative()
|
|
16272
|
+
}),
|
|
16273
|
+
total: object({
|
|
16274
|
+
rows: number().int().nonnegative(),
|
|
16275
|
+
bytes: number().nonnegative()
|
|
16276
|
+
})
|
|
16277
|
+
});
|
|
16132
16278
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
16133
16279
|
/** The independently selectable logical storage classes — every class
|
|
16134
16280
|
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
@@ -47165,52 +47311,7 @@ onBrightnessChanged: { data: object({
|
|
|
47165
47311
|
minIntervalMs: 0
|
|
47166
47312
|
} } }
|
|
47167
47313
|
};
|
|
47168
|
-
|
|
47169
|
-
* `broker` — unified pub/sub broker registry, system-scoped collection.
|
|
47170
|
-
*
|
|
47171
|
-
* The cap models any kind-tagged message broker the user wants to
|
|
47172
|
-
* register and that other addons might consume. The first two kinds
|
|
47173
|
-
* are MQTT (mosquitto / aedes embedded / cloud bridge) and
|
|
47174
|
-
* Home Assistant (HA WebSocket — subscribe_entities + call_service).
|
|
47175
|
-
* Future kinds (Zigbee2MQTT bridge, ZHA, KNX, Telegram, …) plug in
|
|
47176
|
-
* the same surface.
|
|
47177
|
-
*
|
|
47178
|
-
* Why one cap, not one cap per kind:
|
|
47179
|
-
* - The integrations page wants a single table of "every broker"
|
|
47180
|
-
* across kinds; a unified cap drives it without join logic.
|
|
47181
|
-
* - Generic admin operations (add / remove / test / status) are
|
|
47182
|
-
* identical across kinds — duplicating them per cap was busywork.
|
|
47183
|
-
* - The `network-access` cap precedent: same interface, many
|
|
47184
|
-
* `providerKind: 'ingress'` implementations (Tailscale, ngrok, …).
|
|
47185
|
-
*
|
|
47186
|
-
* What stays kind-specific:
|
|
47187
|
-
* - `getBrokerConfig` payload (kind decides whether it returns
|
|
47188
|
-
* mqtt URL+creds, HA baseUrl+token, …) — typed as
|
|
47189
|
-
* `Record<string, unknown>` at the wire; consumers narrow.
|
|
47190
|
-
* - `publish` / `subscribe` / `getState` arguments (target / filter /
|
|
47191
|
-
* key) — also typed as `Record<string, unknown>` so each kind can
|
|
47192
|
-
* evolve its surface (MQTT topic+qos vs HA entity_id+domain)
|
|
47193
|
-
* without rebreaking the cap signature.
|
|
47194
|
-
* - `add` `settings` payload (different fields per kind, validated by
|
|
47195
|
-
* the kind-specific provider).
|
|
47196
|
-
*
|
|
47197
|
-
* Bidirectionality:
|
|
47198
|
-
* - `publish` is RPC — synchronous result (provider-defined).
|
|
47199
|
-
* - `subscribe` returns a subscription handle; the actual message
|
|
47200
|
-
* stream flows over the typed event-bus as `broker.message` events
|
|
47201
|
-
* keyed by `(brokerId, subscriptionId)`. Consumers filter in their
|
|
47202
|
-
* event handler. Subscriptions persist across reconnects — the
|
|
47203
|
-
* provider re-subscribes upstream after a transport drop.
|
|
47204
|
-
*
|
|
47205
|
-
* Why event-bus push (not a subscription RPC stream):
|
|
47206
|
-
* - Matches the rest of CamStack's D8 contract — events for telemetry,
|
|
47207
|
-
* RPC for loss-is-a-bug. Broker messages are telemetry (a single
|
|
47208
|
-
* drop is recoverable via `getState({key})`).
|
|
47209
|
-
* - Frees `broker.subscribe` from holding a long-lived RPC channel.
|
|
47210
|
-
* - Cross-process: events already route through `$event-bus`; no
|
|
47211
|
-
* per-broker wire-shaping needed.
|
|
47212
|
-
*/
|
|
47213
|
-
var BrokerStatusEnum = _enum([
|
|
47314
|
+
_enum([
|
|
47214
47315
|
"connected",
|
|
47215
47316
|
"disconnected",
|
|
47216
47317
|
"connecting",
|
|
@@ -47218,141 +47319,12 @@ var BrokerStatusEnum = _enum([
|
|
|
47218
47319
|
"unreachable",
|
|
47219
47320
|
"error"
|
|
47220
47321
|
]);
|
|
47221
|
-
var BrokerInfoSchema = object({
|
|
47222
|
-
/** Stable broker id. Persisted; survives addon restarts. */
|
|
47223
|
-
id: string(),
|
|
47224
|
-
/** Addon id of the provider that OWNS this broker.
|
|
47225
|
-
*
|
|
47226
|
-
* The `broker` cap is a system-scoped collection: several addons
|
|
47227
|
-
* register a `broker` provider, each owning a DISJOINT set of
|
|
47228
|
-
* brokers (mqtt-broker owns `mqtt_*`, provider-homeassistant owns
|
|
47229
|
-
* `ha_*`). A broker therefore belongs to exactly one addon — like a
|
|
47230
|
-
* device belongs to one integration. The admin UI threads this id
|
|
47231
|
-
* back as the `{ addonId }` system-collection selector on every
|
|
47232
|
-
* id-keyed call (`get` / `getSettings` / `setSettings` / `remove` /
|
|
47233
|
-
* `testConnection`), so the call routes to the OWNING provider
|
|
47234
|
-
* instead of defaulting to the first-registered one. */
|
|
47235
|
-
addonId: string(),
|
|
47236
|
-
/** Human-readable name (operator-chosen at add-time). */
|
|
47237
|
-
name: string(),
|
|
47238
|
-
/** Provider-defined kind tag — `mqtt` / `home-assistant` / future. */
|
|
47239
|
-
kind: string(),
|
|
47240
|
-
status: BrokerStatusEnum,
|
|
47241
|
-
/** Free-form provider-specific info (HA version, MQTT broker
|
|
47242
|
-
* flavour, latency, mTLS active, …). */
|
|
47243
|
-
info: record(string(), unknown()),
|
|
47244
|
-
/** Ms epoch of the last connection probe / status update. */
|
|
47245
|
-
lastCheckedAt: number().nullable(),
|
|
47246
|
-
/** Last error message, when `status` indicates failure. */
|
|
47247
|
-
error: string().nullable()
|
|
47248
|
-
});
|
|
47249
47322
|
var RegistryStatusSchema = object({
|
|
47250
47323
|
brokerCount: number().int().nonnegative(),
|
|
47251
47324
|
connectedCount: number().int().nonnegative()
|
|
47252
47325
|
});
|
|
47253
|
-
/**
|
|
47254
|
-
* One entry per `broker` provider — which addon provides which broker
|
|
47255
|
-
* kind(s). The unified create picker reads this to know, for each
|
|
47256
|
-
* (addonId, kind) pair the operator can pick, where to route the
|
|
47257
|
-
* follow-up `getSettingsSchema` / `testSettings` / `add` calls (via the
|
|
47258
|
-
* `{ addonId }` selector). A provider only advertises a kind here when
|
|
47259
|
-
* it implements `getSettingsSchema` for that kind (so the picker never
|
|
47260
|
-
* offers a kind whose creation form can't be rendered).
|
|
47261
|
-
*/
|
|
47262
|
-
var BrokerProviderInfoSchema = object({
|
|
47263
|
-
/** Addon id of the `broker` provider this entry describes. */
|
|
47264
|
-
addonId: string(),
|
|
47265
|
-
/** Broker kinds this provider can create, with a display label. */
|
|
47266
|
-
kinds: array(object({
|
|
47267
|
-
kind: string(),
|
|
47268
|
-
label: string(),
|
|
47269
|
-
/**
|
|
47270
|
-
* Relative hub path to start an interactive account link (OAuth popup).
|
|
47271
|
-
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
47272
|
-
*/
|
|
47273
|
-
interactiveConnectPath: string().min(1).optional(),
|
|
47274
|
-
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
47275
|
-
interactiveDisconnectPath: string().min(1).optional(),
|
|
47276
|
-
/**
|
|
47277
|
-
* The kind signs in through a server-held interactive session instead of
|
|
47278
|
-
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
47279
|
-
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
47280
|
-
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
47281
|
-
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
47282
|
-
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
47283
|
-
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
47284
|
-
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
47285
|
-
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
47286
|
-
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
47287
|
-
* offers it as the default path; the fields the session fills are
|
|
47288
|
-
* marked `filledBySignIn` in the kind's settings schema.
|
|
47289
|
-
*
|
|
47290
|
-
* `methods` lists the ways the session can sign in, recommended first:
|
|
47291
|
-
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
47292
|
-
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
47293
|
-
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
47294
|
-
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
47295
|
-
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
47296
|
-
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
47297
|
-
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
47298
|
-
* `['proxy']`.
|
|
47299
|
-
*/
|
|
47300
|
-
signIn: object({
|
|
47301
|
-
basePath: string().min(1),
|
|
47302
|
-
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
47303
|
-
}).optional()
|
|
47304
|
-
}))
|
|
47305
|
-
});
|
|
47306
|
-
var ListInputSchema = object({
|
|
47307
|
-
/** Optional kind filter — `list({kind:'home-assistant'})` returns
|
|
47308
|
-
* only HA brokers. Omit to list every broker. */
|
|
47309
|
-
kind: string().optional() });
|
|
47310
47326
|
var GetInputSchema = object({ id: string() });
|
|
47311
|
-
var AddInputSchema = object({
|
|
47312
|
-
kind: string().min(1),
|
|
47313
|
-
name: string().min(1),
|
|
47314
|
-
/**
|
|
47315
|
-
* ADOPT an existing id instead of minting a new one.
|
|
47316
|
-
*
|
|
47317
|
-
* Written the day this cost an outage. A broker's id is not a detail: HA
|
|
47318
|
-
* devices carry it inside their `stableId` (`ha:ha_004:dev:…`), so a broker
|
|
47319
|
-
* lost from config and re-added as `ha_001` leaves every one of its devices
|
|
47320
|
-
* bound to a broker that no longer exists. Re-entering the password under the
|
|
47321
|
-
* ORIGINAL id turns a multi-step device migration back into re-entering a
|
|
47322
|
-
* password.
|
|
47323
|
-
*
|
|
47324
|
-
* A provider MUST refuse an id that is already in use — adopting a live
|
|
47325
|
-
* broker's id would silently take it over.
|
|
47326
|
-
*/
|
|
47327
|
-
id: string().min(1).optional(),
|
|
47328
|
-
/** Kind-specific settings (e.g. MQTT `{url,username,password}` or HA
|
|
47329
|
-
* `{baseUrl,accessToken}`). Validated by the kind-specific provider
|
|
47330
|
-
* branch on receipt — invalid shape rejects the add. */
|
|
47331
|
-
settings: record(string(), unknown())
|
|
47332
|
-
});
|
|
47333
|
-
var AddResultSchema = object({ id: string() });
|
|
47334
|
-
var RemoveInputSchema = object({ id: string() });
|
|
47335
|
-
var TestConnectionResultSchema = discriminatedUnion("ok", [object({
|
|
47336
|
-
ok: literal(true),
|
|
47337
|
-
latencyMs: number().nonnegative()
|
|
47338
|
-
}), object({
|
|
47339
|
-
ok: literal(false),
|
|
47340
|
-
error: string()
|
|
47341
|
-
})]);
|
|
47342
47327
|
var SettingsRecordSchema$1 = record(string(), unknown());
|
|
47343
|
-
var SettingsSchemaInputSchema = object({ kind: string() });
|
|
47344
|
-
var TestSettingsInputSchema = object({
|
|
47345
|
-
kind: string(),
|
|
47346
|
-
settings: SettingsRecordSchema$1
|
|
47347
|
-
});
|
|
47348
|
-
var TestSettingsResultSchema = discriminatedUnion("ok", [object({
|
|
47349
|
-
ok: literal(true),
|
|
47350
|
-
latencyMs: number().nonnegative().optional()
|
|
47351
|
-
}).strict(), object({
|
|
47352
|
-
ok: literal(false),
|
|
47353
|
-
error: string()
|
|
47354
|
-
})]);
|
|
47355
|
-
var SettingsSchemaResultSchema = unknown().nullable();
|
|
47356
47328
|
var PublishInputSchema = object({
|
|
47357
47329
|
brokerId: string(),
|
|
47358
47330
|
/** Kind-specific routing target.
|
|
@@ -47386,25 +47358,7 @@ var GetStateInputSchema = object({
|
|
|
47386
47358
|
* HA: entity_id (returns the cached entity state). */
|
|
47387
47359
|
key: string()
|
|
47388
47360
|
});
|
|
47389
|
-
method(
|
|
47390
|
-
kind: "mutation",
|
|
47391
|
-
auth: "admin"
|
|
47392
|
-
}), method(RemoveInputSchema, _void(), {
|
|
47393
|
-
kind: "mutation",
|
|
47394
|
-
auth: "admin"
|
|
47395
|
-
}), method(GetInputSchema, TestConnectionResultSchema, {
|
|
47396
|
-
kind: "mutation",
|
|
47397
|
-
auth: "admin"
|
|
47398
|
-
}), method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(object({
|
|
47399
|
-
id: string(),
|
|
47400
|
-
settings: SettingsRecordSchema$1
|
|
47401
|
-
}), _void(), {
|
|
47402
|
-
kind: "mutation",
|
|
47403
|
-
auth: "admin"
|
|
47404
|
-
}), method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(SettingsSchemaInputSchema, SettingsSchemaResultSchema, { auth: "admin" }), method(TestSettingsInputSchema, TestSettingsResultSchema, {
|
|
47405
|
-
kind: "mutation",
|
|
47406
|
-
auth: "admin"
|
|
47407
|
-
}), method(PublishInputSchema, unknown(), {
|
|
47361
|
+
method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }), method(PublishInputSchema, unknown(), {
|
|
47408
47362
|
kind: "mutation",
|
|
47409
47363
|
auth: "admin"
|
|
47410
47364
|
}), method(SubscribeInputSchema, SubscribeResultSchema, {
|
|
@@ -52212,7 +52166,14 @@ var DiscoveryCandidateSchema = object({
|
|
|
52212
52166
|
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
52213
52167
|
parentStableId: string().optional(),
|
|
52214
52168
|
/** `false` = shown for context but not pickable. Default true. */
|
|
52215
|
-
selectable: boolean().optional()
|
|
52169
|
+
selectable: boolean().optional(),
|
|
52170
|
+
/**
|
|
52171
|
+
* Keys of the provider's creation form (`getChildCreationSchema`) this candidate
|
|
52172
|
+
* cannot be adopted without — a key for an encrypted board, a pairing code. The
|
|
52173
|
+
* admin asks for exactly these, on the creation form prefilled with
|
|
52174
|
+
* `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
|
|
52175
|
+
*/
|
|
52176
|
+
needsParams: array(string()).optional()
|
|
52216
52177
|
});
|
|
52217
52178
|
/**
|
|
52218
52179
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -52339,8 +52300,14 @@ var deviceProviderCapability = {
|
|
|
52339
52300
|
* provider does not support manual creation. Lets the Add-Device dialog
|
|
52340
52301
|
* pick the right type instead of assuming Camera.
|
|
52341
52302
|
*/
|
|
52342
|
-
getManualCreationType: method(object({
|
|
52343
|
-
|
|
52303
|
+
getManualCreationType: method(object({
|
|
52304
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
52305
|
+
integrationId: string().optional() }), object({ deviceType: _enum(DeviceType).nullable() })),
|
|
52306
|
+
adoptDiscoveredDevice: method(object({
|
|
52307
|
+
candidate: DiscoveryCandidateSchema,
|
|
52308
|
+
/** The integration the operator adopts into; a provider with several instances needs it. */
|
|
52309
|
+
integrationId: string().optional()
|
|
52310
|
+
}), DeviceSummarySchema, {
|
|
52344
52311
|
kind: "mutation",
|
|
52345
52312
|
auth: "admin"
|
|
52346
52313
|
}),
|
|
@@ -52351,10 +52318,16 @@ var deviceProviderCapability = {
|
|
|
52351
52318
|
* devices of that type. The output is a `ConfigUISchema` — the
|
|
52352
52319
|
* router type-asserts it at the boundary.
|
|
52353
52320
|
*/
|
|
52354
|
-
getChildCreationSchema: method(object({
|
|
52321
|
+
getChildCreationSchema: method(object({
|
|
52322
|
+
type: _enum(DeviceType),
|
|
52323
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
52324
|
+
integrationId: string().optional()
|
|
52325
|
+
}), CreationSchemaOutputSchema),
|
|
52355
52326
|
createDevice: method(object({
|
|
52356
52327
|
type: _enum(DeviceType),
|
|
52357
|
-
config: record(string(), unknown())
|
|
52328
|
+
config: record(string(), unknown()),
|
|
52329
|
+
/** The integration the operator adds the device to; a provider with several instances needs it. */
|
|
52330
|
+
integrationId: string().optional()
|
|
52358
52331
|
}), DeviceSummarySchema, {
|
|
52359
52332
|
kind: "mutation",
|
|
52360
52333
|
auth: "admin"
|
|
@@ -53079,7 +53052,10 @@ method(object({
|
|
|
53079
53052
|
auth: "admin"
|
|
53080
53053
|
}), method(object({
|
|
53081
53054
|
addonId: string(),
|
|
53082
|
-
type: _enum(DeviceType)
|
|
53055
|
+
type: _enum(DeviceType),
|
|
53056
|
+
/** The integration the form is for; a provider whose integrations differ (a hub
|
|
53057
|
+
* integration takes no manual add) answers per integration. Absent = the addon's own. */
|
|
53058
|
+
integrationId: string().optional()
|
|
53083
53059
|
}), unknown().nullable()), method(object({
|
|
53084
53060
|
addonId: string(),
|
|
53085
53061
|
type: _enum(DeviceType),
|
|
@@ -53163,7 +53139,11 @@ method(object({
|
|
|
53163
53139
|
kind: "mutation",
|
|
53164
53140
|
auth: "admin",
|
|
53165
53141
|
timeoutMs: 12e4
|
|
53166
|
-
}), method(object({
|
|
53142
|
+
}), method(object({
|
|
53143
|
+
addonId: string(),
|
|
53144
|
+
/** The integration the Add-Device dialog is open on; absent = the addon's own. */
|
|
53145
|
+
integrationId: string().optional()
|
|
53146
|
+
}), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string() }), unknown(), { auth: "admin" }), method(object({
|
|
53167
53147
|
deviceId: number(),
|
|
53168
53148
|
key: string(),
|
|
53169
53149
|
value: unknown()
|
|
@@ -56443,7 +56423,7 @@ var TestConnectionStatusEnum = _enum([
|
|
|
56443
56423
|
"inconclusive",
|
|
56444
56424
|
"unsupported"
|
|
56445
56425
|
]);
|
|
56446
|
-
var TestConnectionResultSchema
|
|
56426
|
+
var TestConnectionResultSchema = object({
|
|
56447
56427
|
/** True ONLY for `validated`. Never true for a test that did not run. */
|
|
56448
56428
|
success: boolean(),
|
|
56449
56429
|
error: string().optional(),
|
|
@@ -56488,7 +56468,7 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
56488
56468
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
56489
56469
|
addonId: string(),
|
|
56490
56470
|
settings: record(string(), unknown())
|
|
56491
|
-
}), TestConnectionResultSchema
|
|
56471
|
+
}), TestConnectionResultSchema, {
|
|
56492
56472
|
kind: "mutation",
|
|
56493
56473
|
auth: "admin"
|
|
56494
56474
|
});
|
|
@@ -56515,6 +56495,14 @@ var ConfigSchemaPassthrough$3 = custom();
|
|
|
56515
56495
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
56516
56496
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
56517
56497
|
/**
|
|
56498
|
+
* What the addon's instances ARE for the Integrations page (planning#416). `devices`: each
|
|
56499
|
+
* instance is an integration that brings devices (the registry owns its on/off switch);
|
|
56500
|
+
* `services`: a connection configured from the addon's own function page, never listed on
|
|
56501
|
+
* Integrations.
|
|
56502
|
+
* It is the same vocabulary as the manifest's `integrationRole`, which stays as an override.
|
|
56503
|
+
*/
|
|
56504
|
+
var IntegrationInstanceRoleSchema = _enum(["devices", "services"]);
|
|
56505
|
+
/**
|
|
56518
56506
|
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
56519
56507
|
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
56520
56508
|
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
@@ -56525,6 +56513,22 @@ var IntegrationInstanceKindSchema = object({
|
|
|
56525
56513
|
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
56526
56514
|
id: string().min(1),
|
|
56527
56515
|
label: string().min(1),
|
|
56516
|
+
/**
|
|
56517
|
+
* The function capability this kind is configured for (`broker`, `smtp-provider`,
|
|
56518
|
+
* `device-provider`, ...). A page section is the kinds whose `functionCap` it names (D1013); a
|
|
56519
|
+
* provider with more than one section must name it on every kind.
|
|
56520
|
+
*/
|
|
56521
|
+
functionCap: string().min(1),
|
|
56522
|
+
/**
|
|
56523
|
+
* For `functionCap: 'broker'`: the registry kind of the broker an instance of this kind is
|
|
56524
|
+
* (`matter`, `home-assistant`, ...). A caller that needs a broker of one kind (a link field)
|
|
56525
|
+
* finds the instances whose kind names it; absent takes the kind `id`.
|
|
56526
|
+
*/
|
|
56527
|
+
brokerKind: string().min(1).optional(),
|
|
56528
|
+
/** What instances of this kind are for the Integrations page; absent takes the provider's `role`. */
|
|
56529
|
+
role: IntegrationInstanceRoleSchema.optional(),
|
|
56530
|
+
/** `testInstance` really probes the remote for this kind; absent takes the provider's `canTest`. */
|
|
56531
|
+
canTest: boolean().optional(),
|
|
56528
56532
|
/** The kind signs in through a server-held interactive session. */
|
|
56529
56533
|
signIn: object({
|
|
56530
56534
|
basePath: string().min(1),
|
|
@@ -56537,11 +56541,7 @@ var IntegrationInstanceKindSchema = object({
|
|
|
56537
56541
|
});
|
|
56538
56542
|
var IntegrationInstanceDescriptionSchema = object({
|
|
56539
56543
|
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
56540
|
-
role:
|
|
56541
|
-
"devices",
|
|
56542
|
-
"services",
|
|
56543
|
-
"both"
|
|
56544
|
-
]),
|
|
56544
|
+
role: IntegrationInstanceRoleSchema,
|
|
56545
56545
|
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
56546
56546
|
discovery: object({
|
|
56547
56547
|
mode: _enum([
|
|
@@ -56616,7 +56616,11 @@ var integrationInstancesCapability = {
|
|
|
56616
56616
|
methods: {
|
|
56617
56617
|
describe: method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }),
|
|
56618
56618
|
/** The form; `id: null` is the create form. */
|
|
56619
|
-
instanceFormSchema: method(object({
|
|
56619
|
+
instanceFormSchema: method(object({
|
|
56620
|
+
id: IntegrationInstanceIdSchema.nullable(),
|
|
56621
|
+
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
56622
|
+
kind: string().min(1).optional()
|
|
56623
|
+
}), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
56620
56624
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
56621
56625
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
56622
56626
|
/**
|
|
@@ -56626,7 +56630,9 @@ var integrationInstancesCapability = {
|
|
|
56626
56630
|
createInstance: method(object({
|
|
56627
56631
|
id: IntegrationInstanceIdSchema.optional(),
|
|
56628
56632
|
name: string().min(1),
|
|
56629
|
-
kind: string().min(1).optional()
|
|
56633
|
+
kind: string().min(1).optional(),
|
|
56634
|
+
/** Form values written with the record (a link picked in the create dialog). */
|
|
56635
|
+
values: record(string(), unknown()).optional()
|
|
56630
56636
|
}), object({ id: string() }), {
|
|
56631
56637
|
kind: "mutation",
|
|
56632
56638
|
auth: "admin"
|
|
@@ -56653,8 +56659,10 @@ var integrationInstancesCapability = {
|
|
|
56653
56659
|
}),
|
|
56654
56660
|
testInstance: optionalMethod(object({
|
|
56655
56661
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
56656
|
-
values: record(string(), unknown())
|
|
56657
|
-
|
|
56662
|
+
values: record(string(), unknown()),
|
|
56663
|
+
/** The kind to test when `id` is null and the provider has several. */
|
|
56664
|
+
kind: string().min(1).optional()
|
|
56665
|
+
}), TestConnectionResultSchema, {
|
|
56658
56666
|
kind: "mutation",
|
|
56659
56667
|
auth: "admin"
|
|
56660
56668
|
})
|
|
@@ -59411,6 +59419,380 @@ var SystemMetricsSchema = object({
|
|
|
59411
59419
|
gpuMemoryPercent: number().optional()
|
|
59412
59420
|
});
|
|
59413
59421
|
method(_void(), SystemResourceSnapshotSchema), method(_void(), SystemResourceSnapshotSchema.nullable()), method(_void(), SystemMetricsSchema), method(object({ dirPath: string() }), DiskSpaceInfoSchema), method(_void(), MetricsGpuInfoSchema.nullable()), method(_void(), number().nullable()), method(object({ pids: array(number()) }), array(PidResourceStatsSchema)), method(_void(), array(AddonInstanceSchema).readonly()), method(object({ addonId: string() }), PidResourceStatsSchema.nullable()), method(_void(), array(NodeProcessSchema).readonly()), method(GetLoadSeriesInputSchema, NodeLoadSeriesSchema);
|
|
59422
|
+
/**
|
|
59423
|
+
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
59424
|
+
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
59425
|
+
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
59426
|
+
*
|
|
59427
|
+
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
59428
|
+
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
59429
|
+
* that with the addon id closed over). It is deliberately NOT in any input
|
|
59430
|
+
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
59431
|
+
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
59432
|
+
* addons go through the owner's capability.
|
|
59433
|
+
*
|
|
59434
|
+
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
59435
|
+
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
59436
|
+
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
59437
|
+
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
59438
|
+
* row's own ref.
|
|
59439
|
+
*
|
|
59440
|
+
* Internal: addons reach it through `ctx.records`, never directly.
|
|
59441
|
+
*/
|
|
59442
|
+
var RecordIdSchema = string().min(1).max(256);
|
|
59443
|
+
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
59444
|
+
var RecordBodySchema = record(string(), unknown());
|
|
59445
|
+
var RecordInputSchema = object({
|
|
59446
|
+
id: RecordIdSchema,
|
|
59447
|
+
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
59448
|
+
deviceId: number().int().optional(),
|
|
59449
|
+
/** The domain clock, ms: observed / created / opened. */
|
|
59450
|
+
at: number().int().optional(),
|
|
59451
|
+
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
59452
|
+
expiresAt: number().int().optional(),
|
|
59453
|
+
data: RecordBodySchema
|
|
59454
|
+
});
|
|
59455
|
+
var RecordViewSchema = object({
|
|
59456
|
+
id: string(),
|
|
59457
|
+
deviceId: number().int().nullable(),
|
|
59458
|
+
at: number().nullable(),
|
|
59459
|
+
expiresAt: number().nullable(),
|
|
59460
|
+
rev: number().int(),
|
|
59461
|
+
updatedAt: number(),
|
|
59462
|
+
data: RecordBodySchema
|
|
59463
|
+
});
|
|
59464
|
+
/** Declared index fields only. Anything else does not compile, by design. */
|
|
59465
|
+
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
59466
|
+
string(),
|
|
59467
|
+
number(),
|
|
59468
|
+
boolean(),
|
|
59469
|
+
object({ in: array(union([
|
|
59470
|
+
string(),
|
|
59471
|
+
number(),
|
|
59472
|
+
boolean()
|
|
59473
|
+
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
59474
|
+
object({
|
|
59475
|
+
gt: union([string(), number()]).optional(),
|
|
59476
|
+
gte: union([string(), number()]).optional(),
|
|
59477
|
+
lt: union([string(), number()]).optional(),
|
|
59478
|
+
lte: union([string(), number()]).optional()
|
|
59479
|
+
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
59480
|
+
]));
|
|
59481
|
+
var KindShape = { kind: RecordKindNameSchema };
|
|
59482
|
+
var TimeWindowShape = {
|
|
59483
|
+
deviceId: number().int().optional(),
|
|
59484
|
+
atFrom: number().optional(),
|
|
59485
|
+
atTo: number().optional()
|
|
59486
|
+
};
|
|
59487
|
+
var RecordQueryInputSchema = object({
|
|
59488
|
+
...KindShape,
|
|
59489
|
+
...TimeWindowShape,
|
|
59490
|
+
where: RecordWhereSchema.optional(),
|
|
59491
|
+
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
59492
|
+
order: _enum(["at", "id"]).optional(),
|
|
59493
|
+
desc: boolean().optional(),
|
|
59494
|
+
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
59495
|
+
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
59496
|
+
cursor: string().min(1).optional()
|
|
59497
|
+
});
|
|
59498
|
+
var RecordQueryResultSchema = object({
|
|
59499
|
+
records: array(RecordViewSchema),
|
|
59500
|
+
nextCursor: string().nullable()
|
|
59501
|
+
});
|
|
59502
|
+
var RecordKindStateSchema = _enum([
|
|
59503
|
+
"created",
|
|
59504
|
+
"unchanged",
|
|
59505
|
+
"updated"
|
|
59506
|
+
]);
|
|
59507
|
+
var DeclareKindResultSchema = object({
|
|
59508
|
+
kindId: number().int(),
|
|
59509
|
+
state: RecordKindStateSchema,
|
|
59510
|
+
createdIndexes: array(string())
|
|
59511
|
+
});
|
|
59512
|
+
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
59513
|
+
var SecretRefsList = array(string());
|
|
59514
|
+
/**
|
|
59515
|
+
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
59516
|
+
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
59517
|
+
*/
|
|
59518
|
+
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
59519
|
+
object({
|
|
59520
|
+
op: literal("put"),
|
|
59521
|
+
kind: RecordKindNameSchema,
|
|
59522
|
+
record: RecordInputSchema,
|
|
59523
|
+
ifRev: number().int().min(0).optional()
|
|
59524
|
+
}),
|
|
59525
|
+
object({
|
|
59526
|
+
op: literal("patch"),
|
|
59527
|
+
kind: RecordKindNameSchema,
|
|
59528
|
+
id: RecordIdSchema,
|
|
59529
|
+
data: RecordBodySchema,
|
|
59530
|
+
at: number().int().nullable().optional(),
|
|
59531
|
+
expiresAt: number().int().nullable().optional(),
|
|
59532
|
+
ifRev: number().int().min(1).optional()
|
|
59533
|
+
}),
|
|
59534
|
+
object({
|
|
59535
|
+
op: literal("delete"),
|
|
59536
|
+
kind: RecordKindNameSchema,
|
|
59537
|
+
id: RecordIdSchema
|
|
59538
|
+
})
|
|
59539
|
+
]);
|
|
59540
|
+
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
59541
|
+
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
59542
|
+
kind: "mutation",
|
|
59543
|
+
auth: "admin"
|
|
59544
|
+
}), method(object({
|
|
59545
|
+
...KindShape,
|
|
59546
|
+
id: RecordIdSchema
|
|
59547
|
+
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
59548
|
+
...KindShape,
|
|
59549
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59550
|
+
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
59551
|
+
...KindShape,
|
|
59552
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59553
|
+
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
59554
|
+
...KindShape,
|
|
59555
|
+
record: RecordInputSchema,
|
|
59556
|
+
/** Compare-and-set. `0` means "the row must not exist". */
|
|
59557
|
+
ifRev: number().int().min(0).optional()
|
|
59558
|
+
}), object({
|
|
59559
|
+
rev: number().int(),
|
|
59560
|
+
releasedRefs: SecretRefsList
|
|
59561
|
+
}), {
|
|
59562
|
+
kind: "mutation",
|
|
59563
|
+
auth: "admin"
|
|
59564
|
+
}), method(object({
|
|
59565
|
+
...KindShape,
|
|
59566
|
+
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59567
|
+
}), object({
|
|
59568
|
+
count: number().int(),
|
|
59569
|
+
releasedRefs: SecretRefsList
|
|
59570
|
+
}), {
|
|
59571
|
+
kind: "mutation",
|
|
59572
|
+
auth: "admin"
|
|
59573
|
+
}), method(object({
|
|
59574
|
+
...KindShape,
|
|
59575
|
+
id: RecordIdSchema,
|
|
59576
|
+
data: RecordBodySchema,
|
|
59577
|
+
at: number().int().nullable().optional(),
|
|
59578
|
+
expiresAt: number().int().nullable().optional(),
|
|
59579
|
+
ifRev: number().int().min(1).optional()
|
|
59580
|
+
}), object({
|
|
59581
|
+
rev: number().int(),
|
|
59582
|
+
releasedRefs: SecretRefsList
|
|
59583
|
+
}), {
|
|
59584
|
+
kind: "mutation",
|
|
59585
|
+
auth: "admin"
|
|
59586
|
+
}), method(object({
|
|
59587
|
+
...KindShape,
|
|
59588
|
+
id: RecordIdSchema
|
|
59589
|
+
}), object({
|
|
59590
|
+
deleted: boolean(),
|
|
59591
|
+
secretRefs: SecretRefsList
|
|
59592
|
+
}), {
|
|
59593
|
+
kind: "mutation",
|
|
59594
|
+
auth: "admin"
|
|
59595
|
+
}), method(object({
|
|
59596
|
+
...KindShape,
|
|
59597
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
59598
|
+
}), object({
|
|
59599
|
+
deleted: number().int(),
|
|
59600
|
+
secretRefs: SecretRefsList
|
|
59601
|
+
}), {
|
|
59602
|
+
kind: "mutation",
|
|
59603
|
+
auth: "admin"
|
|
59604
|
+
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
59605
|
+
...KindShape,
|
|
59606
|
+
...TimeWindowShape,
|
|
59607
|
+
where: RecordWhereSchema.optional()
|
|
59608
|
+
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
59609
|
+
...KindShape,
|
|
59610
|
+
deviceId: number().int().optional(),
|
|
59611
|
+
from: number(),
|
|
59612
|
+
to: number(),
|
|
59613
|
+
bucketMs: number().int().positive()
|
|
59614
|
+
}), object({ buckets: array(object({
|
|
59615
|
+
bucketStart: number(),
|
|
59616
|
+
count: number().int()
|
|
59617
|
+
})) }), { auth: "admin" }), method(object({
|
|
59618
|
+
...KindShape,
|
|
59619
|
+
op: _enum([
|
|
59620
|
+
"sum",
|
|
59621
|
+
"min",
|
|
59622
|
+
"max",
|
|
59623
|
+
"count"
|
|
59624
|
+
]),
|
|
59625
|
+
field: RecordFieldPathSchema.optional(),
|
|
59626
|
+
groupBy: RecordFieldPathSchema.optional(),
|
|
59627
|
+
where: RecordWhereSchema.optional()
|
|
59628
|
+
}), object({ rows: array(object({
|
|
59629
|
+
group: union([
|
|
59630
|
+
string(),
|
|
59631
|
+
number(),
|
|
59632
|
+
_null()
|
|
59633
|
+
]),
|
|
59634
|
+
value: number().nullable()
|
|
59635
|
+
})) }), { auth: "admin" }), method(object({
|
|
59636
|
+
...KindShape,
|
|
59637
|
+
deviceId: number().int().optional(),
|
|
59638
|
+
atBefore: number().optional(),
|
|
59639
|
+
where: RecordWhereSchema.optional()
|
|
59640
|
+
}), object({
|
|
59641
|
+
deleted: number().int(),
|
|
59642
|
+
secretRefs: SecretRefsList
|
|
59643
|
+
}), {
|
|
59644
|
+
kind: "mutation",
|
|
59645
|
+
auth: "admin"
|
|
59646
|
+
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
59647
|
+
results: array(object({
|
|
59648
|
+
rev: number().int().nullable(),
|
|
59649
|
+
deleted: boolean()
|
|
59650
|
+
})),
|
|
59651
|
+
releasedRefs: SecretRefsList,
|
|
59652
|
+
secretRefs: SecretRefsList
|
|
59653
|
+
}), {
|
|
59654
|
+
kind: "mutation",
|
|
59655
|
+
auth: "admin"
|
|
59656
|
+
}), method(object({
|
|
59657
|
+
deviceId: number().int(),
|
|
59658
|
+
kinds: array(RecordKindNameSchema).optional()
|
|
59659
|
+
}), object({
|
|
59660
|
+
deleted: record(string(), number().int()),
|
|
59661
|
+
secretRefs: SecretRefsList
|
|
59662
|
+
}), {
|
|
59663
|
+
kind: "mutation",
|
|
59664
|
+
auth: "admin"
|
|
59665
|
+
}), method(object(KindShape), object({ id: number().int() }), {
|
|
59666
|
+
kind: "mutation",
|
|
59667
|
+
auth: "admin"
|
|
59668
|
+
});
|
|
59669
|
+
/**
|
|
59670
|
+
* The wire shapes of the migration API, version 1 (planning#447).
|
|
59671
|
+
*
|
|
59672
|
+
* A migration is code that outlives the server it was written against: an addon at
|
|
59673
|
+
* version 1.4 that is brought to 1.9 in one jump still runs its 1.4 catalogue, against
|
|
59674
|
+
* the 1.9 kernel. The only thing that code may call is {@link MigrationApiV1}; this file
|
|
59675
|
+
* is the contract of every method of it, one Zod pair per method.
|
|
59676
|
+
*
|
|
59677
|
+
* APPEND-ONLY. A released method is never removed and never changes meaning: an input
|
|
59678
|
+
* field is never removed or made required, an enum never narrows, an output field is
|
|
59679
|
+
* never removed. A change that cannot be expressed that way is `migration-api.v2`, next
|
|
59680
|
+
* to this file. `scripts/check-migration-api-append-only.ts` holds the line against
|
|
59681
|
+
* `scripts/migration-api-contract.json`, and every `behaviours` id below must have an
|
|
59682
|
+
* `it('<id>'` in `migration-api.contract.spec.ts`.
|
|
59683
|
+
*/
|
|
59684
|
+
var Rows = record(string(), unknown());
|
|
59685
|
+
var IntegrationId = object({ integrationId: string().min(1) });
|
|
59686
|
+
var RecordId = object({ id: string().min(1) });
|
|
59687
|
+
var RecordRowSchema = object({
|
|
59688
|
+
id: string(),
|
|
59689
|
+
deviceId: number().int().nullable(),
|
|
59690
|
+
at: number().nullable(),
|
|
59691
|
+
expiresAt: number().nullable(),
|
|
59692
|
+
rev: number().int(),
|
|
59693
|
+
updatedAt: number(),
|
|
59694
|
+
data: Rows
|
|
59695
|
+
});
|
|
59696
|
+
var RecordPutShape = {
|
|
59697
|
+
id: string().min(1),
|
|
59698
|
+
deviceId: number().int().optional(),
|
|
59699
|
+
at: number().int().optional(),
|
|
59700
|
+
expiresAt: number().int().optional(),
|
|
59701
|
+
data: Rows
|
|
59702
|
+
};
|
|
59703
|
+
var MigrationIntegrationSchema = object({
|
|
59704
|
+
id: string(),
|
|
59705
|
+
addonId: string(),
|
|
59706
|
+
name: string(),
|
|
59707
|
+
info: Rows,
|
|
59708
|
+
originStableId: string().optional()
|
|
59709
|
+
});
|
|
59710
|
+
var MigrationDeviceSchema = object({
|
|
59711
|
+
id: number().int(),
|
|
59712
|
+
integrationId: string().nullable().optional(),
|
|
59713
|
+
/** The owning device; null or absent for a root. */
|
|
59714
|
+
parentDeviceId: number().int().nullable().optional(),
|
|
59715
|
+
type: string().optional(),
|
|
59716
|
+
name: string().optional(),
|
|
59717
|
+
stableId: string().optional()
|
|
59718
|
+
});
|
|
59719
|
+
/** What the engine says about the migration API it serves: lets an addon notice an older server. */
|
|
59720
|
+
var MigrationApiInfoSchema = object({
|
|
59721
|
+
version: number().int(),
|
|
59722
|
+
methods: array(string())
|
|
59723
|
+
});
|
|
59724
|
+
object({
|
|
59725
|
+
message: string(),
|
|
59726
|
+
meta: Rows.optional(),
|
|
59727
|
+
/** Grouping tags of the line; a line about a device carries `{ deviceId }`. */
|
|
59728
|
+
tags: object({ deviceId: number().int().optional() }).optional()
|
|
59729
|
+
});
|
|
59730
|
+
object({
|
|
59731
|
+
owner: string().min(1),
|
|
59732
|
+
metas: array(MigrationMetaSchema)
|
|
59733
|
+
}), object({
|
|
59734
|
+
bootId: string(),
|
|
59735
|
+
snapshot: _enum([
|
|
59736
|
+
"none",
|
|
59737
|
+
"written",
|
|
59738
|
+
"refused"
|
|
59739
|
+
]),
|
|
59740
|
+
steps: array(MigrationOwnerStepSchema),
|
|
59741
|
+
/** The migration API the engine serves; absent from a server that predates it. */
|
|
59742
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
59743
|
+
}), object({ id: string().min(1) }), object({
|
|
59744
|
+
proceed: boolean(),
|
|
59745
|
+
state: union([literal("pending"), MigrationStatusSchema]),
|
|
59746
|
+
reason: string().nullable()
|
|
59747
|
+
}), object({
|
|
59748
|
+
id: string().min(1),
|
|
59749
|
+
result: MigrationOwnerResultSchema
|
|
59750
|
+
}), object({
|
|
59751
|
+
status: MigrationStatusSchema,
|
|
59752
|
+
error: string().nullable(),
|
|
59753
|
+
rowsChanged: number().int().nonnegative()
|
|
59754
|
+
}), object({
|
|
59755
|
+
rows: number().int().nonnegative(),
|
|
59756
|
+
secrets: number().int().nonnegative()
|
|
59757
|
+
}), IntegrationId.extend({ patch: Rows }), object({ written: boolean() }), IntegrationId.extend({ keys: array(string().min(1)) }), object({
|
|
59758
|
+
present: boolean(),
|
|
59759
|
+
removed: Rows
|
|
59760
|
+
}), RecordRowSchema.nullable(), RecordRowSchema.nullable(), object({
|
|
59761
|
+
...RecordPutShape,
|
|
59762
|
+
ifRev: number().int().optional()
|
|
59763
|
+
}), object({ rev: number().int() }), object({ records: array(object(RecordPutShape)) }), object({ count: number().int().nonnegative() }), RecordId.extend({
|
|
59764
|
+
partial: Rows,
|
|
59765
|
+
ifRev: number().int().optional()
|
|
59766
|
+
}), object({ rev: number().int() }), object({ deleted: boolean() }), object({
|
|
59767
|
+
deviceId: number().int().optional(),
|
|
59768
|
+
atFrom: number().optional(),
|
|
59769
|
+
atTo: number().optional(),
|
|
59770
|
+
where: RecordWhereSchema.optional(),
|
|
59771
|
+
limit: number().int().min(1).optional(),
|
|
59772
|
+
cursor: string().min(1).optional(),
|
|
59773
|
+
resolveSecrets: boolean().optional()
|
|
59774
|
+
}), object({
|
|
59775
|
+
records: array(RecordRowSchema),
|
|
59776
|
+
nextCursor: string().nullable()
|
|
59777
|
+
}), object({
|
|
59778
|
+
deviceId: number().int().optional(),
|
|
59779
|
+
atBefore: number().optional(),
|
|
59780
|
+
where: RecordWhereSchema.optional()
|
|
59781
|
+
}), object({ count: number().int().nonnegative() }), object({}), object({ store: Rows }), object({ patch: Rows }), object({}), object({ keys: array(string().min(1)) }), object({ removed: array(string()) }), object({ refs: array(string()) }), object({ values: record(string(), string().nullable()) }), object({
|
|
59782
|
+
ref: string().min(1),
|
|
59783
|
+
value: string()
|
|
59784
|
+
}), object({}), object({ refs: array(string()) }), object({ deleted: number().int().nonnegative() }), object({}), object({ integrations: array(MigrationIntegrationSchema) }), object({ id: string().min(1) }), MigrationIntegrationSchema.nullable(), object({
|
|
59785
|
+
addonId: string().min(1),
|
|
59786
|
+
integrationId: string().min(1).optional()
|
|
59787
|
+
}), object({ devices: array(MigrationDeviceSchema) }), object({ deviceId: number().int() }), object({
|
|
59788
|
+
id: number().int(),
|
|
59789
|
+
integrationId: string().nullable(),
|
|
59790
|
+
/** Whether the device carries any stored config; its content is not offered. */
|
|
59791
|
+
hasConfig: boolean()
|
|
59792
|
+
}).nullable(), object({
|
|
59793
|
+
deviceId: number().int(),
|
|
59794
|
+
integrationId: string().min(1)
|
|
59795
|
+
}), object({}), object({}), object({ nodeId: string() }), object({}), object({});
|
|
59414
59796
|
method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { auth: "admin" }), method(object({ owner: string().optional() }), object({ pending: array(MigrationPendingEntrySchema) }), { auth: "admin" }), method(object({ id: string().min(1) }), MigrationDryRunResultSchema, { auth: "admin" }), method(object({
|
|
59415
59797
|
id: string().min(1),
|
|
59416
59798
|
error: string().min(1)
|
|
@@ -59440,7 +59822,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
59440
59822
|
"written",
|
|
59441
59823
|
"refused"
|
|
59442
59824
|
]),
|
|
59443
|
-
steps: array(MigrationOwnerStepSchema)
|
|
59825
|
+
steps: array(MigrationOwnerStepSchema),
|
|
59826
|
+
/** The migration API this engine serves (additive: a server that predates it omits it). */
|
|
59827
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
59444
59828
|
}), {
|
|
59445
59829
|
kind: "mutation",
|
|
59446
59830
|
auth: "admin"
|
|
@@ -62107,7 +62491,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
62107
62491
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
62108
62492
|
kind: "mutation",
|
|
62109
62493
|
auth: "admin"
|
|
62110
|
-
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(
|
|
62494
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(MediaLocationCensusInputSchema, MediaLocationCensusSchema, {
|
|
62495
|
+
kind: "query",
|
|
62496
|
+
auth: "admin"
|
|
62497
|
+
}), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
|
|
62111
62498
|
kind: "query",
|
|
62112
62499
|
auth: "admin"
|
|
62113
62500
|
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
@@ -64762,7 +65149,7 @@ var BrokerStatusSchema$1 = _enum([
|
|
|
64762
65149
|
"unreachable",
|
|
64763
65150
|
"tls-error"
|
|
64764
65151
|
]);
|
|
64765
|
-
var BrokerInfoSchema
|
|
65152
|
+
var BrokerInfoSchema = object({
|
|
64766
65153
|
id: string(),
|
|
64767
65154
|
name: string(),
|
|
64768
65155
|
url: string(),
|
|
@@ -64825,7 +65212,7 @@ var StatusSchema = object({
|
|
|
64825
65212
|
brokerCount: number(),
|
|
64826
65213
|
embeddedRunning: boolean()
|
|
64827
65214
|
});
|
|
64828
|
-
method(_void(), array(BrokerInfoSchema
|
|
65215
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
64829
65216
|
kind: "mutation",
|
|
64830
65217
|
auth: "admin"
|
|
64831
65218
|
}), method(IdInputSchema, _void(), {
|
|
@@ -72007,253 +72394,6 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, met
|
|
|
72007
72394
|
auth: "admin"
|
|
72008
72395
|
});
|
|
72009
72396
|
/**
|
|
72010
|
-
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
72011
|
-
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
72012
|
-
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
72013
|
-
*
|
|
72014
|
-
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
72015
|
-
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
72016
|
-
* that with the addon id closed over). It is deliberately NOT in any input
|
|
72017
|
-
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
72018
|
-
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
72019
|
-
* addons go through the owner's capability.
|
|
72020
|
-
*
|
|
72021
|
-
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
72022
|
-
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
72023
|
-
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
72024
|
-
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
72025
|
-
* row's own ref.
|
|
72026
|
-
*
|
|
72027
|
-
* Internal: addons reach it through `ctx.records`, never directly.
|
|
72028
|
-
*/
|
|
72029
|
-
var RecordIdSchema = string().min(1).max(256);
|
|
72030
|
-
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
72031
|
-
var RecordBodySchema = record(string(), unknown());
|
|
72032
|
-
var RecordInputSchema = object({
|
|
72033
|
-
id: RecordIdSchema,
|
|
72034
|
-
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
72035
|
-
deviceId: number().int().optional(),
|
|
72036
|
-
/** The domain clock, ms: observed / created / opened. */
|
|
72037
|
-
at: number().int().optional(),
|
|
72038
|
-
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
72039
|
-
expiresAt: number().int().optional(),
|
|
72040
|
-
data: RecordBodySchema
|
|
72041
|
-
});
|
|
72042
|
-
var RecordViewSchema = object({
|
|
72043
|
-
id: string(),
|
|
72044
|
-
deviceId: number().int().nullable(),
|
|
72045
|
-
at: number().nullable(),
|
|
72046
|
-
expiresAt: number().nullable(),
|
|
72047
|
-
rev: number().int(),
|
|
72048
|
-
updatedAt: number(),
|
|
72049
|
-
data: RecordBodySchema
|
|
72050
|
-
});
|
|
72051
|
-
/** Declared index fields only. Anything else does not compile, by design. */
|
|
72052
|
-
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
72053
|
-
string(),
|
|
72054
|
-
number(),
|
|
72055
|
-
boolean(),
|
|
72056
|
-
object({ in: array(union([
|
|
72057
|
-
string(),
|
|
72058
|
-
number(),
|
|
72059
|
-
boolean()
|
|
72060
|
-
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
72061
|
-
object({
|
|
72062
|
-
gt: union([string(), number()]).optional(),
|
|
72063
|
-
gte: union([string(), number()]).optional(),
|
|
72064
|
-
lt: union([string(), number()]).optional(),
|
|
72065
|
-
lte: union([string(), number()]).optional()
|
|
72066
|
-
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
72067
|
-
]));
|
|
72068
|
-
var KindShape = { kind: RecordKindNameSchema };
|
|
72069
|
-
var TimeWindowShape = {
|
|
72070
|
-
deviceId: number().int().optional(),
|
|
72071
|
-
atFrom: number().optional(),
|
|
72072
|
-
atTo: number().optional()
|
|
72073
|
-
};
|
|
72074
|
-
var RecordQueryInputSchema = object({
|
|
72075
|
-
...KindShape,
|
|
72076
|
-
...TimeWindowShape,
|
|
72077
|
-
where: RecordWhereSchema.optional(),
|
|
72078
|
-
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
72079
|
-
order: _enum(["at", "id"]).optional(),
|
|
72080
|
-
desc: boolean().optional(),
|
|
72081
|
-
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
72082
|
-
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
72083
|
-
cursor: string().min(1).optional()
|
|
72084
|
-
});
|
|
72085
|
-
var RecordQueryResultSchema = object({
|
|
72086
|
-
records: array(RecordViewSchema),
|
|
72087
|
-
nextCursor: string().nullable()
|
|
72088
|
-
});
|
|
72089
|
-
var RecordKindStateSchema = _enum([
|
|
72090
|
-
"created",
|
|
72091
|
-
"unchanged",
|
|
72092
|
-
"updated"
|
|
72093
|
-
]);
|
|
72094
|
-
var DeclareKindResultSchema = object({
|
|
72095
|
-
kindId: number().int(),
|
|
72096
|
-
state: RecordKindStateSchema,
|
|
72097
|
-
createdIndexes: array(string())
|
|
72098
|
-
});
|
|
72099
|
-
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
72100
|
-
var SecretRefsList = array(string());
|
|
72101
|
-
/**
|
|
72102
|
-
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
72103
|
-
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
72104
|
-
*/
|
|
72105
|
-
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
72106
|
-
object({
|
|
72107
|
-
op: literal("put"),
|
|
72108
|
-
kind: RecordKindNameSchema,
|
|
72109
|
-
record: RecordInputSchema,
|
|
72110
|
-
ifRev: number().int().min(0).optional()
|
|
72111
|
-
}),
|
|
72112
|
-
object({
|
|
72113
|
-
op: literal("patch"),
|
|
72114
|
-
kind: RecordKindNameSchema,
|
|
72115
|
-
id: RecordIdSchema,
|
|
72116
|
-
data: RecordBodySchema,
|
|
72117
|
-
at: number().int().nullable().optional(),
|
|
72118
|
-
expiresAt: number().int().nullable().optional(),
|
|
72119
|
-
ifRev: number().int().min(1).optional()
|
|
72120
|
-
}),
|
|
72121
|
-
object({
|
|
72122
|
-
op: literal("delete"),
|
|
72123
|
-
kind: RecordKindNameSchema,
|
|
72124
|
-
id: RecordIdSchema
|
|
72125
|
-
})
|
|
72126
|
-
]);
|
|
72127
|
-
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
72128
|
-
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
72129
|
-
kind: "mutation",
|
|
72130
|
-
auth: "admin"
|
|
72131
|
-
}), method(object({
|
|
72132
|
-
...KindShape,
|
|
72133
|
-
id: RecordIdSchema
|
|
72134
|
-
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
72135
|
-
...KindShape,
|
|
72136
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72137
|
-
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
72138
|
-
...KindShape,
|
|
72139
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72140
|
-
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
72141
|
-
...KindShape,
|
|
72142
|
-
record: RecordInputSchema,
|
|
72143
|
-
/** Compare-and-set. `0` means "the row must not exist". */
|
|
72144
|
-
ifRev: number().int().min(0).optional()
|
|
72145
|
-
}), object({
|
|
72146
|
-
rev: number().int(),
|
|
72147
|
-
releasedRefs: SecretRefsList
|
|
72148
|
-
}), {
|
|
72149
|
-
kind: "mutation",
|
|
72150
|
-
auth: "admin"
|
|
72151
|
-
}), method(object({
|
|
72152
|
-
...KindShape,
|
|
72153
|
-
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72154
|
-
}), object({
|
|
72155
|
-
count: number().int(),
|
|
72156
|
-
releasedRefs: SecretRefsList
|
|
72157
|
-
}), {
|
|
72158
|
-
kind: "mutation",
|
|
72159
|
-
auth: "admin"
|
|
72160
|
-
}), method(object({
|
|
72161
|
-
...KindShape,
|
|
72162
|
-
id: RecordIdSchema,
|
|
72163
|
-
data: RecordBodySchema,
|
|
72164
|
-
at: number().int().nullable().optional(),
|
|
72165
|
-
expiresAt: number().int().nullable().optional(),
|
|
72166
|
-
ifRev: number().int().min(1).optional()
|
|
72167
|
-
}), object({
|
|
72168
|
-
rev: number().int(),
|
|
72169
|
-
releasedRefs: SecretRefsList
|
|
72170
|
-
}), {
|
|
72171
|
-
kind: "mutation",
|
|
72172
|
-
auth: "admin"
|
|
72173
|
-
}), method(object({
|
|
72174
|
-
...KindShape,
|
|
72175
|
-
id: RecordIdSchema
|
|
72176
|
-
}), object({
|
|
72177
|
-
deleted: boolean(),
|
|
72178
|
-
secretRefs: SecretRefsList
|
|
72179
|
-
}), {
|
|
72180
|
-
kind: "mutation",
|
|
72181
|
-
auth: "admin"
|
|
72182
|
-
}), method(object({
|
|
72183
|
-
...KindShape,
|
|
72184
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
72185
|
-
}), object({
|
|
72186
|
-
deleted: number().int(),
|
|
72187
|
-
secretRefs: SecretRefsList
|
|
72188
|
-
}), {
|
|
72189
|
-
kind: "mutation",
|
|
72190
|
-
auth: "admin"
|
|
72191
|
-
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
72192
|
-
...KindShape,
|
|
72193
|
-
...TimeWindowShape,
|
|
72194
|
-
where: RecordWhereSchema.optional()
|
|
72195
|
-
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
72196
|
-
...KindShape,
|
|
72197
|
-
deviceId: number().int().optional(),
|
|
72198
|
-
from: number(),
|
|
72199
|
-
to: number(),
|
|
72200
|
-
bucketMs: number().int().positive()
|
|
72201
|
-
}), object({ buckets: array(object({
|
|
72202
|
-
bucketStart: number(),
|
|
72203
|
-
count: number().int()
|
|
72204
|
-
})) }), { auth: "admin" }), method(object({
|
|
72205
|
-
...KindShape,
|
|
72206
|
-
op: _enum([
|
|
72207
|
-
"sum",
|
|
72208
|
-
"min",
|
|
72209
|
-
"max",
|
|
72210
|
-
"count"
|
|
72211
|
-
]),
|
|
72212
|
-
field: RecordFieldPathSchema.optional(),
|
|
72213
|
-
groupBy: RecordFieldPathSchema.optional(),
|
|
72214
|
-
where: RecordWhereSchema.optional()
|
|
72215
|
-
}), object({ rows: array(object({
|
|
72216
|
-
group: union([
|
|
72217
|
-
string(),
|
|
72218
|
-
number(),
|
|
72219
|
-
_null()
|
|
72220
|
-
]),
|
|
72221
|
-
value: number().nullable()
|
|
72222
|
-
})) }), { auth: "admin" }), method(object({
|
|
72223
|
-
...KindShape,
|
|
72224
|
-
deviceId: number().int().optional(),
|
|
72225
|
-
atBefore: number().optional(),
|
|
72226
|
-
where: RecordWhereSchema.optional()
|
|
72227
|
-
}), object({
|
|
72228
|
-
deleted: number().int(),
|
|
72229
|
-
secretRefs: SecretRefsList
|
|
72230
|
-
}), {
|
|
72231
|
-
kind: "mutation",
|
|
72232
|
-
auth: "admin"
|
|
72233
|
-
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
72234
|
-
results: array(object({
|
|
72235
|
-
rev: number().int().nullable(),
|
|
72236
|
-
deleted: boolean()
|
|
72237
|
-
})),
|
|
72238
|
-
releasedRefs: SecretRefsList,
|
|
72239
|
-
secretRefs: SecretRefsList
|
|
72240
|
-
}), {
|
|
72241
|
-
kind: "mutation",
|
|
72242
|
-
auth: "admin"
|
|
72243
|
-
}), method(object({
|
|
72244
|
-
deviceId: number().int(),
|
|
72245
|
-
kinds: array(RecordKindNameSchema).optional()
|
|
72246
|
-
}), object({
|
|
72247
|
-
deleted: record(string(), number().int()),
|
|
72248
|
-
secretRefs: SecretRefsList
|
|
72249
|
-
}), {
|
|
72250
|
-
kind: "mutation",
|
|
72251
|
-
auth: "admin"
|
|
72252
|
-
}), method(object(KindShape), object({ id: number().int() }), {
|
|
72253
|
-
kind: "mutation",
|
|
72254
|
-
auth: "admin"
|
|
72255
|
-
});
|
|
72256
|
-
/**
|
|
72257
72397
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
72258
72398
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
72259
72399
|
* declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
|
|
@@ -82085,7 +82225,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82085
82225
|
* `deviceClasses` map (first registered type). `null` when manual creation is
|
|
82086
82226
|
* unsupported. Lets the Add-Device dialog pick the right type per provider.
|
|
82087
82227
|
*/
|
|
82088
|
-
async getManualCreationType() {
|
|
82228
|
+
async getManualCreationType(_input) {
|
|
82089
82229
|
if (!await this.supportsManualCreation()) return { deviceType: null };
|
|
82090
82230
|
return { deviceType: Object.values(DeviceType).find((t) => this.deviceClasses[t] !== void 0) ?? null };
|
|
82091
82231
|
}
|
|
@@ -82096,7 +82236,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82096
82236
|
return true;
|
|
82097
82237
|
}
|
|
82098
82238
|
async getChildCreationSchema(input) {
|
|
82099
|
-
return this.onGetCreationSchema(input.type);
|
|
82239
|
+
return this.onGetCreationSchema(input.type, { integrationId: input.integrationId });
|
|
82100
82240
|
}
|
|
82101
82241
|
/**
|
|
82102
82242
|
* Default kernel-orchestrated `createDevice` implementation. The
|
|
@@ -82108,7 +82248,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
82108
82248
|
* `deviceClasses` instead.
|
|
82109
82249
|
*/
|
|
82110
82250
|
async createDevice(input) {
|
|
82111
|
-
const spec = await this.onCreateDevice(input.type, input.config);
|
|
82251
|
+
const spec = await this.onCreateDevice(input.type, input.config, { ...input.integrationId !== void 0 ? { integrationId: input.integrationId } : {} });
|
|
82112
82252
|
const Class = this.deviceClasses[spec.meta.type];
|
|
82113
82253
|
if (!Class) throw new Error(`${this.providerName} provider: no device class registered for type "${spec.meta.type}" — add it to the deviceClasses map`);
|
|
82114
82254
|
const stableId = this.generateStableId(spec.meta.type, spec.config);
|
|
@@ -83517,20 +83657,6 @@ Object.freeze({
|
|
|
83517
83657
|
access: "create",
|
|
83518
83658
|
role: "viewer"
|
|
83519
83659
|
},
|
|
83520
|
-
"broker.add": {
|
|
83521
|
-
capName: "broker",
|
|
83522
|
-
capScope: "system",
|
|
83523
|
-
addonId: null,
|
|
83524
|
-
access: "create",
|
|
83525
|
-
role: "admin"
|
|
83526
|
-
},
|
|
83527
|
-
"broker.get": {
|
|
83528
|
-
capName: "broker",
|
|
83529
|
-
capScope: "system",
|
|
83530
|
-
addonId: null,
|
|
83531
|
-
access: "view",
|
|
83532
|
-
role: "admin"
|
|
83533
|
-
},
|
|
83534
83660
|
"broker.getBrokerConfig": {
|
|
83535
83661
|
capName: "broker",
|
|
83536
83662
|
capScope: "system",
|
|
@@ -83538,20 +83664,6 @@ Object.freeze({
|
|
|
83538
83664
|
access: "view",
|
|
83539
83665
|
role: "admin"
|
|
83540
83666
|
},
|
|
83541
|
-
"broker.getSettings": {
|
|
83542
|
-
capName: "broker",
|
|
83543
|
-
capScope: "system",
|
|
83544
|
-
addonId: null,
|
|
83545
|
-
access: "view",
|
|
83546
|
-
role: "admin"
|
|
83547
|
-
},
|
|
83548
|
-
"broker.getSettingsSchema": {
|
|
83549
|
-
capName: "broker",
|
|
83550
|
-
capScope: "system",
|
|
83551
|
-
addonId: null,
|
|
83552
|
-
access: "view",
|
|
83553
|
-
role: "admin"
|
|
83554
|
-
},
|
|
83555
83667
|
"broker.getState": {
|
|
83556
83668
|
capName: "broker",
|
|
83557
83669
|
capScope: "system",
|
|
@@ -83566,20 +83678,6 @@ Object.freeze({
|
|
|
83566
83678
|
access: "view",
|
|
83567
83679
|
role: "admin"
|
|
83568
83680
|
},
|
|
83569
|
-
"broker.list": {
|
|
83570
|
-
capName: "broker",
|
|
83571
|
-
capScope: "system",
|
|
83572
|
-
addonId: null,
|
|
83573
|
-
access: "view",
|
|
83574
|
-
role: "admin"
|
|
83575
|
-
},
|
|
83576
|
-
"broker.listProviders": {
|
|
83577
|
-
capName: "broker",
|
|
83578
|
-
capScope: "system",
|
|
83579
|
-
addonId: null,
|
|
83580
|
-
access: "view",
|
|
83581
|
-
role: "admin"
|
|
83582
|
-
},
|
|
83583
83681
|
"broker.publish": {
|
|
83584
83682
|
capName: "broker",
|
|
83585
83683
|
capScope: "system",
|
|
@@ -83587,20 +83685,6 @@ Object.freeze({
|
|
|
83587
83685
|
access: "create",
|
|
83588
83686
|
role: "admin"
|
|
83589
83687
|
},
|
|
83590
|
-
"broker.remove": {
|
|
83591
|
-
capName: "broker",
|
|
83592
|
-
capScope: "system",
|
|
83593
|
-
addonId: null,
|
|
83594
|
-
access: "delete",
|
|
83595
|
-
role: "admin"
|
|
83596
|
-
},
|
|
83597
|
-
"broker.setSettings": {
|
|
83598
|
-
capName: "broker",
|
|
83599
|
-
capScope: "system",
|
|
83600
|
-
addonId: null,
|
|
83601
|
-
access: "create",
|
|
83602
|
-
role: "admin"
|
|
83603
|
-
},
|
|
83604
83688
|
"broker.subscribe": {
|
|
83605
83689
|
capName: "broker",
|
|
83606
83690
|
capScope: "system",
|
|
@@ -83608,20 +83692,6 @@ Object.freeze({
|
|
|
83608
83692
|
access: "create",
|
|
83609
83693
|
role: "admin"
|
|
83610
83694
|
},
|
|
83611
|
-
"broker.testConnection": {
|
|
83612
|
-
capName: "broker",
|
|
83613
|
-
capScope: "system",
|
|
83614
|
-
addonId: null,
|
|
83615
|
-
access: "create",
|
|
83616
|
-
role: "admin"
|
|
83617
|
-
},
|
|
83618
|
-
"broker.testSettings": {
|
|
83619
|
-
capName: "broker",
|
|
83620
|
-
capScope: "system",
|
|
83621
|
-
addonId: null,
|
|
83622
|
-
access: "create",
|
|
83623
|
-
role: "admin"
|
|
83624
|
-
},
|
|
83625
83695
|
"broker.unsubscribe": {
|
|
83626
83696
|
capName: "broker",
|
|
83627
83697
|
capScope: "system",
|
|
@@ -88585,6 +88655,13 @@ Object.freeze({
|
|
|
88585
88655
|
access: "view",
|
|
88586
88656
|
role: "viewer"
|
|
88587
88657
|
},
|
|
88658
|
+
"pipelineAnalytics.getMediaLocationCensus": {
|
|
88659
|
+
capName: "pipeline-analytics",
|
|
88660
|
+
capScope: "device",
|
|
88661
|
+
addonId: null,
|
|
88662
|
+
access: "view",
|
|
88663
|
+
role: "admin"
|
|
88664
|
+
},
|
|
88588
88665
|
"pipelineAnalytics.getMediaReclaimStatus": {
|
|
88589
88666
|
capName: "pipeline-analytics",
|
|
88590
88667
|
capScope: "device",
|
|
@@ -97592,6 +97669,100 @@ object({
|
|
|
97592
97669
|
delay: number().nonnegative().optional(),
|
|
97593
97670
|
commands: record(string(), unknown())
|
|
97594
97671
|
});
|
|
97672
|
+
entryBody({ brokerId: string() });
|
|
97673
|
+
function secretsOverMigrationApi(api) {
|
|
97674
|
+
return {
|
|
97675
|
+
resolve: async (refs) => {
|
|
97676
|
+
const { values } = await api.secrets.resolve({ refs: [...refs] });
|
|
97677
|
+
const out = {};
|
|
97678
|
+
for (const ref of refs) {
|
|
97679
|
+
const value = values[ref];
|
|
97680
|
+
out[ref] = value === null || value === void 0 ? { kind: "missing" } : {
|
|
97681
|
+
kind: "value",
|
|
97682
|
+
value,
|
|
97683
|
+
version: 0
|
|
97684
|
+
};
|
|
97685
|
+
}
|
|
97686
|
+
return out;
|
|
97687
|
+
},
|
|
97688
|
+
delete: async (refs) => (await api.secrets.delete({ refs: [...refs] })).deleted
|
|
97689
|
+
};
|
|
97690
|
+
}
|
|
97691
|
+
/** `ctx.records` as the migration API offers it: bind a kind, read and write by id. */
|
|
97692
|
+
function recordsOverMigrationApi(api) {
|
|
97693
|
+
return { kind: (definition) => {
|
|
97694
|
+
const handle = api.records.kind(definition);
|
|
97695
|
+
return {
|
|
97696
|
+
get: (id) => handle.get({ id }),
|
|
97697
|
+
getResolved: (id) => handle.getResolved({ id }),
|
|
97698
|
+
put: (input, opts) => handle.put({
|
|
97699
|
+
...input,
|
|
97700
|
+
...opts?.ifRev === void 0 ? {} : { ifRev: opts.ifRev }
|
|
97701
|
+
}),
|
|
97702
|
+
delete: (id) => handle.delete({ id })
|
|
97703
|
+
};
|
|
97704
|
+
} };
|
|
97705
|
+
}
|
|
97706
|
+
function integrationRowsApiOver(api) {
|
|
97707
|
+
return {
|
|
97708
|
+
list: async () => (await api.integrations.list({})).integrations,
|
|
97709
|
+
readLegacyIntegrationRows: (input) => api.legacyRows.read(input),
|
|
97710
|
+
clearLegacyIntegrationRows: (input) => api.legacyRows.clear(input)
|
|
97711
|
+
};
|
|
97712
|
+
}
|
|
97713
|
+
/**
|
|
97714
|
+
* One serial lane per migration API: a log line is delivered after the line before it, so the
|
|
97715
|
+
* order the steps wrote them in is the order the hub reads them in. A delivery that fails
|
|
97716
|
+
* is reported once through the same door; if that fails too nothing is left to tell, and the
|
|
97717
|
+
* line is dropped rather than thrown into a migration step or left as an unhandled rejection.
|
|
97718
|
+
*/
|
|
97719
|
+
var lanes = /* @__PURE__ */ new WeakMap();
|
|
97720
|
+
function deliver(api, send) {
|
|
97721
|
+
const next = (lanes.get(api) ?? Promise.resolve()).then(send).then(() => void 0).catch(async (error) => {
|
|
97722
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
97723
|
+
await api.log.warn({ message: `a migration log line could not be delivered: ${reason}` }).catch(() => void 0);
|
|
97724
|
+
});
|
|
97725
|
+
lanes.set(api, next);
|
|
97726
|
+
return next;
|
|
97727
|
+
}
|
|
97728
|
+
function warnOver(api) {
|
|
97729
|
+
return (message) => deliver(api, () => api.log.warn({ message }));
|
|
97730
|
+
}
|
|
97731
|
+
/** What the rows-to-records move needs, over the migration API. */
|
|
97732
|
+
function rowsDepsOver(api) {
|
|
97733
|
+
return {
|
|
97734
|
+
api: integrationRowsApiOver(api),
|
|
97735
|
+
records: recordsOverMigrationApi(api),
|
|
97736
|
+
secrets: secretsOverMigrationApi(api),
|
|
97737
|
+
warn: warnOver(api)
|
|
97738
|
+
};
|
|
97739
|
+
}
|
|
97740
|
+
function defaultIntegrationApiOver$1(api) {
|
|
97741
|
+
return {
|
|
97742
|
+
...integrationRowsApiOver(api),
|
|
97743
|
+
listDevices: async (addonId) => (await api.devices.list({ addonId })).devices,
|
|
97744
|
+
readDeviceHasConfig: async (deviceId) => (await api.devices.get({ deviceId }))?.hasConfig ?? false,
|
|
97745
|
+
setDeviceIntegrationId: async (deviceId, integrationId) => {
|
|
97746
|
+
await api.devices.setIntegrationId({
|
|
97747
|
+
deviceId,
|
|
97748
|
+
integrationId
|
|
97749
|
+
});
|
|
97750
|
+
},
|
|
97751
|
+
readDeviceIntegrationId: async (deviceId) => (await api.devices.get({ deviceId }))?.integrationId ?? null
|
|
97752
|
+
};
|
|
97753
|
+
}
|
|
97754
|
+
/** What the rows-to-records move and the device claim need, over the migration API. */
|
|
97755
|
+
function defaultIntegrationDepsOver(api) {
|
|
97756
|
+
return {
|
|
97757
|
+
...rowsDepsOver(api),
|
|
97758
|
+
api: defaultIntegrationApiOver$1(api),
|
|
97759
|
+
info: (message) => deliver(api, () => api.log.info({ message })),
|
|
97760
|
+
warnDevice: (message, deviceId) => deliver(api, () => api.log.warn({
|
|
97761
|
+
message,
|
|
97762
|
+
tags: { deviceId }
|
|
97763
|
+
}))
|
|
97764
|
+
};
|
|
97765
|
+
}
|
|
97595
97766
|
//#endregion
|
|
97596
97767
|
//#region src/rtsp-instances.ts
|
|
97597
97768
|
/**
|
|
@@ -98239,19 +98410,13 @@ var RtspProviderAddon = class extends BaseDeviceProvider {
|
|
|
98239
98410
|
const records = this.ctx.records;
|
|
98240
98411
|
if (records === void 0) throw new Error("provider-rtsp: ctx.records is required (hub-only addon)");
|
|
98241
98412
|
await records.declare(rtspRecordKinds);
|
|
98242
|
-
|
|
98413
|
+
await this.runMigrations(defaultIntegrationMigrations({
|
|
98243
98414
|
addonId: this.addonId,
|
|
98244
98415
|
date: "2026-11-04"
|
|
98245
|
-
}), defaultIntegrationPortsOver({
|
|
98246
|
-
|
|
98247
|
-
|
|
98248
|
-
records,
|
|
98249
|
-
kind: rtspConnectionsEntryKind,
|
|
98250
|
-
info: (message) => this.ctx.logger.info(message),
|
|
98251
|
-
warn: (message) => this.ctx.logger.warn(message),
|
|
98252
|
-
warnDevice: (message, deviceId) => this.ctx.logger.warn(message, { tags: { deviceId } })
|
|
98416
|
+
}), (api) => defaultIntegrationPortsOver({
|
|
98417
|
+
...defaultIntegrationDepsOver(api),
|
|
98418
|
+
kind: rtspConnectionsEntryKind
|
|
98253
98419
|
}));
|
|
98254
|
-
await this.runMigrations(defaultIntegration);
|
|
98255
98420
|
return { providers: [
|
|
98256
98421
|
...regs,
|
|
98257
98422
|
{
|