@camstack/addon-provider-tuya 0.2.128 → 0.2.129

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
@@ -8616,6 +8616,12 @@ function normalizeAddonInitResult(result) {
8616
8616
  if (Array.isArray(result)) return { providers: result };
8617
8617
  return result;
8618
8618
  }
8619
+ /**
8620
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
8621
+ * and validates them (`share-token.service.ts` builds its enum from this
8622
+ * list); a data plane names the kinds it ACCEPTS in `share`.
8623
+ */
8624
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
8619
8625
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
8620
8626
  var PeerBytesTicketSchema = object({
8621
8627
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -13281,6 +13287,229 @@ var ConvertResultSchema = object({
13281
13287
  files: array(string()).readonly()
13282
13288
  })).readonly()
13283
13289
  });
13290
+ /**
13291
+ * Zod schemas for persisted record types.
13292
+ *
13293
+ * These schemas serve as the single source of truth for types that are
13294
+ * stored in the settings-store backend and retrieved via tRPC. They
13295
+ * replace the manual TypeScript interfaces with schema-derived types,
13296
+ * enabling runtime validation when reading from the backend.
13297
+ *
13298
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
13299
+ * keeps the original name for backward compatibility.
13300
+ */
13301
+ /**
13302
+ * Auth model (v2 — 2026-05-12 caps-only):
13303
+ *
13304
+ * Two principals:
13305
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
13306
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
13307
+ *
13308
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
13309
+ * `super_admin` — collapsed onto the single boolean because every
13310
+ * non-admin caller is governed by the same scope-check rules; the
13311
+ * distinction was only ever used to route role-specific procedures
13312
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
13313
+ * with `isAdmin: false` + an explicit scope list.
13314
+ */
13315
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
13316
+ var MethodAccessSchema = _enum([
13317
+ "view",
13318
+ "create",
13319
+ "delete"
13320
+ ]);
13321
+ /**
13322
+ * A single scope grant. Three forms — checked in matcher with OR
13323
+ * semantics; the first match decides whether the call passes:
13324
+ *
13325
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
13326
+ * cap with the matching `scope` declaration. New caps inherit grants
13327
+ * automatically — adding a device cap to the codebase makes it
13328
+ * reachable to every user with `category:device [...]` without a
13329
+ * re-grant. Use this for "family viewer" / "operator" tiers.
13330
+ *
13331
+ * - `addon` — every cap that ships inside a given addon id. Niche;
13332
+ * useful when an integration needs to interact with one specific
13333
+ * addon's surface regardless of how many caps it exports.
13334
+ *
13335
+ * - `capability` — the most precise form. Single cap name. Use for
13336
+ * integration tokens where you want to grant `addons.installPackage`
13337
+ * and nothing else.
13338
+ *
13339
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
13340
+ * "give me everything" escape; there is no wildcard scope.
13341
+ */
13342
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
13343
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
13344
+ var CapScopeSchema = _enum(["device", "system"]);
13345
+ /**
13346
+ * DeviceSelector (scope model v3 — 2026-08-12).
13347
+ *
13348
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
13349
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
13350
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
13351
+ * AFTER the grant was minted — no re-grant, no re-login.
13352
+ *
13353
+ * - `all` — every device in the deployment. The broad viewer/operator
13354
+ * lever without a `category` grant (a `category` grant also covers device
13355
+ * caps that carry no deviceId; `all` is specifically the device set).
13356
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
13357
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
13358
+ * is NOT covered until the grant is edited.
13359
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
13360
+ * DYNAMIC. A device that changes type, or a new device of the type,
13361
+ * re-resolves on the next request.
13362
+ * - `locations` — every device whose operator-assigned `location` label is
13363
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
13364
+ * null/unset location matches NO `locations` selector.
13365
+ */
13366
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
13367
+ object({ kind: literal("all") }),
13368
+ object({
13369
+ kind: literal("ids"),
13370
+ ids: array(number().int()).min(1)
13371
+ }),
13372
+ object({
13373
+ kind: literal("types"),
13374
+ types: array(_enum(DeviceType)).min(1)
13375
+ }),
13376
+ object({
13377
+ kind: literal("locations"),
13378
+ locations: array(string().min(1)).min(1)
13379
+ })
13380
+ ]);
13381
+ var DeviceTokenScopeSchema = object({
13382
+ type: literal("device"),
13383
+ /** The device SET this grant covers — resolved against the live fleet. */
13384
+ selector: DeviceSelectorSchema,
13385
+ access: array(MethodAccessSchema).min(1),
13386
+ /**
13387
+ * Whether a grant on a PARENT device transparently covers its accessory
13388
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
13389
+ * Direction is parent → children ONLY.
13390
+ *
13391
+ * Absent → the matcher DERIVES it from the access flavour: `view`
13392
+ * inherits (a camera viewer sees the camera's accessories), `create` /
13393
+ * `delete` do NOT (actuating/removing a child is an explicit act the
13394
+ * operator must grant on the child, not inherit from the parent). Set it
13395
+ * explicitly to override that default per grant.
13396
+ */
13397
+ includeLinked: boolean().optional()
13398
+ });
13399
+ /**
13400
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
13401
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
13402
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
13403
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
13404
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
13405
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
13406
+ * migration cannot reach a JWT already in a client's hands; parse-time
13407
+ * migration covers both without a flag day. No cast — the raw object is read
13408
+ * through `Reflect.get` (its static type is `unknown`).
13409
+ */
13410
+ function migrateLegacyTokenScope(raw) {
13411
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
13412
+ if (Reflect.get(raw, "type") !== "device") return raw;
13413
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
13414
+ const targets = Reflect.get(raw, "targets");
13415
+ if (!Array.isArray(targets)) return raw;
13416
+ return {
13417
+ type: "device",
13418
+ selector: {
13419
+ kind: "ids",
13420
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
13421
+ },
13422
+ access: Reflect.get(raw, "access")
13423
+ };
13424
+ }
13425
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
13426
+ object({
13427
+ type: literal("category"),
13428
+ target: CapScopeSchema,
13429
+ access: array(MethodAccessSchema).min(1)
13430
+ }),
13431
+ object({
13432
+ type: literal("capability"),
13433
+ target: string(),
13434
+ access: array(MethodAccessSchema).min(1)
13435
+ }),
13436
+ object({
13437
+ type: literal("addon"),
13438
+ target: string(),
13439
+ access: array(MethodAccessSchema).min(1)
13440
+ }),
13441
+ DeviceTokenScopeSchema
13442
+ ]));
13443
+ object({
13444
+ id: string(),
13445
+ username: string(),
13446
+ passwordHash: string(),
13447
+ /**
13448
+ * Admin bypass. When true, the middleware skips the scope-access
13449
+ * check entirely. There is no other axis of privilege; the legacy
13450
+ * role enum collapsed onto this boolean in v2.
13451
+ */
13452
+ isAdmin: boolean().default(false),
13453
+ allowedProviders: AllowedProviderSchema,
13454
+ allowedDevices: AllowedDevicesSchema,
13455
+ /**
13456
+ * Scopes granted to this user. Admins bypass; their `scopes` is
13457
+ * ignored. Non-admins without scopes are locked out of every
13458
+ * protected call.
13459
+ */
13460
+ scopes: array(TokenScopeSchema).default([]),
13461
+ createdAt: number(),
13462
+ updatedAt: number()
13463
+ });
13464
+ object({
13465
+ id: string(),
13466
+ label: string(),
13467
+ isAdmin: boolean().default(false),
13468
+ allowedProviders: AllowedProviderSchema,
13469
+ allowedDevices: AllowedDevicesSchema,
13470
+ tokenHash: string(),
13471
+ tokenPrefix: string(),
13472
+ createdAt: number(),
13473
+ lastUsedAt: number().optional()
13474
+ });
13475
+ object({
13476
+ id: string(),
13477
+ userId: string(),
13478
+ name: string(),
13479
+ tokenHash: string(),
13480
+ tokenPrefix: string(),
13481
+ scopes: array(TokenScopeSchema),
13482
+ expiresAt: number().nullish(),
13483
+ lastUsedAt: number().nullish(),
13484
+ createdAt: number()
13485
+ });
13486
+ /** What a share-view grant lets its holder see. */
13487
+ var ShareViewGrantSchema = object({
13488
+ kind: _enum(SHARE_VIEW_KINDS),
13489
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13490
+ /** Talk-back, opt-in at mint time; absent means off. */
13491
+ talk: boolean().optional()
13492
+ });
13493
+ var ShareViewScopeSchema = object({
13494
+ type: literal("share-view"),
13495
+ grant: ShareViewGrantSchema
13496
+ });
13497
+ var AddonRoutesScopeSchema = object({
13498
+ type: literal("addon-routes"),
13499
+ access: array(MethodAccessSchema).min(1)
13500
+ });
13501
+ var CredentialScopeSchema = union([
13502
+ literal("*"),
13503
+ ShareViewScopeSchema,
13504
+ AddonRoutesScopeSchema,
13505
+ TokenScopeSchema
13506
+ ]);
13507
+ object({
13508
+ kind: literal("credential"),
13509
+ sub: string().min(1),
13510
+ jti: string().min(16),
13511
+ scopes: array(CredentialScopeSchema).min(1)
13512
+ });
13284
13513
  _enum(["new-session", "step-up"]);
