@camstack/addon-provider-bosch 0.1.37 → 0.1.38
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/{account-integration-offline-VItxeiJg.js → account-integration-offline-CeMaWElV.js} +1 -1
- package/dist/{account-integration-offline-nChgZUlC.mjs → account-integration-offline-DhHNdL3k.mjs} +1 -1
- package/dist/{bosch-instances-BQiyzlPt.mjs → bosch-instances-CIuDwXQ8.mjs} +162 -20
- package/dist/{bosch-instances-CO8yIFzJ.js → bosch-instances-Y9sMCElY.js} +162 -20
- package/dist/home-connect/addon.js +9 -4
- package/dist/home-connect/addon.mjs +9 -4
- package/dist/homecom/addon.js +9 -4
- package/dist/homecom/addon.mjs +9 -4
- package/dist/shc/addon.js +1 -1
- package/dist/shc/addon.mjs +1 -1
- 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
|
+
## [0.1.38] - 2026-11-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Bosch sign-in through the hub: proxy the SingleKey ID script host so the login page shows its password step instead of only the email field
|
|
15
|
+
|
|
10
16
|
## [0.1.37] - 2026-11-05
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/{account-integration-offline-VItxeiJg.js → account-integration-offline-CeMaWElV.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_bosch_instances = require("./bosch-instances-
|
|
1
|
+
const require_bosch_instances = require("./bosch-instances-Y9sMCElY.js");
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
3
|
//#region src/shared/oauth-callback.ts
|
|
4
4
|
/** A code on its own: no scheme, no query, no separators. */
|
package/dist/{account-integration-offline-nChgZUlC.mjs → account-integration-offline-DhHNdL3k.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as errMsg } from "./bosch-instances-
|
|
1
|
+
import { $ as errMsg } from "./bosch-instances-CIuDwXQ8.mjs";
|
|
2
2
|
import { randomBytes } from "node:crypto";
|
|
3
3
|
//#region src/shared/oauth-callback.ts
|
|
4
4
|
/** A code on its own: no scheme, no query, no separators. */
|
|
@@ -5981,7 +5981,7 @@ var ZodIssueCode = {
|
|
|
5981
5981
|
var ZodFirstPartyTypeKind;
|
|
5982
5982
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5983
5983
|
//#endregion
|
|
5984
|
-
//#region ../types/dist/sleep-
|
|
5984
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5985
5985
|
/**
|
|
5986
5986
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5987
5987
|
* window to float samples (D455).
|
|
@@ -6790,6 +6790,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6790
6790
|
}
|
|
6791
6791
|
return out;
|
|
6792
6792
|
}
|
|
6793
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6794
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6795
|
+
"cluster",
|
|
6796
|
+
"device-defaults",
|
|
6797
|
+
"node"
|
|
6798
|
+
];
|
|
6793
6799
|
/**
|
|
6794
6800
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6795
6801
|
*
|
|
@@ -7681,6 +7687,13 @@ var BaseAddon = class {
|
|
|
7681
7687
|
*/
|
|
7682
7688
|
secretStatePaths = [];
|
|
7683
7689
|
_addonSecretTree = null;
|
|
7690
|
+
/**
|
|
7691
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7692
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7693
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7694
|
+
*/
|
|
7695
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7696
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7684
7697
|
_deviceSecretTree = null;
|
|
7685
7698
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7686
7699
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7750,6 +7763,7 @@ var BaseAddon = class {
|
|
|
7750
7763
|
this._ctx = context;
|
|
7751
7764
|
this._secretSettings = null;
|
|
7752
7765
|
}
|
|
7766
|
+
await this.loadScopedSchemas();
|
|
7753
7767
|
await this.resolveConfig();
|
|
7754
7768
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7755
7769
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7759,6 +7773,7 @@ var BaseAddon = class {
|
|
|
7759
7773
|
const normalized = normalizeAddonInitResult(result);
|
|
7760
7774
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7761
7775
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7776
|
+
await this.loadScopedSchemas();
|
|
7762
7777
|
return normalized;
|
|
7763
7778
|
}
|
|
7764
7779
|
/**
|
|
@@ -7847,6 +7862,16 @@ var BaseAddon = class {
|
|
|
7847
7862
|
schema(schema) {
|
|
7848
7863
|
return schema;
|
|
7849
7864
|
}
|
|
7865
|
+
/**
|
|
7866
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7867
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7868
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7869
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7870
|
+
* once known.
|
|
7871
|
+
*/
|
|
7872
|
+
scopedSettingsCapabilities() {
|
|
7873
|
+
return [];
|
|
7874
|
+
}
|
|
7850
7875
|
/** Override to provide global-level settings UI schema. */
|
|
7851
7876
|
globalSettingsSchema(_cap) {
|
|
7852
7877
|
return null;
|
|
@@ -7890,7 +7915,8 @@ var BaseAddon = class {
|
|
|
7890
7915
|
return [];
|
|
7891
7916
|
}
|
|
7892
7917
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7893
|
-
|
|
7918
|
+
await this.loadScopedSchemas(cap);
|
|
7919
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7894
7920
|
if (!schema) return { sections: [] };
|
|
7895
7921
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7896
7922
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8021,6 +8047,7 @@ var BaseAddon = class {
|
|
|
8021
8047
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8022
8048
|
*/
|
|
8023
8049
|
async resolveGlobalStore(nodeId, cap) {
|
|
8050
|
+
await this.loadScopedSchemas(cap);
|
|
8024
8051
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8025
8052
|
return this.projectForNode(raw, nodeId, cap);
|
|
8026
8053
|
}
|
|
@@ -8031,6 +8058,7 @@ var BaseAddon = class {
|
|
|
8031
8058
|
}
|
|
8032
8059
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8033
8060
|
async readRawProjectedStore(nodeId, cap) {
|
|
8061
|
+
await this.loadScopedSchemas(cap);
|
|
8034
8062
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8035
8063
|
return this.projectForNode(raw, nodeId, cap);
|
|
8036
8064
|
}
|
|
@@ -8133,6 +8161,7 @@ var BaseAddon = class {
|
|
|
8133
8161
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8134
8162
|
}
|
|
8135
8163
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8164
|
+
await this.loadScopedSchemas();
|
|
8136
8165
|
const keys = this.perNodeKeys();
|
|
8137
8166
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8138
8167
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8146,7 +8175,8 @@ var BaseAddon = class {
|
|
|
8146
8175
|
await this.resolveConfig();
|
|
8147
8176
|
await this.onConfigChanged();
|
|
8148
8177
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8149
|
-
|
|
8178
|
+
const legacy = this.globalSettingsSchema();
|
|
8179
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8150
8180
|
}
|
|
8151
8181
|
/**
|
|
8152
8182
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8154,14 +8184,67 @@ var BaseAddon = class {
|
|
|
8154
8184
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8155
8185
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8156
8186
|
*/
|
|
8157
|
-
|
|
8187
|
+
/**
|
|
8188
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8189
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8190
|
+
* derived from the previous set. Idempotent.
|
|
8191
|
+
*/
|
|
8192
|
+
async loadScopedSchemas(extraCap) {
|
|
8193
|
+
const caps = new Set([
|
|
8194
|
+
...this.scopedSettingsCapabilities(),
|
|
8195
|
+
...this._registeredCapNames,
|
|
8196
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8197
|
+
]);
|
|
8198
|
+
let added = false;
|
|
8199
|
+
for (const cap of caps) {
|
|
8200
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8201
|
+
const loaded = [];
|
|
8202
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8203
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8204
|
+
if (schema !== null) loaded.push({
|
|
8205
|
+
scope,
|
|
8206
|
+
schema
|
|
8207
|
+
});
|
|
8208
|
+
}
|
|
8209
|
+
this._scopedByCap.set(cap, loaded);
|
|
8210
|
+
added = true;
|
|
8211
|
+
}
|
|
8212
|
+
if (!added) return;
|
|
8213
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8214
|
+
this._addonSecretTree = null;
|
|
8215
|
+
}
|
|
8216
|
+
/**
|
|
8217
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8218
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8219
|
+
*/
|
|
8220
|
+
scopedFormSchema(cap) {
|
|
8221
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8222
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8223
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8224
|
+
}
|
|
8225
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8226
|
+
loadedScopedSchemas(cap) {
|
|
8227
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8228
|
+
return [...this._scopedByCap.values()].flat();
|
|
8229
|
+
}
|
|
8230
|
+
/**
|
|
8231
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8232
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8233
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8234
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8235
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8236
|
+
*/
|
|
8158
8237
|
perNodeKeys(cap) {
|
|
8159
8238
|
const cacheKey = cap ?? "";
|
|
8160
|
-
const cached = this.
|
|
8239
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8161
8240
|
if (cached) return cached;
|
|
8162
|
-
const
|
|
8163
|
-
const keys = new Set(
|
|
8164
|
-
this.
|
|
8241
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8242
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8243
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8244
|
+
if (scope !== "node") continue;
|
|
8245
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8246
|
+
}
|
|
8247
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8165
8248
|
return keys;
|
|
8166
8249
|
}
|
|
8167
8250
|
/**
|
|
@@ -8171,10 +8254,9 @@ var BaseAddon = class {
|
|
|
8171
8254
|
* time to return its response before the addon is torn down and
|
|
8172
8255
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8173
8256
|
*/
|
|
8174
|
-
maybeAutoRestart(patchKeys,
|
|
8175
|
-
if (!schema) return;
|
|
8257
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8176
8258
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8177
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8259
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8178
8260
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8179
8261
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8180
8262
|
}
|
|
@@ -8370,12 +8452,14 @@ var BaseAddon = class {
|
|
|
8370
8452
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8371
8453
|
*/
|
|
8372
8454
|
async getDeclaredSettingsKeys() {
|
|
8455
|
+
await this.loadScopedSchemas();
|
|
8373
8456
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8374
8457
|
}
|
|
8375
8458
|
declaredKeySet() {
|
|
8376
8459
|
const keys = new Set(Object.keys(this.defaults));
|
|
8377
8460
|
const schema = this.globalSettingsSchema();
|
|
8378
8461
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8462
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8379
8463
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8380
8464
|
for (const path of this.secretStatePaths) {
|
|
8381
8465
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8471,7 +8555,11 @@ var BaseAddon = class {
|
|
|
8471
8555
|
}
|
|
8472
8556
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8473
8557
|
addonSecretTree() {
|
|
8474
|
-
this._addonSecretTree
|
|
8558
|
+
if (this._addonSecretTree === null) {
|
|
8559
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8560
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8561
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8562
|
+
}
|
|
8475
8563
|
return this._addonSecretTree;
|
|
8476
8564
|
}
|
|
8477
8565
|
deviceSecretTree() {
|
|
@@ -8721,6 +8809,23 @@ function dropPerNodeFields(fields) {
|
|
|
8721
8809
|
}
|
|
8722
8810
|
return kept;
|
|
8723
8811
|
}
|
|
8812
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8813
|
+
function collectValueFieldKeys(fields) {
|
|
8814
|
+
const collected = [];
|
|
8815
|
+
for (const field of fields) {
|
|
8816
|
+
if (field.type === "group") {
|
|
8817
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8818
|
+
continue;
|
|
8819
|
+
}
|
|
8820
|
+
if (field.type === "sub-tabs") {
|
|
8821
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8822
|
+
continue;
|
|
8823
|
+
}
|
|
8824
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8825
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8826
|
+
}
|
|
8827
|
+
return collected;
|
|
8828
|
+
}
|
|
8724
8829
|
function collectFieldKeys(fields) {
|
|
8725
8830
|
const collected = [];
|
|
8726
8831
|
for (const field of fields) {
|
|
@@ -13427,12 +13532,22 @@ var RecordIntegrationInstances = class {
|
|
|
13427
13532
|
...this.hooks.description,
|
|
13428
13533
|
mintsId: true
|
|
13429
13534
|
};
|
|
13430
|
-
|
|
13535
|
+
/**
|
|
13536
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13537
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13538
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13539
|
+
*/
|
|
13540
|
+
async resolveRecordId(id) {
|
|
13541
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13542
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13543
|
+
return linked === void 0 ? id : linked.id;
|
|
13544
|
+
}
|
|
13545
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13431
13546
|
listInstances = async () => {
|
|
13432
13547
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13433
13548
|
};
|
|
13434
13549
|
getInstance = async ({ id }) => {
|
|
13435
|
-
const record = await this.handle.get(id);
|
|
13550
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13436
13551
|
return record === null ? null : this.viewOf(record);
|
|
13437
13552
|
};
|
|
13438
13553
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13449,7 +13564,8 @@ var RecordIntegrationInstances = class {
|
|
|
13449
13564
|
await this.hooks.onChanged?.(minted, "created");
|
|
13450
13565
|
return { id: minted };
|
|
13451
13566
|
};
|
|
13452
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13567
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13568
|
+
const id = await this.resolveRecordId(askedId);
|
|
13453
13569
|
const patch = {};
|
|
13454
13570
|
for (const [key, value] of Object.entries(values)) {
|
|
13455
13571
|
if (value === void 0) continue;
|
|
@@ -13484,7 +13600,10 @@ var RecordIntegrationInstances = class {
|
|
|
13484
13600
|
status: "unsupported",
|
|
13485
13601
|
error: "This integration declares no connection test"
|
|
13486
13602
|
};
|
|
13487
|
-
return this.hooks.test(input
|
|
13603
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13604
|
+
...input,
|
|
13605
|
+
id: await this.resolveRecordId(input.id)
|
|
13606
|
+
});
|
|
13488
13607
|
};
|
|
13489
13608
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13490
13609
|
toProvider() {
|
|
@@ -51995,7 +52114,14 @@ var DiscoveryCandidateSchema = object({
|
|
|
51995
52114
|
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
51996
52115
|
parentStableId: string().optional(),
|
|
51997
52116
|
/** `false` = shown for context but not pickable. Default true. */
|
|
51998
|
-
selectable: boolean().optional()
|
|
52117
|
+
selectable: boolean().optional(),
|
|
52118
|
+
/**
|
|
52119
|
+
* Keys of the provider's creation form (`getChildCreationSchema`) this candidate
|
|
52120
|
+
* cannot be adopted without — a key for an encrypted board, a pairing code. The
|
|
52121
|
+
* admin asks for exactly these, on the creation form prefilled with
|
|
52122
|
+
* `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
|
|
52123
|
+
*/
|
|
52124
|
+
needsParams: array(string()).optional()
|
|
51999
52125
|
});
|
|
52000
52126
|
/**
|
|
52001
52127
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -52123,7 +52249,11 @@ var deviceProviderCapability = {
|
|
|
52123
52249
|
* pick the right type instead of assuming Camera.
|
|
52124
52250
|
*/
|
|
52125
52251
|
getManualCreationType: method(object({}), object({ deviceType: _enum(DeviceType).nullable() })),
|
|
52126
|
-
adoptDiscoveredDevice: method(object({
|
|
52252
|
+
adoptDiscoveredDevice: method(object({
|
|
52253
|
+
candidate: DiscoveryCandidateSchema,
|
|
52254
|
+
/** The integration the operator adopts into; a provider with several instances needs it. */
|
|
52255
|
+
integrationId: string().optional()
|
|
52256
|
+
}), DeviceSummarySchema, {
|
|
52127
52257
|
kind: "mutation",
|
|
52128
52258
|
auth: "admin"
|
|
52129
52259
|
}),
|
|
@@ -52137,7 +52267,9 @@ var deviceProviderCapability = {
|
|
|
52137
52267
|
getChildCreationSchema: method(object({ type: _enum(DeviceType) }), CreationSchemaOutputSchema),
|
|
52138
52268
|
createDevice: method(object({
|
|
52139
52269
|
type: _enum(DeviceType),
|
|
52140
|
-
config: record(string(), unknown())
|
|
52270
|
+
config: record(string(), unknown()),
|
|
52271
|
+
/** The integration the operator adds the device to; a provider with several instances needs it. */
|
|
52272
|
+
integrationId: string().optional()
|
|
52141
52273
|
}), DeviceSummarySchema, {
|
|
52142
52274
|
kind: "mutation",
|
|
52143
52275
|
auth: "admin"
|
|
@@ -81826,7 +81958,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
81826
81958
|
* `deviceClasses` instead.
|
|
81827
81959
|
*/
|
|
81828
81960
|
async createDevice(input) {
|
|
81829
|
-
const spec = await this.onCreateDevice(input.type, input.config);
|
|
81961
|
+
const spec = await this.onCreateDevice(input.type, input.config, { ...input.integrationId !== void 0 ? { integrationId: input.integrationId } : {} });
|
|
81830
81962
|
const Class = this.deviceClasses[spec.meta.type];
|
|
81831
81963
|
if (!Class) throw new Error(`${this.providerName} provider: no device class registered for type "${spec.meta.type}" — add it to the deviceClasses map`);
|
|
81832
81964
|
const stableId = this.generateStableId(spec.meta.type, spec.config);
|
|
@@ -97436,6 +97568,15 @@ function rewriteLocation(location, currentUpstream, route) {
|
|
|
97436
97568
|
/** Characters that may precede a URL that STARTS a value (never `=`, `?`, `&`, `%3D`). */
|
|
97437
97569
|
var VALUE_START = String.raw`(?<=^|["'(\s>,\[{])`;
|
|
97438
97570
|
/**
|
|
97571
|
+
* Subresource Integrity pins the bytes of a script or stylesheet. The proxy rewrites vendor
|
|
97572
|
+
* URLs inside those bytes (and re-serves them from another origin), so the hash can no longer
|
|
97573
|
+
* match and the browser refuses the file: the page renders with no script at all. The hub
|
|
97574
|
+
* already drops the CSP that would have enforced the same pin; the attribute goes with it.
|
|
97575
|
+
*/
|
|
97576
|
+
function stripIntegrity(html) {
|
|
97577
|
+
return html.replace(/\s+integrity\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
|
|
97578
|
+
}
|
|
97579
|
+
/**
|
|
97439
97580
|
* Rewrite vendor URLs in a text body: absolute (`https://host/…`), protocol-relative
|
|
97440
97581
|
* (`//host/…`) and JSON-escaped (`https:\/\/host\/…`) when they start a value; and, in
|
|
97441
97582
|
* HTML/CSS, root-relative references (`href="/x"`, `url(/x)`) which would otherwise
|
|
@@ -97451,6 +97592,7 @@ function rewriteBody(body, kind, currentUpstream, route) {
|
|
|
97451
97592
|
const escaped = new RegExp(`${VALUE_START}(?:https?:)?\\\\/\\\\/${host}(?=\\\\/|["'?#\\s)\\]},<]|$)`, "g");
|
|
97452
97593
|
out = out.replace(escaped, escapedBase);
|
|
97453
97594
|
}
|
|
97595
|
+
if (kind === "html") out = stripIntegrity(out);
|
|
97454
97596
|
const current = `${route.basePath}/${currentUpstream.host}/`;
|
|
97455
97597
|
const notMounted = `(?!/)(?!${escapeRegex(route.basePath.slice(1))}/)`;
|
|
97456
97598
|
if (kind === "html") {
|
|
@@ -5981,7 +5981,7 @@ var ZodIssueCode = {
|
|
|
5981
5981
|
var ZodFirstPartyTypeKind;
|
|
5982
5982
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5983
5983
|
//#endregion
|
|
5984
|
-
//#region ../types/dist/sleep-
|
|
5984
|
+
//#region ../types/dist/sleep-DEZaLbiu.mjs
|
|
5985
5985
|
/**
|
|
5986
5986
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5987
5987
|
* window to float samples (D455).
|
|
@@ -6790,6 +6790,12 @@ function projectStore(store, perNodeKeys, nodeId) {
|
|
|
6790
6790
|
}
|
|
6791
6791
|
return out;
|
|
6792
6792
|
}
|
|
6793
|
+
/** Every scope, in the order the guard and the docs list them. */
|
|
6794
|
+
var SCOPED_SETTINGS_SCOPES = [
|
|
6795
|
+
"cluster",
|
|
6796
|
+
"device-defaults",
|
|
6797
|
+
"node"
|
|
6798
|
+
];
|
|
6793
6799
|
/**
|
|
6794
6800
|
* The address of one keychain value: `secret://<owner>/<scope>/<fieldPath>`.
|
|
6795
6801
|
*
|
|
@@ -7681,6 +7687,13 @@ var BaseAddon = class {
|
|
|
7681
7687
|
*/
|
|
7682
7688
|
secretStatePaths = [];
|
|
7683
7689
|
_addonSecretTree = null;
|
|
7690
|
+
/**
|
|
7691
|
+
* D986 M4: the scoped schemas this addon declared, resolved once per capability
|
|
7692
|
+
* (`loadScopedSchemas`) so the sync readers (`perNodeKeys`, the secret tree, the restart check)
|
|
7693
|
+
* see them. Empty until loaded: the readers then fall back to {@link globalSettingsSchema}.
|
|
7694
|
+
*/
|
|
7695
|
+
_scopedByCap = /* @__PURE__ */ new Map();
|
|
7696
|
+
_nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
7684
7697
|
_deviceSecretTree = null;
|
|
7685
7698
|
/** Refs the last resolved config pointed at — a change to one re-resolves. */
|
|
7686
7699
|
_configSecretRefs = /* @__PURE__ */ new Set();
|
|
@@ -7750,6 +7763,7 @@ var BaseAddon = class {
|
|
|
7750
7763
|
this._ctx = context;
|
|
7751
7764
|
this._secretSettings = null;
|
|
7752
7765
|
}
|
|
7766
|
+
await this.loadScopedSchemas();
|
|
7753
7767
|
await this.resolveConfig();
|
|
7754
7768
|
if (secrets !== void 0) this._secretChangeUnsub = secrets.onChange((ref) => {
|
|
7755
7769
|
if (this._configSecretRefs.has(ref)) this.scheduleSecretReresolve(SECRET_CHANGE_DEBOUNCE_MS);
|
|
@@ -7759,6 +7773,7 @@ var BaseAddon = class {
|
|
|
7759
7773
|
const normalized = normalizeAddonInitResult(result);
|
|
7760
7774
|
const providers = normalized && "providers" in normalized && normalized.providers ? normalized.providers : [];
|
|
7761
7775
|
this._registeredCapNames = providers.map((p) => p.capability.name);
|
|
7776
|
+
await this.loadScopedSchemas();
|
|
7762
7777
|
return normalized;
|
|
7763
7778
|
}
|
|
7764
7779
|
/**
|
|
@@ -7847,6 +7862,16 @@ var BaseAddon = class {
|
|
|
7847
7862
|
schema(schema) {
|
|
7848
7863
|
return schema;
|
|
7849
7864
|
}
|
|
7865
|
+
/**
|
|
7866
|
+
* The capability names whose scoped settings schemas ({@link clusterSettingsSchema},
|
|
7867
|
+
* {@link nodeSettingsSchema}, {@link deviceDefaultsSchema}) this addon declares. Read BEFORE
|
|
7868
|
+
* `onInitialize`, when the providers it registers are not known yet: the first config resolve
|
|
7869
|
+
* must already know which keys are per node. Names the providers register at init are added
|
|
7870
|
+
* once known.
|
|
7871
|
+
*/
|
|
7872
|
+
scopedSettingsCapabilities() {
|
|
7873
|
+
return [];
|
|
7874
|
+
}
|
|
7850
7875
|
/** Override to provide global-level settings UI schema. */
|
|
7851
7876
|
globalSettingsSchema(_cap) {
|
|
7852
7877
|
return null;
|
|
@@ -7890,7 +7915,8 @@ var BaseAddon = class {
|
|
|
7890
7915
|
return [];
|
|
7891
7916
|
}
|
|
7892
7917
|
async getGlobalSettings(overlay, cap, nodeId) {
|
|
7893
|
-
|
|
7918
|
+
await this.loadScopedSchemas(cap);
|
|
7919
|
+
const schema = this.globalSettingsSchema(cap) ?? this.scopedFormSchema(cap);
|
|
7894
7920
|
if (!schema) return { sections: [] };
|
|
7895
7921
|
const projected = await this.readRawProjectedStore(nodeId, cap);
|
|
7896
7922
|
return hydrateSchema(schema, overlay ? await this.overlayKeepingMarkers(projected, overlay) : projected, { inlineSecretsAsViews: true });
|
|
@@ -8021,6 +8047,7 @@ var BaseAddon = class {
|
|
|
8021
8047
|
* resolved blob back is a no-op for every unchanged secret.
|
|
8022
8048
|
*/
|
|
8023
8049
|
async resolveGlobalStore(nodeId, cap) {
|
|
8050
|
+
await this.loadScopedSchemas(cap);
|
|
8024
8051
|
const raw = await this._ctx?.settings?.readAddonStore() ?? {};
|
|
8025
8052
|
return this.projectForNode(raw, nodeId, cap);
|
|
8026
8053
|
}
|
|
@@ -8031,6 +8058,7 @@ var BaseAddon = class {
|
|
|
8031
8058
|
}
|
|
8032
8059
|
/** The PROJECTED store with its markers — the form hydrate only (D798). */
|
|
8033
8060
|
async readRawProjectedStore(nodeId, cap) {
|
|
8061
|
+
await this.loadScopedSchemas(cap);
|
|
8034
8062
|
const raw = await this.rawSettings()?.readAddonStore() ?? {};
|
|
8035
8063
|
return this.projectForNode(raw, nodeId, cap);
|
|
8036
8064
|
}
|
|
@@ -8133,6 +8161,7 @@ var BaseAddon = class {
|
|
|
8133
8161
|
await this.applyGlobalPatch(patch, nodeId);
|
|
8134
8162
|
}
|
|
8135
8163
|
async applyGlobalPatch(barePatch, nodeId) {
|
|
8164
|
+
await this.loadScopedSchemas();
|
|
8136
8165
|
const keys = this.perNodeKeys();
|
|
8137
8166
|
const localNode = normalizeNodeId(this._ctx?.kernel?.localNodeId);
|
|
8138
8167
|
const target = normalizeNodeId(nodeId ?? this._ctx?.kernel?.localNodeId);
|
|
@@ -8146,7 +8175,8 @@ var BaseAddon = class {
|
|
|
8146
8175
|
await this.resolveConfig();
|
|
8147
8176
|
await this.onConfigChanged();
|
|
8148
8177
|
this.emitLifecycle(EventCategory.AddonUpdated, { level: "global" });
|
|
8149
|
-
|
|
8178
|
+
const legacy = this.globalSettingsSchema();
|
|
8179
|
+
this.maybeAutoRestart(Object.keys(barePatch), [...legacy === null ? [] : [legacy], ...this.loadedScopedSchemas().map((entry) => entry.schema)]);
|
|
8150
8180
|
}
|
|
8151
8181
|
/**
|
|
8152
8182
|
* The set of field keys the global settings schema declares `perNode: true`
|
|
@@ -8154,14 +8184,67 @@ var BaseAddon = class {
|
|
|
8154
8184
|
* declarations). Empty set ⇒ every per-node code path is bypassed and the
|
|
8155
8185
|
* settings API behaves exactly like the legacy node-agnostic one.
|
|
8156
8186
|
*/
|
|
8157
|
-
|
|
8187
|
+
/**
|
|
8188
|
+
* Load the scoped schemas of every capability this addon declares (the override and the
|
|
8189
|
+
* providers registered at init, plus `extraCap`) that is not loaded yet, then drop what was
|
|
8190
|
+
* derived from the previous set. Idempotent.
|
|
8191
|
+
*/
|
|
8192
|
+
async loadScopedSchemas(extraCap) {
|
|
8193
|
+
const caps = new Set([
|
|
8194
|
+
...this.scopedSettingsCapabilities(),
|
|
8195
|
+
...this._registeredCapNames,
|
|
8196
|
+
...extraCap === void 0 ? [] : [extraCap]
|
|
8197
|
+
]);
|
|
8198
|
+
let added = false;
|
|
8199
|
+
for (const cap of caps) {
|
|
8200
|
+
if (this._scopedByCap.has(cap)) continue;
|
|
8201
|
+
const loaded = [];
|
|
8202
|
+
for (const scope of SCOPED_SETTINGS_SCOPES) {
|
|
8203
|
+
const schema = await this.getScopedSettingsSchema(scope, cap);
|
|
8204
|
+
if (schema !== null) loaded.push({
|
|
8205
|
+
scope,
|
|
8206
|
+
schema
|
|
8207
|
+
});
|
|
8208
|
+
}
|
|
8209
|
+
this._scopedByCap.set(cap, loaded);
|
|
8210
|
+
added = true;
|
|
8211
|
+
}
|
|
8212
|
+
if (!added) return;
|
|
8213
|
+
this._nodeKeysByCap = /* @__PURE__ */ new Map();
|
|
8214
|
+
this._addonSecretTree = null;
|
|
8215
|
+
}
|
|
8216
|
+
/**
|
|
8217
|
+
* The form the settings API serves while an addon declares no {@link globalSettingsSchema}: the
|
|
8218
|
+
* sections of its scoped schemas, so dropping the old hook does not blank the form.
|
|
8219
|
+
*/
|
|
8220
|
+
scopedFormSchema(cap) {
|
|
8221
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8222
|
+
for (const entry of this.loadedScopedSchemas(cap)) for (const section of entry.schema.sections) if (!byId.has(section.id)) byId.set(section.id, section);
|
|
8223
|
+
return byId.size === 0 ? null : { sections: [...byId.values()] };
|
|
8224
|
+
}
|
|
8225
|
+
/** Every loaded scoped schema of `cap`, or of every loaded capability. */
|
|
8226
|
+
loadedScopedSchemas(cap) {
|
|
8227
|
+
if (cap !== void 0) return this._scopedByCap.get(cap) ?? [];
|
|
8228
|
+
return [...this._scopedByCap.values()].flat();
|
|
8229
|
+
}
|
|
8230
|
+
/**
|
|
8231
|
+
* The field keys that are stored per node: every field of a {@link nodeSettingsSchema}, plus
|
|
8232
|
+
* the fields the legacy {@link globalSettingsSchema} still marks `perNode: true`. Derived once
|
|
8233
|
+
* per `cap` argument and memoized (schemas are static declarations). Empty set ⇒ every
|
|
8234
|
+
* per-node code path is bypassed and the settings API behaves like the node-agnostic one.
|
|
8235
|
+
* Call {@link loadScopedSchemas} first (the async seams do).
|
|
8236
|
+
*/
|
|
8158
8237
|
perNodeKeys(cap) {
|
|
8159
8238
|
const cacheKey = cap ?? "";
|
|
8160
|
-
const cached = this.
|
|
8239
|
+
const cached = this._nodeKeysByCap.get(cacheKey);
|
|
8161
8240
|
if (cached) return cached;
|
|
8162
|
-
const
|
|
8163
|
-
const keys = new Set(
|
|
8164
|
-
this.
|
|
8241
|
+
const legacy = this.globalSettingsSchema(cap);
|
|
8242
|
+
const keys = new Set(legacy ? legacy.sections.flatMap((section) => collectPerNodeFieldKeys(section.fields)) : []);
|
|
8243
|
+
for (const { scope, schema } of this.loadedScopedSchemas(cap)) {
|
|
8244
|
+
if (scope !== "node") continue;
|
|
8245
|
+
for (const section of schema.sections) for (const key of collectValueFieldKeys(section.fields)) keys.add(key);
|
|
8246
|
+
}
|
|
8247
|
+
this._nodeKeysByCap.set(cacheKey, keys);
|
|
8165
8248
|
return keys;
|
|
8166
8249
|
}
|
|
8167
8250
|
/**
|
|
@@ -8171,10 +8254,9 @@ var BaseAddon = class {
|
|
|
8171
8254
|
* time to return its response before the addon is torn down and
|
|
8172
8255
|
* re-initialised by `AddonRegistryService.restartAddon`.
|
|
8173
8256
|
*/
|
|
8174
|
-
maybeAutoRestart(patchKeys,
|
|
8175
|
-
if (!schema) return;
|
|
8257
|
+
maybeAutoRestart(patchKeys, schemas) {
|
|
8176
8258
|
const restartKeys = /* @__PURE__ */ new Set();
|
|
8177
|
-
for (const section of schema.sections) for (const field of section.fields) {
|
|
8259
|
+
for (const schema of schemas) for (const section of schema.sections) for (const field of section.fields) {
|
|
8178
8260
|
if (field.type === "separator" || field.type === "info") continue;
|
|
8179
8261
|
if (field.requiresRestart) restartKeys.add(field.key);
|
|
8180
8262
|
}
|
|
@@ -8370,12 +8452,14 @@ var BaseAddon = class {
|
|
|
8370
8452
|
* here, in the addon's own process, so the hub keeps no copy of it.
|
|
8371
8453
|
*/
|
|
8372
8454
|
async getDeclaredSettingsKeys() {
|
|
8455
|
+
await this.loadScopedSchemas();
|
|
8373
8456
|
return { keys: [...this.declaredKeySet()].toSorted() };
|
|
8374
8457
|
}
|
|
8375
8458
|
declaredKeySet() {
|
|
8376
8459
|
const keys = new Set(Object.keys(this.defaults));
|
|
8377
8460
|
const schema = this.globalSettingsSchema();
|
|
8378
8461
|
if (schema) for (const section of schema.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8462
|
+
for (const { schema: scoped } of this.loadedScopedSchemas()) for (const section of scoped.sections) for (const key of collectFieldKeys(section.fields)) keys.add(key);
|
|
8379
8463
|
for (const key of this._declaredStateKeys) keys.add(key);
|
|
8380
8464
|
for (const path of this.secretStatePaths) {
|
|
8381
8465
|
const root = path.split(/[.[]/, 1)[0];
|
|
@@ -8471,7 +8555,11 @@ var BaseAddon = class {
|
|
|
8471
8555
|
}
|
|
8472
8556
|
/** The addon store's secret tree: schema fields by TYPE, plus `secretStatePaths`. */
|
|
8473
8557
|
addonSecretTree() {
|
|
8474
|
-
this._addonSecretTree
|
|
8558
|
+
if (this._addonSecretTree === null) {
|
|
8559
|
+
const tree = new Map(secretTreeFromSchema(this.globalSettingsSchema()));
|
|
8560
|
+
for (const { schema } of this.loadedScopedSchemas()) for (const [key, node] of secretTreeFromSchema(schema)) tree.set(key, node);
|
|
8561
|
+
this._addonSecretTree = mergeSecretStatePaths(tree, this.secretStatePaths);
|
|
8562
|
+
}
|
|
8475
8563
|
return this._addonSecretTree;
|
|
8476
8564
|
}
|
|
8477
8565
|
deviceSecretTree() {
|
|
@@ -8721,6 +8809,23 @@ function dropPerNodeFields(fields) {
|
|
|
8721
8809
|
}
|
|
8722
8810
|
return kept;
|
|
8723
8811
|
}
|
|
8812
|
+
/** The keys of the value-holding fields, recursing into layout containers. */
|
|
8813
|
+
function collectValueFieldKeys(fields) {
|
|
8814
|
+
const collected = [];
|
|
8815
|
+
for (const field of fields) {
|
|
8816
|
+
if (field.type === "group") {
|
|
8817
|
+
collected.push(...collectValueFieldKeys(field.fields));
|
|
8818
|
+
continue;
|
|
8819
|
+
}
|
|
8820
|
+
if (field.type === "sub-tabs") {
|
|
8821
|
+
for (const tab of field.tabs) collected.push(...collectValueFieldKeys(tab.fields));
|
|
8822
|
+
continue;
|
|
8823
|
+
}
|
|
8824
|
+
if (field.type === "separator" || field.type === "info") continue;
|
|
8825
|
+
if ("key" in field && typeof field.key === "string") collected.push(field.key);
|
|
8826
|
+
}
|
|
8827
|
+
return collected;
|
|
8828
|
+
}
|
|
8724
8829
|
function collectFieldKeys(fields) {
|
|
8725
8830
|
const collected = [];
|
|
8726
8831
|
for (const field of fields) {
|
|
@@ -13427,12 +13532,22 @@ var RecordIntegrationInstances = class {
|
|
|
13427
13532
|
...this.hooks.description,
|
|
13428
13533
|
mintsId: true
|
|
13429
13534
|
};
|
|
13430
|
-
|
|
13535
|
+
/**
|
|
13536
|
+
* The record an id names. A caller holding the `integrations` registry id (the Integrations
|
|
13537
|
+
* page) reaches a kind whose body links to it through `integrationId`; an id that is itself a
|
|
13538
|
+
* record id answers as it is. An id that names nothing comes back unchanged.
|
|
13539
|
+
*/
|
|
13540
|
+
async resolveRecordId(id) {
|
|
13541
|
+
if (await this.handle.get(id) !== null) return id;
|
|
13542
|
+
const linked = (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.find((record) => record.data["integrationId"] === id);
|
|
13543
|
+
return linked === void 0 ? id : linked.id;
|
|
13544
|
+
}
|
|
13545
|
+
instanceFormSchema = async ({ id }) => this.hooks.formSchema(id === null ? null : await this.resolveRecordId(id));
|
|
13431
13546
|
listInstances = async () => {
|
|
13432
13547
|
return (await this.handle.query({ limit: INSTANCES_LIST_MAX })).records.map((record) => this.viewOf(record));
|
|
13433
13548
|
};
|
|
13434
13549
|
getInstance = async ({ id }) => {
|
|
13435
|
-
const record = await this.handle.get(id);
|
|
13550
|
+
const record = await this.handle.get(await this.resolveRecordId(id));
|
|
13436
13551
|
return record === null ? null : this.viewOf(record);
|
|
13437
13552
|
};
|
|
13438
13553
|
/** Insert ONE row (`ifRev: 0`: an existing id is refused, never overwritten). */
|
|
@@ -13449,7 +13564,8 @@ var RecordIntegrationInstances = class {
|
|
|
13449
13564
|
await this.hooks.onChanged?.(minted, "created");
|
|
13450
13565
|
return { id: minted };
|
|
13451
13566
|
};
|
|
13452
|
-
updateInstance = async ({ id, name, values }) => {
|
|
13567
|
+
updateInstance = async ({ id: askedId, name, values }) => {
|
|
13568
|
+
const id = await this.resolveRecordId(askedId);
|
|
13453
13569
|
const patch = {};
|
|
13454
13570
|
for (const [key, value] of Object.entries(values)) {
|
|
13455
13571
|
if (value === void 0) continue;
|
|
@@ -13484,7 +13600,10 @@ var RecordIntegrationInstances = class {
|
|
|
13484
13600
|
status: "unsupported",
|
|
13485
13601
|
error: "This integration declares no connection test"
|
|
13486
13602
|
};
|
|
13487
|
-
return this.hooks.test(input
|
|
13603
|
+
return this.hooks.test(input.id === null ? input : {
|
|
13604
|
+
...input,
|
|
13605
|
+
id: await this.resolveRecordId(input.id)
|
|
13606
|
+
});
|
|
13488
13607
|
};
|
|
13489
13608
|
/** The flat provider object to hand to `registerProvider`. */
|
|
13490
13609
|
toProvider() {
|
|
@@ -51995,7 +52114,14 @@ var DiscoveryCandidateSchema = object({
|
|
|
51995
52114
|
/** `stableId` of the gateway candidate this device was found BEHIND. */
|
|
51996
52115
|
parentStableId: string().optional(),
|
|
51997
52116
|
/** `false` = shown for context but not pickable. Default true. */
|
|
51998
|
-
selectable: boolean().optional()
|
|
52117
|
+
selectable: boolean().optional(),
|
|
52118
|
+
/**
|
|
52119
|
+
* Keys of the provider's creation form (`getChildCreationSchema`) this candidate
|
|
52120
|
+
* cannot be adopted without — a key for an encrypted board, a pairing code. The
|
|
52121
|
+
* admin asks for exactly these, on the creation form prefilled with
|
|
52122
|
+
* `prefilledConfig`, before it adopts. Absent or empty = adopt as it is.
|
|
52123
|
+
*/
|
|
52124
|
+
needsParams: array(string()).optional()
|
|
51999
52125
|
});
|
|
52000
52126
|
/**
|
|
52001
52127
|
* Flat device summary returned by `createDevice` / `adoptDiscoveredDevice`.
|
|
@@ -52123,7 +52249,11 @@ var deviceProviderCapability = {
|
|
|
52123
52249
|
* pick the right type instead of assuming Camera.
|
|
52124
52250
|
*/
|
|
52125
52251
|
getManualCreationType: method(object({}), object({ deviceType: _enum(DeviceType).nullable() })),
|
|
52126
|
-
adoptDiscoveredDevice: method(object({
|
|
52252
|
+
adoptDiscoveredDevice: method(object({
|
|
52253
|
+
candidate: DiscoveryCandidateSchema,
|
|
52254
|
+
/** The integration the operator adopts into; a provider with several instances needs it. */
|
|
52255
|
+
integrationId: string().optional()
|
|
52256
|
+
}), DeviceSummarySchema, {
|
|
52127
52257
|
kind: "mutation",
|
|
52128
52258
|
auth: "admin"
|
|
52129
52259
|
}),
|
|
@@ -52137,7 +52267,9 @@ var deviceProviderCapability = {
|
|
|
52137
52267
|
getChildCreationSchema: method(object({ type: _enum(DeviceType) }), CreationSchemaOutputSchema),
|
|
52138
52268
|
createDevice: method(object({
|
|
52139
52269
|
type: _enum(DeviceType),
|
|
52140
|
-
config: record(string(), unknown())
|
|
52270
|
+
config: record(string(), unknown()),
|
|
52271
|
+
/** The integration the operator adds the device to; a provider with several instances needs it. */
|
|
52272
|
+
integrationId: string().optional()
|
|
52141
52273
|
}), DeviceSummarySchema, {
|
|
52142
52274
|
kind: "mutation",
|
|
52143
52275
|
auth: "admin"
|
|
@@ -81826,7 +81958,7 @@ var BaseDeviceProvider = class extends BaseAddon {
|
|
|
81826
81958
|
* `deviceClasses` instead.
|
|
81827
81959
|
*/
|
|
81828
81960
|
async createDevice(input) {
|
|
81829
|
-
const spec = await this.onCreateDevice(input.type, input.config);
|
|
81961
|
+
const spec = await this.onCreateDevice(input.type, input.config, { ...input.integrationId !== void 0 ? { integrationId: input.integrationId } : {} });
|
|
81830
81962
|
const Class = this.deviceClasses[spec.meta.type];
|
|
81831
81963
|
if (!Class) throw new Error(`${this.providerName} provider: no device class registered for type "${spec.meta.type}" — add it to the deviceClasses map`);
|
|
81832
81964
|
const stableId = this.generateStableId(spec.meta.type, spec.config);
|
|
@@ -97436,6 +97568,15 @@ function rewriteLocation(location, currentUpstream, route) {
|
|
|
97436
97568
|
/** Characters that may precede a URL that STARTS a value (never `=`, `?`, `&`, `%3D`). */
|
|
97437
97569
|
var VALUE_START = String.raw`(?<=^|["'(\s>,\[{])`;
|
|
97438
97570
|
/**
|
|
97571
|
+
* Subresource Integrity pins the bytes of a script or stylesheet. The proxy rewrites vendor
|
|
97572
|
+
* URLs inside those bytes (and re-serves them from another origin), so the hash can no longer
|
|
97573
|
+
* match and the browser refuses the file: the page renders with no script at all. The hub
|
|
97574
|
+
* already drops the CSP that would have enforced the same pin; the attribute goes with it.
|
|
97575
|
+
*/
|
|
97576
|
+
function stripIntegrity(html) {
|
|
97577
|
+
return html.replace(/\s+integrity\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
|
|
97578
|
+
}
|
|
97579
|
+
/**
|
|
97439
97580
|
* Rewrite vendor URLs in a text body: absolute (`https://host/…`), protocol-relative
|
|
97440
97581
|
* (`//host/…`) and JSON-escaped (`https:\/\/host\/…`) when they start a value; and, in
|
|
97441
97582
|
* HTML/CSS, root-relative references (`href="/x"`, `url(/x)`) which would otherwise
|
|
@@ -97451,6 +97592,7 @@ function rewriteBody(body, kind, currentUpstream, route) {
|
|
|
97451
97592
|
const escaped = new RegExp(`${VALUE_START}(?:https?:)?\\\\/\\\\/${host}(?=\\\\/|["'?#\\s)\\]},<]|$)`, "g");
|
|
97452
97593
|
out = out.replace(escaped, escapedBase);
|
|
97453
97594
|
}
|
|
97595
|
+
if (kind === "html") out = stripIntegrity(out);
|
|
97454
97596
|
const current = `${route.basePath}/${currentUpstream.host}/`;
|
|
97455
97597
|
const notMounted = `(?!/)(?!${escapeRegex(route.basePath.slice(1))}/)`;
|
|
97456
97598
|
if (kind === "html") {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_bosch_instances = require("../bosch-instances-
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_bosch_instances = require("../bosch-instances-Y9sMCElY.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-CeMaWElV.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_tls = require("node:tls");
|
|
6
6
|
let node_zlib = require("node:zlib");
|
|
@@ -1408,9 +1408,14 @@ function homeConnectFinishResult(account) {
|
|
|
1408
1408
|
/**
|
|
1409
1409
|
* Home Connect signs in through its own identity pages, which end on `hcauth://auth/prod?...`
|
|
1410
1410
|
* — an address no browser can open. These are the hosts those pages are served from; the
|
|
1411
|
-
* proxy reaches nothing else.
|
|
1411
|
+
* proxy reaches nothing else. The authorize call redirects to SingleKey ID, whose page loads its
|
|
1412
|
+
* scripts from `cdn.singlekey-id.com` (see `HOMECOM_LOGIN_HOSTS`).
|
|
1412
1413
|
*/
|
|
1413
|
-
var HOME_CONNECT_LOGIN_HOSTS = [
|
|
1414
|
+
var HOME_CONNECT_LOGIN_HOSTS = [
|
|
1415
|
+
"api.home-connect.com",
|
|
1416
|
+
"singlekey-id.com",
|
|
1417
|
+
"cdn.singlekey-id.com"
|
|
1418
|
+
];
|
|
1414
1419
|
var HOME_CONNECT_APP_SCHEME = HOME_CONNECT_REDIRECT_URI.slice(0, HOME_CONNECT_REDIRECT_URI.indexOf("://") + 3);
|
|
1415
1420
|
var homeConnectSignInClaims = require_account_integration_offline.createSignInClaims();
|
|
1416
1421
|
/** Start an attempt whose `authorizeUrl` is the hub-relative proxied login page. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as errMsg, B as isSecretMarkerLike, C as ingestReading, D as quantityFromUnit, E as powerMeterCapability, F as defineAddonMigrations, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, N as createEvent, O as runningFromDishwasherOperation, P as customAction, Q as DeviceType, R as entryBody, T as numericSensorCapability, U as boolean, V as _enum, W as literal, X as unknown, Y as union, Z as DeviceRole, _ as defaultIntegrationApiOver, a as BaseDevice, b as enumSensorCapability, c as RUNNING_STATE_COLD, d as VendorLoginProxy, et as EventCategory, g as consumablesCapability, h as connectionTestCapability, i as testBoschInstance, k as runningStateCapability, l as RecordIntegrationInstances, n as boschRowsPorts, o as BaseDeviceProvider, p as binaryCapability, q as record, r as keepBodyKeys, s as DishwasherOperationStateSchema, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability, x as eventEmitterCapability, y as dishwasherCapability, z as isSecretMarker } from "../bosch-instances-
|
|
2
|
-
import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-
|
|
1
|
+
import { $ as errMsg, B as isSecretMarkerLike, C as ingestReading, D as quantityFromUnit, E as powerMeterCapability, F as defineAddonMigrations, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, N as createEvent, O as runningFromDishwasherOperation, P as customAction, Q as DeviceType, R as entryBody, T as numericSensorCapability, U as boolean, V as _enum, W as literal, X as unknown, Y as union, Z as DeviceRole, _ as defaultIntegrationApiOver, a as BaseDevice, b as enumSensorCapability, c as RUNNING_STATE_COLD, d as VendorLoginProxy, et as EventCategory, g as consumablesCapability, h as connectionTestCapability, i as testBoschInstance, k as runningStateCapability, l as RecordIntegrationInstances, n as boschRowsPorts, o as BaseDeviceProvider, p as binaryCapability, q as record, r as keepBodyKeys, s as DishwasherOperationStateSchema, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability, x as eventEmitterCapability, y as dishwasherCapability, z as isSecretMarker } from "../bosch-instances-CIuDwXQ8.mjs";
|
|
2
|
+
import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-DhHNdL3k.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
import { connect } from "node:tls";
|
|
5
5
|
import { inflateRawSync } from "node:zlib";
|
|
@@ -1407,9 +1407,14 @@ function homeConnectFinishResult(account) {
|
|
|
1407
1407
|
/**
|
|
1408
1408
|
* Home Connect signs in through its own identity pages, which end on `hcauth://auth/prod?...`
|
|
1409
1409
|
* — an address no browser can open. These are the hosts those pages are served from; the
|
|
1410
|
-
* proxy reaches nothing else.
|
|
1410
|
+
* proxy reaches nothing else. The authorize call redirects to SingleKey ID, whose page loads its
|
|
1411
|
+
* scripts from `cdn.singlekey-id.com` (see `HOMECOM_LOGIN_HOSTS`).
|
|
1411
1412
|
*/
|
|
1412
|
-
var HOME_CONNECT_LOGIN_HOSTS = [
|
|
1413
|
+
var HOME_CONNECT_LOGIN_HOSTS = [
|
|
1414
|
+
"api.home-connect.com",
|
|
1415
|
+
"singlekey-id.com",
|
|
1416
|
+
"cdn.singlekey-id.com"
|
|
1417
|
+
];
|
|
1413
1418
|
var HOME_CONNECT_APP_SCHEME = HOME_CONNECT_REDIRECT_URI.slice(0, HOME_CONNECT_REDIRECT_URI.indexOf("://") + 3);
|
|
1414
1419
|
var homeConnectSignInClaims = createSignInClaims();
|
|
1415
1420
|
/** Start an attempt whose `authorizeUrl` is the hub-relative proxied login page. */
|
package/dist/homecom/addon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_bosch_instances = require("../bosch-instances-
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_bosch_instances = require("../bosch-instances-Y9sMCElY.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-CeMaWElV.js");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
//#region src/homecom/config.ts
|
|
6
6
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -1227,8 +1227,13 @@ function finishedFromGateways(tokens, resolved, listFailure) {
|
|
|
1227
1227
|
var homeComSignInAttempts = require_account_integration_offline.createSignInAttemptStore();
|
|
1228
1228
|
//#endregion
|
|
1229
1229
|
//#region src/homecom/homecom-sign-in-proxy.ts
|
|
1230
|
-
/**
|
|
1231
|
-
|
|
1230
|
+
/**
|
|
1231
|
+
* SingleKey ID serves the HomeCom login page; its scripts and styles come from `cdn.singlekey-id.com`
|
|
1232
|
+
* as `type=module` + `crossorigin` files, which the CDN does not CORS-enable for another origin —
|
|
1233
|
+
* served from the hub origin through the proxy they load, loaded directly they are refused and the
|
|
1234
|
+
* page has no script (only the email field, no way to continue).
|
|
1235
|
+
*/
|
|
1236
|
+
var HOMECOM_LOGIN_HOSTS = ["singlekey-id.com", "cdn.singlekey-id.com"];
|
|
1232
1237
|
var HOMECOM_APP_SCHEME = HOMECOM_REDIRECT_URI.slice(0, HOMECOM_REDIRECT_URI.indexOf("://") + 3);
|
|
1233
1238
|
var homeComSignInClaims = require_account_integration_offline.createSignInClaims();
|
|
1234
1239
|
/** Start an attempt whose `authorizeUrl` is the hub-relative proxied login page. */
|
package/dist/homecom/addon.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $ as errMsg, F as defineAddonMigrations, G as number, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, M as waterHeaterCapability, P as customAction, Q as DeviceType, R as entryBody, U as boolean, V as _enum, W as literal, _ as defaultIntegrationApiOver, a as BaseDevice, d as VendorLoginProxy, et as EventCategory, h as connectionTestCapability, i as testBoschInstance, l as RecordIntegrationInstances, m as climateControlCapability, n as boschRowsPorts, o as BaseDeviceProvider, r as keepBodyKeys, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability } from "../bosch-instances-
|
|
2
|
-
import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-
|
|
1
|
+
import { $ as errMsg, F as defineAddonMigrations, G as number, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, M as waterHeaterCapability, P as customAction, Q as DeviceType, R as entryBody, U as boolean, V as _enum, W as literal, _ as defaultIntegrationApiOver, a as BaseDevice, d as VendorLoginProxy, et as EventCategory, h as connectionTestCapability, i as testBoschInstance, l as RecordIntegrationInstances, m as climateControlCapability, n as boschRowsPorts, o as BaseDeviceProvider, r as keepBodyKeys, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability } from "../bosch-instances-CIuDwXQ8.mjs";
|
|
2
|
+
import { a as createSignInAttemptStore, i as openSignInProxy, n as cascadeRemoveDevicesForIntegration, o as loginFailureForAttempt, r as createSignInClaims, s as parseOAuthCallback, t as setIntegrationDevicesOnline } from "../account-integration-offline-DhHNdL3k.mjs";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
4
|
//#region src/homecom/config.ts
|
|
5
5
|
var HOMECOM_BRANDS = ["bosch", "buderus"];
|
|
@@ -1226,8 +1226,13 @@ function finishedFromGateways(tokens, resolved, listFailure) {
|
|
|
1226
1226
|
var homeComSignInAttempts = createSignInAttemptStore();
|
|
1227
1227
|
//#endregion
|
|
1228
1228
|
//#region src/homecom/homecom-sign-in-proxy.ts
|
|
1229
|
-
/**
|
|
1230
|
-
|
|
1229
|
+
/**
|
|
1230
|
+
* SingleKey ID serves the HomeCom login page; its scripts and styles come from `cdn.singlekey-id.com`
|
|
1231
|
+
* as `type=module` + `crossorigin` files, which the CDN does not CORS-enable for another origin —
|
|
1232
|
+
* served from the hub origin through the proxy they load, loaded directly they are refused and the
|
|
1233
|
+
* page has no script (only the email field, no way to continue).
|
|
1234
|
+
*/
|
|
1235
|
+
var HOMECOM_LOGIN_HOSTS = ["singlekey-id.com", "cdn.singlekey-id.com"];
|
|
1231
1236
|
var HOMECOM_APP_SCHEME = HOMECOM_REDIRECT_URI.slice(0, HOMECOM_REDIRECT_URI.indexOf("://") + 3);
|
|
1232
1237
|
var homeComSignInClaims = createSignInClaims();
|
|
1233
1238
|
/** Start an attempt whose `authorizeUrl` is the hub-relative proxied login page. */
|
package/dist/shc/addon.js
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
4
4
|
//#endregion
|
|
5
|
-
const require_bosch_instances = require("../bosch-instances-
|
|
5
|
+
const require_bosch_instances = require("../bosch-instances-Y9sMCElY.js");
|
|
6
6
|
let node_crypto = require("node:crypto");
|
|
7
7
|
let node_https = require("node:https");
|
|
8
8
|
let node_child_process = require("node:child_process");
|
package/dist/shc/addon.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as errMsg, A as switchCapability, F as defineAddonMigrations, G as number, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, P as customAction, Q as DeviceType, R as entryBody, S as heatingValveCapability, V as _enum, W as literal, _ as defaultIntegrationApiOver, a as BaseDevice, et as EventCategory, f as batteryCapability, h as connectionTestCapability, i as testBoschInstance, j as temperatureSensorCapability, l as RecordIntegrationInstances, m as climateControlCapability, n as boschRowsPorts, o as BaseDeviceProvider, r as keepBodyKeys, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability } from "../bosch-instances-
|
|
1
|
+
import { $ as errMsg, A as switchCapability, F as defineAddonMigrations, G as number, H as array, I as defineCustomActions, J as string, K as object, L as defineEntryKind, P as customAction, Q as DeviceType, R as entryBody, S as heatingValveCapability, V as _enum, W as literal, _ as defaultIntegrationApiOver, a as BaseDevice, et as EventCategory, f as batteryCapability, h as connectionTestCapability, i as testBoschInstance, j as temperatureSensorCapability, l as RecordIntegrationInstances, m as climateControlCapability, n as boschRowsPorts, o as BaseDeviceProvider, r as keepBodyKeys, t as boschRowsMigration, v as deviceAdoptionCapability, w as integrationInstancesCapability } from "../bosch-instances-CIuDwXQ8.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { request } from "node:https";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-provider-bosch",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "Bosch smart-home device-provider addons for CamStack — SHC, Home Connect, and HomeCom cloud integrations (scaffold)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
}
|
|
142
142
|
],
|
|
143
143
|
"requires": {
|
|
144
|
-
"server": ">=1.2.
|
|
144
|
+
"server": ">=1.2.778"
|
|
145
145
|
}
|
|
146
146
|
},
|
|
147
147
|
"files": [
|