@camstack/addon-provider-amcrest 0.2.140 → 0.2.141

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
@@ -7832,6 +7832,12 @@ function normalizeAddonInitResult(result) {
7832
7832
  if (Array.isArray(result)) return { providers: result };
7833
7833
  return result;
7834
7834
  }
7835
+ /**
7836
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7837
+ * and validates them (`share-token.service.ts` builds its enum from this
7838
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7839
+ */
7840
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7835
7841
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7836
7842
  var PeerBytesTicketSchema = object({
7837
7843
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12521,6 +12527,229 @@ var ConvertResultSchema = object({
12521
12527
  files: array(string()).readonly()
12522
12528
  })).readonly()
12523
12529
  });
12530
+ /**
12531
+ * Zod schemas for persisted record types.
12532
+ *
12533
+ * These schemas serve as the single source of truth for types that are
12534
+ * stored in the settings-store backend and retrieved via tRPC. They
12535
+ * replace the manual TypeScript interfaces with schema-derived types,
12536
+ * enabling runtime validation when reading from the backend.
12537
+ *
12538
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12539
+ * keeps the original name for backward compatibility.
12540
+ */
12541
+ /**
12542
+ * Auth model (v2 — 2026-05-12 caps-only):
12543
+ *
12544
+ * Two principals:
12545
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12546
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12547
+ *
12548
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12549
+ * `super_admin` — collapsed onto the single boolean because every
12550
+ * non-admin caller is governed by the same scope-check rules; the
12551
+ * distinction was only ever used to route role-specific procedures
12552
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12553
+ * with `isAdmin: false` + an explicit scope list.
12554
+ */
12555
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12556
+ var MethodAccessSchema = _enum([
12557
+ "view",
12558
+ "create",
12559
+ "delete"
12560
+ ]);
12561
+ /**
12562
+ * A single scope grant. Three forms — checked in matcher with OR
12563
+ * semantics; the first match decides whether the call passes:
12564
+ *
12565
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12566
+ * cap with the matching `scope` declaration. New caps inherit grants
12567
+ * automatically — adding a device cap to the codebase makes it
12568
+ * reachable to every user with `category:device [...]` without a
12569
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12570
+ *
12571
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12572
+ * useful when an integration needs to interact with one specific
12573
+ * addon's surface regardless of how many caps it exports.
12574
+ *
12575
+ * - `capability` — the most precise form. Single cap name. Use for
12576
+ * integration tokens where you want to grant `addons.installPackage`
12577
+ * and nothing else.
12578
+ *
12579
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12580
+ * "give me everything" escape; there is no wildcard scope.
12581
+ */
12582
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
12583
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
12584
+ var CapScopeSchema = _enum(["device", "system"]);
12585
+ /**
12586
+ * DeviceSelector (scope model v3 — 2026-08-12).
12587
+ *
12588
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12589
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12590
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12591
+ * AFTER the grant was minted — no re-grant, no re-login.
12592
+ *
12593
+ * - `all` — every device in the deployment. The broad viewer/operator
12594
+ * lever without a `category` grant (a `category` grant also covers device
12595
+ * caps that carry no deviceId; `all` is specifically the device set).
12596
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12597
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12598
+ * is NOT covered until the grant is edited.
12599
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12600
+ * DYNAMIC. A device that changes type, or a new device of the type,
12601
+ * re-resolves on the next request.
12602
+ * - `locations` — every device whose operator-assigned `location` label is
12603
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12604
+ * null/unset location matches NO `locations` selector.
12605
+ */
12606
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12607
+ object({ kind: literal("all") }),
12608
+ object({
12609
+ kind: literal("ids"),
12610
+ ids: array(number().int()).min(1)
12611
+ }),
12612
+ object({
12613
+ kind: literal("types"),
12614
+ types: array(_enum(DeviceType)).min(1)
12615
+ }),
12616
+ object({
12617
+ kind: literal("locations"),
12618
+ locations: array(string().min(1)).min(1)
12619
+ })
12620
+ ]);
12621
+ var DeviceTokenScopeSchema = object({
12622
+ type: literal("device"),
12623
+ /** The device SET this grant covers — resolved against the live fleet. */
12624
+ selector: DeviceSelectorSchema,
12625
+ access: array(MethodAccessSchema).min(1),
12626
+ /**
12627
+ * Whether a grant on a PARENT device transparently covers its accessory
12628
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12629
+ * Direction is parent → children ONLY.
12630
+ *
12631
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12632
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12633
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12634
+ * operator must grant on the child, not inherit from the parent). Set it
12635
+ * explicitly to override that default per grant.
12636
+ */
12637
+ includeLinked: boolean().optional()
12638
+ });
12639
+ /**
12640
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12641
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12642
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12643
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12644
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12645
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12646
+ * migration cannot reach a JWT already in a client's hands; parse-time
12647
+ * migration covers both without a flag day. No cast — the raw object is read
12648
+ * through `Reflect.get` (its static type is `unknown`).
12649
+ */
12650
+ function migrateLegacyTokenScope(raw) {
12651
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12652
+ if (Reflect.get(raw, "type") !== "device") return raw;
12653
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12654
+ const targets = Reflect.get(raw, "targets");
12655
+ if (!Array.isArray(targets)) return raw;
12656
+ return {
12657
+ type: "device",
12658
+ selector: {
12659
+ kind: "ids",
12660
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12661
+ },
12662
+ access: Reflect.get(raw, "access")
12663
+ };
12664
+ }
12665
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12666
+ object({
12667
+ type: literal("category"),
12668
+ target: CapScopeSchema,
12669
+ access: array(MethodAccessSchema).min(1)
12670
+ }),
12671
+ object({
12672
+ type: literal("capability"),
12673
+ target: string(),
12674
+ access: array(MethodAccessSchema).min(1)
12675
+ }),
12676
+ object({
12677
+ type: literal("addon"),
12678
+ target: string(),
12679
+ access: array(MethodAccessSchema).min(1)
12680
+ }),
12681
+ DeviceTokenScopeSchema
12682
+ ]));
12683
+ object({
12684
+ id: string(),
12685
+ username: string(),
12686
+ passwordHash: string(),
12687
+ /**
12688
+ * Admin bypass. When true, the middleware skips the scope-access
12689
+ * check entirely. There is no other axis of privilege; the legacy
12690
+ * role enum collapsed onto this boolean in v2.
12691
+ */
12692
+ isAdmin: boolean().default(false),
12693
+ allowedProviders: AllowedProviderSchema,
12694
+ allowedDevices: AllowedDevicesSchema,
12695
+ /**
12696
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12697
+ * ignored. Non-admins without scopes are locked out of every
12698
+ * protected call.
12699
+ */
12700
+ scopes: array(TokenScopeSchema).default([]),
12701
+ createdAt: number(),
12702
+ updatedAt: number()
12703
+ });
12704
+ object({
12705
+ id: string(),
12706
+ label: string(),
12707
+ isAdmin: boolean().default(false),
12708
+ allowedProviders: AllowedProviderSchema,
12709
+ allowedDevices: AllowedDevicesSchema,
12710
+ tokenHash: string(),
12711
+ tokenPrefix: string(),
12712
+ createdAt: number(),
12713
+ lastUsedAt: number().optional()
12714
+ });
12715
+ object({
12716
+ id: string(),
12717
+ userId: string(),
12718
+ name: string(),
12719
+ tokenHash: string(),
12720
+ tokenPrefix: string(),
12721
+ scopes: array(TokenScopeSchema),
12722
+ expiresAt: number().nullish(),
12723
+ lastUsedAt: number().nullish(),
12724
+ createdAt: number()
12725
+ });
12726
+ /** What a share-view grant lets its holder see. */
12727
+ var ShareViewGrantSchema = object({
12728
+ kind: _enum(SHARE_VIEW_KINDS),
12729
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
12730
+ /** Talk-back, opt-in at mint time; absent means off. */
12731
+ talk: boolean().optional()
12732
+ });
12733
+ var ShareViewScopeSchema = object({
12734
+ type: literal("share-view"),
12735
+ grant: ShareViewGrantSchema
12736
+ });
12737
+ var AddonRoutesScopeSchema = object({
12738
+ type: literal("addon-routes"),
12739
+ access: array(MethodAccessSchema).min(1)
12740
+ });
12741
+ var CredentialScopeSchema = union([
12742
+ literal("*"),
12743
+ ShareViewScopeSchema,
12744
+ AddonRoutesScopeSchema,
12745
+ TokenScopeSchema
12746
+ ]);
12747
+ object({
12748
+ kind: literal("credential"),
12749
+ sub: string().min(1),
12750
+ jti: string().min(16),
12751
+ scopes: array(CredentialScopeSchema).min(1)
12752
+ });
12524
12753
  _enum(["new-session", "step-up"]);