13285
13514
  /** The languages the hub's app landing pages are written in (D858). */
13286
13515
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44775,202 +45004,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44775
45004
  auth: "admin"
44776
45005
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44777
45006
  /**
44778
- * Zod schemas for persisted record types.
44779
- *
44780
- * These schemas serve as the single source of truth for types that are
44781
- * stored in the settings-store backend and retrieved via tRPC. They
44782
- * replace the manual TypeScript interfaces with schema-derived types,
44783
- * enabling runtime validation when reading from the backend.
44784
- *
44785
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44786
- * keeps the original name for backward compatibility.
44787
- */
44788
- /**
44789
- * Auth model (v2 — 2026-05-12 caps-only):
44790
- *
44791
- * Two principals:
44792
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44793
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44794
- *
44795
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44796
- * `super_admin` — collapsed onto the single boolean because every
44797
- * non-admin caller is governed by the same scope-check rules; the
44798
- * distinction was only ever used to route role-specific procedures
44799
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44800
- * with `isAdmin: false` + an explicit scope list.
44801
- */
44802
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44803
- var MethodAccessSchema = _enum([
44804
- "view",
44805
- "create",
44806
- "delete"
44807
- ]);
44808
- /**
44809
- * A single scope grant. Three forms — checked in matcher with OR
44810
- * semantics; the first match decides whether the call passes:
44811
- *
44812
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44813
- * cap with the matching `scope` declaration. New caps inherit grants
44814
- * automatically — adding a device cap to the codebase makes it
44815
- * reachable to every user with `category:device [...]` without a
44816
- * re-grant. Use this for "family viewer" / "operator" tiers.
44817
- *
44818
- * - `addon` — every cap that ships inside a given addon id. Niche;
44819
- * useful when an integration needs to interact with one specific
44820
- * addon's surface regardless of how many caps it exports.
44821
- *
44822
- * - `capability` — the most precise form. Single cap name. Use for
44823
- * integration tokens where you want to grant `addons.installPackage`
44824
- * and nothing else.
44825
- *
44826
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44827
- * "give me everything" escape; there is no wildcard scope.
44828
- */
44829
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44830
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44831
- var CapScopeSchema = _enum(["device", "system"]);
44832
- /**
44833
- * DeviceSelector (scope model v3 — 2026-08-12).
44834
- *
44835
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44836
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44837
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44838
- * AFTER the grant was minted — no re-grant, no re-login.
44839
- *
44840
- * - `all` — every device in the deployment. The broad viewer/operator
44841
- * lever without a `category` grant (a `category` grant also covers device
44842
- * caps that carry no deviceId; `all` is specifically the device set).
44843
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44844
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44845
- * is NOT covered until the grant is edited.
44846
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44847
- * DYNAMIC. A device that changes type, or a new device of the type,
44848
- * re-resolves on the next request.
44849
- * - `locations` — every device whose operator-assigned `location` label is
44850
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44851
- * null/unset location matches NO `locations` selector.
44852
- */
44853
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44854
- object({ kind: literal("all") }),
44855
- object({
44856
- kind: literal("ids"),
44857
- ids: array(number().int()).min(1)
44858
- }),
44859
- object({
44860
- kind: literal("types"),
44861
- types: array(_enum(DeviceType)).min(1)
44862
- }),
44863
- object({
44864
- kind: literal("locations"),
44865
- locations: array(string().min(1)).min(1)
44866
- })
44867
- ]);
44868
- var DeviceTokenScopeSchema = object({
44869
- type: literal("device"),
44870
- /** The device SET this grant covers — resolved against the live fleet. */
44871
- selector: DeviceSelectorSchema,
44872
- access: array(MethodAccessSchema).min(1),
44873
- /**
44874
- * Whether a grant on a PARENT device transparently covers its accessory
44875
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44876
- * Direction is parent → children ONLY.
44877
- *
44878
- * Absent → the matcher DERIVES it from the access flavour: `view`
44879
- * inherits (a camera viewer sees the camera's accessories), `create` /
44880
- * `delete` do NOT (actuating/removing a child is an explicit act the
44881
- * operator must grant on the child, not inherit from the parent). Set it
44882
- * explicitly to override that default per grant.
44883
- */
44884
- includeLinked: boolean().optional()
44885
- });
44886
- /**
44887
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44888
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44889
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44890
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44891
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44892
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44893
- * migration cannot reach a JWT already in a client's hands; parse-time
44894
- * migration covers both without a flag day. No cast — the raw object is read
44895
- * through `Reflect.get` (its static type is `unknown`).
44896
- */
44897
- function migrateLegacyTokenScope(raw) {
44898
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44899
- if (Reflect.get(raw, "type") !== "device") return raw;
44900
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44901
- const targets = Reflect.get(raw, "targets");
44902
- if (!Array.isArray(targets)) return raw;
44903
- return {
44904
- type: "device",
44905
- selector: {
44906
- kind: "ids",
44907
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44908
- },
44909
- access: Reflect.get(raw, "access")
44910
- };
44911
- }
44912
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44913
- object({
44914
- type: literal("category"),
44915
- target: CapScopeSchema,
44916
- access: array(MethodAccessSchema).min(1)
44917
- }),
44918
- object({
44919
- type: literal("capability"),
44920
- target: string(),
44921
- access: array(MethodAccessSchema).min(1)
44922
- }),
44923
- object({
44924
- type: literal("addon"),
44925
- target: string(),
44926
- access: array(MethodAccessSchema).min(1)
44927
- }),
44928
- DeviceTokenScopeSchema
44929
- ]));
44930
- object({
44931
- id: string(),
44932
- username: string(),
44933
- passwordHash: string(),
44934
- /**
44935
- * Admin bypass. When true, the middleware skips the scope-access
44936
- * check entirely. There is no other axis of privilege; the legacy
44937
- * role enum collapsed onto this boolean in v2.
44938
- */
44939
- isAdmin: boolean().default(false),
44940
- allowedProviders: AllowedProviderSchema,
44941
- allowedDevices: AllowedDevicesSchema,
44942
- /**
44943
- * Scopes granted to this user. Admins bypass; their `scopes` is
44944
- * ignored. Non-admins without scopes are locked out of every
44945
- * protected call.
44946
- */
44947
- scopes: array(TokenScopeSchema).default([]),
44948
- createdAt: number(),
44949
- updatedAt: number()
44950
- });
44951
- object({
44952
- id: string(),
44953
- label: string(),
44954
- isAdmin: boolean().default(false),
44955
- allowedProviders: AllowedProviderSchema,
44956
- allowedDevices: AllowedDevicesSchema,
44957
- tokenHash: string(),
44958
- tokenPrefix: string(),
44959
- createdAt: number(),
44960
- lastUsedAt: number().optional()
44961
- });
44962
- object({
44963
- id: string(),
44964
- userId: string(),
44965
- name: string(),
44966
- tokenHash: string(),
44967
- tokenPrefix: string(),
44968
- scopes: array(TokenScopeSchema),
44969
- expiresAt: number().nullish(),
44970
- lastUsedAt: number().nullish(),
44971
- createdAt: number()
44972
- });
44973
- /**
44974
45007
  * `oauth-integration` — a collection cap by which an addon declares
44975
45008
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44976
45009
  * Each provider returns a static descriptor; the core enumerates them
package/dist/addon.mjs CHANGED
@@ -8615,6 +8615,12 @@ function normalizeAddonInitResult(result) {
8615
8615
  if (Array.isArray(result)) return { providers: result };
8616
8616
  return result;
8617
8617
  }
8618
+ /**
8619
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
8620
+ * and validates them (`share-token.service.ts` builds its enum from this
8621
+ * list); a data plane names the kinds it ACCEPTS in `share`.
8622
+ */
8623
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
8618
8624
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
8619
8625
  var PeerBytesTicketSchema = object({
8620
8626
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -13280,6 +13286,229 @@ var ConvertResultSchema = object({
13280
13286
  files: array(string()).readonly()
13281
13287
  })).readonly()
13282
13288
  });
