@camstack/addon-provider-bosch 0.1.3 → 0.1.4
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/dist/account-integration-offline-C_Tvsemc.js +119 -0
- package/dist/account-integration-offline-DKdrL0V9.mjs +102 -0
- package/dist/{dist-B-Un7bqT.js → dist-BsKpHOt8.js} +229 -196
- package/dist/{dist-BiZi19Lw.mjs → dist-LJMt_ZXW.mjs} +229 -196
- package/dist/home-connect/addon.js +50 -21
- package/dist/home-connect/addon.mjs +50 -21
- package/dist/homecom/addon.js +546 -123
- package/dist/homecom/addon.mjs +546 -123
- package/dist/shc/addon.js +1 -1
- package/dist/shc/addon.mjs +1 -1
- package/i18n/en.json +27 -1
- package/i18n/it.json +27 -1
- package/package.json +1 -1
- package/dist/account-integration-offline-BBqEtCgz.js +0 -50
- package/dist/account-integration-offline-BfoLYzxz.mjs +0 -39
|
@@ -7800,6 +7800,12 @@ function normalizeAddonInitResult(result) {
|
|
|
7800
7800
|
if (Array.isArray(result)) return { providers: result };
|
|
7801
7801
|
return result;
|
|
7802
7802
|
}
|
|
7803
|
+
/**
|
|
7804
|
+
* The share-view scope kinds a `csv_*` share token can carry. The hub mints
|
|
7805
|
+
* and validates them (`share-token.service.ts` builds its enum from this
|
|
7806
|
+
* list); a data plane names the kinds it ACCEPTS in `share`.
|
|
7807
|
+
*/
|
|
7808
|
+
var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
|
|
7803
7809
|
/** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
|
|
7804
7810
|
var PeerBytesTicketSchema = object({
|
|
7805
7811
|
/** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
|
|
@@ -12487,6 +12493,229 @@ var ConvertResultSchema = object({
|
|
|
12487
12493
|
files: array(string()).readonly()
|
|
12488
12494
|
})).readonly()
|
|
12489
12495
|
});
|
|
12496
|
+
/**
|
|
12497
|
+
* Zod schemas for persisted record types.
|
|
12498
|
+
*
|
|
12499
|
+
* These schemas serve as the single source of truth for types that are
|
|
12500
|
+
* stored in the settings-store backend and retrieved via tRPC. They
|
|
12501
|
+
* replace the manual TypeScript interfaces with schema-derived types,
|
|
12502
|
+
* enabling runtime validation when reading from the backend.
|
|
12503
|
+
*
|
|
12504
|
+
* Convention: each schema is named `<Type>Schema`, the inferred TS type
|
|
12505
|
+
* keeps the original name for backward compatibility.
|
|
12506
|
+
*/
|
|
12507
|
+
/**
|
|
12508
|
+
* Auth model (v2 — 2026-05-12 caps-only):
|
|
12509
|
+
*
|
|
12510
|
+
* Two principals:
|
|
12511
|
+
* - `isAdmin: true` — full bypass. Admin scope set is ignored.
|
|
12512
|
+
* - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
|
|
12513
|
+
*
|
|
12514
|
+
* No role enum. There used to be `viewer / agent / scoped` and earlier
|
|
12515
|
+
* `super_admin` — collapsed onto the single boolean because every
|
|
12516
|
+
* non-admin caller is governed by the same scope-check rules; the
|
|
12517
|
+
* distinction was only ever used to route role-specific procedures
|
|
12518
|
+
* which we no longer have. CLI / webhook integrations are `cst_*` tokens
|
|
12519
|
+
* with `isAdmin: false` + an explicit scope list.
|
|
12520
|
+
*/
|
|
12521
|
+
/** Per-method access flavour. Materialised on every cap method via codegen. */
|
|
12522
|
+
var MethodAccessSchema = _enum([
|
|
12523
|
+
"view",
|
|
12524
|
+
"create",
|
|
12525
|
+
"delete"
|
|
12526
|
+
]);
|
|
12527
|
+
/**
|
|
12528
|
+
* A single scope grant. Three forms — checked in matcher with OR
|
|
12529
|
+
* semantics; the first match decides whether the call passes:
|
|
12530
|
+
*
|
|
12531
|
+
* - `category` — broadest. `target: 'device' | 'system'` selects every
|
|
12532
|
+
* cap with the matching `scope` declaration. New caps inherit grants
|
|
12533
|
+
* automatically — adding a device cap to the codebase makes it
|
|
12534
|
+
* reachable to every user with `category:device [...]` without a
|
|
12535
|
+
* re-grant. Use this for "family viewer" / "operator" tiers.
|
|
12536
|
+
*
|
|
12537
|
+
* - `addon` — every cap that ships inside a given addon id. Niche;
|
|
12538
|
+
* useful when an integration needs to interact with one specific
|
|
12539
|
+
* addon's surface regardless of how many caps it exports.
|
|
12540
|
+
*
|
|
12541
|
+
* - `capability` — the most precise form. Single cap name. Use for
|
|
12542
|
+
* integration tokens where you want to grant `addons.installPackage`
|
|
12543
|
+
* and nothing else.
|
|
12544
|
+
*
|
|
12545
|
+
* `access` is REQUIRED with min 1 element. `admin` bypass is the
|
|
12546
|
+
* "give me everything" escape; there is no wildcard scope.
|
|
12547
|
+
*/
|
|
12548
|
+
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
12549
|
+
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
12550
|
+
var CapScopeSchema = _enum(["device", "system"]);
|
|
12551
|
+
/**
|
|
12552
|
+
* DeviceSelector (scope model v3 — 2026-08-12).
|
|
12553
|
+
*
|
|
12554
|
+
* A `device` grant no longer carries a frozen list of deviceIds. It carries
|
|
12555
|
+
* a SELECTOR the matcher resolves against the live fleet, so the grant can be
|
|
12556
|
+
* DYNAMIC: a `types:['camera']` selector automatically covers a camera added
|
|
12557
|
+
* AFTER the grant was minted — no re-grant, no re-login.
|
|
12558
|
+
*
|
|
12559
|
+
* - `all` — every device in the deployment. The broad viewer/operator
|
|
12560
|
+
* lever without a `category` grant (a `category` grant also covers device
|
|
12561
|
+
* caps that carry no deviceId; `all` is specifically the device set).
|
|
12562
|
+
* - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
|
|
12563
|
+
* grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
|
|
12564
|
+
* is NOT covered until the grant is edited.
|
|
12565
|
+
* - `types` — every device of a `DeviceType` (e.g. every `camera`).
|
|
12566
|
+
* DYNAMIC. A device that changes type, or a new device of the type,
|
|
12567
|
+
* re-resolves on the next request.
|
|
12568
|
+
* - `locations` — every device whose operator-assigned `location` label is
|
|
12569
|
+
* in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
|
|
12570
|
+
* null/unset location matches NO `locations` selector.
|
|
12571
|
+
*/
|
|
12572
|
+
var DeviceSelectorSchema = discriminatedUnion("kind", [
|
|
12573
|
+
object({ kind: literal("all") }),
|
|
12574
|
+
object({
|
|
12575
|
+
kind: literal("ids"),
|
|
12576
|
+
ids: array(number().int()).min(1)
|
|
12577
|
+
}),
|
|
12578
|
+
object({
|
|
12579
|
+
kind: literal("types"),
|
|
12580
|
+
types: array(_enum(DeviceType)).min(1)
|
|
12581
|
+
}),
|
|
12582
|
+
object({
|
|
12583
|
+
kind: literal("locations"),
|
|
12584
|
+
locations: array(string().min(1)).min(1)
|
|
12585
|
+
})
|
|
12586
|
+
]);
|
|
12587
|
+
var DeviceTokenScopeSchema = object({
|
|
12588
|
+
type: literal("device"),
|
|
12589
|
+
/** The device SET this grant covers — resolved against the live fleet. */
|
|
12590
|
+
selector: DeviceSelectorSchema,
|
|
12591
|
+
access: array(MethodAccessSchema).min(1),
|
|
12592
|
+
/**
|
|
12593
|
+
* Whether a grant on a PARENT device transparently covers its accessory
|
|
12594
|
+
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
12595
|
+
* Direction is parent → children ONLY.
|
|
12596
|
+
*
|
|
12597
|
+
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
12598
|
+
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
12599
|
+
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
12600
|
+
* operator must grant on the child, not inherit from the parent). Set it
|
|
12601
|
+
* explicitly to override that default per grant.
|
|
12602
|
+
*/
|
|
12603
|
+
includeLinked: boolean().optional()
|
|
12604
|
+
});
|
|
12605
|
+
/**
|
|
12606
|
+
* v2 → v3 lazy migration. A pre-v3 `device` grant carried
|
|
12607
|
+
* `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
|
|
12608
|
+
* `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
|
|
12609
|
+
* EVERY parse path — stored records AND the JWT-carried scope arrays
|
|
12610
|
+
* normalised at the request boundary ({@link normalizeTokenScopes} in
|
|
12611
|
+
* `device-selector.ts`). Chosen over a one-time DB migration because a
|
|
12612
|
+
* migration cannot reach a JWT already in a client's hands; parse-time
|
|
12613
|
+
* migration covers both without a flag day. No cast — the raw object is read
|
|
12614
|
+
* through `Reflect.get` (its static type is `unknown`).
|
|
12615
|
+
*/
|
|
12616
|
+
function migrateLegacyTokenScope(raw) {
|
|
12617
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
12618
|
+
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
12619
|
+
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
12620
|
+
const targets = Reflect.get(raw, "targets");
|
|
12621
|
+
if (!Array.isArray(targets)) return raw;
|
|
12622
|
+
return {
|
|
12623
|
+
type: "device",
|
|
12624
|
+
selector: {
|
|
12625
|
+
kind: "ids",
|
|
12626
|
+
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
12627
|
+
},
|
|
12628
|
+
access: Reflect.get(raw, "access")
|
|
12629
|
+
};
|
|
12630
|
+
}
|
|
12631
|
+
var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
|
|
12632
|
+
object({
|
|
12633
|
+
type: literal("category"),
|
|
12634
|
+
target: CapScopeSchema,
|
|
12635
|
+
access: array(MethodAccessSchema).min(1)
|
|
12636
|
+
}),
|
|
12637
|
+
object({
|
|
12638
|
+
type: literal("capability"),
|
|
12639
|
+
target: string(),
|
|
12640
|
+
access: array(MethodAccessSchema).min(1)
|
|
12641
|
+
}),
|
|
12642
|
+
object({
|
|
12643
|
+
type: literal("addon"),
|
|
12644
|
+
target: string(),
|
|
12645
|
+
access: array(MethodAccessSchema).min(1)
|
|
12646
|
+
}),
|
|
12647
|
+
DeviceTokenScopeSchema
|
|
12648
|
+
]));
|
|
12649
|
+
object({
|
|
12650
|
+
id: string(),
|
|
12651
|
+
username: string(),
|
|
12652
|
+
passwordHash: string(),
|
|
12653
|
+
/**
|
|
12654
|
+
* Admin bypass. When true, the middleware skips the scope-access
|
|
12655
|
+
* check entirely. There is no other axis of privilege; the legacy
|
|
12656
|
+
* role enum collapsed onto this boolean in v2.
|
|
12657
|
+
*/
|
|
12658
|
+
isAdmin: boolean().default(false),
|
|
12659
|
+
allowedProviders: AllowedProviderSchema,
|
|
12660
|
+
allowedDevices: AllowedDevicesSchema,
|
|
12661
|
+
/**
|
|
12662
|
+
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
12663
|
+
* ignored. Non-admins without scopes are locked out of every
|
|
12664
|
+
* protected call.
|
|
12665
|
+
*/
|
|
12666
|
+
scopes: array(TokenScopeSchema).default([]),
|
|
12667
|
+
createdAt: number(),
|
|
12668
|
+
updatedAt: number()
|
|
12669
|
+
});
|
|
12670
|
+
object({
|
|
12671
|
+
id: string(),
|
|
12672
|
+
label: string(),
|
|
12673
|
+
isAdmin: boolean().default(false),
|
|
12674
|
+
allowedProviders: AllowedProviderSchema,
|
|
12675
|
+
allowedDevices: AllowedDevicesSchema,
|
|
12676
|
+
tokenHash: string(),
|
|
12677
|
+
tokenPrefix: string(),
|
|
12678
|
+
createdAt: number(),
|
|
12679
|
+
lastUsedAt: number().optional()
|
|
12680
|
+
});
|
|
12681
|
+
object({
|
|
12682
|
+
id: string(),
|
|
12683
|
+
userId: string(),
|
|
12684
|
+
name: string(),
|
|
12685
|
+
tokenHash: string(),
|
|
12686
|
+
tokenPrefix: string(),
|
|
12687
|
+
scopes: array(TokenScopeSchema),
|
|
12688
|
+
expiresAt: number().nullish(),
|
|
12689
|
+
lastUsedAt: number().nullish(),
|
|
12690
|
+
createdAt: number()
|
|
12691
|
+
});
|
|
12692
|
+
/** What a share-view grant lets its holder see. */
|
|
12693
|
+
var ShareViewGrantSchema = object({
|
|
12694
|
+
kind: _enum(SHARE_VIEW_KINDS),
|
|
12695
|
+
deviceIds: array(number().int().nonnegative()).min(1).max(64),
|
|
12696
|
+
/** Talk-back, opt-in at mint time; absent means off. */
|
|
12697
|
+
talk: boolean().optional()
|
|
12698
|
+
});
|
|
12699
|
+
var ShareViewScopeSchema = object({
|
|
12700
|
+
type: literal("share-view"),
|
|
12701
|
+
grant: ShareViewGrantSchema
|
|
12702
|
+
});
|
|
12703
|
+
var AddonRoutesScopeSchema = object({
|
|
12704
|
+
type: literal("addon-routes"),
|
|
12705
|
+
access: array(MethodAccessSchema).min(1)
|
|
12706
|
+
});
|
|
12707
|
+
var CredentialScopeSchema = union([
|
|
12708
|
+
literal("*"),
|
|
12709
|
+
ShareViewScopeSchema,
|
|
12710
|
+
AddonRoutesScopeSchema,
|
|
12711
|
+
TokenScopeSchema
|
|
12712
|
+
]);
|
|
12713
|
+
object({
|
|
12714
|
+
kind: literal("credential"),
|
|
12715
|
+
sub: string().min(1),
|
|
12716
|
+
jti: string().min(16),
|
|
12717
|
+
scopes: array(CredentialScopeSchema).min(1)
|
|
12718
|
+
});
|
|
12490
12719
|
_enum(["new-session", "step-up"]);
|
|
12491
12720
|
/** The languages the hub's app landing pages are written in (D858). */
|
|
12492
12721
|
var AppPageLangSchema = _enum(["en", "it"]);
|
|
@@ -43981,202 +44210,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
43981
44210
|
auth: "admin"
|
|
43982
44211
|
}), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
|
|
43983
44212
|
/**
|
|
43984
|
-
* Zod schemas for persisted record types.
|
|
43985
|
-
*
|
|
43986
|
-
* These schemas serve as the single source of truth for types that are
|
|
43987
|
-
* stored in the settings-store backend and retrieved via tRPC. They
|
|
43988
|
-
* replace the manual TypeScript interfaces with schema-derived types,
|
|
43989
|
-
* enabling runtime validation when reading from the backend.
|
|
43990
|
-
*
|
|
43991
|
-
* Convention: each schema is named `<Type>Schema`, the inferred TS type
|
|
43992
|
-
* keeps the original name for backward compatibility.
|
|
43993
|
-
*/
|
|
43994
|
-
/**
|
|
43995
|
-
* Auth model (v2 — 2026-05-12 caps-only):
|
|
43996
|
-
*
|
|
43997
|
-
* Two principals:
|
|
43998
|
-
* - `isAdmin: true` — full bypass. Admin scope set is ignored.
|
|
43999
|
-
* - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
|
|
44000
|
-
*
|
|
44001
|
-
* No role enum. There used to be `viewer / agent / scoped` and earlier
|
|
44002
|
-
* `super_admin` — collapsed onto the single boolean because every
|
|
44003
|
-
* non-admin caller is governed by the same scope-check rules; the
|
|
44004
|
-
* distinction was only ever used to route role-specific procedures
|
|
44005
|
-
* which we no longer have. CLI / webhook integrations are `cst_*` tokens
|
|
44006
|
-
* with `isAdmin: false` + an explicit scope list.
|
|
44007
|
-
*/
|
|
44008
|
-
/** Per-method access flavour. Materialised on every cap method via codegen. */
|
|
44009
|
-
var MethodAccessSchema = _enum([
|
|
44010
|
-
"view",
|
|
44011
|
-
"create",
|
|
44012
|
-
"delete"
|
|
44013
|
-
]);
|
|
44014
|
-
/**
|
|
44015
|
-
* A single scope grant. Three forms — checked in matcher with OR
|
|
44016
|
-
* semantics; the first match decides whether the call passes:
|
|
44017
|
-
*
|
|
44018
|
-
* - `category` — broadest. `target: 'device' | 'system'` selects every
|
|
44019
|
-
* cap with the matching `scope` declaration. New caps inherit grants
|
|
44020
|
-
* automatically — adding a device cap to the codebase makes it
|
|
44021
|
-
* reachable to every user with `category:device [...]` without a
|
|
44022
|
-
* re-grant. Use this for "family viewer" / "operator" tiers.
|
|
44023
|
-
*
|
|
44024
|
-
* - `addon` — every cap that ships inside a given addon id. Niche;
|
|
44025
|
-
* useful when an integration needs to interact with one specific
|
|
44026
|
-
* addon's surface regardless of how many caps it exports.
|
|
44027
|
-
*
|
|
44028
|
-
* - `capability` — the most precise form. Single cap name. Use for
|
|
44029
|
-
* integration tokens where you want to grant `addons.installPackage`
|
|
44030
|
-
* and nothing else.
|
|
44031
|
-
*
|
|
44032
|
-
* `access` is REQUIRED with min 1 element. `admin` bypass is the
|
|
44033
|
-
* "give me everything" escape; there is no wildcard scope.
|
|
44034
|
-
*/
|
|
44035
|
-
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
44036
|
-
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
44037
|
-
var CapScopeSchema = _enum(["device", "system"]);
|
|
44038
|
-
/**
|
|
44039
|
-
* DeviceSelector (scope model v3 — 2026-08-12).
|
|
44040
|
-
*
|
|
44041
|
-
* A `device` grant no longer carries a frozen list of deviceIds. It carries
|
|
44042
|
-
* a SELECTOR the matcher resolves against the live fleet, so the grant can be
|
|
44043
|
-
* DYNAMIC: a `types:['camera']` selector automatically covers a camera added
|
|
44044
|
-
* AFTER the grant was minted — no re-grant, no re-login.
|
|
44045
|
-
*
|
|
44046
|
-
* - `all` — every device in the deployment. The broad viewer/operator
|
|
44047
|
-
* lever without a `category` grant (a `category` grant also covers device
|
|
44048
|
-
* caps that carry no deviceId; `all` is specifically the device set).
|
|
44049
|
-
* - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
|
|
44050
|
-
* grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
|
|
44051
|
-
* is NOT covered until the grant is edited.
|
|
44052
|
-
* - `types` — every device of a `DeviceType` (e.g. every `camera`).
|
|
44053
|
-
* DYNAMIC. A device that changes type, or a new device of the type,
|
|
44054
|
-
* re-resolves on the next request.
|
|
44055
|
-
* - `locations` — every device whose operator-assigned `location` label is
|
|
44056
|
-
* in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
|
|
44057
|
-
* null/unset location matches NO `locations` selector.
|
|
44058
|
-
*/
|
|
44059
|
-
var DeviceSelectorSchema = discriminatedUnion("kind", [
|
|
44060
|
-
object({ kind: literal("all") }),
|
|
44061
|
-
object({
|
|
44062
|
-
kind: literal("ids"),
|
|
44063
|
-
ids: array(number().int()).min(1)
|
|
44064
|
-
}),
|
|
44065
|
-
object({
|
|
44066
|
-
kind: literal("types"),
|
|
44067
|
-
types: array(_enum(DeviceType)).min(1)
|
|
44068
|
-
}),
|
|
44069
|
-
object({
|
|
44070
|
-
kind: literal("locations"),
|
|
44071
|
-
locations: array(string().min(1)).min(1)
|
|
44072
|
-
})
|
|
44073
|
-
]);
|
|
44074
|
-
var DeviceTokenScopeSchema = object({
|
|
44075
|
-
type: literal("device"),
|
|
44076
|
-
/** The device SET this grant covers — resolved against the live fleet. */
|
|
44077
|
-
selector: DeviceSelectorSchema,
|
|
44078
|
-
access: array(MethodAccessSchema).min(1),
|
|
44079
|
-
/**
|
|
44080
|
-
* Whether a grant on a PARENT device transparently covers its accessory
|
|
44081
|
-
* CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
|
|
44082
|
-
* Direction is parent → children ONLY.
|
|
44083
|
-
*
|
|
44084
|
-
* Absent → the matcher DERIVES it from the access flavour: `view`
|
|
44085
|
-
* inherits (a camera viewer sees the camera's accessories), `create` /
|
|
44086
|
-
* `delete` do NOT (actuating/removing a child is an explicit act the
|
|
44087
|
-
* operator must grant on the child, not inherit from the parent). Set it
|
|
44088
|
-
* explicitly to override that default per grant.
|
|
44089
|
-
*/
|
|
44090
|
-
includeLinked: boolean().optional()
|
|
44091
|
-
});
|
|
44092
|
-
/**
|
|
44093
|
-
* v2 → v3 lazy migration. A pre-v3 `device` grant carried
|
|
44094
|
-
* `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
|
|
44095
|
-
* `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
|
|
44096
|
-
* EVERY parse path — stored records AND the JWT-carried scope arrays
|
|
44097
|
-
* normalised at the request boundary ({@link normalizeTokenScopes} in
|
|
44098
|
-
* `device-selector.ts`). Chosen over a one-time DB migration because a
|
|
44099
|
-
* migration cannot reach a JWT already in a client's hands; parse-time
|
|
44100
|
-
* migration covers both without a flag day. No cast — the raw object is read
|
|
44101
|
-
* through `Reflect.get` (its static type is `unknown`).
|
|
44102
|
-
*/
|
|
44103
|
-
function migrateLegacyTokenScope(raw) {
|
|
44104
|
-
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
44105
|
-
if (Reflect.get(raw, "type") !== "device") return raw;
|
|
44106
|
-
if (Reflect.get(raw, "selector") !== void 0) return raw;
|
|
44107
|
-
const targets = Reflect.get(raw, "targets");
|
|
44108
|
-
if (!Array.isArray(targets)) return raw;
|
|
44109
|
-
return {
|
|
44110
|
-
type: "device",
|
|
44111
|
-
selector: {
|
|
44112
|
-
kind: "ids",
|
|
44113
|
-
ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
|
|
44114
|
-
},
|
|
44115
|
-
access: Reflect.get(raw, "access")
|
|
44116
|
-
};
|
|
44117
|
-
}
|
|
44118
|
-
var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
|
|
44119
|
-
object({
|
|
44120
|
-
type: literal("category"),
|
|
44121
|
-
target: CapScopeSchema,
|
|
44122
|
-
access: array(MethodAccessSchema).min(1)
|
|
44123
|
-
}),
|
|
44124
|
-
object({
|
|
44125
|
-
type: literal("capability"),
|
|
44126
|
-
target: string(),
|
|
44127
|
-
access: array(MethodAccessSchema).min(1)
|
|
44128
|
-
}),
|
|
44129
|
-
object({
|
|
44130
|
-
type: literal("addon"),
|
|
44131
|
-
target: string(),
|
|
44132
|
-
access: array(MethodAccessSchema).min(1)
|
|
44133
|
-
}),
|
|
44134
|
-
DeviceTokenScopeSchema
|
|
44135
|
-
]));
|
|
44136
|
-
object({
|
|
44137
|
-
id: string(),
|
|
44138
|
-
username: string(),
|
|
44139
|
-
passwordHash: string(),
|
|
44140
|
-
/**
|
|
44141
|
-
* Admin bypass. When true, the middleware skips the scope-access
|
|
44142
|
-
* check entirely. There is no other axis of privilege; the legacy
|
|
44143
|
-
* role enum collapsed onto this boolean in v2.
|
|
44144
|
-
*/
|
|
44145
|
-
isAdmin: boolean().default(false),
|
|
44146
|
-
allowedProviders: AllowedProviderSchema,
|
|
44147
|
-
allowedDevices: AllowedDevicesSchema,
|
|
44148
|
-
/**
|
|
44149
|
-
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
44150
|
-
* ignored. Non-admins without scopes are locked out of every
|
|
44151
|
-
* protected call.
|
|
44152
|
-
*/
|
|
44153
|
-
scopes: array(TokenScopeSchema).default([]),
|
|
44154
|
-
createdAt: number(),
|
|
44155
|
-
updatedAt: number()
|
|
44156
|
-
});
|
|
44157
|
-
object({
|
|
44158
|
-
id: string(),
|
|
44159
|
-
label: string(),
|
|
44160
|
-
isAdmin: boolean().default(false),
|
|
44161
|
-
allowedProviders: AllowedProviderSchema,
|
|
44162
|
-
allowedDevices: AllowedDevicesSchema,
|
|
44163
|
-
tokenHash: string(),
|
|
44164
|
-
tokenPrefix: string(),
|
|
44165
|
-
createdAt: number(),
|
|
44166
|
-
lastUsedAt: number().optional()
|
|
44167
|
-
});
|
|
44168
|
-
object({
|
|
44169
|
-
id: string(),
|
|
44170
|
-
userId: string(),
|
|
44171
|
-
name: string(),
|
|
44172
|
-
tokenHash: string(),
|
|
44173
|
-
tokenPrefix: string(),
|
|
44174
|
-
scopes: array(TokenScopeSchema),
|
|
44175
|
-
expiresAt: number().nullish(),
|
|
44176
|
-
lastUsedAt: number().nullish(),
|
|
44177
|
-
createdAt: number()
|
|
44178
|
-
});
|
|
44179
|
-
/**
|
|
44180
44213
|
* `oauth-integration` — a collection cap by which an addon declares
|
|
44181
44214
|
* itself a linkable OAuth client of the hub's core `/oauth2/*` server.
|
|
44182
44215
|
* Each provider returns a static descriptor; the core enumerates them
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_dist = require("../dist-
|
|
3
|
-
const require_account_integration_offline = require("../account-integration-offline-
|
|
2
|
+
const require_dist = require("../dist-BsKpHOt8.js");
|
|
3
|
+
const require_account_integration_offline = require("../account-integration-offline-C_Tvsemc.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");
|
|
@@ -589,6 +589,14 @@ function tokensFromBody(body) {
|
|
|
589
589
|
refresh: typeof refresh === "string" ? refresh : ""
|
|
590
590
|
};
|
|
591
591
|
}
|
|
592
|
+
function isInvalidGrant(body) {
|
|
593
|
+
try {
|
|
594
|
+
const parsed = JSON.parse(body);
|
|
595
|
+
return isRecord$1(parsed) && parsed["error"] === "invalid_grant";
|
|
596
|
+
} catch {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
592
600
|
function accountSummary(appliances) {
|
|
593
601
|
if (appliances.length === 0) return "Signed in. No appliances on this account yet.";
|
|
594
602
|
const names = appliances.map((app) => app.name).join(", ");
|
|
@@ -752,27 +760,34 @@ function beginHomeConnectSignIn() {
|
|
|
752
760
|
state
|
|
753
761
|
};
|
|
754
762
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
763
|
+
var LOGIN_MESSAGES = {
|
|
764
|
+
empty: "Nothing was pasted. Paste the address the browser tried to open after you signed in.",
|
|
765
|
+
"no-code": "No sign-in code was found in what you pasted. Copy the whole address that starts with hcauth://, or just the code.",
|
|
766
|
+
"state-missing": "The pasted address has no state. Copy the complete address, or create the login link again.",
|
|
767
|
+
"state-mismatch": "State mismatch: this address belongs to a different sign-in attempt. Create the login link again and use the address that follows it.",
|
|
768
|
+
denied: "Home Connect refused the sign-in. Create the login link again and approve the access.",
|
|
769
|
+
"code-rejected": "The code was already used or has expired. Create the login link again; a code works once and only for a few minutes."
|
|
770
|
+
};
|
|
771
|
+
/** A failure the operator can act on; `reason` is stable, the message is for the form. */
|
|
772
|
+
var HomeConnectLoginError = class extends Error {
|
|
773
|
+
name = "HomeConnectLoginError";
|
|
774
|
+
reason;
|
|
775
|
+
constructor(reason) {
|
|
776
|
+
super(LOGIN_MESSAGES[reason]);
|
|
777
|
+
this.reason = reason;
|
|
761
778
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
779
|
+
};
|
|
780
|
+
function callbackParams(callback, expectedState) {
|
|
781
|
+
const parsed = require_account_integration_offline.parseOAuthCallback(callback, expectedState);
|
|
782
|
+
if (!parsed.ok) throw new HomeConnectLoginError(parsed.reason);
|
|
765
783
|
return {
|
|
766
|
-
code,
|
|
767
|
-
grantType:
|
|
768
|
-
state
|
|
784
|
+
code: parsed.code,
|
|
785
|
+
grantType: parsed.params.get("grant_type") ?? "authorization_code"
|
|
769
786
|
};
|
|
770
787
|
}
|
|
771
788
|
/** Exchange the pasted redirect for the account token and each appliance profile. */
|
|
772
789
|
async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetch) {
|
|
773
|
-
const
|
|
774
|
-
if (parsed.state !== input.state) throw new Error("State mismatch. Create the login link again.");
|
|
775
|
-
const { code, grantType } = parsed;
|
|
790
|
+
const { code, grantType } = callbackParams(input.callback, input.state);
|
|
776
791
|
const token = await exchange(fetchImpl, new CookieJar(), `${OAUTH_BASE}token`, "POST", { "content-type": "application/x-www-form-urlencoded" }, new URLSearchParams({
|
|
777
792
|
grant_type: grantType,
|
|
778
793
|
client_id: APP_ID$1,
|
|
@@ -780,7 +795,10 @@ async function completeHomeConnectSignIn(input, fetchImpl = homeConnectLoginFetc
|
|
|
780
795
|
code_verifier: input.verifier,
|
|
781
796
|
redirect_uri: REDIRECT_URI
|
|
782
797
|
}).toString());
|
|
783
|
-
if (token.status >= 400)
|
|
798
|
+
if (token.status >= 400) {
|
|
799
|
+
if (isInvalidGrant(token.body)) throw new HomeConnectLoginError("code-rejected");
|
|
800
|
+
throw new Error("Home Connect sign-in was rejected");
|
|
801
|
+
}
|
|
784
802
|
return fetchHomeConnectAccount(tokensFromBody(token.body), fetchImpl);
|
|
785
803
|
}
|
|
786
804
|
//#endregion
|
|
@@ -1233,7 +1251,9 @@ function buildHomeConnectFormSchema() {
|
|
|
1233
1251
|
return { sections: [{
|
|
1234
1252
|
id: "gateway",
|
|
1235
1253
|
title: "Home Connect",
|
|
1236
|
-
|
|
1254
|
+
titleKey: "provider-bosch:home-connect.s_gateway.title",
|
|
1255
|
+
description: "Create the login link and open it in a browser, then sign in. The browser cannot open the app and stops on an address starting with hcauth://auth/prod?code=. Paste that whole address below (or only the code) and press Complete sign-in. The address is shown in Chrome and Edge under DevTools (F12) > Network, with Preserve log ticked before you sign in: select the last redirect and copy its Location header. In Firefox use Network > Headers > Location; in Safari use Develop > Show Web Inspector > Network. A code works once and expires within minutes. The appliances are then downloaded and spoken to on the local network.",
|
|
1256
|
+
descriptionKey: "provider-bosch:home-connect.s_gateway.description",
|
|
1237
1257
|
columns: 1,
|
|
1238
1258
|
fields: [
|
|
1239
1259
|
{
|
|
@@ -1241,6 +1261,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1241
1261
|
addonId: "bosch-home-connect",
|
|
1242
1262
|
key: "__home-connect-begin",
|
|
1243
1263
|
label: "Home Connect",
|
|
1264
|
+
labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-begin.label",
|
|
1244
1265
|
buttonLabel: "Create login link",
|
|
1245
1266
|
action: "beginSignIn",
|
|
1246
1267
|
variant: "primary",
|
|
@@ -1255,13 +1276,15 @@ function buildHomeConnectFormSchema() {
|
|
|
1255
1276
|
type: "textarea",
|
|
1256
1277
|
key: "authorizeUrl",
|
|
1257
1278
|
label: "Login link",
|
|
1279
|
+
labelKey: "provider-bosch:home-connect.s_gateway.authorizeUrl.label",
|
|
1258
1280
|
required: false,
|
|
1259
1281
|
omitFromSettings: true
|
|
1260
1282
|
},
|
|
1261
1283
|
{
|
|
1262
|
-
type: "
|
|
1284
|
+
type: "textarea",
|
|
1263
1285
|
key: "callback",
|
|
1264
|
-
label: "Redirect address",
|
|
1286
|
+
label: "Redirect address or code",
|
|
1287
|
+
labelKey: "provider-bosch:home-connect.s_gateway.callback.label",
|
|
1265
1288
|
required: false,
|
|
1266
1289
|
placeholder: "hcauth://auth/prod?code=…&state=…",
|
|
1267
1290
|
omitFromSettings: true
|
|
@@ -1270,6 +1293,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1270
1293
|
type: "password",
|
|
1271
1294
|
key: "state",
|
|
1272
1295
|
label: "Login state",
|
|
1296
|
+
labelKey: "provider-bosch:home-connect.s_gateway.state.label",
|
|
1273
1297
|
required: false,
|
|
1274
1298
|
omitFromSettings: true,
|
|
1275
1299
|
showWhen: pairedOnly
|
|
@@ -1278,6 +1302,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1278
1302
|
type: "password",
|
|
1279
1303
|
key: "verifier",
|
|
1280
1304
|
label: "Login verifier",
|
|
1305
|
+
labelKey: "provider-bosch:home-connect.s_gateway.verifier.label",
|
|
1281
1306
|
required: false,
|
|
1282
1307
|
omitFromSettings: true,
|
|
1283
1308
|
showWhen: pairedOnly
|
|
@@ -1287,6 +1312,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1287
1312
|
addonId: "bosch-home-connect",
|
|
1288
1313
|
key: "__home-connect-finish",
|
|
1289
1314
|
label: "Finish",
|
|
1315
|
+
labelKey: "provider-bosch:home-connect.s_gateway.__home-connect-finish.label",
|
|
1290
1316
|
buttonLabel: "Complete sign-in",
|
|
1291
1317
|
action: "finishSignIn",
|
|
1292
1318
|
variant: "primary",
|
|
@@ -1307,6 +1333,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1307
1333
|
type: "password",
|
|
1308
1334
|
key: "token",
|
|
1309
1335
|
label: "Access token",
|
|
1336
|
+
labelKey: "provider-bosch:home-connect.s_gateway.token.label",
|
|
1310
1337
|
required: true,
|
|
1311
1338
|
showWhen: pairedOnly
|
|
1312
1339
|
},
|
|
@@ -1314,6 +1341,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1314
1341
|
type: "password",
|
|
1315
1342
|
key: "refresh",
|
|
1316
1343
|
label: "Refresh token",
|
|
1344
|
+
labelKey: "provider-bosch:home-connect.s_gateway.refresh.label",
|
|
1317
1345
|
required: false,
|
|
1318
1346
|
showWhen: pairedOnly
|
|
1319
1347
|
},
|
|
@@ -1321,6 +1349,7 @@ function buildHomeConnectFormSchema() {
|
|
|
1321
1349
|
type: "textarea",
|
|
1322
1350
|
key: "appliances",
|
|
1323
1351
|
label: "Appliances",
|
|
1352
|
+
labelKey: "provider-bosch:home-connect.s_gateway.appliances.label",
|
|
1324
1353
|
required: true,
|
|
1325
1354
|
showWhen: pairedOnly
|
|
1326
1355
|
},
|