12525
12754
  /** The languages the hub's app landing pages are written in (D858). */
12526
12755
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44111,202 +44340,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44111
44340
  auth: "admin"
44112
44341
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44113
44342
  /**
44114
- * Zod schemas for persisted record types.
44115
- *
44116
- * These schemas serve as the single source of truth for types that are
44117
- * stored in the settings-store backend and retrieved via tRPC. They
44118
- * replace the manual TypeScript interfaces with schema-derived types,
44119
- * enabling runtime validation when reading from the backend.
44120
- *
44121
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44122
- * keeps the original name for backward compatibility.
44123
- */
44124
- /**
44125
- * Auth model (v2 — 2026-05-12 caps-only):
44126
- *
44127
- * Two principals:
44128
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44129
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44130
- *
44131
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44132
- * `super_admin` — collapsed onto the single boolean because every
44133
- * non-admin caller is governed by the same scope-check rules; the
44134
- * distinction was only ever used to route role-specific procedures
44135
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44136
- * with `isAdmin: false` + an explicit scope list.
44137
- */
44138
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44139
- var MethodAccessSchema = _enum([
44140
- "view",
44141
- "create",
44142
- "delete"
44143
- ]);
44144
- /**
44145
- * A single scope grant. Three forms — checked in matcher with OR
44146
- * semantics; the first match decides whether the call passes:
44147
- *
44148
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44149
- * cap with the matching `scope` declaration. New caps inherit grants
44150
- * automatically — adding a device cap to the codebase makes it
44151
- * reachable to every user with `category:device [...]` without a
44152
- * re-grant. Use this for "family viewer" / "operator" tiers.
44153
- *
44154
- * - `addon` — every cap that ships inside a given addon id. Niche;
44155
- * useful when an integration needs to interact with one specific
44156
- * addon's surface regardless of how many caps it exports.
44157
- *
44158
- * - `capability` — the most precise form. Single cap name. Use for
44159
- * integration tokens where you want to grant `addons.installPackage`
44160
- * and nothing else.
44161
- *
44162
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44163
- * "give me everything" escape; there is no wildcard scope.
44164
- */
44165
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44166
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44167
- var CapScopeSchema = _enum(["device", "system"]);
44168
- /**
44169
- * DeviceSelector (scope model v3 — 2026-08-12).
44170
- *
44171
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44172
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44173
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44174
- * AFTER the grant was minted — no re-grant, no re-login.
44175
- *
44176
- * - `all` — every device in the deployment. The broad viewer/operator
44177
- * lever without a `category` grant (a `category` grant also covers device
44178
- * caps that carry no deviceId; `all` is specifically the device set).
44179
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44180
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44181
- * is NOT covered until the grant is edited.
44182
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44183
- * DYNAMIC. A device that changes type, or a new device of the type,
44184
- * re-resolves on the next request.
44185
- * - `locations` — every device whose operator-assigned `location` label is
44186
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44187
- * null/unset location matches NO `locations` selector.
44188
- */
44189
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44190
- object({ kind: literal("all") }),
44191
- object({
44192
- kind: literal("ids"),
44193
- ids: array(number().int()).min(1)
44194
- }),
44195
- object({
44196
- kind: literal("types"),
44197
- types: array(_enum(DeviceType)).min(1)
44198
- }),
44199
- object({
44200
- kind: literal("locations"),
44201
- locations: array(string().min(1)).min(1)
44202
- })
44203
- ]);
44204
- var DeviceTokenScopeSchema = object({
44205
- type: literal("device"),
44206
- /** The device SET this grant covers — resolved against the live fleet. */
44207
- selector: DeviceSelectorSchema,
44208
- access: array(MethodAccessSchema).min(1),
44209
- /**
44210
- * Whether a grant on a PARENT device transparently covers its accessory
44211
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44212
- * Direction is parent → children ONLY.
44213
- *
44214
- * Absent → the matcher DERIVES it from the access flavour: `view`
44215
- * inherits (a camera viewer sees the camera's accessories), `create` /
44216
- * `delete` do NOT (actuating/removing a child is an explicit act the
44217
- * operator must grant on the child, not inherit from the parent). Set it
44218
- * explicitly to override that default per grant.
44219
- */
44220
- includeLinked: boolean().optional()
44221
- });
44222
- /**
44223
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44224
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44225
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44226
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44227
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44228
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44229
- * migration cannot reach a JWT already in a client's hands; parse-time
44230
- * migration covers both without a flag day. No cast — the raw object is read
44231
- * through `Reflect.get` (its static type is `unknown`).
44232
- */
44233
- function migrateLegacyTokenScope(raw) {
44234
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44235
- if (Reflect.get(raw, "type") !== "device") return raw;
44236
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44237
- const targets = Reflect.get(raw, "targets");
44238
- if (!Array.isArray(targets)) return raw;
44239
- return {
44240
- type: "device",
44241
- selector: {
44242
- kind: "ids",
44243
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44244
- },
44245
- access: Reflect.get(raw, "access")
44246
- };
44247
- }
44248
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44249
- object({
44250
- type: literal("category"),
44251
- target: CapScopeSchema,
44252
- access: array(MethodAccessSchema).min(1)
44253
- }),
44254
- object({
44255
- type: literal("capability"),
44256
- target: string(),
44257
- access: array(MethodAccessSchema).min(1)
44258
- }),
44259
- object({
44260
- type: literal("addon"),
44261
- target: string(),
44262
- access: array(MethodAccessSchema).min(1)
44263
- }),
44264
- DeviceTokenScopeSchema
44265
- ]));
44266
- object({
44267
- id: string(),
44268
- username: string(),
44269
- passwordHash: string(),
44270
- /**
44271
- * Admin bypass. When true, the middleware skips the scope-access
44272
- * check entirely. There is no other axis of privilege; the legacy
44273
- * role enum collapsed onto this boolean in v2.
44274
- */
44275
- isAdmin: boolean().default(false),
44276
- allowedProviders: AllowedProviderSchema,
44277
- allowedDevices: AllowedDevicesSchema,
44278
- /**
44279
- * Scopes granted to this user. Admins bypass; their `scopes` is
44280
- * ignored. Non-admins without scopes are locked out of every
44281
- * protected call.
44282
- */
44283
- scopes: array(TokenScopeSchema).default([]),
44284
- createdAt: number(),
44285
- updatedAt: number()
44286
- });
44287
- object({
44288
- id: string(),
44289
- label: string(),
44290
- isAdmin: boolean().default(false),
44291
- allowedProviders: AllowedProviderSchema,
44292
- allowedDevices: AllowedDevicesSchema,
44293
- tokenHash: string(),
44294
- tokenPrefix: string(),
44295
- createdAt: number(),
44296
- lastUsedAt: number().optional()
44297
- });
44298
- object({
44299
- id: string(),
44300
- userId: string(),
44301
- name: string(),
44302
- tokenHash: string(),
44303
- tokenPrefix: string(),
44304
- scopes: array(TokenScopeSchema),
44305
- expiresAt: number().nullish(),
44306
- lastUsedAt: number().nullish(),
44307
- createdAt: number()
44308
- });
44309
- /**
44310
44343
  * `oauth-integration` — a collection cap by which an addon declares
44311
44344
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44312
44345
  * Each provider returns a static descriptor; the core enumerates them
package/dist/addon.mjs CHANGED
@@ -7811,6 +7811,12 @@ function normalizeAddonInitResult(result) {
7811
7811
  if (Array.isArray(result)) return { providers: result };
7812
7812
  return result;
7813
7813
  }
7814
+ /**
7815
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7816
+ * and validates them (`share-token.service.ts` builds its enum from this
7817
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7818
+ */
7819
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7814
7820
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7815
7821
  var PeerBytesTicketSchema = object({
7816
7822
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12500,6 +12506,229 @@ var ConvertResultSchema = object({
12500
12506
  files: array(string()).readonly()
12501
12507
  })).readonly()
12502
12508
  });
