@camstack/addon-smtp-nodemailer 1.2.147 → 1.2.149
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 +15 -0
- package/dist/smtp.addon.js +1017 -777
- package/dist/smtp.addon.mjs +1017 -777
- package/package.json +2 -2
package/dist/smtp.addon.js
CHANGED
|
@@ -6020,7 +6020,7 @@ var ZodIssueCode = {
|
|
|
6020
6020
|
var ZodFirstPartyTypeKind;
|
|
6021
6021
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6022
6022
|
//#endregion
|
|
6023
|
-
//#region ../types/dist/sleep-
|
|
6023
|
+
//#region ../types/dist/sleep-BX_MqzUe.mjs
|
|
6024
6024
|
/**
|
|
6025
6025
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6026
6026
|
* window to float samples (D455).
|
|
@@ -6620,7 +6620,7 @@ var NOT_RUN = {
|
|
|
6620
6620
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6621
6621
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6622
6622
|
*/
|
|
6623
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6623
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6624
6624
|
const first = migrations[0];
|
|
6625
6625
|
if (first === void 0) return NOT_RUN;
|
|
6626
6626
|
const owner = first.meta.owner;
|
|
@@ -6649,7 +6649,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6649
6649
|
else if (status === "blocked") blocked.push(id);
|
|
6650
6650
|
else skipped.push(id);
|
|
6651
6651
|
};
|
|
6652
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6652
6653
|
for (const step of declaration.steps) {
|
|
6654
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6653
6655
|
const bound = byId.get(step.id);
|
|
6654
6656
|
if (bound === void 0 || step.action === "skip") {
|
|
6655
6657
|
skipped.push(step.id);
|
|
@@ -6827,6 +6829,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6827
6829
|
}
|
|
6828
6830
|
return out;
|
|
6829
6831
|
}
|
|
6832
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6833
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6834
|
+
"cluster",
|
|
6835
|
+
"device-defaults",
|
|
6836
|
+
"node"
|
|
6837
|
+
];
|
|
6830
6838
|
/**
|
|
6831
6839
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6832
6840
|
*
|
|
@@ -7641,6 +7649,28 @@ var SECRET_RETRY_MAX_MS = 5 * 6e4;
|
|
|
7641
7649
|
/** Coalesces a burst of `keychain.changed` events into one re-resolve. */
|
|
7642
7650
|
var SECRET_CHANGE_DEBOUNCE_MS = 250;
|
|
7643
7651
|
/**
|
|
7652
|
+
* Pair a catalogue with the function that builds its ports FROM THE MIGRATION API. The one
|
|
7653
|
+
* place a catalogue is bound: a migration reaches nothing a ports binder cannot reach
|
|
7654
|
+
* through `api` (guard `check-migration-ports-surface`).
|
|
7655
|
+
*/
|
|
7656
|
+
function migrationSet(catalogue, bindPorts) {
|
|
7657
|
+
return { bind: (api) => defineAddonMigrations(catalogue, bindPorts(api)) };
|
|
7658
|
+
}
|
|
7659
|
+
/** A mixed array is a mistake: the other kind would be dropped without a word. */
|
|
7660
|
+
function refuseMixed(form, kept, dropped) {
|
|
7661
|
+
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`);
|
|
7662
|
+
}
|
|
7663
|
+
function catalogueOf(list) {
|
|
7664
|
+
const kept = list.filter((entry) => !("execute" in entry));
|
|
7665
|
+
refuseMixed("catalogue, (api) => ports", kept.length, list.length - kept.length);
|
|
7666
|
+
return kept;
|
|
7667
|
+
}
|
|
7668
|
+
function boundOf(list) {
|
|
7669
|
+
const kept = list.filter((entry) => "execute" in entry);
|
|
7670
|
+
refuseMixed("bound migrations", kept.length, list.length - kept.length);
|
|
7671
|
+
return kept;
|
|
7672
|
+
}
|
|
7673
|
+
/**
|
|
7644
7674
|
* Base class for CamStack addons. Eliminates settings boilerplate:
|
|
7645
7675
|
*
|
|
7646
7676
|
* - Typed `config` property with automatic resolution from store + defaults
|
|
@@ -7718,6 +7748,13 @@ var BaseAddon = class {
|
|
|
7718
7748
|
*/
|
|
7719
7749
|
secretStatePaths = [];
|
|
7720
7750
|
_addonSecretTree = null;
|
|
7751
|
+
/**
|
|
7752
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7753
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7754
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7755
|
+
*/
|
|
7756
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7757
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7721
7758
|
_deviceSecretTree = null;
|
|
7722
7759
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7723
7760
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7787,6 +7824,7 @@ var BaseAddon = class {
|
|
|
7787
7824
|
this._ctx = context;
|
|
7788
7825
|
this._secretSettings = null;
|
|
7789
7826
|
}
|
|
7827
|
+
await this.loadScopedSchemas();
|
|
7790
7828
|
await this.resolveConfig();
|
|
7791
7829
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7792
7830
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7796,27 +7834,30 @@ var BaseAddon = class {
|
|
|
7796
7834
|
const normalized = normalizeAddonInitResult(result);
|
|
7797
7835
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7798
7836
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7837
|
+
await this.loadScopedSchemas();
|
|
7799
7838
|
return normalized;
|
|
7800
7839
|
}
|
|
7840
|
+
async runMigrations(first, second, third) {
|
|
7841
|
+
if (typeof second === "function") return this.runMigrationSets([migrationSet(catalogueOf(first), second)], third);
|
|
7842
|
+
return this.runBound(boundOf(first), second);
|
|
7843
|
+
}
|
|
7801
7844
|
/**
|
|
7802
|
-
*
|
|
7803
|
-
*
|
|
7804
|
-
*
|
|
7805
|
-
* restored devices — with the catalogue bound to the addon's ports
|
|
7806
|
-
* (`defineAddonMigrations(ADDON_MIGRATIONS, ports)`).
|
|
7807
|
-
*
|
|
7808
|
-
* An engine that cannot be reached or refuses the catalogue is logged and runs
|
|
7809
|
-
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7810
|
-
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7811
|
-
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7845
|
+
* Several catalogues of ONE owner in one declaration (the engine replaces an owner's
|
|
7846
|
+
* catalogue on every declare, so two calls would each judge the other's ids "newer").
|
|
7847
|
+
* Build each entry with `migrationSet(catalogue, (api) => ports)`.
|
|
7812
7848
|
*/
|
|
7813
|
-
async
|
|
7849
|
+
async runMigrationSets(sets, options) {
|
|
7850
|
+
const api = this.ctx.migrationApi;
|
|
7851
|
+
if (api === void 0) throw new Error(`${this.ctx.id}: the migration API is not available on this context`);
|
|
7852
|
+
return this.runBound(sets.flatMap((set) => set.bind(api)), options);
|
|
7853
|
+
}
|
|
7854
|
+
async runBound(migrations, options) {
|
|
7814
7855
|
const api = this.ctx.api.migrations;
|
|
7815
7856
|
const report = await runAddonMigrations({
|
|
7816
7857
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7817
7858
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7818
7859
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7819
|
-
}, migrations, this.ctx.logger);
|
|
7860
|
+
}, migrations, this.ctx.logger, options);
|
|
7820
7861
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7821
7862
|
return report;
|
|
7822
7863
|
}
|
|
@@ -7880,6 +7921,16 @@ var BaseAddon = class {
|
|
|
7880
7921
|
schema(schema) {
|
|
7881
7922
|
return schema;
|
|
7882
7923
|
}
|
|
7924
|
+
/**
|
|
7925
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7926
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7927
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7928
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7929
|
+
* once known.
|
|
7930
|
+
*/
|
|
7931
|
+
scopedSettingsCapabilities() {
|
|
7932
|
+
return [];
|
|
7933
|
+
}
|
|
7883
7934
|
/** Override to provide global-level settings UI schema. */
|
|
7884
7935
|
globalSettingsSchema(_cap) {
|
|
7885
7936
|
return null;
|
|
@@ -7923,7 +7974,8 @@ var BaseAddon = class {
|
|
|
7923
7974
|
return [];
|
|
7924
7975
|
}
|
|
7925
7976
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7926
|
-
|
|
7977
|
+
await this.loadScopedSchemas(cap);
|
|
7978
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7927
7979
|
if (!schema) return { sections: [] };
|
|
7928
7980
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7929
7981
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7958,10 +8010,9 @@ var BaseAddon = class {
|
|
|
7958
8010
|
*
|
|
7959
8011
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7960
8012
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7961
|
-
* SHAPE of the form only — the values live
|
|
7962
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
8013
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7963
8014
|
*/
|
|
7964
|
-
async
|
|
8015
|
+
async integrationFormSchema(_instance) {
|
|
7965
8016
|
return null;
|
|
7966
8017
|
}
|
|
7967
8018
|
/**
|
|
@@ -8007,7 +8058,7 @@ var BaseAddon = class {
|
|
|
8007
8058
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8008
8059
|
*/
|
|
8009
8060
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8010
|
-
const scoped = await this.
|
|
8061
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8011
8062
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8012
8063
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8013
8064
|
}
|
|
@@ -8055,6 +8106,7 @@ var BaseAddon = class {
|
|
|
8055
8106
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8056
8107
|
*/
|
|
8057
8108
|
async resolveGlobalStore(nodeId, cap) {
|
|
8109
|
+
await this.loadScopedSchemas(cap);
|
|
8058
8110
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8059
8111
|
return this.projectForNode(raw, nodeId, cap);
|
|
8060
8112
|
}
|
|
@@ -8065,6 +8117,7 @@ var BaseAddon = class {
|
|
|
8065
8117
|
}
|
|
8066
8118
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8067
8119
|
async readRawProjectedStore(nodeId, cap) {
|
|
8120
|
+
await this.loadScopedSchemas(cap);
|
|
8068
8121
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8069
8122
|
return this.projectForNode(raw, nodeId, cap);
|
|
8070
8123
|
}
|
|
@@ -8167,6 +8220,7 @@ var BaseAddon = class {
|
|
|
8167
8220
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8168
8221
|
}
|
|
8169
8222
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8223
|
+
await this.loadScopedSchemas();
|
|
8170
8224
|
const keys = this.perNodeKeys();
|
|
8171
8225
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8172
8226
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8180,7 +8234,8 @@ var BaseAddon = class {
|
|
|
8180
8234
|
await this.resolveConfig();
|
|
8181
8235
|
await this.onConfigChanged();
|
|
8182
8236
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8183
|
-
|
|
8237
|
+
const legacy = this.globalSettingsSchema();
|
|
8238
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8184
8239
|
}
|
|
8185
8240
|
/**
|
|
8186
8241
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8188,14 +8243,67 @@ var BaseAddon = class {
|
|
|
8188
8243
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8189
8244
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8190
8245
|
*/
|
|
8191
|
-
|
|
8246
|
+
/**
|
|
8247
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8248
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8249
|
+
* derived from the previous set. Idempotent.
|
|
8250
|
+
*/
|
|
8251
|
+
async loadScopedSchemas(extraCap) {
|
|
8252
|
+
const caps = new Set([
|
|
8253
|
+
...this.scopedSettingsCapabilities(),
|
|
8254
|
+
...this._registeredCapNames,
|
|
8255
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8256
|
+
]);
|
|
8257
|
+
let added = false;
|
|
8258
|
+
for (const cap of caps) {
|
|
8259
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8260
|
+
const loaded = [];
|
|
8261
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8262
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8263
|
+
if (schema !== null) loaded.push({
|
|
8264
|
+
scope,
|
|
8265
|
+
schema
|
|
8266
|
+
});
|
|
8267
|
+
}
|
|
8268
|
+
this._scopedByCap.set(cap, loaded);
|
|
8269
|
+
added = true;
|
|
8270
|
+
}
|
|
8271
|
+
if (!added) return;
|
|
8272
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8273
|
+
this._addonSecretTree = null;
|
|
8274
|
+
}
|
|
8275
|
+
/**
|
|
8276
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8277
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8278
|
+
*/
|
|
8279
|
+
scopedFormSchema(cap) {
|
|
8280
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8281
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8282
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8283
|
+
}
|
|
8284
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8285
|
+
loadedScopedSchemas(cap) {
|
|
8286
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8287
|
+
return [...this._scopedByCap.values()].flat();
|
|
8288
|
+
}
|
|
8289
|
+
/**
|
|
8290
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8291
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8292
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8293
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8294
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8295
|
+
*/
|
|
8192
8296
|
perNodeKeys(cap) {
|
|
8193
8297
|
const cacheKey = cap ?? "";
|
|
8194
|
-
const cached = this.
|
|
8298
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8195
8299
|
if (cached) return cached;
|
|
8196
|
-
const
|
|
8197
|
-
const keys = new Set(
|
|
8198
|
-
this.
|
|
8300
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8301
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8302
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8303
|
+
if (scope !== "node") continue;
|
|
8304
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8305
|
+
}
|
|
8306
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8199
8307
|
return keys;
|
|
8200
8308
|
}
|
|
8201
8309
|
/**
|
|
@@ -8205,10 +8313,9 @@ var BaseAddon = class {
|
|
|
8205
8313
|
* time to return its response before the addon is torn down and
|
|
8206
8314
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8207
8315
|
*/
|
|
8208
|
-
maybeAutoRestart(patchKeys,
|
|
8209
|
-
if (!schema) return;
|
|
8316
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8210
8317
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8211
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8318
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8212
8319
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8213
8320
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8214
8321
|
}
|
|
@@ -8404,12 +8511,14 @@ var BaseAddon = class {
|
|
|
8404
8511
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8405
8512
|
*/
|
|
8406
8513
|
async getDeclaredSettingsKeys() {
|
|
8514
|
+
await this.loadScopedSchemas();
|
|
8407
8515
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8408
8516
|
}
|
|
8409
8517
|
declaredKeySet() {
|
|
8410
8518
|
const keys = new Set(Object.keys(this.defaults));
|
|
8411
8519
|
const schema = this.globalSettingsSchema();
|
|
8412
8520
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8521
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8413
8522
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8414
8523
|
for (const path of this.secretStatePaths) {
|
|
8415
8524
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8505,7 +8614,11 @@ var BaseAddon = class {
|
|
|
8505
8614
|
}
|
|
8506
8615
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8507
8616
|
addonSecretTree() {
|
|
8508
|
-
this._addonSecretTree
|
|
8617
|
+
if (this._addonSecretTree === null) {
|
|
8618
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8619
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8620
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8621
|
+
}
|
|
8509
8622
|
return this._addonSecretTree;
|
|
8510
8623
|
}
|
|
8511
8624
|
deviceSecretTree() {
|
|
@@ -8755,6 +8868,23 @@ function dropPerNodeFields(fields) {
|
|
|
8755
8868
|
}
|
|
8756
8869
|
return kept;
|
|
8757
8870
|
}
|
|
8871
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8872
|
+
function collectValueFieldKeys(fields) {
|
|
8873
|
+
const collected = [];
|
|
8874
|
+
for (const field of fields) {
|
|
8875
|
+
if (field.type === "group") {
|
|
8876
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8877
|
+
continue;
|
|
8878
|
+
}
|
|
8879
|
+
if (field.type === "sub-tabs") {
|
|
8880
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8881
|
+
continue;
|
|
8882
|
+
}
|
|
8883
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8884
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8885
|
+
}
|
|
8886
|
+
return collected;
|
|
8887
|
+
}
|
|
8758
8888
|
function collectFieldKeys(fields) {
|
|
8759
8889
|
const collected = [];
|
|
8760
8890
|
for (const field of fields) {
|
|
@@ -9288,6 +9418,7 @@ function defineEntryKind(name, spec) {
|
|
|
9288
9418
|
class: "config",
|
|
9289
9419
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9290
9420
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9421
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9291
9422
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9292
9423
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9293
9424
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
@@ -11967,7 +12098,7 @@ function userError(code, params, englishFallback) {
|
|
|
11967
12098
|
return new CamstackError(code, params, englishFallback);
|
|
11968
12099
|
}
|
|
11969
12100
|
//#endregion
|
|
11970
|
-
//#region ../types/dist/composition-
|
|
12101
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11971
12102
|
/**
|
|
11972
12103
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11973
12104
|
*
|
|
@@ -13465,7 +13596,7 @@ var RecordIntegrationInstances = class {
|
|
|
13465
13596
|
return record === null ? null : this.viewOf(record);
|
|
13466
13597
|
};
|
|
13467
13598
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
13468
|
-
createInstance = async ({ id, name, kind }) => {
|
|
13599
|
+
createInstance = async ({ id, name, kind, values }) => {
|
|
13469
13600
|
const minted = id ?? this.hooks.mintId?.({
|
|
13470
13601
|
name,
|
|
13471
13602
|
...kind !== void 0 ? { kind } : {}
|
|
@@ -13475,10 +13606,20 @@ var RecordIntegrationInstances = class {
|
|
|
13475
13606
|
id: minted,
|
|
13476
13607
|
data: this.hooks.initialBody(name, kind)
|
|
13477
13608
|
}, { ifRev: 0 });
|
|
13609
|
+
const patch = this.patchOf(values ?? {});
|
|
13610
|
+
if (Object.keys(patch).length > 0) await this.handle.patch(minted, patch);
|
|
13478
13611
|
await this.hooks.onChanged?.(minted, "created");
|
|
13479
13612
|
return { id: minted };
|
|
13480
13613
|
};
|
|
13481
13614
|
updateInstance = async ({ id, name, values }) => {
|
|
13615
|
+
const patch = this.patchOf(values);
|
|
13616
|
+
if (name !== void 0) patch[this.nameField] = name;
|
|
13617
|
+
await this.handle.patch(id, patch);
|
|
13618
|
+
await this.hooks.onChanged?.(id, "updated");
|
|
13619
|
+
return { ok: true };
|
|
13620
|
+
};
|
|
13621
|
+
/** The record patch a form `values` object means: secrets kept, cleared or replaced. */
|
|
13622
|
+
patchOf(values) {
|
|
13482
13623
|
const patch = {};
|
|
13483
13624
|
for (const [key, value] of Object.entries(values)) {
|
|
13484
13625
|
if (value === void 0) continue;
|
|
@@ -13489,11 +13630,8 @@ var RecordIntegrationInstances = class {
|
|
|
13489
13630
|
else if (isSecretClear(value)) patch[key] = null;
|
|
13490
13631
|
else if (typeof value === "string" && value !== "" && value !== "__camstack_redacted__" && !isSecretFieldView(value)) patch[key] = value;
|
|
13491
13632
|
}
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
await this.hooks.onChanged?.(id, "updated");
|
|
13495
|
-
return { ok: true };
|
|
13496
|
-
};
|
|
13633
|
+
return patch;
|
|
13634
|
+
}
|
|
13497
13635
|
/** The switch lives in the record body; the addon is told so it applies it (D996). */
|
|
13498
13636
|
setInstanceEnabled = async ({ id, enabled }) => {
|
|
13499
13637
|
if (this.registryOwnsEnabled) throw new Error(`instance "${id}": the integrations registry owns the on/off switch of this addon; set it with integrations.update`);
|
|
@@ -13530,6 +13668,76 @@ var RecordIntegrationInstances = class {
|
|
|
13530
13668
|
};
|
|
13531
13669
|
}
|
|
13532
13670
|
};
|
|
13671
|
+
/** No part holds the id a call named. */
|
|
13672
|
+
var UnknownInstanceError = class extends Error {
|
|
13673
|
+
instanceId;
|
|
13674
|
+
constructor(instanceId) {
|
|
13675
|
+
super(`Unknown instance '${instanceId}'`);
|
|
13676
|
+
this.instanceId = instanceId;
|
|
13677
|
+
this.name = "UnknownInstanceError";
|
|
13678
|
+
}
|
|
13679
|
+
};
|
|
13680
|
+
/** A create or a form named no kind, or a kind no part serves. */
|
|
13681
|
+
var UnknownInstanceKindError = class extends Error {
|
|
13682
|
+
kindId;
|
|
13683
|
+
constructor(kindId) {
|
|
13684
|
+
super(kindId === void 0 ? "This provider has several kinds: name one" : `Unknown instance kind '${kindId}'`);
|
|
13685
|
+
this.kindId = kindId;
|
|
13686
|
+
this.name = "UnknownInstanceKindError";
|
|
13687
|
+
}
|
|
13688
|
+
};
|
|
13689
|
+
function composeInstanceKinds(parts, base) {
|
|
13690
|
+
const byKind = (kindId) => {
|
|
13691
|
+
if (kindId === void 0) {
|
|
13692
|
+
const only = parts.length === 1 ? parts[0] : void 0;
|
|
13693
|
+
if (only === void 0) throw new UnknownInstanceKindError(void 0);
|
|
13694
|
+
return only;
|
|
13695
|
+
}
|
|
13696
|
+
const part = parts.find((candidate) => candidate.kind.id === kindId);
|
|
13697
|
+
if (part === void 0) throw new UnknownInstanceKindError(kindId);
|
|
13698
|
+
return part;
|
|
13699
|
+
};
|
|
13700
|
+
const holderOf = async (id) => {
|
|
13701
|
+
for (const part of parts) if (await part.provider.getInstance({ id }) !== null) return part;
|
|
13702
|
+
throw new UnknownInstanceError(id);
|
|
13703
|
+
};
|
|
13704
|
+
const tagged = (part, instance) => ({
|
|
13705
|
+
...instance,
|
|
13706
|
+
kind: part.kind.id
|
|
13707
|
+
});
|
|
13708
|
+
return {
|
|
13709
|
+
describe: async () => ({
|
|
13710
|
+
...base,
|
|
13711
|
+
kinds: parts.map((part) => part.kind)
|
|
13712
|
+
}),
|
|
13713
|
+
instanceFormSchema: async ({ id, kind }) => {
|
|
13714
|
+
return (id === null ? byKind(kind) : await holderOf(id)).provider.instanceFormSchema({ id });
|
|
13715
|
+
},
|
|
13716
|
+
listInstances: async () => {
|
|
13717
|
+
return (await Promise.all(parts.map(async (part) => (await part.provider.listInstances()).map((instance) => tagged(part, instance))))).flat();
|
|
13718
|
+
},
|
|
13719
|
+
getInstance: async ({ id }) => {
|
|
13720
|
+
for (const part of parts) {
|
|
13721
|
+
const found = await part.provider.getInstance({ id });
|
|
13722
|
+
if (found !== null) return tagged(part, found);
|
|
13723
|
+
}
|
|
13724
|
+
return null;
|
|
13725
|
+
},
|
|
13726
|
+
createInstance: async (input) => byKind(input.kind).provider.createInstance(input),
|
|
13727
|
+
updateInstance: async (input) => (await holderOf(input.id)).provider.updateInstance(input),
|
|
13728
|
+
setInstanceEnabled: async (input) => (await holderOf(input.id)).provider.setInstanceEnabled(input),
|
|
13729
|
+
deleteInstance: async (input) => (await holderOf(input.id)).provider.deleteInstance(input),
|
|
13730
|
+
testInstance: async (input) => {
|
|
13731
|
+
const test = (input.id === null ? byKind(input.kind) : await holderOf(input.id)).provider.testInstance;
|
|
13732
|
+
if (test === void 0) return {
|
|
13733
|
+
success: false,
|
|
13734
|
+
status: "inconclusive",
|
|
13735
|
+
error: "This kind cannot be tested"
|
|
13736
|
+
};
|
|
13737
|
+
return test(input);
|
|
13738
|
+
}
|
|
13739
|
+
};
|
|
13740
|
+
}
|
|
13533
13741
|
var EncodeProfileSchema = object({
|
|
13534
13742
|
video: object({
|
|
13535
13743
|
codec: _enum([
|
|
@@ -14575,7 +14783,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14575
14783
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14576
14784
|
* catalogue lists the id (a newer database).
|
|
14577
14785
|
*/
|
|
14578
|
-
meta: MigrationMetaSchema.optional()
|
|
14786
|
+
meta: MigrationMetaSchema.optional(),
|
|
14787
|
+
/**
|
|
14788
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14789
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14790
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14791
|
+
*/
|
|
14792
|
+
ownerNotLoaded: boolean().optional()
|
|
14579
14793
|
});
|
|
14580
14794
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14581
14795
|
var MigrationPendingEntrySchema = object({
|
|
@@ -15696,6 +15910,39 @@ var UnstampedEventMediaCountSchema = object({
|
|
|
15696
15910
|
/** Sum across both, or `null` when either lane could not be counted. */
|
|
15697
15911
|
total: number().int().nonnegative().nullable()
|
|
15698
15912
|
}).nullable();
|
|
15913
|
+
/**
|
|
15914
|
+
* Where the stored event media sits, per storage location: how many media rows
|
|
15915
|
+
* and how many bytes carry each location id, how many carry none (legacy,
|
|
15916
|
+
* resolved to the default `eventMedia` location), and how many name a location
|
|
15917
|
+
* that was not asked about (a deleted or never-listed one).
|
|
15918
|
+
*
|
|
15919
|
+
* One covering-index aggregate per asked location plus the NULL one, and one
|
|
15920
|
+
* unfiltered total that scans the covering index: O(rows), not a walk of the
|
|
15921
|
+
* rows themselves. `unlisted` is total minus the ASKED locations minus NULL, so
|
|
15922
|
+
* it is a deleted location only when every location id was asked.
|
|
15923
|
+
* `null` is "could not be measured", never zero.
|
|
15924
|
+
*/
|
|
15925
|
+
var MediaLocationCensusInputSchema = object({ locationIds: array(string().min(1)).max(50).readonly() });
|
|
15926
|
+
var MediaLocationCensusSchema = object({
|
|
15927
|
+
locations: array(object({
|
|
15928
|
+
locationId: string(),
|
|
15929
|
+
rows: number().int().nonnegative(),
|
|
15930
|
+
bytes: number().nonnegative()
|
|
15931
|
+
})).readonly(),
|
|
15932
|
+
unstamped: object({
|
|
15933
|
+
rows: number().int().nonnegative(),
|
|
15934
|
+
bytes: number().nonnegative()
|
|
15935
|
+
}),
|
|
15936
|
+
/** Rows stamped with a location id that was not in the asked list. */
|
|
15937
|
+
unlisted: object({
|
|
15938
|
+
rows: number().int().nonnegative(),
|
|
15939
|
+
bytes: number().nonnegative()
|
|
15940
|
+
}),
|
|
15941
|
+
total: object({
|
|
15942
|
+
rows: number().int().nonnegative(),
|
|
15943
|
+
bytes: number().nonnegative()
|
|
15944
|
+
})
|
|
15945
|
+
});
|
|
15699
15946
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
15700
15947
|
/** The independently selectable logical storage classes — every class
|
|
15701
15948
|
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
@@ -46216,52 +46463,7 @@ DeviceType.Light, method(object({
|
|
|
46216
46463
|
level: number().min(0).max(100),
|
|
46217
46464
|
lastChangedAt: number()
|
|
46218
46465
|
});
|
|
46219
|
-
|
|
46220
|
-
* `broker` — unified pub/sub broker registry, system-scoped collection.
|
|
46221
|
-
*
|
|
46222
|
-
* The cap models any kind-tagged message broker the user wants to
|
|
46223
|
-
* register and that other addons might consume. The first two kinds
|
|
46224
|
-
* are MQTT (mosquitto / aedes embedded / cloud bridge) and
|
|
46225
|
-
* Home Assistant (HA WebSocket — subscribe_entities + call_service).
|
|
46226
|
-
* Future kinds (Zigbee2MQTT bridge, ZHA, KNX, Telegram, …) plug in
|
|
46227
|
-
* the same surface.
|
|
46228
|
-
*
|
|
46229
|
-
* Why one cap, not one cap per kind:
|
|
46230
|
-
* - The integrations page wants a single table of "every broker"
|
|
46231
|
-
* across kinds; a unified cap drives it without join logic.
|
|
46232
|
-
* - Generic admin operations (add / remove / test / status) are
|
|
46233
|
-
* identical across kinds — duplicating them per cap was busywork.
|
|
46234
|
-
* - The `network-access` cap precedent: same interface, many
|
|
46235
|
-
* `providerKind: 'ingress'` implementations (Tailscale, ngrok, …).
|
|
46236
|
-
*
|
|
46237
|
-
* What stays kind-specific:
|
|
46238
|
-
* - `getBrokerConfig` payload (kind decides whether it returns
|
|
46239
|
-
* mqtt URL+creds, HA baseUrl+token, …) — typed as
|
|
46240
|
-
* `Record<string, unknown>` at the wire; consumers narrow.
|
|
46241
|
-
* - `publish` / `subscribe` / `getState` arguments (target / filter /
|
|
46242
|
-
* key) — also typed as `Record<string, unknown>` so each kind can
|
|
46243
|
-
* evolve its surface (MQTT topic+qos vs HA entity_id+domain)
|
|
46244
|
-
* without rebreaking the cap signature.
|
|
46245
|
-
* - `add` `settings` payload (different fields per kind, validated by
|
|
46246
|
-
* the kind-specific provider).
|
|
46247
|
-
*
|
|
46248
|
-
* Bidirectionality:
|
|
46249
|
-
* - `publish` is RPC — synchronous result (provider-defined).
|
|
46250
|
-
* - `subscribe` returns a subscription handle; the actual message
|
|
46251
|
-
* stream flows over the typed event-bus as `broker.message` events
|
|
46252
|
-
* keyed by `(brokerId, subscriptionId)`. Consumers filter in their
|
|
46253
|
-
* event handler. Subscriptions persist across reconnects — the
|
|
46254
|
-
* provider re-subscribes upstream after a transport drop.
|
|
46255
|
-
*
|
|
46256
|
-
* Why event-bus push (not a subscription RPC stream):
|
|
46257
|
-
* - Matches the rest of CamStack's D8 contract — events for telemetry,
|
|
46258
|
-
* RPC for loss-is-a-bug. Broker messages are telemetry (a single
|
|
46259
|
-
* drop is recoverable via `getState({key})`).
|
|
46260
|
-
* - Frees `broker.subscribe` from holding a long-lived RPC channel.
|
|
46261
|
-
* - Cross-process: events already route through `$event-bus`; no
|
|
46262
|
-
* per-broker wire-shaping needed.
|
|
46263
|
-
*/
|
|
46264
|
-
var BrokerStatusEnum = _enum([
|
|
46466
|
+
_enum([
|
|
46265
46467
|
"connected",
|
|
46266
46468
|
"disconnected",
|
|
46267
46469
|
"connecting",
|
|
@@ -46269,141 +46471,12 @@ var BrokerStatusEnum = _enum([
|
|
|
46269
46471
|
"unreachable",
|
|
46270
46472
|
"error"
|
|
46271
46473
|
]);
|
|
46272
|
-
var BrokerInfoSchema = object({
|
|
46273
|
-
/** Stable broker id. Persisted; survives addon restarts. */
|
|
46274
|
-
id: string(),
|
|
46275
|
-
/** Addon id of the provider that OWNS this broker.
|
|
46276
|
-
*
|
|
46277
|
-
* The `broker` cap is a system-scoped collection: several addons
|
|
46278
|
-
* register a `broker` provider, each owning a DISJOINT set of
|
|
46279
|
-
* brokers (mqtt-broker owns `mqtt_*`, provider-homeassistant owns
|
|
46280
|
-
* `ha_*`). A broker therefore belongs to exactly one addon — like a
|
|
46281
|
-
* device belongs to one integration. The admin UI threads this id
|
|
46282
|
-
* back as the `{ addonId }` system-collection selector on every
|
|
46283
|
-
* id-keyed call (`get` / `getSettings` / `setSettings` / `remove` /
|
|
46284
|
-
* `testConnection`), so the call routes to the OWNING provider
|
|
46285
|
-
* instead of defaulting to the first-registered one. */
|
|
46286
|
-
addonId: string(),
|
|
46287
|
-
/** Human-readable name (operator-chosen at add-time). */
|
|
46288
|
-
name: string(),
|
|
46289
|
-
/** Provider-defined kind tag — `mqtt` / `home-assistant` / future. */
|
|
46290
|
-
kind: string(),
|
|
46291
|
-
status: BrokerStatusEnum,
|
|
46292
|
-
/** Free-form provider-specific info (HA version, MQTT broker
|
|
46293
|
-
* flavour, latency, mTLS active, …). */
|
|
46294
|
-
info: record(string(), unknown()),
|
|
46295
|
-
/** Ms epoch of the last connection probe / status update. */
|
|
46296
|
-
lastCheckedAt: number().nullable(),
|
|
46297
|
-
/** Last error message, when `status` indicates failure. */
|
|
46298
|
-
error: string().nullable()
|
|
46299
|
-
});
|
|
46300
46474
|
var RegistryStatusSchema = object({
|
|
46301
46475
|
brokerCount: number().int().nonnegative(),
|
|
46302
46476
|
connectedCount: number().int().nonnegative()
|
|
46303
46477
|
});
|
|
46304
|
-
/**
|
|
46305
|
-
* One entry per `broker` provider — which addon provides which broker
|
|
46306
|
-
* kind(s). The unified create picker reads this to know, for each
|
|
46307
|
-
* (addonId, kind) pair the operator can pick, where to route the
|
|
46308
|
-
* follow-up `getSettingsSchema` / `testSettings` / `add` calls (via the
|
|
46309
|
-
* `{ addonId }` selector). A provider only advertises a kind here when
|
|
46310
|
-
* it implements `getSettingsSchema` for that kind (so the picker never
|
|
46311
|
-
* offers a kind whose creation form can't be rendered).
|
|
46312
|
-
*/
|
|
46313
|
-
var BrokerProviderInfoSchema = object({
|
|
46314
|
-
/** Addon id of the `broker` provider this entry describes. */
|
|
46315
|
-
addonId: string(),
|
|
46316
|
-
/** Broker kinds this provider can create, with a display label. */
|
|
46317
|
-
kinds: array(object({
|
|
46318
|
-
kind: string(),
|
|
46319
|
-
label: string(),
|
|
46320
|
-
/**
|
|
46321
|
-
* Relative hub path to start an interactive account link (OAuth popup).
|
|
46322
|
-
* May include `{{brokerId}}`; admin-ui substitutes and appends `?popup=1`.
|
|
46323
|
-
*/
|
|
46324
|
-
interactiveConnectPath: string().min(1).optional(),
|
|
46325
|
-
/** Relative hub path to disconnect the linked account (same popup pattern). */
|
|
46326
|
-
interactiveDisconnectPath: string().min(1).optional(),
|
|
46327
|
-
/**
|
|
46328
|
-
* The kind signs in through a server-held interactive session instead of
|
|
46329
|
-
* (or before) typed credentials. `basePath` is a hub-relative route set
|
|
46330
|
-
* served by the owning addon (`/addon/<id>/...`) speaking the sign-in
|
|
46331
|
-
* contract: `POST {basePath}/start` (body: the form's settings) answers
|
|
46332
|
-
* `{ sessionId, loginUrl }`; `GET {basePath}/status?sessionId=` answers
|
|
46333
|
-
* `{ phase: 'starting' | 'awaiting-login' | 'captured' | 'error' |
|
|
46334
|
-
* 'cancelled', loginUrl, error }`; `POST {basePath}/commit`
|
|
46335
|
-
* (`{ sessionId, name, brokerId? }`) stores the credentials on a new or
|
|
46336
|
-
* existing broker and answers `{ brokerId }`; `POST {basePath}/cancel`
|
|
46337
|
-
* (`{ sessionId }`) drops the session. The dialog that edits a broker
|
|
46338
|
-
* offers it as the default path; the fields the session fills are
|
|
46339
|
-
* marked `filledBySignIn` in the kind's settings schema.
|
|
46340
|
-
*
|
|
46341
|
-
* `methods` lists the ways the session can sign in, recommended first:
|
|
46342
|
-
* `'device-code'` adds `POST {basePath}/code/start` (body: the form's
|
|
46343
|
-
* settings) answering `{ sessionId, userCode, verificationUrl,
|
|
46344
|
-
* expiresAt }` — the operator types `userCode` at `verificationUrl` on
|
|
46345
|
-
* any device, and the same `status` / `commit` / `cancel` routes follow
|
|
46346
|
-
* (status then carries `method`, `userCode`, `verificationUrl`,
|
|
46347
|
-
* `expiresAt` and the phases `'awaiting-code'` / `'expired'`);
|
|
46348
|
-
* `'proxy'` is the `{basePath}/start` flow above. Absent means
|
|
46349
|
-
* `['proxy']`.
|
|
46350
|
-
*/
|
|
46351
|
-
signIn: object({
|
|
46352
|
-
basePath: string().min(1),
|
|
46353
|
-
methods: array(_enum(["device-code", "proxy"])).optional()
|
|
46354
|
-
}).optional()
|
|
46355
|
-
}))
|
|
46356
|
-
});
|
|
46357
|
-
var ListInputSchema = object({
|
|
46358
|
-
/** Optional kind filter — `list({kind:'home-assistant'})` returns
|
|
46359
|
-
* only HA brokers. Omit to list every broker. */
|
|
46360
|
-
kind: string().optional() });
|
|
46361
46478
|
var GetInputSchema = object({ id: string() });
|
|
46362
|
-
var AddInputSchema = object({
|
|
46363
|
-
kind: string().min(1),
|
|
46364
|
-
name: string().min(1),
|
|
46365
|
-
/**
|
|
46366
|
-
* ADOPT an existing id instead of minting a new one.
|
|
46367
|
-
*
|
|
46368
|
-
* Written the day this cost an outage. A broker's id is not a detail: HA
|
|
46369
|
-
* devices carry it inside their `stableId` (`ha:ha_004:dev:…`), so a broker
|
|
46370
|
-
* lost from config and re-added as `ha_001` leaves every one of its devices
|
|
46371
|
-
* bound to a broker that no longer exists. Re-entering the password under the
|
|
46372
|
-
* ORIGINAL id turns a multi-step device migration back into re-entering a
|
|
46373
|
-
* password.
|
|
46374
|
-
*
|
|
46375
|
-
* A provider MUST refuse an id that is already in use — adopting a live
|
|
46376
|
-
* broker's id would silently take it over.
|
|
46377
|
-
*/
|
|
46378
|
-
id: string().min(1).optional(),
|
|
46379
|
-
/** Kind-specific settings (e.g. MQTT `{url,username,password}` or HA
|
|
46380
|
-
* `{baseUrl,accessToken}`). Validated by the kind-specific provider
|
|
46381
|
-
* branch on receipt — invalid shape rejects the add. */
|
|
46382
|
-
settings: record(string(), unknown())
|
|
46383
|
-
});
|
|
46384
|
-
var AddResultSchema = object({ id: string() });
|
|
46385
|
-
var RemoveInputSchema = object({ id: string() });
|
|
46386
|
-
var TestConnectionResultSchema = discriminatedUnion("ok", [object({
|
|
46387
|
-
ok: literal(true),
|
|
46388
|
-
latencyMs: number().nonnegative()
|
|
46389
|
-
}), object({
|
|
46390
|
-
ok: literal(false),
|
|
46391
|
-
error: string()
|
|
46392
|
-
})]);
|
|
46393
46479
|
var SettingsRecordSchema$1 = record(string(), unknown());
|
|
46394
|
-
var SettingsSchemaInputSchema = object({ kind: string() });
|
|
46395
|
-
var TestSettingsInputSchema = object({
|
|
46396
|
-
kind: string(),
|
|
46397
|
-
settings: SettingsRecordSchema$1
|
|
46398
|
-
});
|
|
46399
|
-
var TestSettingsResultSchema = discriminatedUnion("ok", [object({
|
|
46400
|
-
ok: literal(true),
|
|
46401
|
-
latencyMs: number().nonnegative().optional()
|
|
46402
|
-
}).strict(), object({
|
|
46403
|
-
ok: literal(false),
|
|
46404
|
-
error: string()
|
|
46405
|
-
})]);
|
|
46406
|
-
var SettingsSchemaResultSchema = unknown().nullable();
|
|
46407
46480
|
var PublishInputSchema = object({
|
|
46408
46481
|
brokerId: string(),
|
|
46409
46482
|
/** Kind-specific routing target.
|
|
@@ -46447,51 +46520,12 @@ var brokerCapability = {
|
|
|
46447
46520
|
kind: "poll"
|
|
46448
46521
|
},
|
|
46449
46522
|
methods: {
|
|
46450
|
-
list: method(ListInputSchema, array(BrokerInfoSchema)),
|
|
46451
|
-
get: method(GetInputSchema, BrokerInfoSchema.nullable()),
|
|
46452
|
-
/** Enumerate which addon provides which broker kind(s) for the
|
|
46453
|
-
* unified create picker. The auto-mount fans this array across
|
|
46454
|
-
* every registered `broker` provider (array-output method), so the
|
|
46455
|
-
* picker sees every kind from every provider in one call. */
|
|
46456
|
-
listProviders: method(_void(), array(BrokerProviderInfoSchema), { auth: "admin" }),
|
|
46457
|
-
add: method(AddInputSchema, AddResultSchema, {
|
|
46458
|
-
kind: "mutation",
|
|
46459
|
-
auth: "admin"
|
|
46460
|
-
}),
|
|
46461
|
-
remove: method(RemoveInputSchema, _void(), {
|
|
46462
|
-
kind: "mutation",
|
|
46463
|
-
auth: "admin"
|
|
46464
|
-
}),
|
|
46465
|
-
testConnection: method(GetInputSchema, TestConnectionResultSchema, {
|
|
46466
|
-
kind: "mutation",
|
|
46467
|
-
auth: "admin"
|
|
46468
|
-
}),
|
|
46469
|
-
/** Read the persisted settings record for a broker (kind-specific
|
|
46470
|
-
* shape). Admin-only — settings may contain secrets. Returns `null`
|
|
46471
|
-
* when the broker id is unknown to the provider (the collection
|
|
46472
|
-
* fallback may route a foreign id to the first provider). */
|
|
46473
|
-
getSettings: method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }),
|
|
46474
|
-
/** Overwrite the persisted settings record. The kind-specific
|
|
46475
|
-
* provider validates the shape and applies the change (reconnects
|
|
46476
|
-
* if credentials changed). */
|
|
46477
|
-
setSettings: method(object({
|
|
46478
|
-
id: string(),
|
|
46479
|
-
settings: SettingsRecordSchema$1
|
|
46480
|
-
}), _void(), {
|
|
46481
|
-
kind: "mutation",
|
|
46482
|
-
auth: "admin"
|
|
46483
|
-
}),
|
|
46484
46523
|
/** Returns the kind-specific connection config the consumer needs
|
|
46485
46524
|
* to open its own client (MQTT pattern: `{url, username, password,
|
|
46486
46525
|
* clientIdPrefix}`). HA providers MAY return the auth envelope
|
|
46487
46526
|
* but typical HA consumers use `publish` / `subscribe` instead.
|
|
46488
46527
|
* Returns `null` when the broker id is unknown to the provider. */
|
|
46489
46528
|
getBrokerConfig: method(GetInputSchema, SettingsRecordSchema$1.nullable(), { auth: "admin" }),
|
|
46490
|
-
getSettingsSchema: method(SettingsSchemaInputSchema, SettingsSchemaResultSchema, { auth: "admin" }),
|
|
46491
|
-
testSettings: method(TestSettingsInputSchema, TestSettingsResultSchema, {
|
|
46492
|
-
kind: "mutation",
|
|
46493
|
-
auth: "admin"
|
|
46494
|
-
}),
|
|
46495
46529
|
publish: method(PublishInputSchema, unknown(), {
|
|
46496
46530
|
kind: "mutation",
|
|
46497
46531
|
auth: "admin"
|
|
@@ -49115,6 +49149,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
49115
49149
|
runsFound: number().int().nonnegative(),
|
|
49116
49150
|
pausesFound: number().int().nonnegative(),
|
|
49117
49151
|
longestPauseMs: number().nonnegative(),
|
|
49152
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49153
|
+
pulsesFound: number().int().nonnegative(),
|
|
49154
|
+
longestPulseMs: number().nonnegative(),
|
|
49118
49155
|
idleFloorW: number(),
|
|
49119
49156
|
noiseCeilingW: number(),
|
|
49120
49157
|
plateauW: number(),
|
|
@@ -50561,7 +50598,14 @@ var DiscoveryCandidateSchema = object({
|
|
|
50561
50598
|
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
50562
50599
|
parentStableId: string().optional(),
|
|
50563
50600
|
/** `false` = shown for context but not pickable. Default true. */
|
|
50564
|
-
selectable: boolean().optional()
|
|
50601
|
+
selectable: boolean().optional(),
|
|
50602
|
+
/**
|
|
50603
|
+
* Keys of the provider's creation form (`getChildCreationSchema`) this candidate
|
|
50604
|
+
* cannot be adopted without — a key for an encrypted board, a pairing code. The
|
|
50605
|
+
* admin asks for exactly these, on the creation form prefilled with
|
|
50606
|
+
* `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
|
|
50607
|
+
*/
|
|
50608
|
+
needsParams: array(string()).optional()
|
|
50565
50609
|
});
|
|
50566
50610
|
/**
|
|
50567
50611
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -50620,12 +50664,24 @@ method(_void(), _void(), { kind: "mutation" }), method(_void(), _void(), { kind:
|
|
|
50620
50664
|
}), array(DiscoveryCandidateSchema), {
|
|
50621
50665
|
kind: "mutation",
|
|
50622
50666
|
auth: "admin"
|
|
50623
|
-
}), method(object({}), CreationSchemaOutputSchema), method(object({
|
|
50667
|
+
}), method(object({}), CreationSchemaOutputSchema), method(object({
|
|
50668
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
50669
|
+
integrationId: string().optional() }), object({ deviceType: _enum(DeviceType).nullable() })), method(object({
|
|
50670
|
+
candidate: DiscoveryCandidateSchema,
|
|
50671
|
+
/** The integration the operator adopts into; a provider with several instances needs it. */
|
|
50672
|
+
integrationId: string().optional()
|
|
50673
|
+
}), DeviceSummarySchema, {
|
|
50624
50674
|
kind: "mutation",
|
|
50625
50675
|
auth: "admin"
|
|
50626
|
-
}), method(object({}), boolean()), method(object({
|
|
50676
|
+
}), method(object({}), boolean()), method(object({
|
|
50627
50677
|
type: _enum(DeviceType),
|
|
50628
|
-
|
|
50678
|
+
/** The integration asked about; a provider whose integrations differ answers per one. */
|
|
50679
|
+
integrationId: string().optional()
|
|
50680
|
+
}), CreationSchemaOutputSchema), method(object({
|
|
50681
|
+
type: _enum(DeviceType),
|
|
50682
|
+
config: record(string(), unknown()),
|
|
50683
|
+
/** The integration the operator adds the device to; a provider with several instances needs it. */
|
|
50684
|
+
integrationId: string().optional()
|
|
50629
50685
|
}), DeviceSummarySchema, {
|
|
50630
50686
|
kind: "mutation",
|
|
50631
50687
|
auth: "admin"
|
|
@@ -51335,7 +51391,10 @@ method(object({
|
|
|
51335
51391
|
auth: "admin"
|
|
51336
51392
|
}), method(object({
|
|
51337
51393
|
addonId: string(),
|
|
51338
|
-
type: _enum(DeviceType)
|
|
51394
|
+
type: _enum(DeviceType),
|
|
51395
|
+
/** The integration the form is for; a provider whose integrations differ (a hub
|
|
51396
|
+
* integration takes no manual add) answers per integration. Absent = the addon's own. */
|
|
51397
|
+
integrationId: string().optional()
|
|
51339
51398
|
}), unknown().nullable()), method(object({
|
|
51340
51399
|
addonId: string(),
|
|
51341
51400
|
type: _enum(DeviceType),
|
|
@@ -51419,7 +51478,11 @@ method(object({
|
|
|
51419
51478
|
kind: "mutation",
|
|
51420
51479
|
auth: "admin",
|
|
51421
51480
|
timeoutMs: 12e4
|
|
51422
|
-
}), method(object({
|
|
51481
|
+
}), method(object({
|
|
51482
|
+
addonId: string(),
|
|
51483
|
+
/** The integration the Add-Device dialog is open on; absent = the addon's own. */
|
|
51484
|
+
integrationId: string().optional()
|
|
51485
|
+
}), object({ deviceType: _enum(DeviceType).nullable() }), { auth: "admin" }), method(object({ addonId: string() }), unknown(), { auth: "admin" }), method(object({
|
|
51423
51486
|
deviceId: number(),
|
|
51424
51487
|
key: string(),
|
|
51425
51488
|
value: unknown()
|
|
@@ -54015,7 +54078,7 @@ var TestConnectionStatusEnum = _enum([
|
|
|
54015
54078
|
"inconclusive",
|
|
54016
54079
|
"unsupported"
|
|
54017
54080
|
]);
|
|
54018
|
-
var TestConnectionResultSchema
|
|
54081
|
+
var TestConnectionResultSchema = object({
|
|
54019
54082
|
/** True ONLY for `validated`. Never true for a test that did not run. */
|
|
54020
54083
|
success: boolean(),
|
|
54021
54084
|
error: string().optional(),
|
|
@@ -54036,11 +54099,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
54036
54099
|
info: record(string(), unknown()).optional(),
|
|
54037
54100
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
54038
54101
|
originStableId: string().optional(),
|
|
54039
|
-
settings: record(string(), unknown()).optional()
|
|
54040
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
54041
|
-
* the keychain, not the table (D787). */
|
|
54042
|
-
secretKeys: array(string()).optional(),
|
|
54043
|
-
skipRestart: boolean().optional()
|
|
54102
|
+
settings: record(string(), unknown()).optional()
|
|
54044
54103
|
});
|
|
54045
54104
|
var UpdateIntegrationInputSchema = object({
|
|
54046
54105
|
id: string(),
|
|
@@ -54061,20 +54120,10 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54061
54120
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
54062
54121
|
kind: "mutation",
|
|
54063
54122
|
auth: "admin"
|
|
54064
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
54065
|
-
id: string(),
|
|
54066
|
-
settings: record(string(), unknown()),
|
|
54067
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
54068
|
-
secretKeys: array(string()).optional(),
|
|
54069
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
54070
|
-
skipRestart: boolean().optional()
|
|
54071
|
-
}), object({ success: boolean() }), {
|
|
54072
|
-
kind: "mutation",
|
|
54073
|
-
auth: "admin"
|
|
54074
54123
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
54075
54124
|
addonId: string(),
|
|
54076
54125
|
settings: record(string(), unknown())
|
|
54077
|
-
}), TestConnectionResultSchema
|
|
54126
|
+
}), TestConnectionResultSchema, {
|
|
54078
54127
|
kind: "mutation",
|
|
54079
54128
|
auth: "admin"
|
|
54080
54129
|
});
|
|
@@ -54101,6 +54150,14 @@ var ConfigSchemaPassthrough$3 = custom();
|
|
|
54101
54150
|
var IntegrationInstanceIdSchema = string().min(1);
|
|
54102
54151
|
var IntegrationInstanceModeSchema = _enum(["single", "multiple"]);
|
|
54103
54152
|
/**
|
|
54153
|
+
* What the addon's instances ARE for the Integrations page (planning#416). `devices`: each
|
|
54154
|
+
* instance is an integration that brings devices (the registry owns its on/off switch);
|
|
54155
|
+
* `services`: a connection configured from the addon's own function page, never listed on
|
|
54156
|
+
* Integrations.
|
|
54157
|
+
* It is the same vocabulary as the manifest's `integrationRole`, which stays as an override.
|
|
54158
|
+
*/
|
|
54159
|
+
var IntegrationInstanceRoleSchema = _enum(["devices", "services"]);
|
|
54160
|
+
/**
|
|
54104
54161
|
* One kind of instance a provider can create (an adapter, an account type). The sign-in and
|
|
54105
54162
|
* connect fields follow the `broker` contract (`broker.cap.ts`): `signIn.basePath` is a
|
|
54106
54163
|
* hub-relative route set speaking the sign-in contract, `connectPath` / `disconnectPath` open an
|
|
@@ -54111,6 +54168,22 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54111
54168
|
/** Stable id of the kind, sent back as `createInstance.kind`. */
|
|
54112
54169
|
id: string().min(1),
|
|
54113
54170
|
label: string().min(1),
|
|
54171
|
+
/**
|
|
54172
|
+
* The function capability this kind is configured for (`broker`, `smtp-provider`,
|
|
54173
|
+
* `device-provider`, ...). A page section is the kinds whose `functionCap` it names (D1013); a
|
|
54174
|
+
* provider with more than one section must name it on every kind.
|
|
54175
|
+
*/
|
|
54176
|
+
functionCap: string().min(1),
|
|
54177
|
+
/**
|
|
54178
|
+
* For `functionCap: 'broker'`: the registry kind of the broker an instance of this kind is
|
|
54179
|
+
* (`matter`, `home-assistant`, ...). A caller that needs a broker of one kind (a link field)
|
|
54180
|
+
* finds the instances whose kind names it; absent takes the kind `id`.
|
|
54181
|
+
*/
|
|
54182
|
+
brokerKind: string().min(1).optional(),
|
|
54183
|
+
/** What instances of this kind are for the Integrations page; absent takes the provider's `role`. */
|
|
54184
|
+
role: IntegrationInstanceRoleSchema.optional(),
|
|
54185
|
+
/** `testInstance` really probes the remote for this kind; absent takes the provider's `canTest`. */
|
|
54186
|
+
canTest: boolean().optional(),
|
|
54114
54187
|
/** The kind signs in through a server-held interactive session. */
|
|
54115
54188
|
signIn: object({
|
|
54116
54189
|
basePath: string().min(1),
|
|
@@ -54123,11 +54196,7 @@ var IntegrationInstanceKindSchema = object({
|
|
|
54123
54196
|
});
|
|
54124
54197
|
var IntegrationInstanceDescriptionSchema = object({
|
|
54125
54198
|
/** What the instances are for the Integrations page; see {@link IntegrationInstanceRoleSchema}. */
|
|
54126
|
-
role:
|
|
54127
|
-
"devices",
|
|
54128
|
-
"services",
|
|
54129
|
-
"both"
|
|
54130
|
-
]),
|
|
54199
|
+
role: IntegrationInstanceRoleSchema,
|
|
54131
54200
|
/** How the devices of an instance are found; absent means `{ mode: 'none' }`. */
|
|
54132
54201
|
discovery: object({
|
|
54133
54202
|
mode: _enum([
|
|
@@ -54202,7 +54271,11 @@ var integrationInstancesCapability = {
|
|
|
54202
54271
|
methods: {
|
|
54203
54272
|
describe: method(_void(), IntegrationInstanceDescriptionSchema, { auth: "admin" }),
|
|
54204
54273
|
/** The form; `id: null` is the create form. */
|
|
54205
|
-
instanceFormSchema: method(object({
|
|
54274
|
+
instanceFormSchema: method(object({
|
|
54275
|
+
id: IntegrationInstanceIdSchema.nullable(),
|
|
54276
|
+
/** The kind of the create form (`id: null`) of a provider with several. */
|
|
54277
|
+
kind: string().min(1).optional()
|
|
54278
|
+
}), ConfigSchemaPassthrough$3, { auth: "admin" }),
|
|
54206
54279
|
listInstances: method(_void(), array(IntegrationInstanceSchema).readonly(), { auth: "admin" }),
|
|
54207
54280
|
getInstance: method(object({ id: IntegrationInstanceIdSchema }), IntegrationInstanceSchema.nullable(), { auth: "admin" }),
|
|
54208
54281
|
/**
|
|
@@ -54212,7 +54285,9 @@ var integrationInstancesCapability = {
|
|
|
54212
54285
|
createInstance: method(object({
|
|
54213
54286
|
id: IntegrationInstanceIdSchema.optional(),
|
|
54214
54287
|
name: string().min(1),
|
|
54215
|
-
kind: string().min(1).optional()
|
|
54288
|
+
kind: string().min(1).optional(),
|
|
54289
|
+
/** Form values written with the record (a link picked in the create dialog). */
|
|
54290
|
+
values: record(string(), unknown()).optional()
|
|
54216
54291
|
}), object({ id: string() }), {
|
|
54217
54292
|
kind: "mutation",
|
|
54218
54293
|
auth: "admin"
|
|
@@ -54239,8 +54314,10 @@ var integrationInstancesCapability = {
|
|
|
54239
54314
|
}),
|
|
54240
54315
|
testInstance: optionalMethod(object({
|
|
54241
54316
|
id: IntegrationInstanceIdSchema.nullable(),
|
|
54242
|
-
values: record(string(), unknown())
|
|
54243
|
-
|
|
54317
|
+
values: record(string(), unknown()),
|
|
54318
|
+
/** The kind to test when `id` is null and the provider has several. */
|
|
54319
|
+
kind: string().min(1).optional()
|
|
54320
|
+
}), TestConnectionResultSchema, {
|
|
54244
54321
|
kind: "mutation",
|
|
54245
54322
|
auth: "admin"
|
|
54246
54323
|
})
|
|
@@ -56745,6 +56822,380 @@ var SystemMetricsSchema = object({
|
|
|
56745
56822
|
gpuMemoryPercent: number().optional()
|
|
56746
56823
|
});
|
|
56747
56824
|
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);
|
|
56825
|
+
/**
|
|
56826
|
+
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
56827
|
+
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
56828
|
+
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
56829
|
+
*
|
|
56830
|
+
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
56831
|
+
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
56832
|
+
* that with the addon id closed over). It is deliberately NOT in any input
|
|
56833
|
+
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
56834
|
+
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
56835
|
+
* addons go through the owner's capability.
|
|
56836
|
+
*
|
|
56837
|
+
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
56838
|
+
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
56839
|
+
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
56840
|
+
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
56841
|
+
* row's own ref.
|
|
56842
|
+
*
|
|
56843
|
+
* Internal: addons reach it through `ctx.records`, never directly.
|
|
56844
|
+
*/
|
|
56845
|
+
var RecordIdSchema = string().min(1).max(256);
|
|
56846
|
+
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
56847
|
+
var RecordBodySchema = record(string(), unknown());
|
|
56848
|
+
var RecordInputSchema = object({
|
|
56849
|
+
id: RecordIdSchema,
|
|
56850
|
+
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
56851
|
+
deviceId: number().int().optional(),
|
|
56852
|
+
/** The domain clock, ms: observed / created / opened. */
|
|
56853
|
+
at: number().int().optional(),
|
|
56854
|
+
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
56855
|
+
expiresAt: number().int().optional(),
|
|
56856
|
+
data: RecordBodySchema
|
|
56857
|
+
});
|
|
56858
|
+
var RecordViewSchema = object({
|
|
56859
|
+
id: string(),
|
|
56860
|
+
deviceId: number().int().nullable(),
|
|
56861
|
+
at: number().nullable(),
|
|
56862
|
+
expiresAt: number().nullable(),
|
|
56863
|
+
rev: number().int(),
|
|
56864
|
+
updatedAt: number(),
|
|
56865
|
+
data: RecordBodySchema
|
|
56866
|
+
});
|
|
56867
|
+
/** Declared index fields only. Anything else does not compile, by design. */
|
|
56868
|
+
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
56869
|
+
string(),
|
|
56870
|
+
number(),
|
|
56871
|
+
boolean(),
|
|
56872
|
+
object({ in: array(union([
|
|
56873
|
+
string(),
|
|
56874
|
+
number(),
|
|
56875
|
+
boolean()
|
|
56876
|
+
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
56877
|
+
object({
|
|
56878
|
+
gt: union([string(), number()]).optional(),
|
|
56879
|
+
gte: union([string(), number()]).optional(),
|
|
56880
|
+
lt: union([string(), number()]).optional(),
|
|
56881
|
+
lte: union([string(), number()]).optional()
|
|
56882
|
+
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
56883
|
+
]));
|
|
56884
|
+
var KindShape = { kind: RecordKindNameSchema };
|
|
56885
|
+
var TimeWindowShape = {
|
|
56886
|
+
deviceId: number().int().optional(),
|
|
56887
|
+
atFrom: number().optional(),
|
|
56888
|
+
atTo: number().optional()
|
|
56889
|
+
};
|
|
56890
|
+
var RecordQueryInputSchema = object({
|
|
56891
|
+
...KindShape,
|
|
56892
|
+
...TimeWindowShape,
|
|
56893
|
+
where: RecordWhereSchema.optional(),
|
|
56894
|
+
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
56895
|
+
order: _enum(["at", "id"]).optional(),
|
|
56896
|
+
desc: boolean().optional(),
|
|
56897
|
+
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
56898
|
+
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
56899
|
+
cursor: string().min(1).optional()
|
|
56900
|
+
});
|
|
56901
|
+
var RecordQueryResultSchema = object({
|
|
56902
|
+
records: array(RecordViewSchema),
|
|
56903
|
+
nextCursor: string().nullable()
|
|
56904
|
+
});
|
|
56905
|
+
var RecordKindStateSchema = _enum([
|
|
56906
|
+
"created",
|
|
56907
|
+
"unchanged",
|
|
56908
|
+
"updated"
|
|
56909
|
+
]);
|
|
56910
|
+
var DeclareKindResultSchema = object({
|
|
56911
|
+
kindId: number().int(),
|
|
56912
|
+
state: RecordKindStateSchema,
|
|
56913
|
+
createdIndexes: array(string())
|
|
56914
|
+
});
|
|
56915
|
+
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
56916
|
+
var SecretRefsList = array(string());
|
|
56917
|
+
/**
|
|
56918
|
+
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
56919
|
+
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
56920
|
+
*/
|
|
56921
|
+
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
56922
|
+
object({
|
|
56923
|
+
op: literal("put"),
|
|
56924
|
+
kind: RecordKindNameSchema,
|
|
56925
|
+
record: RecordInputSchema,
|
|
56926
|
+
ifRev: number().int().min(0).optional()
|
|
56927
|
+
}),
|
|
56928
|
+
object({
|
|
56929
|
+
op: literal("patch"),
|
|
56930
|
+
kind: RecordKindNameSchema,
|
|
56931
|
+
id: RecordIdSchema,
|
|
56932
|
+
data: RecordBodySchema,
|
|
56933
|
+
at: number().int().nullable().optional(),
|
|
56934
|
+
expiresAt: number().int().nullable().optional(),
|
|
56935
|
+
ifRev: number().int().min(1).optional()
|
|
56936
|
+
}),
|
|
56937
|
+
object({
|
|
56938
|
+
op: literal("delete"),
|
|
56939
|
+
kind: RecordKindNameSchema,
|
|
56940
|
+
id: RecordIdSchema
|
|
56941
|
+
})
|
|
56942
|
+
]);
|
|
56943
|
+
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
56944
|
+
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
56945
|
+
kind: "mutation",
|
|
56946
|
+
auth: "admin"
|
|
56947
|
+
}), method(object({
|
|
56948
|
+
...KindShape,
|
|
56949
|
+
id: RecordIdSchema
|
|
56950
|
+
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
56951
|
+
...KindShape,
|
|
56952
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
56953
|
+
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
56954
|
+
...KindShape,
|
|
56955
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
56956
|
+
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
56957
|
+
...KindShape,
|
|
56958
|
+
record: RecordInputSchema,
|
|
56959
|
+
/** Compare-and-set. `0` means "the row must not exist". */
|
|
56960
|
+
ifRev: number().int().min(0).optional()
|
|
56961
|
+
}), object({
|
|
56962
|
+
rev: number().int(),
|
|
56963
|
+
releasedRefs: SecretRefsList
|
|
56964
|
+
}), {
|
|
56965
|
+
kind: "mutation",
|
|
56966
|
+
auth: "admin"
|
|
56967
|
+
}), method(object({
|
|
56968
|
+
...KindShape,
|
|
56969
|
+
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
56970
|
+
}), object({
|
|
56971
|
+
count: number().int(),
|
|
56972
|
+
releasedRefs: SecretRefsList
|
|
56973
|
+
}), {
|
|
56974
|
+
kind: "mutation",
|
|
56975
|
+
auth: "admin"
|
|
56976
|
+
}), method(object({
|
|
56977
|
+
...KindShape,
|
|
56978
|
+
id: RecordIdSchema,
|
|
56979
|
+
data: RecordBodySchema,
|
|
56980
|
+
at: number().int().nullable().optional(),
|
|
56981
|
+
expiresAt: number().int().nullable().optional(),
|
|
56982
|
+
ifRev: number().int().min(1).optional()
|
|
56983
|
+
}), object({
|
|
56984
|
+
rev: number().int(),
|
|
56985
|
+
releasedRefs: SecretRefsList
|
|
56986
|
+
}), {
|
|
56987
|
+
kind: "mutation",
|
|
56988
|
+
auth: "admin"
|
|
56989
|
+
}), method(object({
|
|
56990
|
+
...KindShape,
|
|
56991
|
+
id: RecordIdSchema
|
|
56992
|
+
}), object({
|
|
56993
|
+
deleted: boolean(),
|
|
56994
|
+
secretRefs: SecretRefsList
|
|
56995
|
+
}), {
|
|
56996
|
+
kind: "mutation",
|
|
56997
|
+
auth: "admin"
|
|
56998
|
+
}), method(object({
|
|
56999
|
+
...KindShape,
|
|
57000
|
+
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
57001
|
+
}), object({
|
|
57002
|
+
deleted: number().int(),
|
|
57003
|
+
secretRefs: SecretRefsList
|
|
57004
|
+
}), {
|
|
57005
|
+
kind: "mutation",
|
|
57006
|
+
auth: "admin"
|
|
57007
|
+
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
57008
|
+
...KindShape,
|
|
57009
|
+
...TimeWindowShape,
|
|
57010
|
+
where: RecordWhereSchema.optional()
|
|
57011
|
+
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
57012
|
+
...KindShape,
|
|
57013
|
+
deviceId: number().int().optional(),
|
|
57014
|
+
from: number(),
|
|
57015
|
+
to: number(),
|
|
57016
|
+
bucketMs: number().int().positive()
|
|
57017
|
+
}), object({ buckets: array(object({
|
|
57018
|
+
bucketStart: number(),
|
|
57019
|
+
count: number().int()
|
|
57020
|
+
})) }), { auth: "admin" }), method(object({
|
|
57021
|
+
...KindShape,
|
|
57022
|
+
op: _enum([
|
|
57023
|
+
"sum",
|
|
57024
|
+
"min",
|
|
57025
|
+
"max",
|
|
57026
|
+
"count"
|
|
57027
|
+
]),
|
|
57028
|
+
field: RecordFieldPathSchema.optional(),
|
|
57029
|
+
groupBy: RecordFieldPathSchema.optional(),
|
|
57030
|
+
where: RecordWhereSchema.optional()
|
|
57031
|
+
}), object({ rows: array(object({
|
|
57032
|
+
group: union([
|
|
57033
|
+
string(),
|
|
57034
|
+
number(),
|
|
57035
|
+
_null()
|
|
57036
|
+
]),
|
|
57037
|
+
value: number().nullable()
|
|
57038
|
+
})) }), { auth: "admin" }), method(object({
|
|
57039
|
+
...KindShape,
|
|
57040
|
+
deviceId: number().int().optional(),
|
|
57041
|
+
atBefore: number().optional(),
|
|
57042
|
+
where: RecordWhereSchema.optional()
|
|
57043
|
+
}), object({
|
|
57044
|
+
deleted: number().int(),
|
|
57045
|
+
secretRefs: SecretRefsList
|
|
57046
|
+
}), {
|
|
57047
|
+
kind: "mutation",
|
|
57048
|
+
auth: "admin"
|
|
57049
|
+
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
57050
|
+
results: array(object({
|
|
57051
|
+
rev: number().int().nullable(),
|
|
57052
|
+
deleted: boolean()
|
|
57053
|
+
})),
|
|
57054
|
+
releasedRefs: SecretRefsList,
|
|
57055
|
+
secretRefs: SecretRefsList
|
|
57056
|
+
}), {
|
|
57057
|
+
kind: "mutation",
|
|
57058
|
+
auth: "admin"
|
|
57059
|
+
}), method(object({
|
|
57060
|
+
deviceId: number().int(),
|
|
57061
|
+
kinds: array(RecordKindNameSchema).optional()
|
|
57062
|
+
}), object({
|
|
57063
|
+
deleted: record(string(), number().int()),
|
|
57064
|
+
secretRefs: SecretRefsList
|
|
57065
|
+
}), {
|
|
57066
|
+
kind: "mutation",
|
|
57067
|
+
auth: "admin"
|
|
57068
|
+
}), method(object(KindShape), object({ id: number().int() }), {
|
|
57069
|
+
kind: "mutation",
|
|
57070
|
+
auth: "admin"
|
|
57071
|
+
});
|
|
57072
|
+
/**
|
|
57073
|
+
* The wire shapes of the migration API, version 1 (planning#447).
|
|
57074
|
+
*
|
|
57075
|
+
* A migration is code that outlives the server it was written against: an addon at
|
|
57076
|
+
* version 1.4 that is brought to 1.9 in one jump still runs its 1.4 catalogue, against
|
|
57077
|
+
* the 1.9 kernel. The only thing that code may call is {@link MigrationApiV1}; this file
|
|
57078
|
+
* is the contract of every method of it, one Zod pair per method.
|
|
57079
|
+
*
|
|
57080
|
+
* APPEND-ONLY. A released method is never removed and never changes meaning: an input
|
|
57081
|
+
* field is never removed or made required, an enum never narrows, an output field is
|
|
57082
|
+
* never removed. A change that cannot be expressed that way is `migration-api.v2`, next
|
|
57083
|
+
* to this file. `scripts/check-migration-api-append-only.ts` holds the line against
|
|
57084
|
+
* `scripts/migration-api-contract.json`, and every `behaviours` id below must have an
|
|
57085
|
+
* `it('<id>'` in `migration-api.contract.spec.ts`.
|
|
57086
|
+
*/
|
|
57087
|
+
var Rows = record(string(), unknown());
|
|
57088
|
+
var IntegrationId = object({ integrationId: string().min(1) });
|
|
57089
|
+
var RecordId = object({ id: string().min(1) });
|
|
57090
|
+
var RecordRowSchema = object({
|
|
57091
|
+
id: string(),
|
|
57092
|
+
deviceId: number().int().nullable(),
|
|
57093
|
+
at: number().nullable(),
|
|
57094
|
+
expiresAt: number().nullable(),
|
|
57095
|
+
rev: number().int(),
|
|
57096
|
+
updatedAt: number(),
|
|
57097
|
+
data: Rows
|
|
57098
|
+
});
|
|
57099
|
+
var RecordPutShape = {
|
|
57100
|
+
id: string().min(1),
|
|
57101
|
+
deviceId: number().int().optional(),
|
|
57102
|
+
at: number().int().optional(),
|
|
57103
|
+
expiresAt: number().int().optional(),
|
|
57104
|
+
data: Rows
|
|
57105
|
+
};
|
|
57106
|
+
var MigrationIntegrationSchema = object({
|
|
57107
|
+
id: string(),
|
|
57108
|
+
addonId: string(),
|
|
57109
|
+
name: string(),
|
|
57110
|
+
info: Rows,
|
|
57111
|
+
originStableId: string().optional()
|
|
57112
|
+
});
|
|
57113
|
+
var MigrationDeviceSchema = object({
|
|
57114
|
+
id: number().int(),
|
|
57115
|
+
integrationId: string().nullable().optional(),
|
|
57116
|
+
/** The owning device; null or absent for a root. */
|
|
57117
|
+
parentDeviceId: number().int().nullable().optional(),
|
|
57118
|
+
type: string().optional(),
|
|
57119
|
+
name: string().optional(),
|
|
57120
|
+
stableId: string().optional()
|
|
57121
|
+
});
|
|
57122
|
+
/** What the engine says about the migration API it serves: lets an addon notice an older server. */
|
|
57123
|
+
var MigrationApiInfoSchema = object({
|
|
57124
|
+
version: number().int(),
|
|
57125
|
+
methods: array(string())
|
|
57126
|
+
});
|
|
57127
|
+
object({
|
|
57128
|
+
message: string(),
|
|
57129
|
+
meta: Rows.optional(),
|
|
57130
|
+
/** Grouping tags of the line; a line about a device carries `{ deviceId }`. */
|
|
57131
|
+
tags: object({ deviceId: number().int().optional() }).optional()
|
|
57132
|
+
});
|
|
57133
|
+
object({
|
|
57134
|
+
owner: string().min(1),
|
|
57135
|
+
metas: array(MigrationMetaSchema)
|
|
57136
|
+
}), object({
|
|
57137
|
+
bootId: string(),
|
|
57138
|
+
snapshot: _enum([
|
|
57139
|
+
"none",
|
|
57140
|
+
"written",
|
|
57141
|
+
"refused"
|
|
57142
|
+
]),
|
|
57143
|
+
steps: array(MigrationOwnerStepSchema),
|
|
57144
|
+
/** The migration API the engine serves; absent from a server that predates it. */
|
|
57145
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
57146
|
+
}), object({ id: string().min(1) }), object({
|
|
57147
|
+
proceed: boolean(),
|
|
57148
|
+
state: union([literal("pending"), MigrationStatusSchema]),
|
|
57149
|
+
reason: string().nullable()
|
|
57150
|
+
}), object({
|
|
57151
|
+
id: string().min(1),
|
|
57152
|
+
result: MigrationOwnerResultSchema
|
|
57153
|
+
}), object({
|
|
57154
|
+
status: MigrationStatusSchema,
|
|
57155
|
+
error: string().nullable(),
|
|
57156
|
+
rowsChanged: number().int().nonnegative()
|
|
57157
|
+
}), object({
|
|
57158
|
+
rows: number().int().nonnegative(),
|
|
57159
|
+
secrets: number().int().nonnegative()
|
|
57160
|
+
}), IntegrationId.extend({ patch: Rows }), object({ written: boolean() }), IntegrationId.extend({ keys: array(string().min(1)) }), object({
|
|
57161
|
+
present: boolean(),
|
|
57162
|
+
removed: Rows
|
|
57163
|
+
}), RecordRowSchema.nullable(), RecordRowSchema.nullable(), object({
|
|
57164
|
+
...RecordPutShape,
|
|
57165
|
+
ifRev: number().int().optional()
|
|
57166
|
+
}), object({ rev: number().int() }), object({ records: array(object(RecordPutShape)) }), object({ count: number().int().nonnegative() }), RecordId.extend({
|
|
57167
|
+
partial: Rows,
|
|
57168
|
+
ifRev: number().int().optional()
|
|
57169
|
+
}), object({ rev: number().int() }), object({ deleted: boolean() }), object({
|
|
57170
|
+
deviceId: number().int().optional(),
|
|
57171
|
+
atFrom: number().optional(),
|
|
57172
|
+
atTo: number().optional(),
|
|
57173
|
+
where: RecordWhereSchema.optional(),
|
|
57174
|
+
limit: number().int().min(1).optional(),
|
|
57175
|
+
cursor: string().min(1).optional(),
|
|
57176
|
+
resolveSecrets: boolean().optional()
|
|
57177
|
+
}), object({
|
|
57178
|
+
records: array(RecordRowSchema),
|
|
57179
|
+
nextCursor: string().nullable()
|
|
57180
|
+
}), object({
|
|
57181
|
+
deviceId: number().int().optional(),
|
|
57182
|
+
atBefore: number().optional(),
|
|
57183
|
+
where: RecordWhereSchema.optional()
|
|
57184
|
+
}), 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({
|
|
57185
|
+
ref: string().min(1),
|
|
57186
|
+
value: string()
|
|
57187
|
+
}), object({}), object({ refs: array(string()) }), object({ deleted: number().int().nonnegative() }), object({}), object({ integrations: array(MigrationIntegrationSchema) }), object({ id: string().min(1) }), MigrationIntegrationSchema.nullable(), object({
|
|
57188
|
+
addonId: string().min(1),
|
|
57189
|
+
integrationId: string().min(1).optional()
|
|
57190
|
+
}), object({ devices: array(MigrationDeviceSchema) }), object({ deviceId: number().int() }), object({
|
|
57191
|
+
id: number().int(),
|
|
57192
|
+
integrationId: string().nullable(),
|
|
57193
|
+
/** Whether the device carries any stored config; its content is not offered. */
|
|
57194
|
+
hasConfig: boolean()
|
|
57195
|
+
}).nullable(), object({
|
|
57196
|
+
deviceId: number().int(),
|
|
57197
|
+
integrationId: string().min(1)
|
|
57198
|
+
}), object({}), object({}), object({ nodeId: string() }), object({}), object({});
|
|
56748
57199
|
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({
|
|
56749
57200
|
id: string().min(1),
|
|
56750
57201
|
error: string().min(1)
|
|
@@ -56774,7 +57225,9 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56774
57225
|
"written",
|
|
56775
57226
|
"refused"
|
|
56776
57227
|
]),
|
|
56777
|
-
steps: array(MigrationOwnerStepSchema)
|
|
57228
|
+
steps: array(MigrationOwnerStepSchema),
|
|
57229
|
+
/** The migration API this engine serves (additive: a server that predates it omits it). */
|
|
57230
|
+
migrationApi: MigrationApiInfoSchema.optional()
|
|
56778
57231
|
}), {
|
|
56779
57232
|
kind: "mutation",
|
|
56780
57233
|
auth: "admin"
|
|
@@ -56795,6 +57248,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56795
57248
|
}), {
|
|
56796
57249
|
kind: "mutation",
|
|
56797
57250
|
auth: "admin"
|
|
57251
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
57252
|
+
rows: number().int().nonnegative(),
|
|
57253
|
+
secrets: number().int().nonnegative()
|
|
57254
|
+
}), {
|
|
57255
|
+
kind: "mutation",
|
|
57256
|
+
auth: "admin"
|
|
57257
|
+
}), method(object({
|
|
57258
|
+
integrationId: string().min(1),
|
|
57259
|
+
patch: record(string(), unknown())
|
|
57260
|
+
}), object({ written: boolean() }), {
|
|
57261
|
+
kind: "mutation",
|
|
57262
|
+
auth: "admin"
|
|
57263
|
+
}), method(object({
|
|
57264
|
+
integrationId: string().min(1),
|
|
57265
|
+
keys: array(string().min(1))
|
|
57266
|
+
}), object({
|
|
57267
|
+
present: boolean(),
|
|
57268
|
+
removed: record(string(), unknown())
|
|
57269
|
+
}), {
|
|
57270
|
+
kind: "mutation",
|
|
57271
|
+
auth: "admin"
|
|
56798
57272
|
});
|
|
56799
57273
|
method(object({
|
|
56800
57274
|
sourceUrl: string(),
|
|
@@ -59420,7 +59894,10 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
59420
59894
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
59421
59895
|
kind: "mutation",
|
|
59422
59896
|
auth: "admin"
|
|
59423
|
-
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(
|
|
59897
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(MediaLocationCensusInputSchema, MediaLocationCensusSchema, {
|
|
59898
|
+
kind: "query",
|
|
59899
|
+
auth: "admin"
|
|
59900
|
+
}), method(RelocatableMediaCountInputSchema, RelocatableMediaCountSchema, {
|
|
59424
59901
|
kind: "query",
|
|
59425
59902
|
auth: "admin"
|
|
59426
59903
|
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
@@ -61827,7 +62304,7 @@ var BrokerStatusSchema$1 = _enum([
|
|
|
61827
62304
|
"unreachable",
|
|
61828
62305
|
"tls-error"
|
|
61829
62306
|
]);
|
|
61830
|
-
var BrokerInfoSchema
|
|
62307
|
+
var BrokerInfoSchema = object({
|
|
61831
62308
|
id: string(),
|
|
61832
62309
|
name: string(),
|
|
61833
62310
|
url: string(),
|
|
@@ -61890,7 +62367,7 @@ var StatusSchema = object({
|
|
|
61890
62367
|
brokerCount: number(),
|
|
61891
62368
|
embeddedRunning: boolean()
|
|
61892
62369
|
});
|
|
61893
|
-
method(_void(), array(BrokerInfoSchema
|
|
62370
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
61894
62371
|
kind: "mutation",
|
|
61895
62372
|
auth: "admin"
|
|
61896
62373
|
}), method(IdInputSchema, _void(), {
|
|
@@ -68208,253 +68685,6 @@ DeviceType.Camera, DeviceType.Sensor, DeviceType.Switch, DeviceType.Gateway, met
|
|
|
68208
68685
|
auth: "admin"
|
|
68209
68686
|
});
|
|
68210
68687
|
/**
|
|
68211
|
-
* records - an addon's own facts and documents (D881): ledgers, audit rows,
|
|
68212
|
-
* config documents, mirrors. The third storage API next to `ctx.settings`
|
|
68213
|
-
* (operator intent, D870) and device-history (numbers over time, D850).
|
|
68214
|
-
*
|
|
68215
|
-
* Who the OWNER is: `callerAddonId`, stamped by the transport on a call that
|
|
68216
|
-
* crossed a process and stated by a hub in-process caller (`ctx.records` does
|
|
68217
|
-
* that with the addon id closed over). It is deliberately NOT in any input
|
|
68218
|
-
* schema, so a session can never supply it, and a call with no stamp is refused
|
|
68219
|
-
* `records/no-caller`. A kind is readable and writable only by its owner; other
|
|
68220
|
-
* addons go through the owner's capability.
|
|
68221
|
-
*
|
|
68222
|
-
* Every read is three-way: a row, `null`, or the call THROWS. A failed read is
|
|
68223
|
-
* never `[]`. Secret fields are never on the wire: a read shows the
|
|
68224
|
-
* redaction token, `secretRefs` hands the owner's `ctx.secrets` the refs it
|
|
68225
|
-
* resolves itself, and a write may carry only a keychain marker bound to the
|
|
68226
|
-
* row's own ref.
|
|
68227
|
-
*
|
|
68228
|
-
* Internal: addons reach it through `ctx.records`, never directly.
|
|
68229
|
-
*/
|
|
68230
|
-
var RecordIdSchema = string().min(1).max(256);
|
|
68231
|
-
/** A JSON object body. Size is the engine's check (`maxDataBytes`), not a schema's. */
|
|
68232
|
-
var RecordBodySchema = record(string(), unknown());
|
|
68233
|
-
var RecordInputSchema = object({
|
|
68234
|
-
id: RecordIdSchema,
|
|
68235
|
-
/** The device this row belongs to (D865 removal, per-device reads). */
|
|
68236
|
-
deviceId: number().int().optional(),
|
|
68237
|
-
/** The domain clock, ms: observed / created / opened. */
|
|
68238
|
-
at: number().int().optional(),
|
|
68239
|
-
/** Producer-set expiry, ms. Refused on a kind whose retention is an engine TTL. */
|
|
68240
|
-
expiresAt: number().int().optional(),
|
|
68241
|
-
data: RecordBodySchema
|
|
68242
|
-
});
|
|
68243
|
-
var RecordViewSchema = object({
|
|
68244
|
-
id: string(),
|
|
68245
|
-
deviceId: number().int().nullable(),
|
|
68246
|
-
at: number().nullable(),
|
|
68247
|
-
expiresAt: number().nullable(),
|
|
68248
|
-
rev: number().int(),
|
|
68249
|
-
updatedAt: number(),
|
|
68250
|
-
data: RecordBodySchema
|
|
68251
|
-
});
|
|
68252
|
-
/** Declared index fields only. Anything else does not compile, by design. */
|
|
68253
|
-
var RecordWhereSchema = record(RecordFieldPathSchema, union([
|
|
68254
|
-
string(),
|
|
68255
|
-
number(),
|
|
68256
|
-
boolean(),
|
|
68257
|
-
object({ in: array(union([
|
|
68258
|
-
string(),
|
|
68259
|
-
number(),
|
|
68260
|
-
boolean()
|
|
68261
|
-
])).min(1).max(RECORD_BATCH_MAX) }),
|
|
68262
|
-
object({
|
|
68263
|
-
gt: union([string(), number()]).optional(),
|
|
68264
|
-
gte: union([string(), number()]).optional(),
|
|
68265
|
-
lt: union([string(), number()]).optional(),
|
|
68266
|
-
lte: union([string(), number()]).optional()
|
|
68267
|
-
}).refine((r) => Object.keys(r).length > 0, { message: "name at least one bound" })
|
|
68268
|
-
]));
|
|
68269
|
-
var KindShape = { kind: RecordKindNameSchema };
|
|
68270
|
-
var TimeWindowShape = {
|
|
68271
|
-
deviceId: number().int().optional(),
|
|
68272
|
-
atFrom: number().optional(),
|
|
68273
|
-
atTo: number().optional()
|
|
68274
|
-
};
|
|
68275
|
-
var RecordQueryInputSchema = object({
|
|
68276
|
-
...KindShape,
|
|
68277
|
-
...TimeWindowShape,
|
|
68278
|
-
where: RecordWhereSchema.optional(),
|
|
68279
|
-
/** Without `where`: `at` (default for a kind with a clock) or `id`. With `where` the order is the index's. */
|
|
68280
|
-
order: _enum(["at", "id"]).optional(),
|
|
68281
|
-
desc: boolean().optional(),
|
|
68282
|
-
limit: number().int().min(1).max(RECORD_QUERY_MAX).default(100),
|
|
68283
|
-
/** Opaque keyset cursor from a previous answer. There is no offset. */
|
|
68284
|
-
cursor: string().min(1).optional()
|
|
68285
|
-
});
|
|
68286
|
-
var RecordQueryResultSchema = object({
|
|
68287
|
-
records: array(RecordViewSchema),
|
|
68288
|
-
nextCursor: string().nullable()
|
|
68289
|
-
});
|
|
68290
|
-
var RecordKindStateSchema = _enum([
|
|
68291
|
-
"created",
|
|
68292
|
-
"unchanged",
|
|
68293
|
-
"updated"
|
|
68294
|
-
]);
|
|
68295
|
-
var DeclareKindResultSchema = object({
|
|
68296
|
-
kindId: number().int(),
|
|
68297
|
-
state: RecordKindStateSchema,
|
|
68298
|
-
createdIndexes: array(string())
|
|
68299
|
-
});
|
|
68300
|
-
/** Keychain refs a write released (the old marker is gone from the row): the owner deletes the values. */
|
|
68301
|
-
var SecretRefsList = array(string());
|
|
68302
|
-
/**
|
|
68303
|
-
* One op of a `transact`: the single-row writes, each on a kind of the caller.
|
|
68304
|
-
* All ops of one call share ONE database file and ONE `transactFamily`.
|
|
68305
|
-
*/
|
|
68306
|
-
var RecordTransactOpSchema = discriminatedUnion("op", [
|
|
68307
|
-
object({
|
|
68308
|
-
op: literal("put"),
|
|
68309
|
-
kind: RecordKindNameSchema,
|
|
68310
|
-
record: RecordInputSchema,
|
|
68311
|
-
ifRev: number().int().min(0).optional()
|
|
68312
|
-
}),
|
|
68313
|
-
object({
|
|
68314
|
-
op: literal("patch"),
|
|
68315
|
-
kind: RecordKindNameSchema,
|
|
68316
|
-
id: RecordIdSchema,
|
|
68317
|
-
data: RecordBodySchema,
|
|
68318
|
-
at: number().int().nullable().optional(),
|
|
68319
|
-
expiresAt: number().int().nullable().optional(),
|
|
68320
|
-
ifRev: number().int().min(1).optional()
|
|
68321
|
-
}),
|
|
68322
|
-
object({
|
|
68323
|
-
op: literal("delete"),
|
|
68324
|
-
kind: RecordKindNameSchema,
|
|
68325
|
-
id: RecordIdSchema
|
|
68326
|
-
})
|
|
68327
|
-
]);
|
|
68328
|
-
var RecordSecretRefsSchema = record(string(), record(string(), string()));
|
|
68329
|
-
method(object({ kinds: record(RecordKindNameSchema, RecordKindDeclarationSchema) }), object({ kinds: record(string(), DeclareKindResultSchema) }), {
|
|
68330
|
-
kind: "mutation",
|
|
68331
|
-
auth: "admin"
|
|
68332
|
-
}), method(object({
|
|
68333
|
-
...KindShape,
|
|
68334
|
-
id: RecordIdSchema
|
|
68335
|
-
}), RecordViewSchema.nullable(), { auth: "admin" }), method(object({
|
|
68336
|
-
...KindShape,
|
|
68337
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
68338
|
-
}), array(RecordViewSchema), { auth: "admin" }), method(object({
|
|
68339
|
-
...KindShape,
|
|
68340
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
68341
|
-
}), RecordSecretRefsSchema, { auth: "admin" }), method(object({
|
|
68342
|
-
...KindShape,
|
|
68343
|
-
record: RecordInputSchema,
|
|
68344
|
-
/** Compare-and-set. `0` means "the row must not exist". */
|
|
68345
|
-
ifRev: number().int().min(0).optional()
|
|
68346
|
-
}), object({
|
|
68347
|
-
rev: number().int(),
|
|
68348
|
-
releasedRefs: SecretRefsList
|
|
68349
|
-
}), {
|
|
68350
|
-
kind: "mutation",
|
|
68351
|
-
auth: "admin"
|
|
68352
|
-
}), method(object({
|
|
68353
|
-
...KindShape,
|
|
68354
|
-
records: array(RecordInputSchema).min(1).max(RECORD_BATCH_MAX)
|
|
68355
|
-
}), object({
|
|
68356
|
-
count: number().int(),
|
|
68357
|
-
releasedRefs: SecretRefsList
|
|
68358
|
-
}), {
|
|
68359
|
-
kind: "mutation",
|
|
68360
|
-
auth: "admin"
|
|
68361
|
-
}), method(object({
|
|
68362
|
-
...KindShape,
|
|
68363
|
-
id: RecordIdSchema,
|
|
68364
|
-
data: RecordBodySchema,
|
|
68365
|
-
at: number().int().nullable().optional(),
|
|
68366
|
-
expiresAt: number().int().nullable().optional(),
|
|
68367
|
-
ifRev: number().int().min(1).optional()
|
|
68368
|
-
}), object({
|
|
68369
|
-
rev: number().int(),
|
|
68370
|
-
releasedRefs: SecretRefsList
|
|
68371
|
-
}), {
|
|
68372
|
-
kind: "mutation",
|
|
68373
|
-
auth: "admin"
|
|
68374
|
-
}), method(object({
|
|
68375
|
-
...KindShape,
|
|
68376
|
-
id: RecordIdSchema
|
|
68377
|
-
}), object({
|
|
68378
|
-
deleted: boolean(),
|
|
68379
|
-
secretRefs: SecretRefsList
|
|
68380
|
-
}), {
|
|
68381
|
-
kind: "mutation",
|
|
68382
|
-
auth: "admin"
|
|
68383
|
-
}), method(object({
|
|
68384
|
-
...KindShape,
|
|
68385
|
-
ids: array(RecordIdSchema).min(1).max(RECORD_BATCH_MAX)
|
|
68386
|
-
}), object({
|
|
68387
|
-
deleted: number().int(),
|
|
68388
|
-
secretRefs: SecretRefsList
|
|
68389
|
-
}), {
|
|
68390
|
-
kind: "mutation",
|
|
68391
|
-
auth: "admin"
|
|
68392
|
-
}), method(RecordQueryInputSchema, RecordQueryResultSchema, { auth: "admin" }), method(object({
|
|
68393
|
-
...KindShape,
|
|
68394
|
-
...TimeWindowShape,
|
|
68395
|
-
where: RecordWhereSchema.optional()
|
|
68396
|
-
}), object({ count: number().int() }), { auth: "admin" }), method(object({
|
|
68397
|
-
...KindShape,
|
|
68398
|
-
deviceId: number().int().optional(),
|
|
68399
|
-
from: number(),
|
|
68400
|
-
to: number(),
|
|
68401
|
-
bucketMs: number().int().positive()
|
|
68402
|
-
}), object({ buckets: array(object({
|
|
68403
|
-
bucketStart: number(),
|
|
68404
|
-
count: number().int()
|
|
68405
|
-
})) }), { auth: "admin" }), method(object({
|
|
68406
|
-
...KindShape,
|
|
68407
|
-
op: _enum([
|
|
68408
|
-
"sum",
|
|
68409
|
-
"min",
|
|
68410
|
-
"max",
|
|
68411
|
-
"count"
|
|
68412
|
-
]),
|
|
68413
|
-
field: RecordFieldPathSchema.optional(),
|
|
68414
|
-
groupBy: RecordFieldPathSchema.optional(),
|
|
68415
|
-
where: RecordWhereSchema.optional()
|
|
68416
|
-
}), object({ rows: array(object({
|
|
68417
|
-
group: union([
|
|
68418
|
-
string(),
|
|
68419
|
-
number(),
|
|
68420
|
-
_null()
|
|
68421
|
-
]),
|
|
68422
|
-
value: number().nullable()
|
|
68423
|
-
})) }), { auth: "admin" }), method(object({
|
|
68424
|
-
...KindShape,
|
|
68425
|
-
deviceId: number().int().optional(),
|
|
68426
|
-
atBefore: number().optional(),
|
|
68427
|
-
where: RecordWhereSchema.optional()
|
|
68428
|
-
}), object({
|
|
68429
|
-
deleted: number().int(),
|
|
68430
|
-
secretRefs: SecretRefsList
|
|
68431
|
-
}), {
|
|
68432
|
-
kind: "mutation",
|
|
68433
|
-
auth: "admin"
|
|
68434
|
-
}), method(object({ ops: array(RecordTransactOpSchema).min(1).max(RECORD_BATCH_MAX) }), object({
|
|
68435
|
-
results: array(object({
|
|
68436
|
-
rev: number().int().nullable(),
|
|
68437
|
-
deleted: boolean()
|
|
68438
|
-
})),
|
|
68439
|
-
releasedRefs: SecretRefsList,
|
|
68440
|
-
secretRefs: SecretRefsList
|
|
68441
|
-
}), {
|
|
68442
|
-
kind: "mutation",
|
|
68443
|
-
auth: "admin"
|
|
68444
|
-
}), method(object({
|
|
68445
|
-
deviceId: number().int(),
|
|
68446
|
-
kinds: array(RecordKindNameSchema).optional()
|
|
68447
|
-
}), object({
|
|
68448
|
-
deleted: record(string(), number().int()),
|
|
68449
|
-
secretRefs: SecretRefsList
|
|
68450
|
-
}), {
|
|
68451
|
-
kind: "mutation",
|
|
68452
|
-
auth: "admin"
|
|
68453
|
-
}), method(object(KindShape), object({ id: number().int() }), {
|
|
68454
|
-
kind: "mutation",
|
|
68455
|
-
auth: "admin"
|
|
68456
|
-
});
|
|
68457
|
-
/**
|
|
68458
68688
|
* record-entries - the ONE admin surface for every list of user-created entries
|
|
68459
68689
|
* (D881): notification targets, brokers, LLM profiles, anything an addon
|
|
68460
68690
|
* declares as an ENTRY kind (`entry: { title }` in `defineRecordKinds`).
|
|
@@ -75969,11 +76199,15 @@ discriminatedUnion("kind", [object({
|
|
|
75969
76199
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
75970
76200
|
* near F. A device that is not near one level most of the time has no floor.
|
|
75971
76201
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
75972
|
-
* 3.
|
|
75973
|
-
*
|
|
76202
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76203
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76204
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76205
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76206
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76207
|
+
* is no run.
|
|
75974
76208
|
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
75975
|
-
* grid a person would type; stop dwell covers the longest pause
|
|
75976
|
-
* covers the longest standby blip above T.
|
|
76209
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76210
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
75977
76211
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
75978
76212
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
75979
76213
|
*/
|
|
@@ -75984,6 +76218,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
75984
76218
|
30 * SECOND_MS;
|
|
75985
76219
|
60 * SECOND_MS;
|
|
75986
76220
|
20 * MINUTE_MS$1;
|
|
76221
|
+
90 * SECOND_MS;
|
|
76222
|
+
30 * MINUTE_MS$1;
|
|
76223
|
+
10 * MINUTE_MS$1;
|
|
76224
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
75987
76225
|
10 * SECOND_MS;
|
|
75988
76226
|
10 * SECOND_MS;
|
|
75989
76227
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -77002,20 +77240,6 @@ Object.freeze({
|
|
|
77002
77240
|
access: "create",
|
|
77003
77241
|
role: "viewer"
|
|
77004
77242
|
},
|
|
77005
|
-
"broker.add": {
|
|
77006
|
-
capName: "broker",
|
|
77007
|
-
capScope: "system",
|
|
77008
|
-
addonId: null,
|
|
77009
|
-
access: "create",
|
|
77010
|
-
role: "admin"
|
|
77011
|
-
},
|
|
77012
|
-
"broker.get": {
|
|
77013
|
-
capName: "broker",
|
|
77014
|
-
capScope: "system",
|
|
77015
|
-
addonId: null,
|
|
77016
|
-
access: "view",
|
|
77017
|
-
role: "admin"
|
|
77018
|
-
},
|
|
77019
77243
|
"broker.getBrokerConfig": {
|
|
77020
77244
|
capName: "broker",
|
|
77021
77245
|
capScope: "system",
|
|
@@ -77023,20 +77247,6 @@ Object.freeze({
|
|
|
77023
77247
|
access: "view",
|
|
77024
77248
|
role: "admin"
|
|
77025
77249
|
},
|
|
77026
|
-
"broker.getSettings": {
|
|
77027
|
-
capName: "broker",
|
|
77028
|
-
capScope: "system",
|
|
77029
|
-
addonId: null,
|
|
77030
|
-
access: "view",
|
|
77031
|
-
role: "admin"
|
|
77032
|
-
},
|
|
77033
|
-
"broker.getSettingsSchema": {
|
|
77034
|
-
capName: "broker",
|
|
77035
|
-
capScope: "system",
|
|
77036
|
-
addonId: null,
|
|
77037
|
-
access: "view",
|
|
77038
|
-
role: "admin"
|
|
77039
|
-
},
|
|
77040
77250
|
"broker.getState": {
|
|
77041
77251
|
capName: "broker",
|
|
77042
77252
|
capScope: "system",
|
|
@@ -77051,20 +77261,6 @@ Object.freeze({
|
|
|
77051
77261
|
access: "view",
|
|
77052
77262
|
role: "admin"
|
|
77053
77263
|
},
|
|
77054
|
-
"broker.list": {
|
|
77055
|
-
capName: "broker",
|
|
77056
|
-
capScope: "system",
|
|
77057
|
-
addonId: null,
|
|
77058
|
-
access: "view",
|
|
77059
|
-
role: "admin"
|
|
77060
|
-
},
|
|
77061
|
-
"broker.listProviders": {
|
|
77062
|
-
capName: "broker",
|
|
77063
|
-
capScope: "system",
|
|
77064
|
-
addonId: null,
|
|
77065
|
-
access: "view",
|
|
77066
|
-
role: "admin"
|
|
77067
|
-
},
|
|
77068
77264
|
"broker.publish": {
|
|
77069
77265
|
capName: "broker",
|
|
77070
77266
|
capScope: "system",
|
|
@@ -77072,20 +77268,6 @@ Object.freeze({
|
|
|
77072
77268
|
access: "create",
|
|
77073
77269
|
role: "admin"
|
|
77074
77270
|
},
|
|
77075
|
-
"broker.remove": {
|
|
77076
|
-
capName: "broker",
|
|
77077
|
-
capScope: "system",
|
|
77078
|
-
addonId: null,
|
|
77079
|
-
access: "delete",
|
|
77080
|
-
role: "admin"
|
|
77081
|
-
},
|
|
77082
|
-
"broker.setSettings": {
|
|
77083
|
-
capName: "broker",
|
|
77084
|
-
capScope: "system",
|
|
77085
|
-
addonId: null,
|
|
77086
|
-
access: "create",
|
|
77087
|
-
role: "admin"
|
|
77088
|
-
},
|
|
77089
77271
|
"broker.subscribe": {
|
|
77090
77272
|
capName: "broker",
|
|
77091
77273
|
capScope: "system",
|
|
@@ -77093,20 +77275,6 @@ Object.freeze({
|
|
|
77093
77275
|
access: "create",
|
|
77094
77276
|
role: "admin"
|
|
77095
77277
|
},
|
|
77096
|
-
"broker.testConnection": {
|
|
77097
|
-
capName: "broker",
|
|
77098
|
-
capScope: "system",
|
|
77099
|
-
addonId: null,
|
|
77100
|
-
access: "create",
|
|
77101
|
-
role: "admin"
|
|
77102
|
-
},
|
|
77103
|
-
"broker.testSettings": {
|
|
77104
|
-
capName: "broker",
|
|
77105
|
-
capScope: "system",
|
|
77106
|
-
addonId: null,
|
|
77107
|
-
access: "create",
|
|
77108
|
-
role: "admin"
|
|
77109
|
-
},
|
|
77110
77278
|
"broker.unsubscribe": {
|
|
77111
77279
|
capName: "broker",
|
|
77112
77280
|
capScope: "system",
|
|
@@ -79739,13 +79907,6 @@ Object.freeze({
|
|
|
79739
79907
|
access: "view",
|
|
79740
79908
|
role: "admin"
|
|
79741
79909
|
},
|
|
79742
|
-
"integrations.getSettings": {
|
|
79743
|
-
capName: "integrations",
|
|
79744
|
-
capScope: "system",
|
|
79745
|
-
addonId: null,
|
|
79746
|
-
access: "view",
|
|
79747
|
-
role: "admin"
|
|
79748
|
-
},
|
|
79749
79910
|
"integrations.list": {
|
|
79750
79911
|
capName: "integrations",
|
|
79751
79912
|
capScope: "system",
|
|
@@ -79753,13 +79914,6 @@ Object.freeze({
|
|
|
79753
79914
|
access: "view",
|
|
79754
79915
|
role: "admin"
|
|
79755
79916
|
},
|
|
79756
|
-
"integrations.setSettings": {
|
|
79757
|
-
capName: "integrations",
|
|
79758
|
-
capScope: "system",
|
|
79759
|
-
addonId: null,
|
|
79760
|
-
access: "create",
|
|
79761
|
-
role: "admin"
|
|
79762
|
-
},
|
|
79763
79917
|
"integrations.testConnection": {
|
|
79764
79918
|
capName: "integrations",
|
|
79765
79919
|
capScope: "system",
|
|
@@ -80754,6 +80908,13 @@ Object.freeze({
|
|
|
80754
80908
|
access: "view",
|
|
80755
80909
|
role: "admin"
|
|
80756
80910
|
},
|
|
80911
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80912
|
+
capName: "migrations",
|
|
80913
|
+
capScope: "system",
|
|
80914
|
+
addonId: null,
|
|
80915
|
+
access: "delete",
|
|
80916
|
+
role: "admin"
|
|
80917
|
+
},
|
|
80757
80918
|
"migrations.declareOwner": {
|
|
80758
80919
|
capName: "migrations",
|
|
80759
80920
|
capScope: "system",
|
|
@@ -80761,6 +80922,13 @@ Object.freeze({
|
|
|
80761
80922
|
access: "create",
|
|
80762
80923
|
role: "admin"
|
|
80763
80924
|
},
|
|
80925
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80926
|
+
capName: "migrations",
|
|
80927
|
+
capScope: "system",
|
|
80928
|
+
addonId: null,
|
|
80929
|
+
access: "delete",
|
|
80930
|
+
role: "admin"
|
|
80931
|
+
},
|
|
80764
80932
|
"migrations.dryRun": {
|
|
80765
80933
|
capName: "migrations",
|
|
80766
80934
|
capScope: "system",
|
|
@@ -80796,6 +80964,20 @@ Object.freeze({
|
|
|
80796
80964
|
access: "create",
|
|
80797
80965
|
role: "admin"
|
|
80798
80966
|
},
|
|
80967
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80968
|
+
capName: "migrations",
|
|
80969
|
+
capScope: "system",
|
|
80970
|
+
addonId: null,
|
|
80971
|
+
access: "create",
|
|
80972
|
+
role: "admin"
|
|
80973
|
+
},
|
|
80974
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80975
|
+
capName: "migrations",
|
|
80976
|
+
capScope: "system",
|
|
80977
|
+
addonId: null,
|
|
80978
|
+
access: "view",
|
|
80979
|
+
role: "admin"
|
|
80980
|
+
},
|
|
80799
80981
|
"migrations.restorePreMigration": {
|
|
80800
80982
|
capName: "migrations",
|
|
80801
80983
|
capScope: "system",
|
|
@@ -82056,6 +82238,13 @@ Object.freeze({
|
|
|
82056
82238
|
access: "view",
|
|
82057
82239
|
role: "viewer"
|
|
82058
82240
|
},
|
|
82241
|
+
"pipelineAnalytics.getMediaLocationCensus": {
|
|
82242
|
+
capName: "pipeline-analytics",
|
|
82243
|
+
capScope: "device",
|
|
82244
|
+
addonId: null,
|
|
82245
|
+
access: "view",
|
|
82246
|
+
role: "admin"
|
|
82247
|
+
},
|
|
82059
82248
|
"pipelineAnalytics.getMediaReclaimStatus": {
|
|
82060
82249
|
capName: "pipeline-analytics",
|
|
82061
82250
|
capScope: "device",
|
|
@@ -90997,6 +91186,23 @@ object({
|
|
|
90997
91186
|
delay: number().nonnegative().optional(),
|
|
90998
91187
|
commands: record(string(), unknown())
|
|
90999
91188
|
});
|
|
91189
|
+
var BrokerInUseError = class extends Error {
|
|
91190
|
+
brokerId;
|
|
91191
|
+
users;
|
|
91192
|
+
name = "BrokerInUseError";
|
|
91193
|
+
constructor(brokerId, users) {
|
|
91194
|
+
super(`broker '${brokerId}' is still in use by ${users.map((u) => `${u.kind} '${u.name ?? u.id}' (${u.id})`).join(", ")}; unlink them first`);
|
|
91195
|
+
this.brokerId = brokerId;
|
|
91196
|
+
this.users = users;
|
|
91197
|
+
}
|
|
91198
|
+
};
|
|
91199
|
+
/** Throw {@link BrokerInUseError} when any finder names a user of `brokerId`. */
|
|
91200
|
+
async function assertBrokerUnused(brokerId, finders) {
|
|
91201
|
+
const users = [];
|
|
91202
|
+
for (const finder of finders) users.push(...await finder.find(brokerId));
|
|
91203
|
+
if (users.length > 0) throw new BrokerInUseError(brokerId, users);
|
|
91204
|
+
}
|
|
91205
|
+
entryBody({ brokerId: string() });
|
|
91000
91206
|
//#endregion
|
|
91001
91207
|
//#region src/relay-record-kinds.ts
|
|
91002
91208
|
/**
|
|
@@ -91246,29 +91452,6 @@ var MessageRing = class {
|
|
|
91246
91452
|
}
|
|
91247
91453
|
};
|
|
91248
91454
|
//#endregion
|
|
91249
|
-
//#region src/mailbox-record-kinds.ts
|
|
91250
|
-
/**
|
|
91251
|
-
* The `smtp-inbound` mailbox as a record (D999): one `mailboxes` row per mailbox, owned by
|
|
91252
|
-
* `smtp-nodemailer`. The row id is the broker id. The SMTP AUTH password is a keychain-bound secret
|
|
91253
|
-
* field; nothing else holds a copy.
|
|
91254
|
-
*/
|
|
91255
|
-
var MailboxBodySchema = entryBody({
|
|
91256
|
-
port: number().int().min(1).max(65535),
|
|
91257
|
-
bindHost: string().min(1),
|
|
91258
|
-
domain: string().min(1),
|
|
91259
|
-
requireAuth: boolean(),
|
|
91260
|
-
username: string(),
|
|
91261
|
-
password: string().optional().meta({ secret: true }),
|
|
91262
|
-
starttls: boolean(),
|
|
91263
|
-
maxMessageBytes: number().int().positive()
|
|
91264
|
-
});
|
|
91265
|
-
var mailboxesKind = defineEntryKind("mailboxes", {
|
|
91266
|
-
title: "Inbound mailboxes",
|
|
91267
|
-
nameField: "name",
|
|
91268
|
-
schema: MailboxBodySchema,
|
|
91269
|
-
writeTrigger: "an operator (or a consumer addon, once) adds or edits an inbound mailbox"
|
|
91270
|
-
});
|
|
91271
|
-
//#endregion
|
|
91272
91455
|
//#region src/inbound-broker.ts
|
|
91273
91456
|
/**
|
|
91274
91457
|
* The `smtp-inbound` broker kind (D999): a mailbox a device can send alarm mail to.
|
|
@@ -91282,7 +91465,6 @@ var mailboxesKind = defineEntryKind("mailboxes", {
|
|
|
91282
91465
|
var REBIND_DELAY_MS = 5e3;
|
|
91283
91466
|
var MAX_REBIND_ATTEMPTS = 6;
|
|
91284
91467
|
var NO_INTERNAL_HOST_REASON = "The hub has no internal address. Flag one as \"Internal\" under System → Network; cameras need it to reach this mailbox.";
|
|
91285
|
-
var KIND_LABEL = "Inbound mailbox (SMTP)";
|
|
91286
91468
|
var PAGE = 500;
|
|
91287
91469
|
/** The hostname of a role's resolved base URL (`http://192.0.2.10:4480` → `192.0.2.10`). */
|
|
91288
91470
|
function hostFromRole(resolved) {
|
|
@@ -91355,6 +91537,20 @@ var SmtpInboundBroker = class {
|
|
|
91355
91537
|
this.rebindTimers.clear();
|
|
91356
91538
|
await Promise.all([...this.runtimes.keys()].map((id) => this.unbind(id)));
|
|
91357
91539
|
}
|
|
91540
|
+
/** Bring the listener in line with the record after the instances page changed it. */
|
|
91541
|
+
reconcileMailbox(id) {
|
|
91542
|
+
return this.reconcile(id);
|
|
91543
|
+
}
|
|
91544
|
+
/** The runtime's view of one mailbox; null while it has no listener. */
|
|
91545
|
+
runtimeOf(id) {
|
|
91546
|
+
const runtime = this.runtimes.get(id);
|
|
91547
|
+
if (runtime === void 0) return null;
|
|
91548
|
+
return {
|
|
91549
|
+
status: runtime.status,
|
|
91550
|
+
error: runtime.error,
|
|
91551
|
+
checkedAt: runtime.checkedAt
|
|
91552
|
+
};
|
|
91553
|
+
}
|
|
91358
91554
|
reconcile(id) {
|
|
91359
91555
|
const next = (this.chains.get(id) ?? Promise.resolve()).then(() => this.applyRecord(id));
|
|
91360
91556
|
this.chains.set(id, next.catch(() => void 0));
|
|
@@ -91472,28 +91668,6 @@ var SmtpInboundBroker = class {
|
|
|
91472
91668
|
payload: message
|
|
91473
91669
|
});
|
|
91474
91670
|
}
|
|
91475
|
-
async exists(id) {
|
|
91476
|
-
return await this.deps.handle.get(id) !== null;
|
|
91477
|
-
}
|
|
91478
|
-
async info(id) {
|
|
91479
|
-
const row = await this.deps.handle.get(id);
|
|
91480
|
-
if (row === null) return null;
|
|
91481
|
-
const runtime = this.runtimes.get(id);
|
|
91482
|
-
return {
|
|
91483
|
-
id,
|
|
91484
|
-
addonId: this.deps.addonId,
|
|
91485
|
-
name: row.data.name,
|
|
91486
|
-
kind: SMTP_INBOUND_KIND,
|
|
91487
|
-
status: runtime?.status ?? "disconnected",
|
|
91488
|
-
info: {
|
|
91489
|
-
port: row.data.port,
|
|
91490
|
-
domain: row.data.domain,
|
|
91491
|
-
recipients: runtime === void 0 ? 0 : [...runtime.subscriptions.values()].reduce((n, s) => n + s.recipients.size, 0)
|
|
91492
|
-
},
|
|
91493
|
-
lastCheckedAt: runtime?.checkedAt ?? null,
|
|
91494
|
-
error: runtime?.error ?? null
|
|
91495
|
-
};
|
|
91496
|
-
}
|
|
91497
91671
|
async subscribe(brokerId, filter) {
|
|
91498
91672
|
const parsed = SmtpInboundFilterSchema.parse(filter);
|
|
91499
91673
|
const runtime = this.runtimes.get(brokerId);
|
|
@@ -91513,85 +91687,19 @@ var SmtpInboundBroker = class {
|
|
|
91513
91687
|
runtime.subscriptions = next;
|
|
91514
91688
|
return { subscriptionId: id };
|
|
91515
91689
|
}
|
|
91690
|
+
/** The subscribers (camera email push) that still read one mailbox. */
|
|
91691
|
+
subscriberUsers() {
|
|
91692
|
+
return { find: async (brokerId) => [...this.runtimes.get(brokerId)?.subscriptions.values() ?? []].map((sub) => ({
|
|
91693
|
+
kind: "subscriber",
|
|
91694
|
+
id: sub.subscriberId
|
|
91695
|
+
})) };
|
|
91696
|
+
}
|
|
91697
|
+
/**
|
|
91698
|
+
* The `broker` cap is the bus only (D1013): the mailboxes themselves are the `mailboxes` kind of
|
|
91699
|
+
* the addon's `integration-instances` provider (`mailbox-instances.ts`).
|
|
91700
|
+
*/
|
|
91516
91701
|
provider() {
|
|
91517
|
-
const owns = (id) => this.exists(id);
|
|
91518
91702
|
return {
|
|
91519
|
-
list: async ({ kind }) => {
|
|
91520
|
-
if (kind !== void 0 && kind !== "smtp-inbound") return [];
|
|
91521
|
-
const page = await this.deps.handle.query({ limit: PAGE });
|
|
91522
|
-
return (await Promise.all(page.records.map((r) => this.info(r.id)))).flatMap((i) => i === null ? [] : [i]);
|
|
91523
|
-
},
|
|
91524
|
-
get: async ({ id }) => this.info(id),
|
|
91525
|
-
listProviders: async () => [{
|
|
91526
|
-
addonId: this.deps.addonId,
|
|
91527
|
-
kinds: [{
|
|
91528
|
-
kind: SMTP_INBOUND_KIND,
|
|
91529
|
-
label: KIND_LABEL
|
|
91530
|
-
}]
|
|
91531
|
-
}],
|
|
91532
|
-
add: async ({ kind, name, id, settings }) => {
|
|
91533
|
-
if (kind !== "smtp-inbound") throw new Error(`smtp-nodemailer: only kind '${SMTP_INBOUND_KIND}' is handled here (got '${kind}')`);
|
|
91534
|
-
const parsed = SmtpInboundSettingsSchema.parse(settings);
|
|
91535
|
-
const brokerId = id ?? `smtp-inbound-${(0, node_crypto.randomUUID)().slice(0, 8)}`;
|
|
91536
|
-
if (await this.exists(brokerId)) throw new Error(`broker id '${brokerId}' is already in use`);
|
|
91537
|
-
const body = MailboxBodySchema.parse({
|
|
91538
|
-
name,
|
|
91539
|
-
port: parsed.port,
|
|
91540
|
-
bindHost: parsed.bindHost,
|
|
91541
|
-
domain: parsed.domain,
|
|
91542
|
-
requireAuth: parsed.requireAuth,
|
|
91543
|
-
username: parsed.username.length > 0 ? parsed.username : `camstack-${(0, node_crypto.randomBytes)(4).toString("hex")}`,
|
|
91544
|
-
password: parsed.password.length > 0 ? parsed.password : (0, node_crypto.randomBytes)(18).toString("base64url"),
|
|
91545
|
-
starttls: parsed.starttls,
|
|
91546
|
-
maxMessageBytes: parsed.maxMessageBytes
|
|
91547
|
-
});
|
|
91548
|
-
await this.deps.handle.put({
|
|
91549
|
-
id: brokerId,
|
|
91550
|
-
data: body
|
|
91551
|
-
}, { ifRev: 0 });
|
|
91552
|
-
await this.reconcile(brokerId);
|
|
91553
|
-
return { id: brokerId };
|
|
91554
|
-
},
|
|
91555
|
-
remove: async ({ id }) => {
|
|
91556
|
-
if (!await owns(id)) return;
|
|
91557
|
-
await this.deps.handle.delete(id);
|
|
91558
|
-
await this.reconcile(id);
|
|
91559
|
-
},
|
|
91560
|
-
testConnection: async ({ id }) => {
|
|
91561
|
-
if (!await owns(id)) return {
|
|
91562
|
-
ok: false,
|
|
91563
|
-
error: "unknown broker"
|
|
91564
|
-
};
|
|
91565
|
-
const runtime = this.runtimes.get(id);
|
|
91566
|
-
if (runtime?.status === "connected") return {
|
|
91567
|
-
ok: true,
|
|
91568
|
-
latencyMs: 0
|
|
91569
|
-
};
|
|
91570
|
-
return {
|
|
91571
|
-
ok: false,
|
|
91572
|
-
error: runtime?.error ?? "The mailbox is not listening"
|
|
91573
|
-
};
|
|
91574
|
-
},
|
|
91575
|
-
getSettings: async ({ id }) => {
|
|
91576
|
-
const row = await this.deps.handle.get(id);
|
|
91577
|
-
if (row === null) return null;
|
|
91578
|
-
const { name: _name, password: _password, ...settings } = row.data;
|
|
91579
|
-
return settings;
|
|
91580
|
-
},
|
|
91581
|
-
setSettings: async ({ id, settings }) => {
|
|
91582
|
-
const row = await this.deps.handle.getResolved(id);
|
|
91583
|
-
if (row === null) return;
|
|
91584
|
-
const parsed = SmtpInboundSettingsSchema.partial().parse(settings);
|
|
91585
|
-
const next = MailboxBodySchema.parse({
|
|
91586
|
-
...row.data,
|
|
91587
|
-
...Object.fromEntries(Object.entries(parsed).filter(([, v]) => v !== void 0 && v !== ""))
|
|
91588
|
-
});
|
|
91589
|
-
await this.deps.handle.put({
|
|
91590
|
-
id,
|
|
91591
|
-
data: next
|
|
91592
|
-
});
|
|
91593
|
-
await this.reconcile(id);
|
|
91594
|
-
},
|
|
91595
91703
|
getBrokerConfig: async ({ id }) => {
|
|
91596
91704
|
let row;
|
|
91597
91705
|
try {
|
|
@@ -91613,19 +91721,6 @@ var SmtpInboundBroker = class {
|
|
|
91613
91721
|
starttls: row.data.starttls
|
|
91614
91722
|
};
|
|
91615
91723
|
},
|
|
91616
|
-
getSettingsSchema: async ({ kind }) => kind === "smtp-inbound" ? mailboxSettingsSchema() : null,
|
|
91617
|
-
testSettings: async ({ kind, settings }) => {
|
|
91618
|
-
if (kind !== "smtp-inbound") return {
|
|
91619
|
-
ok: false,
|
|
91620
|
-
error: `unsupported kind: ${kind}`
|
|
91621
|
-
};
|
|
91622
|
-
const parsed = SmtpInboundSettingsSchema.safeParse(settings);
|
|
91623
|
-
if (!parsed.success) return {
|
|
91624
|
-
ok: false,
|
|
91625
|
-
error: parsed.error.message
|
|
91626
|
-
};
|
|
91627
|
-
return probeBind(parsed.data.port, parsed.data.bindHost);
|
|
91628
|
-
},
|
|
91629
91724
|
publish: async () => {
|
|
91630
91725
|
throw new Error("smtp-inbound: a mailbox only receives — publish is not supported");
|
|
91631
91726
|
},
|
|
@@ -91653,23 +91748,6 @@ var SmtpInboundBroker = class {
|
|
|
91653
91748
|
};
|
|
91654
91749
|
}
|
|
91655
91750
|
};
|
|
91656
|
-
/** A bind probe: can this port be taken on this host right now? */
|
|
91657
|
-
function probeBind(port, host) {
|
|
91658
|
-
const started = Date.now();
|
|
91659
|
-
return new Promise((resolve) => {
|
|
91660
|
-
const probe = (0, node_net.createServer)();
|
|
91661
|
-
probe.once("error", (err) => resolve({
|
|
91662
|
-
ok: false,
|
|
91663
|
-
error: err.message
|
|
91664
|
-
}));
|
|
91665
|
-
probe.listen(port, host, () => {
|
|
91666
|
-
probe.close(() => resolve({
|
|
91667
|
-
ok: true,
|
|
91668
|
-
latencyMs: Date.now() - started
|
|
91669
|
-
}));
|
|
91670
|
-
});
|
|
91671
|
-
});
|
|
91672
|
-
}
|
|
91673
91751
|
function mailboxSettingsSchema() {
|
|
91674
91752
|
return { sections: [{
|
|
91675
91753
|
id: "mailbox",
|
|
@@ -135959,6 +136037,145 @@ var SmtpServerListener = class {
|
|
|
135959
136037
|
}
|
|
135960
136038
|
};
|
|
135961
136039
|
//#endregion
|
|
136040
|
+
//#region src/bind-probe.ts
|
|
136041
|
+
/** A bind probe: can this port be taken on this host right now? */
|
|
136042
|
+
function probeBind(port, host) {
|
|
136043
|
+
const started = Date.now();
|
|
136044
|
+
return new Promise((resolve) => {
|
|
136045
|
+
const probe = (0, node_net.createServer)();
|
|
136046
|
+
probe.once("error", (err) => resolve({
|
|
136047
|
+
ok: false,
|
|
136048
|
+
error: err.message
|
|
136049
|
+
}));
|
|
136050
|
+
probe.listen(port, host, () => {
|
|
136051
|
+
probe.close(() => resolve({
|
|
136052
|
+
ok: true,
|
|
136053
|
+
latencyMs: Date.now() - started
|
|
136054
|
+
}));
|
|
136055
|
+
});
|
|
136056
|
+
});
|
|
136057
|
+
}
|
|
136058
|
+
//#endregion
|
|
136059
|
+
//#region src/mailbox-record-kinds.ts
|
|
136060
|
+
/**
|
|
136061
|
+
* The `smtp-inbound` mailbox as a record (D999): one `mailboxes` row per mailbox, owned by
|
|
136062
|
+
* `smtp-nodemailer`. The row id is the broker id. The SMTP AUTH password is a keychain-bound secret
|
|
136063
|
+
* field; nothing else holds a copy.
|
|
136064
|
+
*/
|
|
136065
|
+
var MailboxBodySchema = entryBody({
|
|
136066
|
+
port: number().int().min(1).max(65535),
|
|
136067
|
+
bindHost: string().min(1),
|
|
136068
|
+
domain: string().min(1),
|
|
136069
|
+
requireAuth: boolean(),
|
|
136070
|
+
username: string(),
|
|
136071
|
+
password: string().optional().meta({ secret: true }),
|
|
136072
|
+
starttls: boolean(),
|
|
136073
|
+
maxMessageBytes: number().int().positive()
|
|
136074
|
+
});
|
|
136075
|
+
var mailboxesKind = defineEntryKind("mailboxes", {
|
|
136076
|
+
title: "Inbound mailboxes",
|
|
136077
|
+
nameField: "name",
|
|
136078
|
+
schema: MailboxBodySchema,
|
|
136079
|
+
writeTrigger: "an operator (or a consumer addon, once) adds or edits an inbound mailbox"
|
|
136080
|
+
});
|
|
136081
|
+
//#endregion
|
|
136082
|
+
//#region src/mailbox-instances.ts
|
|
136083
|
+
/**
|
|
136084
|
+
* The inbound mailboxes as `integration-instances` (D1013): one instance per `mailboxes` record,
|
|
136085
|
+
* over the SAME records and ids the `smtp-inbound` broker serves; the `mailboxes` kind of the
|
|
136086
|
+
* composed provider (the relays are the `relays` kind). A change re-applies the listener; removing
|
|
136087
|
+
* a mailbox a subscriber (camera email push) still reads is refused.
|
|
136088
|
+
*/
|
|
136089
|
+
var MAILBOX_INSTANCE_KIND = "mailboxes";
|
|
136090
|
+
function statusOf(runtime) {
|
|
136091
|
+
if (runtime === null) return {
|
|
136092
|
+
state: "unknown",
|
|
136093
|
+
label: "Not listening"
|
|
136094
|
+
};
|
|
136095
|
+
if (runtime.status === "connected") return {
|
|
136096
|
+
state: "ok",
|
|
136097
|
+
label: "Listening"
|
|
136098
|
+
};
|
|
136099
|
+
return {
|
|
136100
|
+
state: "error",
|
|
136101
|
+
label: runtime.status,
|
|
136102
|
+
...runtime.error !== null ? { detail: runtime.error } : {}
|
|
136103
|
+
};
|
|
136104
|
+
}
|
|
136105
|
+
function newMailboxBody(name) {
|
|
136106
|
+
const defaults = SmtpInboundSettingsSchema.parse({});
|
|
136107
|
+
return MailboxBodySchema.parse({
|
|
136108
|
+
name,
|
|
136109
|
+
port: defaults.port,
|
|
136110
|
+
bindHost: defaults.bindHost,
|
|
136111
|
+
domain: defaults.domain,
|
|
136112
|
+
requireAuth: defaults.requireAuth,
|
|
136113
|
+
username: `camstack-${(0, node_crypto.randomBytes)(4).toString("hex")}`,
|
|
136114
|
+
password: (0, node_crypto.randomBytes)(18).toString("base64url"),
|
|
136115
|
+
starttls: defaults.starttls,
|
|
136116
|
+
maxMessageBytes: defaults.maxMessageBytes
|
|
136117
|
+
});
|
|
136118
|
+
}
|
|
136119
|
+
function buildMailboxInstances(records, deps) {
|
|
136120
|
+
const users = deps.inbound.subscriberUsers();
|
|
136121
|
+
const base = new RecordIntegrationInstances(records, mailboxesKind, {
|
|
136122
|
+
description: {
|
|
136123
|
+
role: "services",
|
|
136124
|
+
instanceMode: "multiple",
|
|
136125
|
+
kind: "mailbox",
|
|
136126
|
+
canTest: true
|
|
136127
|
+
},
|
|
136128
|
+
formSchema: () => mailboxSettingsSchema(),
|
|
136129
|
+
initialBody: (name) => newMailboxBody(name),
|
|
136130
|
+
mintId: () => `smtp-inbound-${(0, node_crypto.randomUUID)().slice(0, 8)}`,
|
|
136131
|
+
configured: () => true,
|
|
136132
|
+
health: (_body, id) => {
|
|
136133
|
+
const runtime = deps.inbound.runtimeOf(id);
|
|
136134
|
+
return {
|
|
136135
|
+
status: statusOf(runtime),
|
|
136136
|
+
...runtime !== null ? { lastCheckedAt: runtime.checkedAt } : {}
|
|
136137
|
+
};
|
|
136138
|
+
},
|
|
136139
|
+
onChanged: (id) => deps.inbound.reconcileMailbox(id),
|
|
136140
|
+
test: async ({ id, values }) => {
|
|
136141
|
+
if (id === null) {
|
|
136142
|
+
const parsed = SmtpInboundSettingsSchema.safeParse(values);
|
|
136143
|
+
if (!parsed.success) return {
|
|
136144
|
+
success: false,
|
|
136145
|
+
status: "rejected",
|
|
136146
|
+
error: parsed.error.message
|
|
136147
|
+
};
|
|
136148
|
+
const bound = await probeBind(parsed.data.port, parsed.data.bindHost);
|
|
136149
|
+
return bound.ok ? {
|
|
136150
|
+
success: true,
|
|
136151
|
+
status: "validated",
|
|
136152
|
+
latencyMs: bound.latencyMs
|
|
136153
|
+
} : {
|
|
136154
|
+
success: false,
|
|
136155
|
+
status: "rejected",
|
|
136156
|
+
error: bound.error
|
|
136157
|
+
};
|
|
136158
|
+
}
|
|
136159
|
+
const runtime = deps.inbound.runtimeOf(id);
|
|
136160
|
+
return runtime?.status === "connected" ? {
|
|
136161
|
+
success: true,
|
|
136162
|
+
status: "validated"
|
|
136163
|
+
} : {
|
|
136164
|
+
success: false,
|
|
136165
|
+
status: "rejected",
|
|
136166
|
+
error: runtime?.error ?? "The mailbox is not listening"
|
|
136167
|
+
};
|
|
136168
|
+
}
|
|
136169
|
+
}).toProvider();
|
|
136170
|
+
return {
|
|
136171
|
+
...base,
|
|
136172
|
+
deleteInstance: async (input) => {
|
|
136173
|
+
await assertBrokerUnused(input.id, [users]);
|
|
136174
|
+
return base.deleteInstance(input);
|
|
136175
|
+
}
|
|
136176
|
+
};
|
|
136177
|
+
}
|
|
136178
|
+
//#endregion
|
|
135962
136179
|
//#region ../../node_modules/nodemailer/lib/mime-funcs/mime-types.js
|
|
135963
136180
|
var require_mime_types = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
135964
136181
|
var path$1 = require("path");
|
|
@@ -145692,7 +145909,29 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145692
145909
|
},
|
|
145693
145910
|
{
|
|
145694
145911
|
capability: integrationInstancesCapability,
|
|
145695
|
-
provider:
|
|
145912
|
+
provider: composeInstanceKinds([{
|
|
145913
|
+
kind: {
|
|
145914
|
+
id: "relays",
|
|
145915
|
+
label: "SMTP relay",
|
|
145916
|
+
functionCap: "smtp-provider",
|
|
145917
|
+
role: "services"
|
|
145918
|
+
},
|
|
145919
|
+
provider: this.instances.toProvider()
|
|
145920
|
+
}, {
|
|
145921
|
+
kind: {
|
|
145922
|
+
id: MAILBOX_INSTANCE_KIND,
|
|
145923
|
+
label: "Inbound mailbox (SMTP)",
|
|
145924
|
+
functionCap: "broker",
|
|
145925
|
+
brokerKind: SMTP_INBOUND_KIND,
|
|
145926
|
+
role: "services"
|
|
145927
|
+
},
|
|
145928
|
+
provider: buildMailboxInstances(records, { inbound })
|
|
145929
|
+
}], {
|
|
145930
|
+
role: "services",
|
|
145931
|
+
instanceMode: "multiple",
|
|
145932
|
+
kind: "relays",
|
|
145933
|
+
canTest: true
|
|
145934
|
+
})
|
|
145696
145935
|
},
|
|
145697
145936
|
{
|
|
145698
145937
|
capability: brokerCapability,
|
|
@@ -145846,7 +146085,8 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145846
146085
|
const rows = (await records.kind(smtpRelaysKind).query({ limit: RELAY_LIST_MAX })).records;
|
|
145847
146086
|
return (rows.find((row) => row.data.host.length > 0) ?? rows[0])?.id;
|
|
145848
146087
|
}
|
|
145849
|
-
|
|
146088
|
+
/** The relay form. Its values live on the relay record, never in `addon-settings`. */
|
|
146089
|
+
async integrationFormSchema(_instance) {
|
|
145850
146090
|
return this.relayForm();
|
|
145851
146091
|
}
|
|
145852
146092
|
async getGlobalSettings() {
|