@camstack/addon-matter-broker 0.2.130 → 0.2.131

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.
Files changed (3) hide show
  1. package/dist/addon.js +229 -196
  2. package/dist/addon.mjs +229 -196
  3. package/package.json +1 -1
package/dist/addon.js CHANGED
@@ -7943,6 +7943,12 @@ function normalizeAddonInitResult(result) {
7943
7943
  if (Array.isArray(result)) return { providers: result };
7944
7944
  return result;
7945
7945
  }
7946
+ /**
7947
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7948
+ * and validates them (`share-token.service.ts` builds its enum from this
7949
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7950
+ */
7951
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7946
7952
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7947
7953
  var PeerBytesTicketSchema = object({
7948
7954
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12808,6 +12814,229 @@ var ConvertResultSchema = object({
12808
12814
  files: array(string$2()).readonly()
12809
12815
  })).readonly()
12810
12816
  });
12817
+ /**
12818
+ * Zod schemas for persisted record types.
12819
+ *
12820
+ * These schemas serve as the single source of truth for types that are
12821
+ * stored in the settings-store backend and retrieved via tRPC. They
12822
+ * replace the manual TypeScript interfaces with schema-derived types,
12823
+ * enabling runtime validation when reading from the backend.
12824
+ *
12825
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12826
+ * keeps the original name for backward compatibility.
12827
+ */
12828
+ /**
12829
+ * Auth model (v2 — 2026-05-12 caps-only):
12830
+ *
12831
+ * Two principals:
12832
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12833
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12834
+ *
12835
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12836
+ * `super_admin` — collapsed onto the single boolean because every
12837
+ * non-admin caller is governed by the same scope-check rules; the
12838
+ * distinction was only ever used to route role-specific procedures
12839
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12840
+ * with `isAdmin: false` + an explicit scope list.
12841
+ */
12842
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12843
+ var MethodAccessSchema = _enum([
12844
+ "view",
12845
+ "create",
12846
+ "delete"
12847
+ ]);
12848
+ /**
12849
+ * A single scope grant. Three forms — checked in matcher with OR
12850
+ * semantics; the first match decides whether the call passes:
12851
+ *
12852
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12853
+ * cap with the matching `scope` declaration. New caps inherit grants
12854
+ * automatically — adding a device cap to the codebase makes it
12855
+ * reachable to every user with `category:device [...]` without a
12856
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12857
+ *
12858
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12859
+ * useful when an integration needs to interact with one specific
12860
+ * addon's surface regardless of how many caps it exports.
12861
+ *
12862
+ * - `capability` — the most precise form. Single cap name. Use for
12863
+ * integration tokens where you want to grant `addons.installPackage`
12864
+ * and nothing else.
12865
+ *
12866
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12867
+ * "give me everything" escape; there is no wildcard scope.
12868
+ */
12869
+ var AllowedProviderSchema = union([literal("*"), array(string$2())]);
12870
+ var AllowedDevicesSchema = record(string$2(), union([literal("*"), array(string$2())]));
12871
+ var CapScopeSchema = _enum(["device", "system"]);
12872
+ /**
12873
+ * DeviceSelector (scope model v3 — 2026-08-12).
12874
+ *
12875
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12876
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12877
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12878
+ * AFTER the grant was minted — no re-grant, no re-login.
12879
+ *
12880
+ * - `all` — every device in the deployment. The broad viewer/operator
12881
+ * lever without a `category` grant (a `category` grant also covers device
12882
+ * caps that carry no deviceId; `all` is specifically the device set).
12883
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12884
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12885
+ * is NOT covered until the grant is edited.
12886
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12887
+ * DYNAMIC. A device that changes type, or a new device of the type,
12888
+ * re-resolves on the next request.
12889
+ * - `locations` — every device whose operator-assigned `location` label is
12890
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12891
+ * null/unset location matches NO `locations` selector.
12892
+ */
12893
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12894
+ object({ kind: literal("all") }),
12895
+ object({
12896
+ kind: literal("ids"),
12897
+ ids: array(number().int()).min(1)
12898
+ }),
12899
+ object({
12900
+ kind: literal("types"),
12901
+ types: array(_enum(DeviceType)).min(1)
12902
+ }),
12903
+ object({
12904
+ kind: literal("locations"),
12905
+ locations: array(string$2().min(1)).min(1)
12906
+ })
12907
+ ]);
12908
+ var DeviceTokenScopeSchema = object({
12909
+ type: literal("device"),
12910
+ /** The device SET this grant covers — resolved against the live fleet. */
12911
+ selector: DeviceSelectorSchema,
12912
+ access: array(MethodAccessSchema).min(1),
12913
+ /**
12914
+ * Whether a grant on a PARENT device transparently covers its accessory
12915
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12916
+ * Direction is parent → children ONLY.
12917
+ *
12918
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12919
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12920
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12921
+ * operator must grant on the child, not inherit from the parent). Set it
12922
+ * explicitly to override that default per grant.
12923
+ */
12924
+ includeLinked: boolean().optional()
12925
+ });
12926
+ /**
12927
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12928
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12929
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12930
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12931
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12932
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12933
+ * migration cannot reach a JWT already in a client's hands; parse-time
12934
+ * migration covers both without a flag day. No cast — the raw object is read
12935
+ * through `Reflect.get` (its static type is `unknown`).
12936
+ */
12937
+ function migrateLegacyTokenScope(raw) {
12938
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12939
+ if (Reflect.get(raw, "type") !== "device") return raw;
12940
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12941
+ const targets = Reflect.get(raw, "targets");
12942
+ if (!Array.isArray(targets)) return raw;
12943
+ return {
12944
+ type: "device",
12945
+ selector: {
12946
+ kind: "ids",
12947
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12948
+ },
12949
+ access: Reflect.get(raw, "access")
12950
+ };
12951
+ }
12952
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12953
+ object({
12954
+ type: literal("category"),
12955
+ target: CapScopeSchema,
12956
+ access: array(MethodAccessSchema).min(1)
12957
+ }),
12958
+ object({
12959
+ type: literal("capability"),
12960
+ target: string$2(),
12961
+ access: array(MethodAccessSchema).min(1)
12962
+ }),
12963
+ object({
12964
+ type: literal("addon"),
12965
+ target: string$2(),
12966
+ access: array(MethodAccessSchema).min(1)
12967
+ }),
12968
+ DeviceTokenScopeSchema
12969
+ ]));
12970
+ object({
12971
+ id: string$2(),
12972
+ username: string$2(),
12973
+ passwordHash: string$2(),
12974
+ /**
12975
+ * Admin bypass. When true, the middleware skips the scope-access
12976
+ * check entirely. There is no other axis of privilege; the legacy
12977
+ * role enum collapsed onto this boolean in v2.
12978
+ */
12979
+ isAdmin: boolean().default(false),
12980
+ allowedProviders: AllowedProviderSchema,
12981
+ allowedDevices: AllowedDevicesSchema,
12982
+ /**
12983
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12984
+ * ignored. Non-admins without scopes are locked out of every
12985
+ * protected call.
12986
+ */
12987
+ scopes: array(TokenScopeSchema).default([]),
12988
+ createdAt: number(),
12989
+ updatedAt: number()
12990
+ });
12991
+ object({
12992
+ id: string$2(),
12993
+ label: string$2(),
12994
+ isAdmin: boolean().default(false),
12995
+ allowedProviders: AllowedProviderSchema,
12996
+ allowedDevices: AllowedDevicesSchema,
12997
+ tokenHash: string$2(),
12998
+ tokenPrefix: string$2(),
12999
+ createdAt: number(),
13000
+ lastUsedAt: number().optional()
13001
+ });
13002
+ object({
13003
+ id: string$2(),
13004
+ userId: string$2(),
13005
+ name: string$2(),
13006
+ tokenHash: string$2(),
13007
+ tokenPrefix: string$2(),
13008
+ scopes: array(TokenScopeSchema),
13009
+ expiresAt: number().nullish(),
13010
+ lastUsedAt: number().nullish(),
13011
+ createdAt: number()
13012
+ });
13013
+ /** What a share-view grant lets its holder see. */
13014
+ var ShareViewGrantSchema = object({
13015
+ kind: _enum(SHARE_VIEW_KINDS),
13016
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13017
+ /** Talk-back, opt-in at mint time; absent means off. */
13018
+ talk: boolean().optional()
13019
+ });
13020
+ var ShareViewScopeSchema = object({
13021
+ type: literal("share-view"),
13022
+ grant: ShareViewGrantSchema
13023
+ });
13024
+ var AddonRoutesScopeSchema = object({
13025
+ type: literal("addon-routes"),
13026
+ access: array(MethodAccessSchema).min(1)
13027
+ });
13028
+ var CredentialScopeSchema = union([
13029
+ literal("*"),
13030
+ ShareViewScopeSchema,
13031
+ AddonRoutesScopeSchema,
13032
+ TokenScopeSchema
13033
+ ]);
13034
+ object({
13035
+ kind: literal("credential"),
13036
+ sub: string$2().min(1),
13037
+ jti: string$2().min(16),
13038
+ scopes: array(CredentialScopeSchema).min(1)
13039
+ });
12811
13040
  _enum(["new-session", "step-up"]);