12509
+ /**
12510
+ * Zod schemas for persisted record types.
12511
+ *
12512
+ * These schemas serve as the single source of truth for types that are
12513
+ * stored in the settings-store backend and retrieved via tRPC. They
12514
+ * replace the manual TypeScript interfaces with schema-derived types,
12515
+ * enabling runtime validation when reading from the backend.
12516
+ *
12517
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12518
+ * keeps the original name for backward compatibility.
12519
+ */
12520
+ /**
12521
+ * Auth model (v2 — 2026-05-12 caps-only):
12522
+ *
12523
+ * Two principals:
12524
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12525
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12526
+ *
12527
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12528
+ * `super_admin` — collapsed onto the single boolean because every
12529
+ * non-admin caller is governed by the same scope-check rules; the
12530
+ * distinction was only ever used to route role-specific procedures
12531
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12532
+ * with `isAdmin: false` + an explicit scope list.
12533
+ */
12534
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12535
+ var MethodAccessSchema = _enum([
12536
+ "view",
12537
+ "create",
12538
+ "delete"
12539
+ ]);
12540
+ /**
12541
+ * A single scope grant. Three forms — checked in matcher with OR
12542
+ * semantics; the first match decides whether the call passes:
12543
+ *
12544
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12545
+ * cap with the matching `scope` declaration. New caps inherit grants
12546
+ * automatically — adding a device cap to the codebase makes it
12547
+ * reachable to every user with `category:device [...]` without a
12548
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12549
+ *
12550
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12551
+ * useful when an integration needs to interact with one specific
12552
+ * addon's surface regardless of how many caps it exports.
12553
+ *
12554
+ * - `capability` — the most precise form. Single cap name. Use for
12555
+ * integration tokens where you want to grant `addons.installPackage`
12556
+ * and nothing else.
12557
+ *
12558
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12559
+ * "give me everything" escape; there is no wildcard scope.
12560
+ */
12561
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
12562
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
12563
+ var CapScopeSchema = _enum(["device", "system"]);
12564
+ /**
12565
+ * DeviceSelector (scope model v3 — 2026-08-12).
12566
+ *
12567
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12568
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12569
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12570
+ * AFTER the grant was minted — no re-grant, no re-login.
12571
+ *
12572
+ * - `all` — every device in the deployment. The broad viewer/operator
12573
+ * lever without a `category` grant (a `category` grant also covers device
12574
+ * caps that carry no deviceId; `all` is specifically the device set).
12575
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12576
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12577
+ * is NOT covered until the grant is edited.
12578
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12579
+ * DYNAMIC. A device that changes type, or a new device of the type,
12580
+ * re-resolves on the next request.
12581
+ * - `locations` — every device whose operator-assigned `location` label is
12582
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12583
+ * null/unset location matches NO `locations` selector.
12584
+ */
12585
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12586
+ object({ kind: literal("all") }),
12587
+ object({
12588
+ kind: literal("ids"),
12589
+ ids: array(number().int()).min(1)
12590
+ }),
12591
+ object({
12592
+ kind: literal("types"),
12593
+ types: array(_enum(DeviceType)).min(1)
12594
+ }),
12595
+ object({
12596
+ kind: literal("locations"),
12597
+ locations: array(string().min(1)).min(1)
12598
+ })
12599
+ ]);
12600
+ var DeviceTokenScopeSchema = object({
12601
+ type: literal("device"),
12602
+ /** The device SET this grant covers — resolved against the live fleet. */
12603
+ selector: DeviceSelectorSchema,
12604
+ access: array(MethodAccessSchema).min(1),
12605
+ /**
12606
+ * Whether a grant on a PARENT device transparently covers its accessory
12607
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12608
+ * Direction is parent → children ONLY.
12609
+ *
12610
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12611
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12612
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12613
+ * operator must grant on the child, not inherit from the parent). Set it
12614
+ * explicitly to override that default per grant.
12615
+ */
12616
+ includeLinked: boolean().optional()
12617
+ });
12618
+ /**
12619
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12620
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12621
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12622
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12623
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12624
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12625
+ * migration cannot reach a JWT already in a client's hands; parse-time
12626
+ * migration covers both without a flag day. No cast — the raw object is read
12627
+ * through `Reflect.get` (its static type is `unknown`).
12628
+ */
12629
+ function migrateLegacyTokenScope(raw) {
12630
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12631
+ if (Reflect.get(raw, "type") !== "device") return raw;
12632
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12633
+ const targets = Reflect.get(raw, "targets");
12634
+ if (!Array.isArray(targets)) return raw;
12635
+ return {
12636
+ type: "device",
12637
+ selector: {
12638
+ kind: "ids",
12639
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12640
+ },
12641
+ access: Reflect.get(raw, "access")
12642
+ };
12643
+ }
12644
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12645
+ object({
12646
+ type: literal("category"),
12647
+ target: CapScopeSchema,
12648
+ access: array(MethodAccessSchema).min(1)
12649
+ }),
12650
+ object({
12651
+ type: literal("capability"),
12652
+ target: string(),
12653
+ access: array(MethodAccessSchema).min(1)
12654
+ }),
12655
+ object({
12656
+ type: literal("addon"),
12657
+ target: string(),
12658
+ access: array(MethodAccessSchema).min(1)
12659
+ }),
12660
+ DeviceTokenScopeSchema
12661
+ ]));
12662
+ object({
12663
+ id: string(),
12664
+ username: string(),
12665
+ passwordHash: string(),
12666
+ /**
12667
+ * Admin bypass. When true, the middleware skips the scope-access
12668
+ * check entirely. There is no other axis of privilege; the legacy
12669
+ * role enum collapsed onto this boolean in v2.
12670
+ */
12671
+ isAdmin: boolean().default(false),
12672
+ allowedProviders: AllowedProviderSchema,
12673
+ allowedDevices: AllowedDevicesSchema,
12674
+ /**
12675
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12676
+ * ignored. Non-admins without scopes are locked out of every
12677
+ * protected call.
12678
+ */
12679
+ scopes: array(TokenScopeSchema).default([]),
12680
+ createdAt: number(),
12681
+ updatedAt: number()
12682
+ });
12683
+ object({
12684
+ id: string(),
12685
+ label: string(),
12686
+ isAdmin: boolean().default(false),
12687
+ allowedProviders: AllowedProviderSchema,
12688
+ allowedDevices: AllowedDevicesSchema,
12689
+ tokenHash: string(),
12690
+ tokenPrefix: string(),
12691
+ createdAt: number(),
12692
+ lastUsedAt: number().optional()
12693
+ });
12694
+ object({
12695
+ id: string(),
12696
+ userId: string(),
12697
+ name: string(),
12698
+ tokenHash: string(),
12699
+ tokenPrefix: string(),
12700
+ scopes: array(TokenScopeSchema),
12701
+ expiresAt: number().nullish(),
12702
+ lastUsedAt: number().nullish(),
12703
+ createdAt: number()
12704
+ });
12705
+ /** What a share-view grant lets its holder see. */
12706
+ var ShareViewGrantSchema = object({
12707
+ kind: _enum(SHARE_VIEW_KINDS),
12708
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
12709
+ /** Talk-back, opt-in at mint time; absent means off. */
12710
+ talk: boolean().optional()
12711
+ });
12712
+ var ShareViewScopeSchema = object({
12713
+ type: literal("share-view"),
12714
+ grant: ShareViewGrantSchema
12715
+ });
12716
+ var AddonRoutesScopeSchema = object({
12717
+ type: literal("addon-routes"),
12718
+ access: array(MethodAccessSchema).min(1)
12719
+ });
12720
+ var CredentialScopeSchema = union([
12721
+ literal("*"),
12722
+ ShareViewScopeSchema,
12723
+ AddonRoutesScopeSchema,
12724
+ TokenScopeSchema
12725
+ ]);
12726
+ object({
12727
+ kind: literal("credential"),
12728
+ sub: string().min(1),
12729
+ jti: string().min(16),
12730
+ scopes: array(CredentialScopeSchema).min(1)
12731
+ });
12503
12732
  _enum(["new-session", "step-up"]);