13289
+ /**
13290
+ * Zod schemas for persisted record types.
13291
+ *
13292
+ * These schemas serve as the single source of truth for types that are
13293
+ * stored in the settings-store backend and retrieved via tRPC. They
13294
+ * replace the manual TypeScript interfaces with schema-derived types,
13295
+ * enabling runtime validation when reading from the backend.
13296
+ *
13297
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
13298
+ * keeps the original name for backward compatibility.
13299
+ */
13300
+ /**
13301
+ * Auth model (v2 — 2026-05-12 caps-only):
13302
+ *
13303
+ * Two principals:
13304
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
13305
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
13306
+ *
13307
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
13308
+ * `super_admin` — collapsed onto the single boolean because every
13309
+ * non-admin caller is governed by the same scope-check rules; the
13310
+ * distinction was only ever used to route role-specific procedures
13311
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
13312
+ * with `isAdmin: false` + an explicit scope list.
13313
+ */
13314
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
13315
+ var MethodAccessSchema = _enum([
13316
+ "view",
13317
+ "create",
13318
+ "delete"
13319
+ ]);
13320
+ /**
13321
+ * A single scope grant. Three forms — checked in matcher with OR
13322
+ * semantics; the first match decides whether the call passes:
13323
+ *
13324
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
13325
+ * cap with the matching `scope` declaration. New caps inherit grants
13326
+ * automatically — adding a device cap to the codebase makes it
13327
+ * reachable to every user with `category:device [...]` without a
13328
+ * re-grant. Use this for "family viewer" / "operator" tiers.
13329
+ *
13330
+ * - `addon` — every cap that ships inside a given addon id. Niche;
13331
+ * useful when an integration needs to interact with one specific
13332
+ * addon's surface regardless of how many caps it exports.
13333
+ *
13334
+ * - `capability` — the most precise form. Single cap name. Use for
13335
+ * integration tokens where you want to grant `addons.installPackage`
13336
+ * and nothing else.
13337
+ *
13338
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
13339
+ * "give me everything" escape; there is no wildcard scope.
13340
+ */
13341
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
13342
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
13343
+ var CapScopeSchema = _enum(["device", "system"]);
13344
+ /**
13345
+ * DeviceSelector (scope model v3 — 2026-08-12).
13346
+ *
13347
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
13348
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
13349
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
13350
+ * AFTER the grant was minted — no re-grant, no re-login.
13351
+ *
13352
+ * - `all` — every device in the deployment. The broad viewer/operator
13353
+ * lever without a `category` grant (a `category` grant also covers device
13354
+ * caps that carry no deviceId; `all` is specifically the device set).
13355
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
13356
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
13357
+ * is NOT covered until the grant is edited.
13358
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
13359
+ * DYNAMIC. A device that changes type, or a new device of the type,
13360
+ * re-resolves on the next request.
13361
+ * - `locations` — every device whose operator-assigned `location` label is
13362
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
13363
+ * null/unset location matches NO `locations` selector.
13364
+ */
13365
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
13366
+ object({ kind: literal("all") }),
13367
+ object({
13368
+ kind: literal("ids"),
13369
+ ids: array(number().int()).min(1)
13370
+ }),
13371
+ object({
13372
+ kind: literal("types"),
13373
+ types: array(_enum(DeviceType)).min(1)
13374
+ }),
13375
+ object({
13376
+ kind: literal("locations"),
13377
+ locations: array(string().min(1)).min(1)
13378
+ })
13379
+ ]);
13380
+ var DeviceTokenScopeSchema = object({
13381
+ type: literal("device"),
13382
+ /** The device SET this grant covers — resolved against the live fleet. */
13383
+ selector: DeviceSelectorSchema,
13384
+ access: array(MethodAccessSchema).min(1),
13385
+ /**
13386
+ * Whether a grant on a PARENT device transparently covers its accessory
13387
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
13388
+ * Direction is parent → children ONLY.
13389
+ *
13390
+ * Absent → the matcher DERIVES it from the access flavour: `view`
13391
+ * inherits (a camera viewer sees the camera's accessories), `create` /
13392
+ * `delete` do NOT (actuating/removing a child is an explicit act the
13393
+ * operator must grant on the child, not inherit from the parent). Set it
13394
+ * explicitly to override that default per grant.
13395
+ */
13396
+ includeLinked: boolean().optional()
13397
+ });
13398
+ /**
13399
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
13400
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
13401
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
13402
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
13403
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
13404
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
13405
+ * migration cannot reach a JWT already in a client's hands; parse-time
13406
+ * migration covers both without a flag day. No cast — the raw object is read
13407
+ * through `Reflect.get` (its static type is `unknown`).
13408
+ */
13409
+ function migrateLegacyTokenScope(raw) {
13410
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
13411
+ if (Reflect.get(raw, "type") !== "device") return raw;
13412
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
13413
+ const targets = Reflect.get(raw, "targets");
13414
+ if (!Array.isArray(targets)) return raw;
13415
+ return {
13416
+ type: "device",
13417
+ selector: {
13418
+ kind: "ids",
13419
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
13420
+ },
13421
+ access: Reflect.get(raw, "access")
13422
+ };
13423
+ }
13424
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
13425
+ object({
13426
+ type: literal("category"),
13427
+ target: CapScopeSchema,
13428
+ access: array(MethodAccessSchema).min(1)
13429
+ }),
13430
+ object({
13431
+ type: literal("capability"),
13432
+ target: string(),
13433
+ access: array(MethodAccessSchema).min(1)
13434
+ }),
13435
+ object({
13436
+ type: literal("addon"),
13437
+ target: string(),
13438
+ access: array(MethodAccessSchema).min(1)
13439
+ }),
13440
+ DeviceTokenScopeSchema
13441
+ ]));
13442
+ object({
13443
+ id: string(),
13444
+ username: string(),
13445
+ passwordHash: string(),
13446
+ /**
13447
+ * Admin bypass. When true, the middleware skips the scope-access
13448
+ * check entirely. There is no other axis of privilege; the legacy
13449
+ * role enum collapsed onto this boolean in v2.
13450
+ */
13451
+ isAdmin: boolean().default(false),
13452
+ allowedProviders: AllowedProviderSchema,
13453
+ allowedDevices: AllowedDevicesSchema,
13454
+ /**
13455
+ * Scopes granted to this user. Admins bypass; their `scopes` is
13456
+ * ignored. Non-admins without scopes are locked out of every
13457
+ * protected call.
13458
+ */
13459
+ scopes: array(TokenScopeSchema).default([]),
13460
+ createdAt: number(),
13461
+ updatedAt: number()
13462
+ });
13463
+ object({
13464
+ id: string(),
13465
+ label: string(),
13466
+ isAdmin: boolean().default(false),
13467
+ allowedProviders: AllowedProviderSchema,
13468
+ allowedDevices: AllowedDevicesSchema,
13469
+ tokenHash: string(),
13470
+ tokenPrefix: string(),
13471
+ createdAt: number(),
13472
+ lastUsedAt: number().optional()
13473
+ });
13474
+ object({
13475
+ id: string(),
13476
+ userId: string(),
13477
+ name: string(),
13478
+ tokenHash: string(),
13479
+ tokenPrefix: string(),
13480
+ scopes: array(TokenScopeSchema),
13481
+ expiresAt: number().nullish(),
13482
+ lastUsedAt: number().nullish(),
13483
+ createdAt: number()
13484
+ });
13485
+ /** What a share-view grant lets its holder see. */
13486
+ var ShareViewGrantSchema = object({
13487
+ kind: _enum(SHARE_VIEW_KINDS),
13488
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13489
+ /** Talk-back, opt-in at mint time; absent means off. */
13490
+ talk: boolean().optional()
13491
+ });
13492
+ var ShareViewScopeSchema = object({
13493
+ type: literal("share-view"),
13494
+ grant: ShareViewGrantSchema
13495
+ });
13496
+ var AddonRoutesScopeSchema = object({
13497
+ type: literal("addon-routes"),
13498
+ access: array(MethodAccessSchema).min(1)
13499
+ });
13500
+ var CredentialScopeSchema = union([
13501
+ literal("*"),
13502
+ ShareViewScopeSchema,
13503
+ AddonRoutesScopeSchema,
13504
+ TokenScopeSchema
13505
+ ]);
13506
+ object({
13507
+ kind: literal("credential"),
13508
+ sub: string().min(1),
13509
+ jti: string().min(16),
13510
+ scopes: array(CredentialScopeSchema).min(1)
13511
+ });
13283
13512
  _enum(["new-session", "step-up"]);