12812
13041
  /** The languages the hub's app landing pages are written in (D858). */
12813
13042
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44351,202 +44580,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44351
44580
  auth: "admin"
44352
44581
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44353
44582
  /**
44354
- * Zod schemas for persisted record types.
44355
- *
44356
- * These schemas serve as the single source of truth for types that are
44357
- * stored in the settings-store backend and retrieved via tRPC. They
44358
- * replace the manual TypeScript interfaces with schema-derived types,
44359
- * enabling runtime validation when reading from the backend.
44360
- *
44361
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44362
- * keeps the original name for backward compatibility.
44363
- */
44364
- /**
44365
- * Auth model (v2 — 2026-05-12 caps-only):
44366
- *
44367
- * Two principals:
44368
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44369
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44370
- *
44371
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44372
- * `super_admin` — collapsed onto the single boolean because every
44373
- * non-admin caller is governed by the same scope-check rules; the
44374
- * distinction was only ever used to route role-specific procedures
44375
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44376
- * with `isAdmin: false` + an explicit scope list.
44377
- */
44378
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44379
- var MethodAccessSchema = _enum([
44380
- "view",
44381
- "create",
44382
- "delete"
44383
- ]);
44384
- /**
44385
- * A single scope grant. Three forms — checked in matcher with OR
44386
- * semantics; the first match decides whether the call passes:
44387
- *
44388
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44389
- * cap with the matching `scope` declaration. New caps inherit grants
44390
- * automatically — adding a device cap to the codebase makes it
44391
- * reachable to every user with `category:device [...]` without a
44392
- * re-grant. Use this for "family viewer" / "operator" tiers.
44393
- *
44394
- * - `addon` — every cap that ships inside a given addon id. Niche;
44395
- * useful when an integration needs to interact with one specific
44396
- * addon's surface regardless of how many caps it exports.
44397
- *
44398
- * - `capability` — the most precise form. Single cap name. Use for
44399
- * integration tokens where you want to grant `addons.installPackage`
44400
- * and nothing else.
44401
- *
44402
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44403
- * "give me everything" escape; there is no wildcard scope.
44404
- */
44405
- var AllowedProviderSchema = union([literal("*"), array(string$2())]);
44406
- var AllowedDevicesSchema = record(string$2(), union([literal("*"), array(string$2())]));
44407
- var CapScopeSchema = _enum(["device", "system"]);
44408
- /**
44409
- * DeviceSelector (scope model v3 — 2026-08-12).
44410
- *
44411
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44412
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44413
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44414
- * AFTER the grant was minted — no re-grant, no re-login.
44415
- *
44416
- * - `all` — every device in the deployment. The broad viewer/operator
44417
- * lever without a `category` grant (a `category` grant also covers device
44418
- * caps that carry no deviceId; `all` is specifically the device set).
44419
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44420
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44421
- * is NOT covered until the grant is edited.
44422
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44423
- * DYNAMIC. A device that changes type, or a new device of the type,
44424
- * re-resolves on the next request.
44425
- * - `locations` — every device whose operator-assigned `location` label is
44426
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44427
- * null/unset location matches NO `locations` selector.
44428
- */
44429
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44430
- object({ kind: literal("all") }),
44431
- object({
44432
- kind: literal("ids"),
44433
- ids: array(number().int()).min(1)
44434
- }),
44435
- object({
44436
- kind: literal("types"),
44437
- types: array(_enum(DeviceType)).min(1)
44438
- }),
44439
- object({
44440
- kind: literal("locations"),
44441
- locations: array(string$2().min(1)).min(1)
44442
- })
44443
- ]);
44444
- var DeviceTokenScopeSchema = object({
44445
- type: literal("device"),
44446
- /** The device SET this grant covers — resolved against the live fleet. */
44447
- selector: DeviceSelectorSchema,
44448
- access: array(MethodAccessSchema).min(1),
44449
- /**
44450
- * Whether a grant on a PARENT device transparently covers its accessory
44451
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44452
- * Direction is parent → children ONLY.
44453
- *
44454
- * Absent → the matcher DERIVES it from the access flavour: `view`
44455
- * inherits (a camera viewer sees the camera's accessories), `create` /
44456
- * `delete` do NOT (actuating/removing a child is an explicit act the
44457
- * operator must grant on the child, not inherit from the parent). Set it
44458
- * explicitly to override that default per grant.
44459
- */
44460
- includeLinked: boolean().optional()
44461
- });
44462
- /**
44463
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44464
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44465
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44466
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44467
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44468
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44469
- * migration cannot reach a JWT already in a client's hands; parse-time
44470
- * migration covers both without a flag day. No cast — the raw object is read
44471
- * through `Reflect.get` (its static type is `unknown`).
44472
- */
44473
- function migrateLegacyTokenScope(raw) {
44474
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44475
- if (Reflect.get(raw, "type") !== "device") return raw;
44476
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44477
- const targets = Reflect.get(raw, "targets");
44478
- if (!Array.isArray(targets)) return raw;
44479
- return {
44480
- type: "device",
44481
- selector: {
44482
- kind: "ids",
44483
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44484
- },
44485
- access: Reflect.get(raw, "access")
44486
- };
44487
- }
44488
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44489
- object({
44490
- type: literal("category"),
44491
- target: CapScopeSchema,
44492
- access: array(MethodAccessSchema).min(1)
44493
- }),
44494
- object({
44495
- type: literal("capability"),
44496
- target: string$2(),
44497
- access: array(MethodAccessSchema).min(1)
44498
- }),
44499
- object({
44500
- type: literal("addon"),
44501
- target: string$2(),
44502
- access: array(MethodAccessSchema).min(1)
44503
- }),
44504
- DeviceTokenScopeSchema
44505
- ]));
44506
- object({
44507
- id: string$2(),
44508
- username: string$2(),
44509
- passwordHash: string$2(),
44510
- /**
44511
- * Admin bypass. When true, the middleware skips the scope-access
44512
- * check entirely. There is no other axis of privilege; the legacy
44513
- * role enum collapsed onto this boolean in v2.
44514
- */
44515
- isAdmin: boolean().default(false),
44516
- allowedProviders: AllowedProviderSchema,
44517
- allowedDevices: AllowedDevicesSchema,
44518
- /**
44519
- * Scopes granted to this user. Admins bypass; their `scopes` is
44520
- * ignored. Non-admins without scopes are locked out of every
44521
- * protected call.
44522
- */
44523
- scopes: array(TokenScopeSchema).default([]),
44524
- createdAt: number(),
44525
- updatedAt: number()
44526
- });
44527
- object({
44528
- id: string$2(),
44529
- label: string$2(),
44530
- isAdmin: boolean().default(false),
44531
- allowedProviders: AllowedProviderSchema,
44532
- allowedDevices: AllowedDevicesSchema,
44533
- tokenHash: string$2(),
44534
- tokenPrefix: string$2(),
44535
- createdAt: number(),
44536
- lastUsedAt: number().optional()
44537
- });
44538
- object({
44539
- id: string$2(),
44540
- userId: string$2(),
44541
- name: string$2(),
44542
- tokenHash: string$2(),
44543
- tokenPrefix: string$2(),
44544
- scopes: array(TokenScopeSchema),
44545
- expiresAt: number().nullish(),
44546
- lastUsedAt: number().nullish(),
44547
- createdAt: number()
44548
- });
44549
- /**
44550
44583
  * `oauth-integration` — a collection cap by which an addon declares
44551
44584
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44552
44585
  * Each provider returns a static descriptor; the core enumerates them
package/dist/addon.mjs CHANGED
@@ -7941,6 +7941,12 @@ function normalizeAddonInitResult(result) {
7941
7941
  if (Array.isArray(result)) return { providers: result };
7942
7942
  return result;
7943
7943
  }
7944
+ /**
7945
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7946
+ * and validates them (`share-token.service.ts` builds its enum from this
7947
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7948
+ */
7949
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7944
7950
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7945
7951
  var PeerBytesTicketSchema = object({
7946
7952
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12806,6 +12812,229 @@ var ConvertResultSchema = object({
12806
12812
  files: array(string$2()).readonly()
12807
12813
  })).readonly()
12808
12814
  });