12504
12733
  /** The languages the hub's app landing pages are written in (D858). */
12505
12734
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -44090,202 +44319,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
44090
44319
  auth: "admin"
44091
44320
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
44092
44321
  /**
44093
- * Zod schemas for persisted record types.
44094
- *
44095
- * These schemas serve as the single source of truth for types that are
44096
- * stored in the settings-store backend and retrieved via tRPC. They
44097
- * replace the manual TypeScript interfaces with schema-derived types,
44098
- * enabling runtime validation when reading from the backend.
44099
- *
44100
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
44101
- * keeps the original name for backward compatibility.
44102
- */
44103
- /**
44104
- * Auth model (v2 — 2026-05-12 caps-only):
44105
- *
44106
- * Two principals:
44107
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
44108
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
44109
- *
44110
- * No role enum. There used to be `viewer / agent / scoped` and earlier
44111
- * `super_admin` — collapsed onto the single boolean because every
44112
- * non-admin caller is governed by the same scope-check rules; the
44113
- * distinction was only ever used to route role-specific procedures
44114
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
44115
- * with `isAdmin: false` + an explicit scope list.
44116
- */
44117
- /** Per-method access flavour. Materialised on every cap method via codegen. */
44118
- var MethodAccessSchema = _enum([
44119
- "view",
44120
- "create",
44121
- "delete"
44122
- ]);
44123
- /**
44124
- * A single scope grant. Three forms — checked in matcher with OR
44125
- * semantics; the first match decides whether the call passes:
44126
- *
44127
- * - `category` — broadest. `target: 'device' | 'system'` selects every
44128
- * cap with the matching `scope` declaration. New caps inherit grants
44129
- * automatically — adding a device cap to the codebase makes it
44130
- * reachable to every user with `category:device [...]` without a
44131
- * re-grant. Use this for "family viewer" / "operator" tiers.
44132
- *
44133
- * - `addon` — every cap that ships inside a given addon id. Niche;
44134
- * useful when an integration needs to interact with one specific
44135
- * addon's surface regardless of how many caps it exports.
44136
- *
44137
- * - `capability` — the most precise form. Single cap name. Use for
44138
- * integration tokens where you want to grant `addons.installPackage`
44139
- * and nothing else.
44140
- *
44141
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44142
- * "give me everything" escape; there is no wildcard scope.
44143
- */
44144
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44145
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44146
- var CapScopeSchema = _enum(["device", "system"]);
44147
- /**
44148
- * DeviceSelector (scope model v3 — 2026-08-12).
44149
- *
44150
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44151
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44152
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44153
- * AFTER the grant was minted — no re-grant, no re-login.
44154
- *
44155
- * - `all` — every device in the deployment. The broad viewer/operator
44156
- * lever without a `category` grant (a `category` grant also covers device
44157
- * caps that carry no deviceId; `all` is specifically the device set).
44158
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44159
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44160
- * is NOT covered until the grant is edited.
44161
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44162
- * DYNAMIC. A device that changes type, or a new device of the type,
44163
- * re-resolves on the next request.
44164
- * - `locations` — every device whose operator-assigned `location` label is
44165
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44166
- * null/unset location matches NO `locations` selector.
44167
- */
44168
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44169
- object({ kind: literal("all") }),
44170
- object({
44171
- kind: literal("ids"),
44172
- ids: array(number().int()).min(1)
44173
- }),
44174
- object({
44175
- kind: literal("types"),
44176
- types: array(_enum(DeviceType)).min(1)
44177
- }),
44178
- object({
44179
- kind: literal("locations"),
44180
- locations: array(string().min(1)).min(1)
44181
- })
44182
- ]);
44183
- var DeviceTokenScopeSchema = object({
44184
- type: literal("device"),
44185
- /** The device SET this grant covers — resolved against the live fleet. */
44186
- selector: DeviceSelectorSchema,
44187
- access: array(MethodAccessSchema).min(1),
44188
- /**
44189
- * Whether a grant on a PARENT device transparently covers its accessory
44190
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44191
- * Direction is parent → children ONLY.
44192
- *
44193
- * Absent → the matcher DERIVES it from the access flavour: `view`
44194
- * inherits (a camera viewer sees the camera's accessories), `create` /
44195
- * `delete` do NOT (actuating/removing a child is an explicit act the
44196
- * operator must grant on the child, not inherit from the parent). Set it
44197
- * explicitly to override that default per grant.
44198
- */
44199
- includeLinked: boolean().optional()
44200
- });
44201
- /**
44202
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44203
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44204
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44205
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44206
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44207
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44208
- * migration cannot reach a JWT already in a client's hands; parse-time
44209
- * migration covers both without a flag day. No cast — the raw object is read
44210
- * through `Reflect.get` (its static type is `unknown`).
44211
- */
44212
- function migrateLegacyTokenScope(raw) {
44213
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44214
- if (Reflect.get(raw, "type") !== "device") return raw;
44215
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44216
- const targets = Reflect.get(raw, "targets");
44217
- if (!Array.isArray(targets)) return raw;
44218
- return {
44219
- type: "device",
44220
- selector: {
44221
- kind: "ids",
44222
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44223
- },
44224
- access: Reflect.get(raw, "access")
44225
- };
44226
- }
44227
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44228
- object({
44229
- type: literal("category"),
44230
- target: CapScopeSchema,
44231
- access: array(MethodAccessSchema).min(1)
44232
- }),
44233
- object({
44234
- type: literal("capability"),
44235
- target: string(),
44236
- access: array(MethodAccessSchema).min(1)
44237
- }),
44238
- object({
44239
- type: literal("addon"),
44240
- target: string(),
44241
- access: array(MethodAccessSchema).min(1)
44242
- }),
44243
- DeviceTokenScopeSchema
44244
- ]));
44245
- object({
44246
- id: string(),
44247
- username: string(),
44248
- passwordHash: string(),
44249
- /**
44250
- * Admin bypass. When true, the middleware skips the scope-access
44251
- * check entirely. There is no other axis of privilege; the legacy
44252
- * role enum collapsed onto this boolean in v2.
44253
- */
44254
- isAdmin: boolean().default(false),
44255
- allowedProviders: AllowedProviderSchema,
44256
- allowedDevices: AllowedDevicesSchema,
44257
- /**
44258
- * Scopes granted to this user. Admins bypass; their `scopes` is
44259
- * ignored. Non-admins without scopes are locked out of every
44260
- * protected call.
44261
- */
44262
- scopes: array(TokenScopeSchema).default([]),
44263
- createdAt: number(),
44264
- updatedAt: number()
44265
- });
44266
- object({
44267
- id: string(),
44268
- label: string(),
44269
- isAdmin: boolean().default(false),
44270
- allowedProviders: AllowedProviderSchema,
44271
- allowedDevices: AllowedDevicesSchema,
44272
- tokenHash: string(),
44273
- tokenPrefix: string(),
44274
- createdAt: number(),
44275
- lastUsedAt: number().optional()
44276
- });
44277
- object({
44278
- id: string(),
44279
- userId: string(),
44280
- name: string(),
44281
- tokenHash: string(),
44282
- tokenPrefix: string(),
44283
- scopes: array(TokenScopeSchema),
44284
- expiresAt: number().nullish(),
44285
- lastUsedAt: number().nullish(),
44286
- createdAt: number()
44287
- });
44288
- /**
44289
44322
  * `oauth-integration` — a collection cap by which an addon declares
44290
44323
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44291
44324
  * 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-amcrest",
3
- "version": "0.2.140",
3
+ "version": "0.2.141",
4
4
  "description": "Amcrest/Dahua camera device provider addon for CamStack — Dahua CGI over HTTP(S) with digest auth (snapshot, RTSP catalog, PTZ, image/day-night config)",
5
5
  "keywords": [
6
6
  "camstack",