13284
13513
  /** The languages the hub's app landing pages are written in (D858). */
13285
13514
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44774,202 +45003,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44774
45003
  auth: "admin"
44775
45004
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44776
45005
  /**
44777
- * Zod schemas for persisted record types.
44778
- *
44779
- * These schemas serve as the single source of truth for types that are
44780
- * stored in the settings-store backend and retrieved via tRPC. They
44781
- * replace the manual TypeScript interfaces with schema-derived types,
44782
- * enabling runtime validation when reading from the backend.
44783
- *
44784
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44785
- * keeps the original name for backward compatibility.
44786
- */
44787
- /**
44788
- * Auth model (v2 — 2026-05-12 caps-only):
44789
- *
44790
- * Two principals:
44791
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44792
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44793
- *
44794
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44795
- * `super_admin` — collapsed onto the single boolean because every
44796
- * non-admin caller is governed by the same scope-check rules; the
44797
- * distinction was only ever used to route role-specific procedures
44798
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44799
- * with `isAdmin: false` + an explicit scope list.
44800
- */
44801
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44802
- var MethodAccessSchema = _enum([
44803
- "view",
44804
- "create",
44805
- "delete"
44806
- ]);
44807
- /**
44808
- * A single scope grant. Three forms — checked in matcher with OR
44809
- * semantics; the first match decides whether the call passes:
44810
- *
44811
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44812
- * cap with the matching `scope` declaration. New caps inherit grants
44813
- * automatically — adding a device cap to the codebase makes it
44814
- * reachable to every user with `category:device [...]` without a
44815
- * re-grant. Use this for "family viewer" / "operator" tiers.
44816
- *
44817
- * - `addon` — every cap that ships inside a given addon id. Niche;
44818
- * useful when an integration needs to interact with one specific
44819
- * addon's surface regardless of how many caps it exports.
44820
- *
44821
- * - `capability` — the most precise form. Single cap name. Use for
44822
- * integration tokens where you want to grant `addons.installPackage`
44823
- * and nothing else.
44824
- *
44825
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44826
- * "give me everything" escape; there is no wildcard scope.
44827
- */
44828
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44829
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44830
- var CapScopeSchema = _enum(["device", "system"]);
44831
- /**
44832
- * DeviceSelector (scope model v3 — 2026-08-12).
44833
- *
44834
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44835
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44836
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44837
- * AFTER the grant was minted — no re-grant, no re-login.
44838
- *
44839
- * - `all` — every device in the deployment. The broad viewer/operator
44840
- * lever without a `category` grant (a `category` grant also covers device
44841
- * caps that carry no deviceId; `all` is specifically the device set).
44842
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44843
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44844
- * is NOT covered until the grant is edited.
44845
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44846
- * DYNAMIC. A device that changes type, or a new device of the type,
44847
- * re-resolves on the next request.
44848
- * - `locations` — every device whose operator-assigned `location` label is
44849
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44850
- * null/unset location matches NO `locations` selector.
44851
- */
44852
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44853
- object({ kind: literal("all") }),
44854
- object({
44855
- kind: literal("ids"),
44856
- ids: array(number().int()).min(1)
44857
- }),
44858
- object({
44859
- kind: literal("types"),
44860
- types: array(_enum(DeviceType)).min(1)
44861
- }),
44862
- object({
44863
- kind: literal("locations"),
44864
- locations: array(string().min(1)).min(1)
44865
- })
44866
- ]);
44867
- var DeviceTokenScopeSchema = object({
44868
- type: literal("device"),
44869
- /** The device SET this grant covers — resolved against the live fleet. */
44870
- selector: DeviceSelectorSchema,
44871
- access: array(MethodAccessSchema).min(1),
44872
- /**
44873
- * Whether a grant on a PARENT device transparently covers its accessory
44874
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44875
- * Direction is parent → children ONLY.
44876
- *
44877
- * Absent → the matcher DERIVES it from the access flavour: `view`
44878
- * inherits (a camera viewer sees the camera's accessories), `create` /
44879
- * `delete` do NOT (actuating/removing a child is an explicit act the
44880
- * operator must grant on the child, not inherit from the parent). Set it
44881
- * explicitly to override that default per grant.
44882
- */
44883
- includeLinked: boolean().optional()
44884
- });
44885
- /**
44886
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44887
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44888
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44889
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44890
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44891
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44892
- * migration cannot reach a JWT already in a client's hands; parse-time
44893
- * migration covers both without a flag day. No cast — the raw object is read
44894
- * through `Reflect.get` (its static type is `unknown`).
44895
- */
44896
- function migrateLegacyTokenScope(raw) {
44897
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44898
- if (Reflect.get(raw, "type") !== "device") return raw;
44899
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44900
- const targets = Reflect.get(raw, "targets");
44901
- if (!Array.isArray(targets)) return raw;
44902
- return {
44903
- type: "device",
44904
- selector: {
44905
- kind: "ids",
44906
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44907
- },
44908
- access: Reflect.get(raw, "access")
44909
- };
44910
- }
44911
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44912
- object({
44913
- type: literal("category"),
44914
- target: CapScopeSchema,
44915
- access: array(MethodAccessSchema).min(1)
44916
- }),
44917
- object({
44918
- type: literal("capability"),
44919
- target: string(),
44920
- access: array(MethodAccessSchema).min(1)
44921
- }),
44922
- object({
44923
- type: literal("addon"),
44924
- target: string(),
44925
- access: array(MethodAccessSchema).min(1)
44926
- }),
44927
- DeviceTokenScopeSchema
44928
- ]));
44929
- object({
44930
- id: string(),
44931
- username: string(),
44932
- passwordHash: string(),
44933
- /**
44934
- * Admin bypass. When true, the middleware skips the scope-access
44935
- * check entirely. There is no other axis of privilege; the legacy
44936
- * role enum collapsed onto this boolean in v2.
44937
- */
44938
- isAdmin: boolean().default(false),
44939
- allowedProviders: AllowedProviderSchema,
44940
- allowedDevices: AllowedDevicesSchema,
44941
- /**
44942
- * Scopes granted to this user. Admins bypass; their `scopes` is
44943
- * ignored. Non-admins without scopes are locked out of every
44944
- * protected call.
44945
- */
44946
- scopes: array(TokenScopeSchema).default([]),
44947
- createdAt: number(),
44948
- updatedAt: number()
44949
- });
44950
- object({
44951
- id: string(),
44952
- label: string(),
44953
- isAdmin: boolean().default(false),
44954
- allowedProviders: AllowedProviderSchema,
44955
- allowedDevices: AllowedDevicesSchema,
44956
- tokenHash: string(),
44957
- tokenPrefix: string(),
44958
- createdAt: number(),
44959
- lastUsedAt: number().optional()
44960
- });
44961
- object({
44962
- id: string(),
44963
- userId: string(),
44964
- name: string(),
44965
- tokenHash: string(),
44966
- tokenPrefix: string(),
44967
- scopes: array(TokenScopeSchema),
44968
- expiresAt: number().nullish(),
44969
- lastUsedAt: number().nullish(),
44970
- createdAt: number()
44971
- });
44972
- /**
44973
45006
  * `oauth-integration` — a collection cap by which an addon declares
44974
45007
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44975
45008
  * 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-provider-tuya",
3
- "version": "0.2.128",
3
+ "version": "0.2.129",
4
4
  "description": "Tuya / Smart Life device-provider addon for CamStack — account-onboarded (Tuya IoT cloud fetch of device localKeys) + LOCAL DP control via the @apocaliss92/nodetuya encrypted-LAN client, exposing switch / water-heater-family kettle entities",
5
5
  "keywords": [
6
6
  "camstack",