@camstack/addon-provider-bosch 0.1.29 → 0.1.30
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-DMtMWixt.js → account-integration-offline-BMoYdiX2.js} +126 -1
- package/dist/{account-integration-offline-C-XbrgLs.mjs → account-integration-offline-pv6cqOKu.mjs} +115 -2
- package/dist/{dist-CyLs51d2.js → dist-BioIJ3NA.js} +1139 -82
- package/dist/{dist-DzSesBKb.mjs → dist-DBUnIovD.mjs} +1128 -83
- package/dist/home-connect/addon.js +349 -259
- package/dist/home-connect/addon.mjs +349 -259
- package/dist/homecom/addon.js +94 -12
- package/dist/homecom/addon.mjs +94 -12
- package/dist/shc/addon.js +1 -1
- package/dist/shc/addon.mjs +1 -1
- package/i18n/en.json +14 -1
- package/i18n/it.json +14 -1
- package/package.json +1 -1
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.30] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Home Connect and HomeCom now sign in through a page served by the hub; no more copying the redirect address out of the browser's developer tools.
|
|
15
|
+
|
|
10
16
|
## [0.1.29] - 2026-10-27
|
|
11
17
|
|
|
12
18
|
### Changed
|
package/dist/{account-integration-offline-DMtMWixt.js → account-integration-offline-BMoYdiX2.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_dist = require("./dist-
|
|
1
|
+
const require_dist = require("./dist-BioIJ3NA.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. */
|
|
@@ -118,6 +118,119 @@ function loginFailureForAttempt(reason) {
|
|
|
118
118
|
return reason === "expired" ? "code-rejected" : "state-mismatch";
|
|
119
119
|
}
|
|
120
120
|
//#endregion
|
|
121
|
+
//#region src/shared/login-proxy-sign-in.ts
|
|
122
|
+
function createSignInClaims(ttlMs = SIGN_IN_ATTEMPT_TTL_MS) {
|
|
123
|
+
const parked = /* @__PURE__ */ new Map();
|
|
124
|
+
return {
|
|
125
|
+
settle(attemptId, outcome, nowMs) {
|
|
126
|
+
parked.set(attemptId, {
|
|
127
|
+
outcome,
|
|
128
|
+
expiresAtMs: nowMs + ttlMs
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
claim(attemptId, nowMs) {
|
|
132
|
+
const found = parked.get(attemptId);
|
|
133
|
+
if (found === void 0) return { state: "pending" };
|
|
134
|
+
parked.delete(attemptId);
|
|
135
|
+
if (nowMs > found.expiresAtMs) return { state: "pending" };
|
|
136
|
+
if (!found.outcome.ok) throw new Error(found.outcome.message);
|
|
137
|
+
return {
|
|
138
|
+
state: "done",
|
|
139
|
+
result: found.outcome.result
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** An `&`-escaped address inside HTML is the same address. */
|
|
145
|
+
function unescapeHtml(text) {
|
|
146
|
+
return text.replace(/&/g, "&");
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Terminal condition for an app-scheme redirect: the `Location` of a response, or — when the
|
|
150
|
+
* vendor's page navigates by script — the first address in its body, that starts with the app
|
|
151
|
+
* scheme.
|
|
152
|
+
*/
|
|
153
|
+
function appSchemeTerminal(scheme) {
|
|
154
|
+
const inBody = new RegExp(`${scheme.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"'<>\\s\\\\]+`);
|
|
155
|
+
return (view) => {
|
|
156
|
+
if (view.location !== null && view.location.startsWith(scheme)) return {
|
|
157
|
+
kind: "done",
|
|
158
|
+
payload: view.location
|
|
159
|
+
};
|
|
160
|
+
const fromBody = view.bodyText === null ? null : inBody.exec(unescapeHtml(view.bodyText));
|
|
161
|
+
if (fromBody !== null) return {
|
|
162
|
+
kind: "done",
|
|
163
|
+
payload: fromBody[0]
|
|
164
|
+
};
|
|
165
|
+
return { kind: "continue" };
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** The result-page reason code for a sign-in failure reason (`HomeConnectLoginFailure`, …). */
|
|
169
|
+
function landingReasonForLogin(reason) {
|
|
170
|
+
switch (reason) {
|
|
171
|
+
case "denied": return "access_denied";
|
|
172
|
+
case "state-mismatch":
|
|
173
|
+
case "state-missing": return "invalid_state";
|
|
174
|
+
case "empty":
|
|
175
|
+
case "no-code": return "invalid_request";
|
|
176
|
+
case "code-rejected":
|
|
177
|
+
case "rejected": return "exchange_failed";
|
|
178
|
+
case "no-refresh": return "no_refresh_token";
|
|
179
|
+
default: return "unknown";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function reasonOf(err) {
|
|
183
|
+
if (typeof err === "object" && err !== null && "reason" in err) {
|
|
184
|
+
const reason = err.reason;
|
|
185
|
+
if (typeof reason === "string") return reason;
|
|
186
|
+
}
|
|
187
|
+
return "unknown";
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Open the proxied login for one attempt and return the hub-relative path the browser opens.
|
|
191
|
+
* A failed exchange is parked as an operator-facing message and the browser lands on the
|
|
192
|
+
* result page with a reason code; nothing about the captured address leaves this function.
|
|
193
|
+
*/
|
|
194
|
+
function openSignInProxy(input) {
|
|
195
|
+
const started = input.nowMs();
|
|
196
|
+
input.proxy.open({
|
|
197
|
+
attemptId: input.attemptId,
|
|
198
|
+
expiresAtMs: started + SIGN_IN_ATTEMPT_TTL_MS,
|
|
199
|
+
upstreams: input.hosts.map((host) => ({
|
|
200
|
+
host,
|
|
201
|
+
scheme: input.scheme ?? "https"
|
|
202
|
+
})),
|
|
203
|
+
terminal: appSchemeTerminal(input.appScheme),
|
|
204
|
+
onDone: async (callback) => {
|
|
205
|
+
try {
|
|
206
|
+
const result = await input.finish(callback);
|
|
207
|
+
input.claims.settle(input.attemptId, {
|
|
208
|
+
ok: true,
|
|
209
|
+
result
|
|
210
|
+
}, input.nowMs());
|
|
211
|
+
return {
|
|
212
|
+
status: "success",
|
|
213
|
+
addonId: input.addonId
|
|
214
|
+
};
|
|
215
|
+
} catch (err) {
|
|
216
|
+
const message = err instanceof Error ? err.message : "The sign-in failed.";
|
|
217
|
+
input.claims.settle(input.attemptId, {
|
|
218
|
+
ok: false,
|
|
219
|
+
message
|
|
220
|
+
}, input.nowMs());
|
|
221
|
+
return {
|
|
222
|
+
status: "error",
|
|
223
|
+
addonId: input.addonId,
|
|
224
|
+
reason: landingReasonForLogin(reasonOf(err))
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const entry = input.proxy.entryPath(input.attemptId, input.authorizeUrl);
|
|
230
|
+
if (entry === null) throw new Error("The sign-in page host is not reachable through the proxy.");
|
|
231
|
+
return entry;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
121
234
|
//#region src/shared/account-integration-cascade.ts
|
|
122
235
|
async function cascadeRemoveDevicesForIntegration(input) {
|
|
123
236
|
const { reg, devices, addonId, system, integrationId, logger } = input;
|
|
@@ -167,12 +280,24 @@ Object.defineProperty(exports, "createSignInAttemptStore", {
|
|
|
167
280
|
return createSignInAttemptStore;
|
|
168
281
|
}
|
|
169
282
|
});
|
|
283
|
+
Object.defineProperty(exports, "createSignInClaims", {
|
|
284
|
+
enumerable: true,
|
|
285
|
+
get: function() {
|
|
286
|
+
return createSignInClaims;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
170
289
|
Object.defineProperty(exports, "loginFailureForAttempt", {
|
|
171
290
|
enumerable: true,
|
|
172
291
|
get: function() {
|
|
173
292
|
return loginFailureForAttempt;
|
|
174
293
|
}
|
|
175
294
|
});
|
|
295
|
+
Object.defineProperty(exports, "openSignInProxy", {
|
|
296
|
+
enumerable: true,
|
|
297
|
+
get: function() {
|
|
298
|
+
return openSignInProxy;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
176
301
|
Object.defineProperty(exports, "parseOAuthCallback", {
|
|
177
302
|
enumerable: true,
|
|
178
303
|
get: function() {
|
package/dist/{account-integration-offline-C-XbrgLs.mjs → account-integration-offline-pv6cqOKu.mjs}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { D as errMsg } from "./dist-DBUnIovD.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. */
|
|
@@ -118,6 +118,119 @@ function loginFailureForAttempt(reason) {
|
|
|
118
118
|
return reason === "expired" ? "code-rejected" : "state-mismatch";
|
|
119
119
|
}
|
|
120
120
|
//#endregion
|
|
121
|
+
//#region src/shared/login-proxy-sign-in.ts
|
|
122
|
+
function createSignInClaims(ttlMs = SIGN_IN_ATTEMPT_TTL_MS) {
|
|
123
|
+
const parked = /* @__PURE__ */ new Map();
|
|
124
|
+
return {
|
|
125
|
+
settle(attemptId, outcome, nowMs) {
|
|
126
|
+
parked.set(attemptId, {
|
|
127
|
+
outcome,
|
|
128
|
+
expiresAtMs: nowMs + ttlMs
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
claim(attemptId, nowMs) {
|
|
132
|
+
const found = parked.get(attemptId);
|
|
133
|
+
if (found === void 0) return { state: "pending" };
|
|
134
|
+
parked.delete(attemptId);
|
|
135
|
+
if (nowMs > found.expiresAtMs) return { state: "pending" };
|
|
136
|
+
if (!found.outcome.ok) throw new Error(found.outcome.message);
|
|
137
|
+
return {
|
|
138
|
+
state: "done",
|
|
139
|
+
result: found.outcome.result
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** An `&`-escaped address inside HTML is the same address. */
|
|
145
|
+
function unescapeHtml(text) {
|
|
146
|
+
return text.replace(/&/g, "&");
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Terminal condition for an app-scheme redirect: the `Location` of a response, or — when the
|
|
150
|
+
* vendor's page navigates by script — the first address in its body, that starts with the app
|
|
151
|
+
* scheme.
|
|
152
|
+
*/
|
|
153
|
+
function appSchemeTerminal(scheme) {
|
|
154
|
+
const inBody = new RegExp(`${scheme.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[^"'<>\\s\\\\]+`);
|
|
155
|
+
return (view) => {
|
|
156
|
+
if (view.location !== null && view.location.startsWith(scheme)) return {
|
|
157
|
+
kind: "done",
|
|
158
|
+
payload: view.location
|
|
159
|
+
};
|
|
160
|
+
const fromBody = view.bodyText === null ? null : inBody.exec(unescapeHtml(view.bodyText));
|
|
161
|
+
if (fromBody !== null) return {
|
|
162
|
+
kind: "done",
|
|
163
|
+
payload: fromBody[0]
|
|
164
|
+
};
|
|
165
|
+
return { kind: "continue" };
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** The result-page reason code for a sign-in failure reason (`HomeConnectLoginFailure`, …). */
|
|
169
|
+
function landingReasonForLogin(reason) {
|
|
170
|
+
switch (reason) {
|
|
171
|
+
case "denied": return "access_denied";
|
|
172
|
+
case "state-mismatch":
|
|
173
|
+
case "state-missing": return "invalid_state";
|
|
174
|
+
case "empty":
|
|
175
|
+
case "no-code": return "invalid_request";
|
|
176
|
+
case "code-rejected":
|
|
177
|
+
case "rejected": return "exchange_failed";
|
|
178
|
+
case "no-refresh": return "no_refresh_token";
|
|
179
|
+
default: return "unknown";
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function reasonOf(err) {
|
|
183
|
+
if (typeof err === "object" && err !== null && "reason" in err) {
|
|
184
|
+
const reason = err.reason;
|
|
185
|
+
if (typeof reason === "string") return reason;
|
|
186
|
+
}
|
|
187
|
+
return "unknown";
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Open the proxied login for one attempt and return the hub-relative path the browser opens.
|
|
191
|
+
* A failed exchange is parked as an operator-facing message and the browser lands on the
|
|
192
|
+
* result page with a reason code; nothing about the captured address leaves this function.
|
|
193
|
+
*/
|
|
194
|
+
function openSignInProxy(input) {
|
|
195
|
+
const started = input.nowMs();
|
|
196
|
+
input.proxy.open({
|
|
197
|
+
attemptId: input.attemptId,
|
|
198
|
+
expiresAtMs: started + SIGN_IN_ATTEMPT_TTL_MS,
|
|
199
|
+
upstreams: input.hosts.map((host) => ({
|
|
200
|
+
host,
|
|
201
|
+
scheme: input.scheme ?? "https"
|
|
202
|
+
})),
|
|
203
|
+
terminal: appSchemeTerminal(input.appScheme),
|
|
204
|
+
onDone: async (callback) => {
|
|
205
|
+
try {
|
|
206
|
+
const result = await input.finish(callback);
|
|
207
|
+
input.claims.settle(input.attemptId, {
|
|
208
|
+
ok: true,
|
|
209
|
+
result
|
|
210
|
+
}, input.nowMs());
|
|
211
|
+
return {
|
|
212
|
+
status: "success",
|
|
213
|
+
addonId: input.addonId
|
|
214
|
+
};
|
|
215
|
+
} catch (err) {
|
|
216
|
+
const message = err instanceof Error ? err.message : "The sign-in failed.";
|
|
217
|
+
input.claims.settle(input.attemptId, {
|
|
218
|
+
ok: false,
|
|
219
|
+
message
|
|
220
|
+
}, input.nowMs());
|
|
221
|
+
return {
|
|
222
|
+
status: "error",
|
|
223
|
+
addonId: input.addonId,
|
|
224
|
+
reason: landingReasonForLogin(reasonOf(err))
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
const entry = input.proxy.entryPath(input.attemptId, input.authorizeUrl);
|
|
230
|
+
if (entry === null) throw new Error("The sign-in page host is not reachable through the proxy.");
|
|
231
|
+
return entry;
|
|
232
|
+
}
|
|
233
|
+
//#endregion
|
|
121
234
|
//#region src/shared/account-integration-cascade.ts
|
|
122
235
|
async function cascadeRemoveDevicesForIntegration(input) {
|
|
123
236
|
const { reg, devices, addonId, system, integrationId, logger } = input;
|
|
@@ -155,4 +268,4 @@ function setIntegrationDevicesOnline(devices, system, integrationId, online) {
|
|
|
155
268
|
return count;
|
|
156
269
|
}
|
|
157
270
|
//#endregion
|
|
158
|
-
export {
|
|
271
|
+
export { createSignInAttemptStore as a, openSignInProxy as i, cascadeRemoveDevicesForIntegration as n, loginFailureForAttempt as o, createSignInClaims as r, parseOAuthCallback as s, setIntegrationDevicesOnline as t };
|