12815
+ /**
12816
+ * Zod schemas for persisted record types.
12817
+ *
12818
+ * These schemas serve as the single source of truth for types that are
12819
+ * stored in the settings-store backend and retrieved via tRPC. They
12820
+ * replace the manual TypeScript interfaces with schema-derived types,
12821
+ * enabling runtime validation when reading from the backend.
12822
+ *
12823
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12824
+ * keeps the original name for backward compatibility.
12825
+ */
12826
+ /**
12827
+ * Auth model (v2 — 2026-05-12 caps-only):
12828
+ *
12829
+ * Two principals:
12830
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12831
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12832
+ *
12833
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12834
+ * `super_admin` — collapsed onto the single boolean because every
12835
+ * non-admin caller is governed by the same scope-check rules; the
12836
+ * distinction was only ever used to route role-specific procedures
12837
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12838
+ * with `isAdmin: false` + an explicit scope list.
12839
+ */
12840
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12841
+ var MethodAccessSchema = _enum([
12842
+ "view",
12843
+ "create",
12844
+ "delete"
12845
+ ]);
12846
+ /**
12847
+ * A single scope grant. Three forms — checked in matcher with OR
12848
+ * semantics; the first match decides whether the call passes:
12849
+ *
12850
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12851
+ * cap with the matching `scope` declaration. New caps inherit grants
12852
+ * automatically — adding a device cap to the codebase makes it
12853
+ * reachable to every user with `category:device [...]` without a
12854
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12855
+ *
12856
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12857
+ * useful when an integration needs to interact with one specific
12858
+ * addon's surface regardless of how many caps it exports.
12859
+ *
12860
+ * - `capability` — the most precise form. Single cap name. Use for
12861
+ * integration tokens where you want to grant `addons.installPackage`
12862
+ * and nothing else.
12863
+ *
12864
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12865
+ * "give me everything" escape; there is no wildcard scope.
12866
+ */
12867
+ var AllowedProviderSchema = union([literal("*"), array(string$2())]);
12868
+ var AllowedDevicesSchema = record(string$2(), union([literal("*"), array(string$2())]));
12869
+ var CapScopeSchema = _enum(["device", "system"]);
12870
+ /**
12871
+ * DeviceSelector (scope model v3 — 2026-08-12).
12872
+ *
12873
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12874
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12875
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12876
+ * AFTER the grant was minted — no re-grant, no re-login.
12877
+ *
12878
+ * - `all` — every device in the deployment. The broad viewer/operator
12879
+ * lever without a `category` grant (a `category` grant also covers device
12880
+ * caps that carry no deviceId; `all` is specifically the device set).
12881
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12882
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12883
+ * is NOT covered until the grant is edited.
12884
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12885
+ * DYNAMIC. A device that changes type, or a new device of the type,
12886
+ * re-resolves on the next request.
12887
+ * - `locations` — every device whose operator-assigned `location` label is
12888
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12889
+ * null/unset location matches NO `locations` selector.
12890
+ */
12891
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12892
+ object({ kind: literal("all") }),
12893
+ object({
12894
+ kind: literal("ids"),
12895
+ ids: array(number().int()).min(1)
12896
+ }),
12897
+ object({
12898
+ kind: literal("types"),
12899
+ types: array(_enum(DeviceType)).min(1)
12900
+ }),
12901
+ object({
12902
+ kind: literal("locations"),
12903
+ locations: array(string$2().min(1)).min(1)
12904
+ })
12905
+ ]);
12906
+ var DeviceTokenScopeSchema = object({
12907
+ type: literal("device"),
12908
+ /** The device SET this grant covers — resolved against the live fleet. */
12909
+ selector: DeviceSelectorSchema,
12910
+ access: array(MethodAccessSchema).min(1),
12911
+ /**
12912
+ * Whether a grant on a PARENT device transparently covers its accessory
12913
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12914
+ * Direction is parent → children ONLY.
12915
+ *
12916
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12917
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12918
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12919
+ * operator must grant on the child, not inherit from the parent). Set it
12920
+ * explicitly to override that default per grant.
12921
+ */
12922
+ includeLinked: boolean().optional()
12923
+ });
12924
+ /**
12925
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12926
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12927
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12928
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12929
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12930
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12931
+ * migration cannot reach a JWT already in a client's hands; parse-time
12932
+ * migration covers both without a flag day. No cast — the raw object is read
12933
+ * through `Reflect.get` (its static type is `unknown`).
12934
+ */
12935
+ function migrateLegacyTokenScope(raw) {
12936
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12937
+ if (Reflect.get(raw, "type") !== "device") return raw;
12938
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12939
+ const targets = Reflect.get(raw, "targets");
12940
+ if (!Array.isArray(targets)) return raw;
12941
+ return {
12942
+ type: "device",
12943
+ selector: {
12944
+ kind: "ids",
12945
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12946
+ },
12947
+ access: Reflect.get(raw, "access")
12948
+ };
12949
+ }
12950
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12951
+ object({
12952
+ type: literal("category"),
12953
+ target: CapScopeSchema,
12954
+ access: array(MethodAccessSchema).min(1)
12955
+ }),
12956
+ object({
12957
+ type: literal("capability"),
12958
+ target: string$2(),
12959
+ access: array(MethodAccessSchema).min(1)
12960
+ }),
12961
+ object({
12962
+ type: literal("addon"),
12963
+ target: string$2(),
12964
+ access: array(MethodAccessSchema).min(1)
12965
+ }),
12966
+ DeviceTokenScopeSchema
12967
+ ]));
12968
+ object({
12969
+ id: string$2(),
12970
+ username: string$2(),
12971
+ passwordHash: string$2(),
12972
+ /**
12973
+ * Admin bypass. When true, the middleware skips the scope-access
12974
+ * check entirely. There is no other axis of privilege; the legacy
12975
+ * role enum collapsed onto this boolean in v2.
12976
+ */
12977
+ isAdmin: boolean().default(false),
12978
+ allowedProviders: AllowedProviderSchema,
12979
+ allowedDevices: AllowedDevicesSchema,
12980
+ /**
12981
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12982
+ * ignored. Non-admins without scopes are locked out of every
12983
+ * protected call.
12984
+ */
12985
+ scopes: array(TokenScopeSchema).default([]),
12986
+ createdAt: number(),
12987
+ updatedAt: number()
12988
+ });
12989
+ object({
12990
+ id: string$2(),
12991
+ label: string$2(),
12992
+ isAdmin: boolean().default(false),
12993
+ allowedProviders: AllowedProviderSchema,
12994
+ allowedDevices: AllowedDevicesSchema,
12995
+ tokenHash: string$2(),
12996
+ tokenPrefix: string$2(),
12997
+ createdAt: number(),
12998
+ lastUsedAt: number().optional()
12999
+ });
13000
+ object({
13001
+ id: string$2(),
13002
+ userId: string$2(),
13003
+ name: string$2(),
13004
+ tokenHash: string$2(),
13005
+ tokenPrefix: string$2(),
13006
+ scopes: array(TokenScopeSchema),
13007
+ expiresAt: number().nullish(),
13008
+ lastUsedAt: number().nullish(),
13009
+ createdAt: number()
13010
+ });
13011
+ /** What a share-view grant lets its holder see. */
13012
+ var ShareViewGrantSchema = object({
13013
+ kind: _enum(SHARE_VIEW_KINDS),
13014
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13015
+ /** Talk-back, opt-in at mint time; absent means off. */
13016
+ talk: boolean().optional()
13017
+ });
13018
+ var ShareViewScopeSchema = object({
13019
+ type: literal("share-view"),
13020
+ grant: ShareViewGrantSchema
13021
+ });
13022
+ var AddonRoutesScopeSchema = object({
13023
+ type: literal("addon-routes"),
13024
+ access: array(MethodAccessSchema).min(1)
13025
+ });
13026
+ var CredentialScopeSchema = union([
13027
+ literal("*"),
13028
+ ShareViewScopeSchema,
13029
+ AddonRoutesScopeSchema,
13030
+ TokenScopeSchema
13031
+ ]);
13032
+ object({
13033
+ kind: literal("credential"),
13034
+ sub: string$2().min(1),
13035
+ jti: string$2().min(16),
13036
+ scopes: array(CredentialScopeSchema).min(1)
13037
+ });
12809
13038
  _enum(["new-session", "step-up"]);
