@camstack/addon-smtp-nodemailer 1.2.147 → 1.2.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/smtp.addon.js +223 -59
- package/dist/smtp.addon.mjs +223 -59
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.2.148] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The relay form is declared as the integration instance form; the form and stored relays are unchanged.
|
|
15
|
+
|
|
10
16
|
## [1.2.147] - 2026-11-04
|
|
11
17
|
|
|
12
18
|
### Changed
|
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-DEZaLbiu.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
|
*
|
|
@@ -7718,6 +7726,13 @@ var BaseAddon = class {
|
|
|
7718
7726
|
*/
|
|
7719
7727
|
secretStatePaths = [];
|
|
7720
7728
|
_addonSecretTree = null;
|
|
7729
|
+
/**
|
|
7730
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7731
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7732
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7733
|
+
*/
|
|
7734
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7735
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7721
7736
|
_deviceSecretTree = null;
|
|
7722
7737
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7723
7738
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7787,6 +7802,7 @@ var BaseAddon = class {
|
|
|
7787
7802
|
this._ctx = context;
|
|
7788
7803
|
this._secretSettings = null;
|
|
7789
7804
|
}
|
|
7805
|
+
await this.loadScopedSchemas();
|
|
7790
7806
|
await this.resolveConfig();
|
|
7791
7807
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7792
7808
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7796,6 +7812,7 @@ var BaseAddon = class {
|
|
|
7796
7812
|
const normalized = normalizeAddonInitResult(result);
|
|
7797
7813
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7798
7814
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7815
|
+
await this.loadScopedSchemas();
|
|
7799
7816
|
return normalized;
|
|
7800
7817
|
}
|
|
7801
7818
|
/**
|
|
@@ -7809,14 +7826,18 @@ var BaseAddon = class {
|
|
|
7809
7826
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7810
7827
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7811
7828
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7829
|
+
*
|
|
7830
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7831
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7832
|
+
* omitted applied ids "newer").
|
|
7812
7833
|
*/
|
|
7813
|
-
async runMigrations(migrations) {
|
|
7834
|
+
async runMigrations(migrations, options) {
|
|
7814
7835
|
const api = this.ctx.api.migrations;
|
|
7815
7836
|
const report = await runAddonMigrations({
|
|
7816
7837
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7817
7838
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7818
7839
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7819
|
-
}, migrations, this.ctx.logger);
|
|
7840
|
+
}, migrations, this.ctx.logger, options);
|
|
7820
7841
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7821
7842
|
return report;
|
|
7822
7843
|
}
|
|
@@ -7880,6 +7901,16 @@ var BaseAddon = class {
|
|
|
7880
7901
|
schema(schema) {
|
|
7881
7902
|
return schema;
|
|
7882
7903
|
}
|
|
7904
|
+
/**
|
|
7905
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7906
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7907
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7908
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7909
|
+
* once known.
|
|
7910
|
+
*/
|
|
7911
|
+
scopedSettingsCapabilities() {
|
|
7912
|
+
return [];
|
|
7913
|
+
}
|
|
7883
7914
|
/** Override to provide global-level settings UI schema. */
|
|
7884
7915
|
globalSettingsSchema(_cap) {
|
|
7885
7916
|
return null;
|
|
@@ -7923,7 +7954,8 @@ var BaseAddon = class {
|
|
|
7923
7954
|
return [];
|
|
7924
7955
|
}
|
|
7925
7956
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7926
|
-
|
|
7957
|
+
await this.loadScopedSchemas(cap);
|
|
7958
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7927
7959
|
if (!schema) return { sections: [] };
|
|
7928
7960
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7929
7961
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7958,10 +7990,9 @@ var BaseAddon = class {
|
|
|
7958
7990
|
*
|
|
7959
7991
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7960
7992
|
* 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).
|
|
7993
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7963
7994
|
*/
|
|
7964
|
-
async
|
|
7995
|
+
async integrationFormSchema(_instance) {
|
|
7965
7996
|
return null;
|
|
7966
7997
|
}
|
|
7967
7998
|
/**
|
|
@@ -8007,7 +8038,7 @@ var BaseAddon = class {
|
|
|
8007
8038
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8008
8039
|
*/
|
|
8009
8040
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8010
|
-
const scoped = await this.
|
|
8041
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8011
8042
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8012
8043
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8013
8044
|
}
|
|
@@ -8055,6 +8086,7 @@ var BaseAddon = class {
|
|
|
8055
8086
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8056
8087
|
*/
|
|
8057
8088
|
async resolveGlobalStore(nodeId, cap) {
|
|
8089
|
+
await this.loadScopedSchemas(cap);
|
|
8058
8090
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8059
8091
|
return this.projectForNode(raw, nodeId, cap);
|
|
8060
8092
|
}
|
|
@@ -8065,6 +8097,7 @@ var BaseAddon = class {
|
|
|
8065
8097
|
}
|
|
8066
8098
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8067
8099
|
async readRawProjectedStore(nodeId, cap) {
|
|
8100
|
+
await this.loadScopedSchemas(cap);
|
|
8068
8101
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8069
8102
|
return this.projectForNode(raw, nodeId, cap);
|
|
8070
8103
|
}
|
|
@@ -8167,6 +8200,7 @@ var BaseAddon = class {
|
|
|
8167
8200
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8168
8201
|
}
|
|
8169
8202
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8203
|
+
await this.loadScopedSchemas();
|
|
8170
8204
|
const keys = this.perNodeKeys();
|
|
8171
8205
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8172
8206
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8180,7 +8214,8 @@ var BaseAddon = class {
|
|
|
8180
8214
|
await this.resolveConfig();
|
|
8181
8215
|
await this.onConfigChanged();
|
|
8182
8216
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8183
|
-
|
|
8217
|
+
const legacy = this.globalSettingsSchema();
|
|
8218
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8184
8219
|
}
|
|
8185
8220
|
/**
|
|
8186
8221
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8188,14 +8223,67 @@ var BaseAddon = class {
|
|
|
8188
8223
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8189
8224
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8190
8225
|
*/
|
|
8191
|
-
|
|
8226
|
+
/**
|
|
8227
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8228
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8229
|
+
* derived from the previous set. Idempotent.
|
|
8230
|
+
*/
|
|
8231
|
+
async loadScopedSchemas(extraCap) {
|
|
8232
|
+
const caps = new Set([
|
|
8233
|
+
...this.scopedSettingsCapabilities(),
|
|
8234
|
+
...this._registeredCapNames,
|
|
8235
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8236
|
+
]);
|
|
8237
|
+
let added = false;
|
|
8238
|
+
for (const cap of caps) {
|
|
8239
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8240
|
+
const loaded = [];
|
|
8241
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8242
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8243
|
+
if (schema !== null) loaded.push({
|
|
8244
|
+
scope,
|
|
8245
|
+
schema
|
|
8246
|
+
});
|
|
8247
|
+
}
|
|
8248
|
+
this._scopedByCap.set(cap, loaded);
|
|
8249
|
+
added = true;
|
|
8250
|
+
}
|
|
8251
|
+
if (!added) return;
|
|
8252
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8253
|
+
this._addonSecretTree = null;
|
|
8254
|
+
}
|
|
8255
|
+
/**
|
|
8256
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8257
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8258
|
+
*/
|
|
8259
|
+
scopedFormSchema(cap) {
|
|
8260
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8261
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8262
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8263
|
+
}
|
|
8264
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8265
|
+
loadedScopedSchemas(cap) {
|
|
8266
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8267
|
+
return [...this._scopedByCap.values()].flat();
|
|
8268
|
+
}
|
|
8269
|
+
/**
|
|
8270
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8271
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8272
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8273
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8274
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8275
|
+
*/
|
|
8192
8276
|
perNodeKeys(cap) {
|
|
8193
8277
|
const cacheKey = cap ?? "";
|
|
8194
|
-
const cached = this.
|
|
8278
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8195
8279
|
if (cached) return cached;
|
|
8196
|
-
const
|
|
8197
|
-
const keys = new Set(
|
|
8198
|
-
this.
|
|
8280
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8281
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8282
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8283
|
+
if (scope !== "node") continue;
|
|
8284
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8285
|
+
}
|
|
8286
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8199
8287
|
return keys;
|
|
8200
8288
|
}
|
|
8201
8289
|
/**
|
|
@@ -8205,10 +8293,9 @@ var BaseAddon = class {
|
|
|
8205
8293
|
* time to return its response before the addon is torn down and
|
|
8206
8294
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8207
8295
|
*/
|
|
8208
|
-
maybeAutoRestart(patchKeys,
|
|
8209
|
-
if (!schema) return;
|
|
8296
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8210
8297
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8211
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8298
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8212
8299
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8213
8300
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8214
8301
|
}
|
|
@@ -8404,12 +8491,14 @@ var BaseAddon = class {
|
|
|
8404
8491
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8405
8492
|
*/
|
|
8406
8493
|
async getDeclaredSettingsKeys() {
|
|
8494
|
+
await this.loadScopedSchemas();
|
|
8407
8495
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8408
8496
|
}
|
|
8409
8497
|
declaredKeySet() {
|
|
8410
8498
|
const keys = new Set(Object.keys(this.defaults));
|
|
8411
8499
|
const schema = this.globalSettingsSchema();
|
|
8412
8500
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8501
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8413
8502
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8414
8503
|
for (const path of this.secretStatePaths) {
|
|
8415
8504
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8505,7 +8594,11 @@ var BaseAddon = class {
|
|
|
8505
8594
|
}
|
|
8506
8595
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8507
8596
|
addonSecretTree() {
|
|
8508
|
-
this._addonSecretTree
|
|
8597
|
+
if (this._addonSecretTree === null) {
|
|
8598
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8599
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8600
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8601
|
+
}
|
|
8509
8602
|
return this._addonSecretTree;
|
|
8510
8603
|
}
|
|
8511
8604
|
deviceSecretTree() {
|
|
@@ -8755,6 +8848,23 @@ function dropPerNodeFields(fields) {
|
|
|
8755
8848
|
}
|
|
8756
8849
|
return kept;
|
|
8757
8850
|
}
|
|
8851
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8852
|
+
function collectValueFieldKeys(fields) {
|
|
8853
|
+
const collected = [];
|
|
8854
|
+
for (const field of fields) {
|
|
8855
|
+
if (field.type === "group") {
|
|
8856
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8857
|
+
continue;
|
|
8858
|
+
}
|
|
8859
|
+
if (field.type === "sub-tabs") {
|
|
8860
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8861
|
+
continue;
|
|
8862
|
+
}
|
|
8863
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8864
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8865
|
+
}
|
|
8866
|
+
return collected;
|
|
8867
|
+
}
|
|
8758
8868
|
function collectFieldKeys(fields) {
|
|
8759
8869
|
const collected = [];
|
|
8760
8870
|
for (const field of fields) {
|
|
@@ -9288,6 +9398,7 @@ function defineEntryKind(name, spec) {
|
|
|
9288
9398
|
class: "config",
|
|
9289
9399
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9290
9400
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9401
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9291
9402
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9292
9403
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9293
9404
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
@@ -11967,7 +12078,7 @@ function userError(code, params, englishFallback) {
|
|
|
11967
12078
|
return new CamstackError(code, params, englishFallback);
|
|
11968
12079
|
}
|
|
11969
12080
|
//#endregion
|
|
11970
|
-
//#region ../types/dist/composition-
|
|
12081
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11971
12082
|
/**
|
|
11972
12083
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11973
12084
|
*
|
|
@@ -13456,12 +13567,22 @@ var RecordIntegrationInstances = class {
|
|
|
13456
13567
|
...this.hooks.description,
|
|
13457
13568
|
mintsId: true
|
|
13458
13569
|
};
|
|
13459
|
-
|
|
13570
|
+
/**
|
|
13571
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13572
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13573
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13574
|
+
*/
|
|
13575
|
+
async resolveRecordId(id) {
|
|
13576
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13577
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13578
|
+
return linked === void 0 ? id : linked.id;
|
|
13579
|
+
}
|
|
13580
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13460
13581
|
listInstances = async () => {
|
|
13461
13582
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13462
13583
|
};
|
|
13463
13584
|
getInstance = async ({ id }) => {
|
|
13464
|
-
const record = await this.handle.get(id);
|
|
13585
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13465
13586
|
return record === null ? null : this.viewOf(record);
|
|
13466
13587
|
};
|
|
13467
13588
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13478,7 +13599,8 @@ var RecordIntegrationInstances = class {
|
|
|
13478
13599
|
await this.hooks.onChanged?.(minted, "created");
|
|
13479
13600
|
return { id: minted };
|
|
13480
13601
|
};
|
|
13481
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13602
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13603
|
+
const id = await this.resolveRecordId(askedId);
|
|
13482
13604
|
const patch = {};
|
|
13483
13605
|
for (const [key, value] of Object.entries(values)) {
|
|
13484
13606
|
if (value === void 0) continue;
|
|
@@ -13513,7 +13635,10 @@ var RecordIntegrationInstances = class {
|
|
|
13513
13635
|
status: "unsupported",
|
|
13514
13636
|
error: "This integration declares no connection test"
|
|
13515
13637
|
};
|
|
13516
|
-
return this.hooks.test(input
|
|
13638
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13639
|
+
...input,
|
|
13640
|
+
id: await this.resolveRecordId(input.id)
|
|
13641
|
+
});
|
|
13517
13642
|
};
|
|
13518
13643
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13519
13644
|
toProvider() {
|
|
@@ -14575,7 +14700,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14575
14700
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14576
14701
|
* catalogue lists the id (a newer database).
|
|
14577
14702
|
*/
|
|
14578
|
-
meta: MigrationMetaSchema.optional()
|
|
14703
|
+
meta: MigrationMetaSchema.optional(),
|
|
14704
|
+
/**
|
|
14705
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14706
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14707
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14708
|
+
*/
|
|
14709
|
+
ownerNotLoaded: boolean().optional()
|
|
14579
14710
|
});
|
|
14580
14711
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14581
14712
|
var MigrationPendingEntrySchema = object({
|
|
@@ -49115,6 +49246,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
49115
49246
|
runsFound: number().int().nonnegative(),
|
|
49116
49247
|
pausesFound: number().int().nonnegative(),
|
|
49117
49248
|
longestPauseMs: number().nonnegative(),
|
|
49249
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49250
|
+
pulsesFound: number().int().nonnegative(),
|
|
49251
|
+
longestPulseMs: number().nonnegative(),
|
|
49118
49252
|
idleFloorW: number(),
|
|
49119
49253
|
noiseCeilingW: number(),
|
|
49120
49254
|
plateauW: number(),
|
|
@@ -54036,11 +54170,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
54036
54170
|
info: record(string(), unknown()).optional(),
|
|
54037
54171
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
54038
54172
|
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()
|
|
54173
|
+
settings: record(string(), unknown()).optional()
|
|
54044
54174
|
});
|
|
54045
54175
|
var UpdateIntegrationInputSchema = object({
|
|
54046
54176
|
id: string(),
|
|
@@ -54061,16 +54191,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54061
54191
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
54062
54192
|
kind: "mutation",
|
|
54063
54193
|
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
54194
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
54075
54195
|
addonId: string(),
|
|
54076
54196
|
settings: record(string(), unknown())
|
|
@@ -56795,6 +56915,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56795
56915
|
}), {
|
|
56796
56916
|
kind: "mutation",
|
|
56797
56917
|
auth: "admin"
|
|
56918
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
56919
|
+
rows: number().int().nonnegative(),
|
|
56920
|
+
secrets: number().int().nonnegative()
|
|
56921
|
+
}), {
|
|
56922
|
+
kind: "mutation",
|
|
56923
|
+
auth: "admin"
|
|
56924
|
+
}), method(object({
|
|
56925
|
+
integrationId: string().min(1),
|
|
56926
|
+
patch: record(string(), unknown())
|
|
56927
|
+
}), object({ written: boolean() }), {
|
|
56928
|
+
kind: "mutation",
|
|
56929
|
+
auth: "admin"
|
|
56930
|
+
}), method(object({
|
|
56931
|
+
integrationId: string().min(1),
|
|
56932
|
+
keys: array(string().min(1))
|
|
56933
|
+
}), object({
|
|
56934
|
+
present: boolean(),
|
|
56935
|
+
removed: record(string(), unknown())
|
|
56936
|
+
}), {
|
|
56937
|
+
kind: "mutation",
|
|
56938
|
+
auth: "admin"
|
|
56798
56939
|
});
|
|
56799
56940
|
method(object({
|
|
56800
56941
|
sourceUrl: string(),
|
|
@@ -75969,11 +76110,15 @@ discriminatedUnion("kind", [object({
|
|
|
75969
76110
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
75970
76111
|
* near F. A device that is not near one level most of the time has no floor.
|
|
75971
76112
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
75972
|
-
* 3.
|
|
75973
|
-
*
|
|
76113
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76114
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76115
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76116
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76117
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76118
|
+
* is no run.
|
|
75974
76119
|
* 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.
|
|
76120
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76121
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
75977
76122
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
75978
76123
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
75979
76124
|
*/
|
|
@@ -75984,6 +76129,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
75984
76129
|
30 * SECOND_MS;
|
|
75985
76130
|
60 * SECOND_MS;
|
|
75986
76131
|
20 * MINUTE_MS$1;
|
|
76132
|
+
90 * SECOND_MS;
|
|
76133
|
+
30 * MINUTE_MS$1;
|
|
76134
|
+
10 * MINUTE_MS$1;
|
|
76135
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
75987
76136
|
10 * SECOND_MS;
|
|
75988
76137
|
10 * SECOND_MS;
|
|
75989
76138
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -79739,13 +79888,6 @@ Object.freeze({
|
|
|
79739
79888
|
access: "view",
|
|
79740
79889
|
role: "admin"
|
|
79741
79890
|
},
|
|
79742
|
-
"integrations.getSettings": {
|
|
79743
|
-
capName: "integrations",
|
|
79744
|
-
capScope: "system",
|
|
79745
|
-
addonId: null,
|
|
79746
|
-
access: "view",
|
|
79747
|
-
role: "admin"
|
|
79748
|
-
},
|
|
79749
79891
|
"integrations.list": {
|
|
79750
79892
|
capName: "integrations",
|
|
79751
79893
|
capScope: "system",
|
|
@@ -79753,13 +79895,6 @@ Object.freeze({
|
|
|
79753
79895
|
access: "view",
|
|
79754
79896
|
role: "admin"
|
|
79755
79897
|
},
|
|
79756
|
-
"integrations.setSettings": {
|
|
79757
|
-
capName: "integrations",
|
|
79758
|
-
capScope: "system",
|
|
79759
|
-
addonId: null,
|
|
79760
|
-
access: "create",
|
|
79761
|
-
role: "admin"
|
|
79762
|
-
},
|
|
79763
79898
|
"integrations.testConnection": {
|
|
79764
79899
|
capName: "integrations",
|
|
79765
79900
|
capScope: "system",
|
|
@@ -80754,6 +80889,13 @@ Object.freeze({
|
|
|
80754
80889
|
access: "view",
|
|
80755
80890
|
role: "admin"
|
|
80756
80891
|
},
|
|
80892
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80893
|
+
capName: "migrations",
|
|
80894
|
+
capScope: "system",
|
|
80895
|
+
addonId: null,
|
|
80896
|
+
access: "delete",
|
|
80897
|
+
role: "admin"
|
|
80898
|
+
},
|
|
80757
80899
|
"migrations.declareOwner": {
|
|
80758
80900
|
capName: "migrations",
|
|
80759
80901
|
capScope: "system",
|
|
@@ -80761,6 +80903,13 @@ Object.freeze({
|
|
|
80761
80903
|
access: "create",
|
|
80762
80904
|
role: "admin"
|
|
80763
80905
|
},
|
|
80906
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80907
|
+
capName: "migrations",
|
|
80908
|
+
capScope: "system",
|
|
80909
|
+
addonId: null,
|
|
80910
|
+
access: "delete",
|
|
80911
|
+
role: "admin"
|
|
80912
|
+
},
|
|
80764
80913
|
"migrations.dryRun": {
|
|
80765
80914
|
capName: "migrations",
|
|
80766
80915
|
capScope: "system",
|
|
@@ -80796,6 +80945,20 @@ Object.freeze({
|
|
|
80796
80945
|
access: "create",
|
|
80797
80946
|
role: "admin"
|
|
80798
80947
|
},
|
|
80948
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80949
|
+
capName: "migrations",
|
|
80950
|
+
capScope: "system",
|
|
80951
|
+
addonId: null,
|
|
80952
|
+
access: "create",
|
|
80953
|
+
role: "admin"
|
|
80954
|
+
},
|
|
80955
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80956
|
+
capName: "migrations",
|
|
80957
|
+
capScope: "system",
|
|
80958
|
+
addonId: null,
|
|
80959
|
+
access: "view",
|
|
80960
|
+
role: "admin"
|
|
80961
|
+
},
|
|
80799
80962
|
"migrations.restorePreMigration": {
|
|
80800
80963
|
capName: "migrations",
|
|
80801
80964
|
capScope: "system",
|
|
@@ -145846,7 +146009,8 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145846
146009
|
const rows = (await records.kind(smtpRelaysKind).query({ limit: RELAY_LIST_MAX })).records;
|
|
145847
146010
|
return (rows.find((row) => row.data.host.length > 0) ?? rows[0])?.id;
|
|
145848
146011
|
}
|
|
145849
|
-
|
|
146012
|
+
/** The relay form. Its values live on the relay record, never in `addon-settings`. */
|
|
146013
|
+
async integrationFormSchema(_instance) {
|
|
145850
146014
|
return this.relayForm();
|
|
145851
146015
|
}
|
|
145852
146016
|
async getGlobalSettings() {
|
package/dist/smtp.addon.mjs
CHANGED
|
@@ -6018,7 +6018,7 @@ var ZodIssueCode = {
|
|
|
6018
6018
|
var ZodFirstPartyTypeKind;
|
|
6019
6019
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
6020
6020
|
//#endregion
|
|
6021
|
-
//#region ../types/dist/sleep-
|
|
6021
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
6022
6022
|
/**
|
|
6023
6023
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6024
6024
|
* window to float samples (D455).
|
|
@@ -6618,7 +6618,7 @@ var NOT_RUN = {
|
|
|
6618
6618
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6619
6619
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6620
6620
|
*/
|
|
6621
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6621
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6622
6622
|
const first = migrations[0];
|
|
6623
6623
|
if (first === void 0) return NOT_RUN;
|
|
6624
6624
|
const owner = first.meta.owner;
|
|
@@ -6647,7 +6647,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6647
6647
|
else if (status === "blocked") blocked.push(id);
|
|
6648
6648
|
else skipped.push(id);
|
|
6649
6649
|
};
|
|
6650
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6650
6651
|
for (const step of declaration.steps) {
|
|
6652
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6651
6653
|
const bound = byId.get(step.id);
|
|
6652
6654
|
if (bound === void 0 || step.action === "skip") {
|
|
6653
6655
|
skipped.push(step.id);
|
|
@@ -6825,6 +6827,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6825
6827
|
}
|
|
6826
6828
|
return out;
|
|
6827
6829
|
}
|
|
6830
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6831
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6832
|
+
"cluster",
|
|
6833
|
+
"device-defaults",
|
|
6834
|
+
"node"
|
|
6835
|
+
];
|
|
6828
6836
|
/**
|
|
6829
6837
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6830
6838
|
*
|
|
@@ -7716,6 +7724,13 @@ var BaseAddon = class {
|
|
|
7716
7724
|
*/
|
|
7717
7725
|
secretStatePaths = [];
|
|
7718
7726
|
_addonSecretTree = null;
|
|
7727
|
+
/**
|
|
7728
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7729
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7730
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7731
|
+
*/
|
|
7732
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7733
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7719
7734
|
_deviceSecretTree = null;
|
|
7720
7735
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7721
7736
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7785,6 +7800,7 @@ var BaseAddon = class {
|
|
|
7785
7800
|
this._ctx = context;
|
|
7786
7801
|
this._secretSettings = null;
|
|
7787
7802
|
}
|
|
7803
|
+
await this.loadScopedSchemas();
|
|
7788
7804
|
await this.resolveConfig();
|
|
7789
7805
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7790
7806
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7794,6 +7810,7 @@ var BaseAddon = class {
|
|
|
7794
7810
|
const normalized = normalizeAddonInitResult(result);
|
|
7795
7811
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7796
7812
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7813
|
+
await this.loadScopedSchemas();
|
|
7797
7814
|
return normalized;
|
|
7798
7815
|
}
|
|
7799
7816
|
/**
|
|
@@ -7807,14 +7824,18 @@ var BaseAddon = class {
|
|
|
7807
7824
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7808
7825
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7809
7826
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7827
|
+
*
|
|
7828
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7829
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7830
|
+
* omitted applied ids "newer").
|
|
7810
7831
|
*/
|
|
7811
|
-
async runMigrations(migrations) {
|
|
7832
|
+
async runMigrations(migrations, options) {
|
|
7812
7833
|
const api = this.ctx.api.migrations;
|
|
7813
7834
|
const report = await runAddonMigrations({
|
|
7814
7835
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7815
7836
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7816
7837
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7817
|
-
}, migrations, this.ctx.logger);
|
|
7838
|
+
}, migrations, this.ctx.logger, options);
|
|
7818
7839
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7819
7840
|
return report;
|
|
7820
7841
|
}
|
|
@@ -7878,6 +7899,16 @@ var BaseAddon = class {
|
|
|
7878
7899
|
schema(schema) {
|
|
7879
7900
|
return schema;
|
|
7880
7901
|
}
|
|
7902
|
+
/**
|
|
7903
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7904
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7905
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7906
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7907
|
+
* once known.
|
|
7908
|
+
*/
|
|
7909
|
+
scopedSettingsCapabilities() {
|
|
7910
|
+
return [];
|
|
7911
|
+
}
|
|
7881
7912
|
/** Override to provide global-level settings UI schema. */
|
|
7882
7913
|
globalSettingsSchema(_cap) {
|
|
7883
7914
|
return null;
|
|
@@ -7921,7 +7952,8 @@ var BaseAddon = class {
|
|
|
7921
7952
|
return [];
|
|
7922
7953
|
}
|
|
7923
7954
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7924
|
-
|
|
7955
|
+
await this.loadScopedSchemas(cap);
|
|
7956
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7925
7957
|
if (!schema) return { sections: [] };
|
|
7926
7958
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7927
7959
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -7956,10 +7988,9 @@ var BaseAddon = class {
|
|
|
7956
7988
|
*
|
|
7957
7989
|
* Override it only when an `instanceMode: multiple` addon's instances differ: an NVR integration
|
|
7958
7990
|
* needs its connection fields, the default integration the addon's own sections. It is the
|
|
7959
|
-
* SHAPE of the form only — the values live
|
|
7960
|
-
* unchanged (`integrations.setSettings` for an account integration).
|
|
7991
|
+
* SHAPE of the form only — the values live on the instance record.
|
|
7961
7992
|
*/
|
|
7962
|
-
async
|
|
7993
|
+
async integrationFormSchema(_instance) {
|
|
7963
7994
|
return null;
|
|
7964
7995
|
}
|
|
7965
7996
|
/**
|
|
@@ -8005,7 +8036,7 @@ var BaseAddon = class {
|
|
|
8005
8036
|
* per-instance schema when it declares one, else its global schema (what they always showed).
|
|
8006
8037
|
*/
|
|
8007
8038
|
async getIntegrationFormSchema(instance, nodeId) {
|
|
8008
|
-
const scoped = await this.
|
|
8039
|
+
const scoped = await this.integrationFormSchema(instance);
|
|
8009
8040
|
if (scoped === null) return this.getGlobalSettings(void 0, void 0, nodeId);
|
|
8010
8041
|
return hydrateSchema(scoped, {}, { inlineSecretsAsViews: true });
|
|
8011
8042
|
}
|
|
@@ -8053,6 +8084,7 @@ var BaseAddon = class {
|
|
|
8053
8084
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8054
8085
|
*/
|
|
8055
8086
|
async resolveGlobalStore(nodeId, cap) {
|
|
8087
|
+
await this.loadScopedSchemas(cap);
|
|
8056
8088
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8057
8089
|
return this.projectForNode(raw, nodeId, cap);
|
|
8058
8090
|
}
|
|
@@ -8063,6 +8095,7 @@ var BaseAddon = class {
|
|
|
8063
8095
|
}
|
|
8064
8096
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8065
8097
|
async readRawProjectedStore(nodeId, cap) {
|
|
8098
|
+
await this.loadScopedSchemas(cap);
|
|
8066
8099
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8067
8100
|
return this.projectForNode(raw, nodeId, cap);
|
|
8068
8101
|
}
|
|
@@ -8165,6 +8198,7 @@ var BaseAddon = class {
|
|
|
8165
8198
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8166
8199
|
}
|
|
8167
8200
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8201
|
+
await this.loadScopedSchemas();
|
|
8168
8202
|
const keys = this.perNodeKeys();
|
|
8169
8203
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8170
8204
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8178,7 +8212,8 @@ var BaseAddon = class {
|
|
|
8178
8212
|
await this.resolveConfig();
|
|
8179
8213
|
await this.onConfigChanged();
|
|
8180
8214
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8181
|
-
|
|
8215
|
+
const legacy = this.globalSettingsSchema();
|
|
8216
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8182
8217
|
}
|
|
8183
8218
|
/**
|
|
8184
8219
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8186,14 +8221,67 @@ var BaseAddon = class {
|
|
|
8186
8221
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8187
8222
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8188
8223
|
*/
|
|
8189
|
-
|
|
8224
|
+
/**
|
|
8225
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8226
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8227
|
+
* derived from the previous set. Idempotent.
|
|
8228
|
+
*/
|
|
8229
|
+
async loadScopedSchemas(extraCap) {
|
|
8230
|
+
const caps = new Set([
|
|
8231
|
+
...this.scopedSettingsCapabilities(),
|
|
8232
|
+
...this._registeredCapNames,
|
|
8233
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8234
|
+
]);
|
|
8235
|
+
let added = false;
|
|
8236
|
+
for (const cap of caps) {
|
|
8237
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8238
|
+
const loaded = [];
|
|
8239
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8240
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8241
|
+
if (schema !== null) loaded.push({
|
|
8242
|
+
scope,
|
|
8243
|
+
schema
|
|
8244
|
+
});
|
|
8245
|
+
}
|
|
8246
|
+
this._scopedByCap.set(cap, loaded);
|
|
8247
|
+
added = true;
|
|
8248
|
+
}
|
|
8249
|
+
if (!added) return;
|
|
8250
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8251
|
+
this._addonSecretTree = null;
|
|
8252
|
+
}
|
|
8253
|
+
/**
|
|
8254
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8255
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8256
|
+
*/
|
|
8257
|
+
scopedFormSchema(cap) {
|
|
8258
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8259
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8260
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8261
|
+
}
|
|
8262
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8263
|
+
loadedScopedSchemas(cap) {
|
|
8264
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8265
|
+
return [...this._scopedByCap.values()].flat();
|
|
8266
|
+
}
|
|
8267
|
+
/**
|
|
8268
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8269
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8270
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8271
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8272
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8273
|
+
*/
|
|
8190
8274
|
perNodeKeys(cap) {
|
|
8191
8275
|
const cacheKey = cap ?? "";
|
|
8192
|
-
const cached = this.
|
|
8276
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8193
8277
|
if (cached) return cached;
|
|
8194
|
-
const
|
|
8195
|
-
const keys = new Set(
|
|
8196
|
-
this.
|
|
8278
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8279
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8280
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8281
|
+
if (scope !== "node") continue;
|
|
8282
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8283
|
+
}
|
|
8284
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8197
8285
|
return keys;
|
|
8198
8286
|
}
|
|
8199
8287
|
/**
|
|
@@ -8203,10 +8291,9 @@ var BaseAddon = class {
|
|
|
8203
8291
|
* time to return its response before the addon is torn down and
|
|
8204
8292
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8205
8293
|
*/
|
|
8206
|
-
maybeAutoRestart(patchKeys,
|
|
8207
|
-
if (!schema) return;
|
|
8294
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8208
8295
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8209
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8296
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8210
8297
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8211
8298
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8212
8299
|
}
|
|
@@ -8402,12 +8489,14 @@ var BaseAddon = class {
|
|
|
8402
8489
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8403
8490
|
*/
|
|
8404
8491
|
async getDeclaredSettingsKeys() {
|
|
8492
|
+
await this.loadScopedSchemas();
|
|
8405
8493
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8406
8494
|
}
|
|
8407
8495
|
declaredKeySet() {
|
|
8408
8496
|
const keys = new Set(Object.keys(this.defaults));
|
|
8409
8497
|
const schema = this.globalSettingsSchema();
|
|
8410
8498
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8499
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8411
8500
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8412
8501
|
for (const path of this.secretStatePaths) {
|
|
8413
8502
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8503,7 +8592,11 @@ var BaseAddon = class {
|
|
|
8503
8592
|
}
|
|
8504
8593
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8505
8594
|
addonSecretTree() {
|
|
8506
|
-
this._addonSecretTree
|
|
8595
|
+
if (this._addonSecretTree === null) {
|
|
8596
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8597
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8598
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8599
|
+
}
|
|
8507
8600
|
return this._addonSecretTree;
|
|
8508
8601
|
}
|
|
8509
8602
|
deviceSecretTree() {
|
|
@@ -8753,6 +8846,23 @@ function dropPerNodeFields(fields) {
|
|
|
8753
8846
|
}
|
|
8754
8847
|
return kept;
|
|
8755
8848
|
}
|
|
8849
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8850
|
+
function collectValueFieldKeys(fields) {
|
|
8851
|
+
const collected = [];
|
|
8852
|
+
for (const field of fields) {
|
|
8853
|
+
if (field.type === "group") {
|
|
8854
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8855
|
+
continue;
|
|
8856
|
+
}
|
|
8857
|
+
if (field.type === "sub-tabs") {
|
|
8858
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8859
|
+
continue;
|
|
8860
|
+
}
|
|
8861
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8862
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8863
|
+
}
|
|
8864
|
+
return collected;
|
|
8865
|
+
}
|
|
8756
8866
|
function collectFieldKeys(fields) {
|
|
8757
8867
|
const collected = [];
|
|
8758
8868
|
for (const field of fields) {
|
|
@@ -9286,6 +9396,7 @@ function defineEntryKind(name, spec) {
|
|
|
9286
9396
|
class: "config",
|
|
9287
9397
|
writeTrigger: spec.writeTrigger ?? "an operator adds or edits an entry in the entries UI",
|
|
9288
9398
|
retention: { none: "an entry goes only when the operator deletes it" },
|
|
9399
|
+
...spec.maxDataBytes === void 0 ? {} : { maxDataBytes: spec.maxDataBytes },
|
|
9289
9400
|
...spec.database === void 0 ? {} : { database: spec.database },
|
|
9290
9401
|
...spec.indexes === void 0 ? {} : { indexes: spec.indexes },
|
|
9291
9402
|
...spec.secretArrayKeys === void 0 ? {} : { secretArrayKeys: spec.secretArrayKeys },
|
|
@@ -11965,7 +12076,7 @@ function userError(code, params, englishFallback) {
|
|
|
11965
12076
|
return new CamstackError(code, params, englishFallback);
|
|
11966
12077
|
}
|
|
11967
12078
|
//#endregion
|
|
11968
|
-
//#region ../types/dist/composition-
|
|
12079
|
+
//#region ../types/dist/composition-pOHbTzRB.mjs
|
|
11969
12080
|
/**
|
|
11970
12081
|
* The ffmpeg build every CamStack node downloads, per platform and arch.
|
|
11971
12082
|
*
|
|
@@ -13454,12 +13565,22 @@ var RecordIntegrationInstances = class {
|
|
|
13454
13565
|
...this.hooks.description,
|
|
13455
13566
|
mintsId: true
|
|
13456
13567
|
};
|
|
13457
|
-
|
|
13568
|
+
/**
|
|
13569
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13570
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13571
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13572
|
+
*/
|
|
13573
|
+
async resolveRecordId(id) {
|
|
13574
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13575
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13576
|
+
return linked === void 0 ? id : linked.id;
|
|
13577
|
+
}
|
|
13578
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13458
13579
|
listInstances = async () => {
|
|
13459
13580
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13460
13581
|
};
|
|
13461
13582
|
getInstance = async ({ id }) => {
|
|
13462
|
-
const record = await this.handle.get(id);
|
|
13583
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13463
13584
|
return record === null ? null : this.viewOf(record);
|
|
13464
13585
|
};
|
|
13465
13586
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13476,7 +13597,8 @@ var RecordIntegrationInstances = class {
|
|
|
13476
13597
|
await this.hooks.onChanged?.(minted, "created");
|
|
13477
13598
|
return { id: minted };
|
|
13478
13599
|
};
|
|
13479
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13600
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13601
|
+
const id = await this.resolveRecordId(askedId);
|
|
13480
13602
|
const patch = {};
|
|
13481
13603
|
for (const [key, value] of Object.entries(values)) {
|
|
13482
13604
|
if (value === void 0) continue;
|
|
@@ -13511,7 +13633,10 @@ var RecordIntegrationInstances = class {
|
|
|
13511
13633
|
status: "unsupported",
|
|
13512
13634
|
error: "This integration declares no connection test"
|
|
13513
13635
|
};
|
|
13514
|
-
return this.hooks.test(input
|
|
13636
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13637
|
+
...input,
|
|
13638
|
+
id: await this.resolveRecordId(input.id)
|
|
13639
|
+
});
|
|
13515
13640
|
};
|
|
13516
13641
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13517
13642
|
toProvider() {
|
|
@@ -14573,7 +14698,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14573
14698
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14574
14699
|
* catalogue lists the id (a newer database).
|
|
14575
14700
|
*/
|
|
14576
|
-
meta: MigrationMetaSchema.optional()
|
|
14701
|
+
meta: MigrationMetaSchema.optional(),
|
|
14702
|
+
/**
|
|
14703
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14704
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14705
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14706
|
+
*/
|
|
14707
|
+
ownerNotLoaded: boolean().optional()
|
|
14577
14708
|
});
|
|
14578
14709
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14579
14710
|
var MigrationPendingEntrySchema = object({
|
|
@@ -49113,6 +49244,9 @@ var RunningProposalEvidenceSchema = object({
|
|
|
49113
49244
|
runsFound: number().int().nonnegative(),
|
|
49114
49245
|
pausesFound: number().int().nonnegative(),
|
|
49115
49246
|
longestPauseMs: number().nonnegative(),
|
|
49247
|
+
/** Episodes shorter than ~90 s (an anti-crease pulse, a display waking): never a run, never a pause. */
|
|
49248
|
+
pulsesFound: number().int().nonnegative(),
|
|
49249
|
+
longestPulseMs: number().nonnegative(),
|
|
49116
49250
|
idleFloorW: number(),
|
|
49117
49251
|
noiseCeilingW: number(),
|
|
49118
49252
|
plateauW: number(),
|
|
@@ -54034,11 +54168,7 @@ var CreateIntegrationInputSchema = object({
|
|
|
54034
54168
|
info: record(string(), unknown()).optional(),
|
|
54035
54169
|
/** `stableId` of the discovery candidate the form was seeded from (D984). */
|
|
54036
54170
|
originStableId: string().optional(),
|
|
54037
|
-
settings: record(string(), unknown()).optional()
|
|
54038
|
-
/** Keys of `settings` the form's schema declares secret (by type) — kept in
|
|
54039
|
-
* the keychain, not the table (D787). */
|
|
54040
|
-
secretKeys: array(string()).optional(),
|
|
54041
|
-
skipRestart: boolean().optional()
|
|
54171
|
+
settings: record(string(), unknown()).optional()
|
|
54042
54172
|
});
|
|
54043
54173
|
var UpdateIntegrationInputSchema = object({
|
|
54044
54174
|
id: string(),
|
|
@@ -54059,16 +54189,6 @@ method(_void(), array(IntegrationWithStateSchema)), method(object({ id: string()
|
|
|
54059
54189
|
}), method(object({ id: string() }), DeleteIntegrationResultSchema, {
|
|
54060
54190
|
kind: "mutation",
|
|
54061
54191
|
auth: "admin"
|
|
54062
|
-
}), method(object({ id: string() }), record(string(), unknown()), { auth: "admin" }), method(object({
|
|
54063
|
-
id: string(),
|
|
54064
|
-
settings: record(string(), unknown()),
|
|
54065
|
-
/** Keys the form's schema declares secret (by type) — keychain-stored (D787). */
|
|
54066
|
-
secretKeys: array(string()).optional(),
|
|
54067
|
-
/** The integration's own addon applies the change in-process and asks the hub not to respawn it. */
|
|
54068
|
-
skipRestart: boolean().optional()
|
|
54069
|
-
}), object({ success: boolean() }), {
|
|
54070
|
-
kind: "mutation",
|
|
54071
|
-
auth: "admin"
|
|
54072
54192
|
}), method(_void(), array(AvailableIntegrationTypeSchema), { auth: "admin" }), method(object({
|
|
54073
54193
|
addonId: string(),
|
|
54074
54194
|
settings: record(string(), unknown())
|
|
@@ -56793,6 +56913,27 @@ method(object({}), object({ entries: array(MigrationLedgerEntrySchema) }), { aut
|
|
|
56793
56913
|
}), {
|
|
56794
56914
|
kind: "mutation",
|
|
56795
56915
|
auth: "admin"
|
|
56916
|
+
}), method(object({ integrationId: string().min(1) }), record(string(), unknown()), { auth: "admin" }), method(object({ integrationId: string().min(1) }), object({
|
|
56917
|
+
rows: number().int().nonnegative(),
|
|
56918
|
+
secrets: number().int().nonnegative()
|
|
56919
|
+
}), {
|
|
56920
|
+
kind: "mutation",
|
|
56921
|
+
auth: "admin"
|
|
56922
|
+
}), method(object({
|
|
56923
|
+
integrationId: string().min(1),
|
|
56924
|
+
patch: record(string(), unknown())
|
|
56925
|
+
}), object({ written: boolean() }), {
|
|
56926
|
+
kind: "mutation",
|
|
56927
|
+
auth: "admin"
|
|
56928
|
+
}), method(object({
|
|
56929
|
+
integrationId: string().min(1),
|
|
56930
|
+
keys: array(string().min(1))
|
|
56931
|
+
}), object({
|
|
56932
|
+
present: boolean(),
|
|
56933
|
+
removed: record(string(), unknown())
|
|
56934
|
+
}), {
|
|
56935
|
+
kind: "mutation",
|
|
56936
|
+
auth: "admin"
|
|
56796
56937
|
});
|
|
56797
56938
|
method(object({
|
|
56798
56939
|
sourceUrl: string(),
|
|
@@ -75967,11 +76108,15 @@ discriminatedUnion("kind", [object({
|
|
|
75967
76108
|
* 1. idle floor F = time-weighted median; noise ceiling N = highest value seen
|
|
75968
76109
|
* near F. A device that is not near one level most of the time has no floor.
|
|
75969
76110
|
* 2. coarse activity = above max(2N, N + 5 W); consecutive stretches are episodes.
|
|
75970
|
-
* 3.
|
|
75971
|
-
*
|
|
76111
|
+
* 3. an episode shorter than `PULSE_MAX_MS` is a PULSE (an anti-crease tumble, a
|
|
76112
|
+
* display waking): it is neither a run nor a pause. Among the remaining
|
|
76113
|
+
* episodes, gaps over `PAUSE_MAX_MS` always separate runs; the rest split in
|
|
76114
|
+
* two by the largest log-scale jump (>= 3x): the short ones are pauses INSIDE
|
|
76115
|
+
* a run (a soak), the long ones separate runs. A stretch under `MIN_RUN_MS`
|
|
76116
|
+
* is no run.
|
|
75972
76117
|
* 4. T = geometric mean of N and the plateau's 10th percentile, snapped to a
|
|
75973
|
-
* grid a person would type; stop dwell covers the longest pause
|
|
75974
|
-
* covers the longest standby blip above T.
|
|
76118
|
+
* grid a person would type; stop dwell covers the longest pause and the
|
|
76119
|
+
* kind's floor; start dwell covers the longest pulse and standby blip above T.
|
|
75975
76120
|
* 5. the numbers are proved by REPLAY through the real `dwell` evaluation: each
|
|
75976
76121
|
* run must hold exactly one true interval and nothing may flap outside.
|
|
75977
76122
|
*/
|
|
@@ -75982,6 +76127,10 @@ var MINUTE_MS$1 = 60 * SECOND_MS;
|
|
|
75982
76127
|
30 * SECOND_MS;
|
|
75983
76128
|
60 * SECOND_MS;
|
|
75984
76129
|
20 * MINUTE_MS$1;
|
|
76130
|
+
90 * SECOND_MS;
|
|
76131
|
+
30 * MINUTE_MS$1;
|
|
76132
|
+
10 * MINUTE_MS$1;
|
|
76133
|
+
DeviceType.Dryer, 3 * MINUTE_MS$1, DeviceType.Dishwasher, 3 * MINUTE_MS$1, DeviceType.Washer, 10 * MINUTE_MS$1;
|
|
75985
76134
|
10 * SECOND_MS;
|
|
75986
76135
|
10 * SECOND_MS;
|
|
75987
76136
|
DeviceFeature.FanSpeed, DeviceFeature.FanPreset, DeviceFeature.FanDirection, DeviceFeature.FanOscillating, DeviceFeature.CoverStoppable, DeviceFeature.CoverPositionable, DeviceFeature.CoverTilt, DeviceFeature.ValvePositionable, DeviceFeature.MediaPlayerVolume, DeviceFeature.ClimateFanMode;
|
|
@@ -79737,13 +79886,6 @@ Object.freeze({
|
|
|
79737
79886
|
access: "view",
|
|
79738
79887
|
role: "admin"
|
|
79739
79888
|
},
|
|
79740
|
-
"integrations.getSettings": {
|
|
79741
|
-
capName: "integrations",
|
|
79742
|
-
capScope: "system",
|
|
79743
|
-
addonId: null,
|
|
79744
|
-
access: "view",
|
|
79745
|
-
role: "admin"
|
|
79746
|
-
},
|
|
79747
79889
|
"integrations.list": {
|
|
79748
79890
|
capName: "integrations",
|
|
79749
79891
|
capScope: "system",
|
|
@@ -79751,13 +79893,6 @@ Object.freeze({
|
|
|
79751
79893
|
access: "view",
|
|
79752
79894
|
role: "admin"
|
|
79753
79895
|
},
|
|
79754
|
-
"integrations.setSettings": {
|
|
79755
|
-
capName: "integrations",
|
|
79756
|
-
capScope: "system",
|
|
79757
|
-
addonId: null,
|
|
79758
|
-
access: "create",
|
|
79759
|
-
role: "admin"
|
|
79760
|
-
},
|
|
79761
79896
|
"integrations.testConnection": {
|
|
79762
79897
|
capName: "integrations",
|
|
79763
79898
|
capScope: "system",
|
|
@@ -80752,6 +80887,13 @@ Object.freeze({
|
|
|
80752
80887
|
access: "view",
|
|
80753
80888
|
role: "admin"
|
|
80754
80889
|
},
|
|
80890
|
+
"migrations.clearLegacyIntegrationRows": {
|
|
80891
|
+
capName: "migrations",
|
|
80892
|
+
capScope: "system",
|
|
80893
|
+
addonId: null,
|
|
80894
|
+
access: "delete",
|
|
80895
|
+
role: "admin"
|
|
80896
|
+
},
|
|
80755
80897
|
"migrations.declareOwner": {
|
|
80756
80898
|
capName: "migrations",
|
|
80757
80899
|
capScope: "system",
|
|
@@ -80759,6 +80901,13 @@ Object.freeze({
|
|
|
80759
80901
|
access: "create",
|
|
80760
80902
|
role: "admin"
|
|
80761
80903
|
},
|
|
80904
|
+
"migrations.deleteLegacyIntegrationRowKeys": {
|
|
80905
|
+
capName: "migrations",
|
|
80906
|
+
capScope: "system",
|
|
80907
|
+
addonId: null,
|
|
80908
|
+
access: "delete",
|
|
80909
|
+
role: "admin"
|
|
80910
|
+
},
|
|
80762
80911
|
"migrations.dryRun": {
|
|
80763
80912
|
capName: "migrations",
|
|
80764
80913
|
capScope: "system",
|
|
@@ -80794,6 +80943,20 @@ Object.freeze({
|
|
|
80794
80943
|
access: "create",
|
|
80795
80944
|
role: "admin"
|
|
80796
80945
|
},
|
|
80946
|
+
"migrations.patchLegacyIntegrationRows": {
|
|
80947
|
+
capName: "migrations",
|
|
80948
|
+
capScope: "system",
|
|
80949
|
+
addonId: null,
|
|
80950
|
+
access: "create",
|
|
80951
|
+
role: "admin"
|
|
80952
|
+
},
|
|
80953
|
+
"migrations.readLegacyIntegrationRows": {
|
|
80954
|
+
capName: "migrations",
|
|
80955
|
+
capScope: "system",
|
|
80956
|
+
addonId: null,
|
|
80957
|
+
access: "view",
|
|
80958
|
+
role: "admin"
|
|
80959
|
+
},
|
|
80797
80960
|
"migrations.restorePreMigration": {
|
|
80798
80961
|
capName: "migrations",
|
|
80799
80962
|
capScope: "system",
|
|
@@ -145844,7 +146007,8 @@ var SmtpNodemailerAddon = class extends BaseAddon {
|
|
|
145844
146007
|
const rows = (await records.kind(smtpRelaysKind).query({ limit: RELAY_LIST_MAX })).records;
|
|
145845
146008
|
return (rows.find((row) => row.data.host.length > 0) ?? rows[0])?.id;
|
|
145846
146009
|
}
|
|
145847
|
-
|
|
146010
|
+
/** The relay form. Its values live on the relay record, never in `addon-settings`. */
|
|
146011
|
+
async integrationFormSchema(_instance) {
|
|
145848
146012
|
return this.relayForm();
|
|
145849
146013
|
}
|
|
145850
146014
|
async getGlobalSettings() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-smtp-nodemailer",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.148",
|
|
4
4
|
"description": "SMTP email provider addon for CamStack — wraps `nodemailer` and registers a `smtp-provider` cap collection entry. Used by notifier addons.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"smtp-server"
|
|
60
60
|
],
|
|
61
61
|
"requires": {
|
|
62
|
-
"server": ">=1.2.
|
|
62
|
+
"server": ">=1.2.777"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"files": [
|