12810
13039
  /** The languages the hub's app landing pages are written in (D858). */
12811
13040
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44349,202 +44578,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44349
44578
  auth: "admin"
44350
44579
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44351
44580
  /**
44352
- * Zod schemas for persisted record types.
44353
- *
44354
- * These schemas serve as the single source of truth for types that are
44355
- * stored in the settings-store backend and retrieved via tRPC. They
44356
- * replace the manual TypeScript interfaces with schema-derived types,
44357
- * enabling runtime validation when reading from the backend.
44358
- *
44359
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44360
- * keeps the original name for backward compatibility.
44361
- */
44362
- /**
44363
- * Auth model (v2 — 2026-05-12 caps-only):
44364
- *
44365
- * Two principals:
44366
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44367
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44368
- *
44369
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44370
- * `super_admin` — collapsed onto the single boolean because every
44371
- * non-admin caller is governed by the same scope-check rules; the
44372
- * distinction was only ever used to route role-specific procedures
44373
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44374
- * with `isAdmin: false` + an explicit scope list.
44375
- */
44376
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44377
- var MethodAccessSchema = _enum([
44378
- "view",
44379
- "create",
44380
- "delete"
44381
- ]);
44382
- /**
44383
- * A single scope grant. Three forms — checked in matcher with OR
44384
- * semantics; the first match decides whether the call passes:
44385
- *
44386
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44387
- * cap with the matching `scope` declaration. New caps inherit grants
44388
- * automatically — adding a device cap to the codebase makes it
44389
- * reachable to every user with `category:device [...]` without a
44390
- * re-grant. Use this for "family viewer" / "operator" tiers.
44391
- *
44392
- * - `addon` — every cap that ships inside a given addon id. Niche;
44393
- * useful when an integration needs to interact with one specific
44394
- * addon's surface regardless of how many caps it exports.
44395
- *
44396
- * - `capability` — the most precise form. Single cap name. Use for
44397
- * integration tokens where you want to grant `addons.installPackage`
44398
- * and nothing else.
44399
- *
44400
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44401
- * "give me everything" escape; there is no wildcard scope.
44402
- */
44403
- var AllowedProviderSchema = union([literal("*"), array(string$2())]);
44404
- var AllowedDevicesSchema = record(string$2(), union([literal("*"), array(string$2())]));
44405
- var CapScopeSchema = _enum(["device", "system"]);
44406
- /**
44407
- * DeviceSelector (scope model v3 — 2026-08-12).
44408
- *
44409
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44410
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44411
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44412
- * AFTER the grant was minted — no re-grant, no re-login.
44413
- *
44414
- * - `all` — every device in the deployment. The broad viewer/operator
44415
- * lever without a `category` grant (a `category` grant also covers device
44416
- * caps that carry no deviceId; `all` is specifically the device set).
44417
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44418
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44419
- * is NOT covered until the grant is edited.
44420
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44421
- * DYNAMIC. A device that changes type, or a new device of the type,
44422
- * re-resolves on the next request.
44423
- * - `locations` — every device whose operator-assigned `location` label is
44424
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44425
- * null/unset location matches NO `locations` selector.
44426
- */
44427
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44428
- object({ kind: literal("all") }),
44429
- object({
44430
- kind: literal("ids"),
44431
- ids: array(number().int()).min(1)
44432
- }),
44433
- object({
44434
- kind: literal("types"),
44435
- types: array(_enum(DeviceType)).min(1)
44436
- }),
44437
- object({
44438
- kind: literal("locations"),
44439
- locations: array(string$2().min(1)).min(1)
44440
- })
44441
- ]);
44442
- var DeviceTokenScopeSchema = object({
44443
- type: literal("device"),
44444
- /** The device SET this grant covers — resolved against the live fleet. */
44445
- selector: DeviceSelectorSchema,
44446
- access: array(MethodAccessSchema).min(1),
44447
- /**
44448
- * Whether a grant on a PARENT device transparently covers its accessory
44449
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44450
- * Direction is parent → children ONLY.
44451
- *
44452
- * Absent → the matcher DERIVES it from the access flavour: `view`
44453
- * inherits (a camera viewer sees the camera's accessories), `create` /
44454
- * `delete` do NOT (actuating/removing a child is an explicit act the
44455
- * operator must grant on the child, not inherit from the parent). Set it
44456
- * explicitly to override that default per grant.
44457
- */
44458
- includeLinked: boolean().optional()
44459
- });
44460
- /**
44461
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44462
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44463
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44464
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44465
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44466
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44467
- * migration cannot reach a JWT already in a client's hands; parse-time
44468
- * migration covers both without a flag day. No cast — the raw object is read
44469
- * through `Reflect.get` (its static type is `unknown`).
44470
- */
44471
- function migrateLegacyTokenScope(raw) {
44472
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44473
- if (Reflect.get(raw, "type") !== "device") return raw;
44474
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44475
- const targets = Reflect.get(raw, "targets");
44476
- if (!Array.isArray(targets)) return raw;
44477
- return {
44478
- type: "device",
44479
- selector: {
44480
- kind: "ids",
44481
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44482
- },
44483
- access: Reflect.get(raw, "access")
44484
- };
44485
- }
44486
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44487
- object({
44488
- type: literal("category"),
44489
- target: CapScopeSchema,
44490
- access: array(MethodAccessSchema).min(1)
44491
- }),
44492
- object({
44493
- type: literal("capability"),
44494
- target: string$2(),
44495
- access: array(MethodAccessSchema).min(1)
44496
- }),
44497
- object({
44498
- type: literal("addon"),
44499
- target: string$2(),
44500
- access: array(MethodAccessSchema).min(1)
44501
- }),
44502
- DeviceTokenScopeSchema
44503
- ]));
44504
- object({
44505
- id: string$2(),
44506
- username: string$2(),
44507
- passwordHash: string$2(),
44508
- /**
44509
- * Admin bypass. When true, the middleware skips the scope-access
44510
- * check entirely. There is no other axis of privilege; the legacy
44511
- * role enum collapsed onto this boolean in v2.
44512
- */
44513
- isAdmin: boolean().default(false),
44514
- allowedProviders: AllowedProviderSchema,
44515
- allowedDevices: AllowedDevicesSchema,
44516
- /**
44517
- * Scopes granted to this user. Admins bypass; their `scopes` is
44518
- * ignored. Non-admins without scopes are locked out of every
44519
- * protected call.
44520
- */
44521
- scopes: array(TokenScopeSchema).default([]),
44522
- createdAt: number(),
44523
- updatedAt: number()
44524
- });
44525
- object({
44526
- id: string$2(),
44527
- label: string$2(),
44528
- isAdmin: boolean().default(false),
44529
- allowedProviders: AllowedProviderSchema,
44530
- allowedDevices: AllowedDevicesSchema,
44531
- tokenHash: string$2(),
44532
- tokenPrefix: string$2(),
44533
- createdAt: number(),
44534
- lastUsedAt: number().optional()
44535
- });
44536
- object({
44537
- id: string$2(),
44538
- userId: string$2(),
44539
- name: string$2(),
44540
- tokenHash: string$2(),
44541
- tokenPrefix: string$2(),
44542
- scopes: array(TokenScopeSchema),
44543
- expiresAt: number().nullish(),
44544
- lastUsedAt: number().nullish(),
44545
- createdAt: number()
44546
- });
44547
- /**
44548
44581
  * `oauth-integration` — a collection cap by which an addon declares
44549
44582
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44550
44583
  * Each provider returns a static descriptor; the core enumerates them
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-matter-broker",
3
- "version": "0.2.130",
3
+ "version": "0.2.131",
4
4
  "description": "Matter broker addon for CamStack — owns a Matter fabric (commissioning + the long-lived controller) via the matter.js controller and brokers commissioned Matter nodes into CamStack",
5
5
  "keywords": [
6
6
  "camstack",