@camstack/addon-waste-collections 0.1.10 → 0.1.11

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.
@@ -7946,6 +7946,12 @@ function normalizeAddonInitResult(result) {
7946
7946
  if (Array.isArray(result)) return { providers: result };
7947
7947
  return result;
7948
7948
  }
7949
+ /**
7950
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7951
+ * and validates them (`share-token.service.ts` builds its enum from this
7952
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7953
+ */
7954
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7949
7955
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7950
7956
  var PeerBytesTicketSchema = object({
7951
7957
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12818,6 +12824,229 @@ var ConvertResultSchema = object({
12818
12824
  files: array(string()).readonly()
12819
12825
  })).readonly()
12820
12826
  });
12827
+ /**
12828
+ * Zod schemas for persisted record types.
12829
+ *
12830
+ * These schemas serve as the single source of truth for types that are
12831
+ * stored in the settings-store backend and retrieved via tRPC. They
12832
+ * replace the manual TypeScript interfaces with schema-derived types,
12833
+ * enabling runtime validation when reading from the backend.
12834
+ *
12835
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12836
+ * keeps the original name for backward compatibility.
12837
+ */
12838
+ /**
12839
+ * Auth model (v2 — 2026-05-12 caps-only):
12840
+ *
12841
+ * Two principals:
12842
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12843
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12844
+ *
12845
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12846
+ * `super_admin` — collapsed onto the single boolean because every
12847
+ * non-admin caller is governed by the same scope-check rules; the
12848
+ * distinction was only ever used to route role-specific procedures
12849
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12850
+ * with `isAdmin: false` + an explicit scope list.
12851
+ */
12852
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12853
+ var MethodAccessSchema = _enum([
12854
+ "view",
12855
+ "create",
12856
+ "delete"
12857
+ ]);
12858
+ /**
12859
+ * A single scope grant. Three forms — checked in matcher with OR
12860
+ * semantics; the first match decides whether the call passes:
12861
+ *
12862
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12863
+ * cap with the matching `scope` declaration. New caps inherit grants
12864
+ * automatically — adding a device cap to the codebase makes it
12865
+ * reachable to every user with `category:device [...]` without a
12866
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12867
+ *
12868
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12869
+ * useful when an integration needs to interact with one specific
12870
+ * addon's surface regardless of how many caps it exports.
12871
+ *
12872
+ * - `capability` — the most precise form. Single cap name. Use for
12873
+ * integration tokens where you want to grant `addons.installPackage`
12874
+ * and nothing else.
12875
+ *
12876
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12877
+ * "give me everything" escape; there is no wildcard scope.
12878
+ */
12879
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
12880
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
12881
+ var CapScopeSchema = _enum(["device", "system"]);
12882
+ /**
12883
+ * DeviceSelector (scope model v3 — 2026-08-12).
12884
+ *
12885
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12886
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12887
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12888
+ * AFTER the grant was minted — no re-grant, no re-login.
12889
+ *
12890
+ * - `all` — every device in the deployment. The broad viewer/operator
12891
+ * lever without a `category` grant (a `category` grant also covers device
12892
+ * caps that carry no deviceId; `all` is specifically the device set).
12893
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12894
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12895
+ * is NOT covered until the grant is edited.
12896
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12897
+ * DYNAMIC. A device that changes type, or a new device of the type,
12898
+ * re-resolves on the next request.
12899
+ * - `locations` — every device whose operator-assigned `location` label is
12900
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12901
+ * null/unset location matches NO `locations` selector.
12902
+ */
12903
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12904
+ object({ kind: literal("all") }),
12905
+ object({
12906
+ kind: literal("ids"),
12907
+ ids: array(number().int()).min(1)
12908
+ }),
12909
+ object({
12910
+ kind: literal("types"),
12911
+ types: array(_enum(DeviceType)).min(1)
12912
+ }),
12913
+ object({
12914
+ kind: literal("locations"),
12915
+ locations: array(string().min(1)).min(1)
12916
+ })
12917
+ ]);
12918
+ var DeviceTokenScopeSchema = object({
12919
+ type: literal("device"),
12920
+ /** The device SET this grant covers — resolved against the live fleet. */
12921
+ selector: DeviceSelectorSchema,
12922
+ access: array(MethodAccessSchema).min(1),
12923
+ /**
12924
+ * Whether a grant on a PARENT device transparently covers its accessory
12925
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12926
+ * Direction is parent → children ONLY.
12927
+ *
12928
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12929
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12930
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12931
+ * operator must grant on the child, not inherit from the parent). Set it
12932
+ * explicitly to override that default per grant.
12933
+ */
12934
+ includeLinked: boolean().optional()
12935
+ });
12936
+ /**
12937
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12938
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12939
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12940
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12941
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12942
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12943
+ * migration cannot reach a JWT already in a client's hands; parse-time
12944
+ * migration covers both without a flag day. No cast — the raw object is read
12945
+ * through `Reflect.get` (its static type is `unknown`).
12946
+ */
12947
+ function migrateLegacyTokenScope(raw) {
12948
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12949
+ if (Reflect.get(raw, "type") !== "device") return raw;
12950
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12951
+ const targets = Reflect.get(raw, "targets");
12952
+ if (!Array.isArray(targets)) return raw;
12953
+ return {
12954
+ type: "device",
12955
+ selector: {
12956
+ kind: "ids",
12957
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12958
+ },
12959
+ access: Reflect.get(raw, "access")
12960
+ };
12961
+ }
12962
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12963
+ object({
12964
+ type: literal("category"),
12965
+ target: CapScopeSchema,
12966
+ access: array(MethodAccessSchema).min(1)
12967
+ }),
12968
+ object({
12969
+ type: literal("capability"),
12970
+ target: string(),
12971
+ access: array(MethodAccessSchema).min(1)
12972
+ }),
12973
+ object({
12974
+ type: literal("addon"),
12975
+ target: string(),
12976
+ access: array(MethodAccessSchema).min(1)
12977
+ }),
12978
+ DeviceTokenScopeSchema
12979
+ ]));
12980
+ object({
12981
+ id: string(),
12982
+ username: string(),
12983
+ passwordHash: string(),
12984
+ /**
12985
+ * Admin bypass. When true, the middleware skips the scope-access
12986
+ * check entirely. There is no other axis of privilege; the legacy
12987
+ * role enum collapsed onto this boolean in v2.
12988
+ */
12989
+ isAdmin: boolean().default(false),
12990
+ allowedProviders: AllowedProviderSchema,
12991
+ allowedDevices: AllowedDevicesSchema,
12992
+ /**
12993
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12994
+ * ignored. Non-admins without scopes are locked out of every
12995
+ * protected call.
12996
+ */
12997
+ scopes: array(TokenScopeSchema).default([]),
12998
+ createdAt: number(),
12999
+ updatedAt: number()
13000
+ });
13001
+ object({
13002
+ id: string(),
13003
+ label: string(),
13004
+ isAdmin: boolean().default(false),
13005
+ allowedProviders: AllowedProviderSchema,
13006
+ allowedDevices: AllowedDevicesSchema,
13007
+ tokenHash: string(),
13008
+ tokenPrefix: string(),
13009
+ createdAt: number(),
13010
+ lastUsedAt: number().optional()
13011
+ });
13012
+ object({
13013
+ id: string(),
13014
+ userId: string(),
13015
+ name: string(),
13016
+ tokenHash: string(),
13017
+ tokenPrefix: string(),
13018
+ scopes: array(TokenScopeSchema),
13019
+ expiresAt: number().nullish(),
13020
+ lastUsedAt: number().nullish(),
13021
+ createdAt: number()
13022
+ });
13023
+ /** What a share-view grant lets its holder see. */
13024
+ var ShareViewGrantSchema = object({
13025
+ kind: _enum(SHARE_VIEW_KINDS),
13026
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13027
+ /** Talk-back, opt-in at mint time; absent means off. */
13028
+ talk: boolean().optional()
13029
+ });
13030
+ var ShareViewScopeSchema = object({
13031
+ type: literal("share-view"),
13032
+ grant: ShareViewGrantSchema
13033
+ });
13034
+ var AddonRoutesScopeSchema = object({
13035
+ type: literal("addon-routes"),
13036
+ access: array(MethodAccessSchema).min(1)
13037
+ });
13038
+ var CredentialScopeSchema = union([
13039
+ literal("*"),
13040
+ ShareViewScopeSchema,
13041
+ AddonRoutesScopeSchema,
13042
+ TokenScopeSchema
13043
+ ]);
13044
+ object({
13045
+ kind: literal("credential"),
13046
+ sub: string().min(1),
13047
+ jti: string().min(16),
13048
+ scopes: array(CredentialScopeSchema).min(1)
13049
+ });
12821
13050
  _enum(["new-session", "step-up"]);
12822
13051
  /** The languages the hub's app landing pages are written in (D858). */
12823
13052
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -43955,202 +44184,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
43955
44184
  auth: "admin"
43956
44185
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
43957
44186
  /**
43958
- * Zod schemas for persisted record types.
43959
- *
43960
- * These schemas serve as the single source of truth for types that are
43961
- * stored in the settings-store backend and retrieved via tRPC. They
43962
- * replace the manual TypeScript interfaces with schema-derived types,
43963
- * enabling runtime validation when reading from the backend.
43964
- *
43965
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
43966
- * keeps the original name for backward compatibility.
43967
- */
43968
- /**
43969
- * Auth model (v2 — 2026-05-12 caps-only):
43970
- *
43971
- * Two principals:
43972
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
43973
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
43974
- *
43975
- * No role enum. There used to be `viewer / agent / scoped` and earlier
43976
- * `super_admin` — collapsed onto the single boolean because every
43977
- * non-admin caller is governed by the same scope-check rules; the
43978
- * distinction was only ever used to route role-specific procedures
43979
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
43980
- * with `isAdmin: false` + an explicit scope list.
43981
- */
43982
- /** Per-method access flavour. Materialised on every cap method via codegen. */
43983
- var MethodAccessSchema = _enum([
43984
- "view",
43985
- "create",
43986
- "delete"
43987
- ]);
43988
- /**
43989
- * A single scope grant. Three forms — checked in matcher with OR
43990
- * semantics; the first match decides whether the call passes:
43991
- *
43992
- * - `category` — broadest. `target: 'device' | 'system'` selects every
43993
- * cap with the matching `scope` declaration. New caps inherit grants
43994
- * automatically — adding a device cap to the codebase makes it
43995
- * reachable to every user with `category:device [...]` without a
43996
- * re-grant. Use this for "family viewer" / "operator" tiers.
43997
- *
43998
- * - `addon` — every cap that ships inside a given addon id. Niche;
43999
- * useful when an integration needs to interact with one specific
44000
- * addon's surface regardless of how many caps it exports.
44001
- *
44002
- * - `capability` — the most precise form. Single cap name. Use for
44003
- * integration tokens where you want to grant `addons.installPackage`
44004
- * and nothing else.
44005
- *
44006
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44007
- * "give me everything" escape; there is no wildcard scope.
44008
- */
44009
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44010
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44011
- var CapScopeSchema = _enum(["device", "system"]);
44012
- /**
44013
- * DeviceSelector (scope model v3 — 2026-08-12).
44014
- *
44015
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44016
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44017
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44018
- * AFTER the grant was minted — no re-grant, no re-login.
44019
- *
44020
- * - `all` — every device in the deployment. The broad viewer/operator
44021
- * lever without a `category` grant (a `category` grant also covers device
44022
- * caps that carry no deviceId; `all` is specifically the device set).
44023
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44024
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44025
- * is NOT covered until the grant is edited.
44026
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44027
- * DYNAMIC. A device that changes type, or a new device of the type,
44028
- * re-resolves on the next request.
44029
- * - `locations` — every device whose operator-assigned `location` label is
44030
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44031
- * null/unset location matches NO `locations` selector.
44032
- */
44033
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44034
- object({ kind: literal("all") }),
44035
- object({
44036
- kind: literal("ids"),
44037
- ids: array(number().int()).min(1)
44038
- }),
44039
- object({
44040
- kind: literal("types"),
44041
- types: array(_enum(DeviceType)).min(1)
44042
- }),
44043
- object({
44044
- kind: literal("locations"),
44045
- locations: array(string().min(1)).min(1)
44046
- })
44047
- ]);
44048
- var DeviceTokenScopeSchema = object({
44049
- type: literal("device"),
44050
- /** The device SET this grant covers — resolved against the live fleet. */
44051
- selector: DeviceSelectorSchema,
44052
- access: array(MethodAccessSchema).min(1),
44053
- /**
44054
- * Whether a grant on a PARENT device transparently covers its accessory
44055
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44056
- * Direction is parent → children ONLY.
44057
- *
44058
- * Absent → the matcher DERIVES it from the access flavour: `view`
44059
- * inherits (a camera viewer sees the camera's accessories), `create` /
44060
- * `delete` do NOT (actuating/removing a child is an explicit act the
44061
- * operator must grant on the child, not inherit from the parent). Set it
44062
- * explicitly to override that default per grant.
44063
- */
44064
- includeLinked: boolean().optional()
44065
- });
44066
- /**
44067
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44068
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44069
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44070
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44071
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44072
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44073
- * migration cannot reach a JWT already in a client's hands; parse-time
44074
- * migration covers both without a flag day. No cast — the raw object is read
44075
- * through `Reflect.get` (its static type is `unknown`).
44076
- */
44077
- function migrateLegacyTokenScope(raw) {
44078
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44079
- if (Reflect.get(raw, "type") !== "device") return raw;
44080
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44081
- const targets = Reflect.get(raw, "targets");
44082
- if (!Array.isArray(targets)) return raw;
44083
- return {
44084
- type: "device",
44085
- selector: {
44086
- kind: "ids",
44087
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44088
- },
44089
- access: Reflect.get(raw, "access")
44090
- };
44091
- }
44092
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44093
- object({
44094
- type: literal("category"),
44095
- target: CapScopeSchema,
44096
- access: array(MethodAccessSchema).min(1)
44097
- }),
44098
- object({
44099
- type: literal("capability"),
44100
- target: string(),
44101
- access: array(MethodAccessSchema).min(1)
44102
- }),
44103
- object({
44104
- type: literal("addon"),
44105
- target: string(),
44106
- access: array(MethodAccessSchema).min(1)
44107
- }),
44108
- DeviceTokenScopeSchema
44109
- ]));
44110
- object({
44111
- id: string(),
44112
- username: string(),
44113
- passwordHash: string(),
44114
- /**
44115
- * Admin bypass. When true, the middleware skips the scope-access
44116
- * check entirely. There is no other axis of privilege; the legacy
44117
- * role enum collapsed onto this boolean in v2.
44118
- */
44119
- isAdmin: boolean().default(false),
44120
- allowedProviders: AllowedProviderSchema,
44121
- allowedDevices: AllowedDevicesSchema,
44122
- /**
44123
- * Scopes granted to this user. Admins bypass; their `scopes` is
44124
- * ignored. Non-admins without scopes are locked out of every
44125
- * protected call.
44126
- */
44127
- scopes: array(TokenScopeSchema).default([]),
44128
- createdAt: number(),
44129
- updatedAt: number()
44130
- });
44131
- object({
44132
- id: string(),
44133
- label: string(),
44134
- isAdmin: boolean().default(false),
44135
- allowedProviders: AllowedProviderSchema,
44136
- allowedDevices: AllowedDevicesSchema,
44137
- tokenHash: string(),
44138
- tokenPrefix: string(),
44139
- createdAt: number(),
44140
- lastUsedAt: number().optional()
44141
- });
44142
- object({
44143
- id: string(),
44144
- userId: string(),
44145
- name: string(),
44146
- tokenHash: string(),
44147
- tokenPrefix: string(),
44148
- scopes: array(TokenScopeSchema),
44149
- expiresAt: number().nullish(),
44150
- lastUsedAt: number().nullish(),
44151
- createdAt: number()
44152
- });
44153
- /**
44154
44187
  * `oauth-integration` — a collection cap by which an addon declares
44155
44188
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44156
44189
  * Each provider returns a static descriptor; the core enumerates them
@@ -7942,6 +7942,12 @@ function normalizeAddonInitResult(result) {
7942
7942
  if (Array.isArray(result)) return { providers: result };
7943
7943
  return result;
7944
7944
  }
7945
+ /**
7946
+ * The share-view scope kinds a `csv_*` share token can carry. The hub mints
7947
+ * and validates them (`share-token.service.ts` builds its enum from this
7948
+ * list); a data plane names the kinds it ACCEPTS in `share`.
7949
+ */
7950
+ var SHARE_VIEW_KINDS = ["grid-view", "events-view"];
7945
7951
  /** The wire shape of {@link PeerBytesTicket} — see the type for what it is. */
7946
7952
  var PeerBytesTicketSchema = object({
7947
7953
  /** `http://127.0.0.1:<port>/<token>`. One `GET` takes it. */
@@ -12814,6 +12820,229 @@ var ConvertResultSchema = object({
12814
12820
  files: array(string()).readonly()
12815
12821
  })).readonly()
12816
12822
  });
12823
+ /**
12824
+ * Zod schemas for persisted record types.
12825
+ *
12826
+ * These schemas serve as the single source of truth for types that are
12827
+ * stored in the settings-store backend and retrieved via tRPC. They
12828
+ * replace the manual TypeScript interfaces with schema-derived types,
12829
+ * enabling runtime validation when reading from the backend.
12830
+ *
12831
+ * Convention: each schema is named `<Type>Schema`, the inferred TS type
12832
+ * keeps the original name for backward compatibility.
12833
+ */
12834
+ /**
12835
+ * Auth model (v2 — 2026-05-12 caps-only):
12836
+ *
12837
+ * Two principals:
12838
+ * - `isAdmin: true` — full bypass. Admin scope set is ignored.
12839
+ * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
12840
+ *
12841
+ * No role enum. There used to be `viewer / agent / scoped` and earlier
12842
+ * `super_admin` — collapsed onto the single boolean because every
12843
+ * non-admin caller is governed by the same scope-check rules; the
12844
+ * distinction was only ever used to route role-specific procedures
12845
+ * which we no longer have. CLI / webhook integrations are `cst_*` tokens
12846
+ * with `isAdmin: false` + an explicit scope list.
12847
+ */
12848
+ /** Per-method access flavour. Materialised on every cap method via codegen. */
12849
+ var MethodAccessSchema = _enum([
12850
+ "view",
12851
+ "create",
12852
+ "delete"
12853
+ ]);
12854
+ /**
12855
+ * A single scope grant. Three forms — checked in matcher with OR
12856
+ * semantics; the first match decides whether the call passes:
12857
+ *
12858
+ * - `category` — broadest. `target: 'device' | 'system'` selects every
12859
+ * cap with the matching `scope` declaration. New caps inherit grants
12860
+ * automatically — adding a device cap to the codebase makes it
12861
+ * reachable to every user with `category:device [...]` without a
12862
+ * re-grant. Use this for "family viewer" / "operator" tiers.
12863
+ *
12864
+ * - `addon` — every cap that ships inside a given addon id. Niche;
12865
+ * useful when an integration needs to interact with one specific
12866
+ * addon's surface regardless of how many caps it exports.
12867
+ *
12868
+ * - `capability` — the most precise form. Single cap name. Use for
12869
+ * integration tokens where you want to grant `addons.installPackage`
12870
+ * and nothing else.
12871
+ *
12872
+ * `access` is REQUIRED with min 1 element. `admin` bypass is the
12873
+ * "give me everything" escape; there is no wildcard scope.
12874
+ */
12875
+ var AllowedProviderSchema = union([literal("*"), array(string())]);
12876
+ var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
12877
+ var CapScopeSchema = _enum(["device", "system"]);
12878
+ /**
12879
+ * DeviceSelector (scope model v3 — 2026-08-12).
12880
+ *
12881
+ * A `device` grant no longer carries a frozen list of deviceIds. It carries
12882
+ * a SELECTOR the matcher resolves against the live fleet, so the grant can be
12883
+ * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
12884
+ * AFTER the grant was minted — no re-grant, no re-login.
12885
+ *
12886
+ * - `all` — every device in the deployment. The broad viewer/operator
12887
+ * lever without a `category` grant (a `category` grant also covers device
12888
+ * caps that carry no deviceId; `all` is specifically the device set).
12889
+ * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
12890
+ * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
12891
+ * is NOT covered until the grant is edited.
12892
+ * - `types` — every device of a `DeviceType` (e.g. every `camera`).
12893
+ * DYNAMIC. A device that changes type, or a new device of the type,
12894
+ * re-resolves on the next request.
12895
+ * - `locations` — every device whose operator-assigned `location` label is
12896
+ * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
12897
+ * null/unset location matches NO `locations` selector.
12898
+ */
12899
+ var DeviceSelectorSchema = discriminatedUnion("kind", [
12900
+ object({ kind: literal("all") }),
12901
+ object({
12902
+ kind: literal("ids"),
12903
+ ids: array(number().int()).min(1)
12904
+ }),
12905
+ object({
12906
+ kind: literal("types"),
12907
+ types: array(_enum(DeviceType)).min(1)
12908
+ }),
12909
+ object({
12910
+ kind: literal("locations"),
12911
+ locations: array(string().min(1)).min(1)
12912
+ })
12913
+ ]);
12914
+ var DeviceTokenScopeSchema = object({
12915
+ type: literal("device"),
12916
+ /** The device SET this grant covers — resolved against the live fleet. */
12917
+ selector: DeviceSelectorSchema,
12918
+ access: array(MethodAccessSchema).min(1),
12919
+ /**
12920
+ * Whether a grant on a PARENT device transparently covers its accessory
12921
+ * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
12922
+ * Direction is parent → children ONLY.
12923
+ *
12924
+ * Absent → the matcher DERIVES it from the access flavour: `view`
12925
+ * inherits (a camera viewer sees the camera's accessories), `create` /
12926
+ * `delete` do NOT (actuating/removing a child is an explicit act the
12927
+ * operator must grant on the child, not inherit from the parent). Set it
12928
+ * explicitly to override that default per grant.
12929
+ */
12930
+ includeLinked: boolean().optional()
12931
+ });
12932
+ /**
12933
+ * v2 → v3 lazy migration. A pre-v3 `device` grant carried
12934
+ * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
12935
+ * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
12936
+ * EVERY parse path — stored records AND the JWT-carried scope arrays
12937
+ * normalised at the request boundary ({@link normalizeTokenScopes} in
12938
+ * `device-selector.ts`). Chosen over a one-time DB migration because a
12939
+ * migration cannot reach a JWT already in a client's hands; parse-time
12940
+ * migration covers both without a flag day. No cast — the raw object is read
12941
+ * through `Reflect.get` (its static type is `unknown`).
12942
+ */
12943
+ function migrateLegacyTokenScope(raw) {
12944
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
12945
+ if (Reflect.get(raw, "type") !== "device") return raw;
12946
+ if (Reflect.get(raw, "selector") !== void 0) return raw;
12947
+ const targets = Reflect.get(raw, "targets");
12948
+ if (!Array.isArray(targets)) return raw;
12949
+ return {
12950
+ type: "device",
12951
+ selector: {
12952
+ kind: "ids",
12953
+ ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
12954
+ },
12955
+ access: Reflect.get(raw, "access")
12956
+ };
12957
+ }
12958
+ var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
12959
+ object({
12960
+ type: literal("category"),
12961
+ target: CapScopeSchema,
12962
+ access: array(MethodAccessSchema).min(1)
12963
+ }),
12964
+ object({
12965
+ type: literal("capability"),
12966
+ target: string(),
12967
+ access: array(MethodAccessSchema).min(1)
12968
+ }),
12969
+ object({
12970
+ type: literal("addon"),
12971
+ target: string(),
12972
+ access: array(MethodAccessSchema).min(1)
12973
+ }),
12974
+ DeviceTokenScopeSchema
12975
+ ]));
12976
+ object({
12977
+ id: string(),
12978
+ username: string(),
12979
+ passwordHash: string(),
12980
+ /**
12981
+ * Admin bypass. When true, the middleware skips the scope-access
12982
+ * check entirely. There is no other axis of privilege; the legacy
12983
+ * role enum collapsed onto this boolean in v2.
12984
+ */
12985
+ isAdmin: boolean().default(false),
12986
+ allowedProviders: AllowedProviderSchema,
12987
+ allowedDevices: AllowedDevicesSchema,
12988
+ /**
12989
+ * Scopes granted to this user. Admins bypass; their `scopes` is
12990
+ * ignored. Non-admins without scopes are locked out of every
12991
+ * protected call.
12992
+ */
12993
+ scopes: array(TokenScopeSchema).default([]),
12994
+ createdAt: number(),
12995
+ updatedAt: number()
12996
+ });
12997
+ object({
12998
+ id: string(),
12999
+ label: string(),
13000
+ isAdmin: boolean().default(false),
13001
+ allowedProviders: AllowedProviderSchema,
13002
+ allowedDevices: AllowedDevicesSchema,
13003
+ tokenHash: string(),
13004
+ tokenPrefix: string(),
13005
+ createdAt: number(),
13006
+ lastUsedAt: number().optional()
13007
+ });
13008
+ object({
13009
+ id: string(),
13010
+ userId: string(),
13011
+ name: string(),
13012
+ tokenHash: string(),
13013
+ tokenPrefix: string(),
13014
+ scopes: array(TokenScopeSchema),
13015
+ expiresAt: number().nullish(),
13016
+ lastUsedAt: number().nullish(),
13017
+ createdAt: number()
13018
+ });
13019
+ /** What a share-view grant lets its holder see. */
13020
+ var ShareViewGrantSchema = object({
13021
+ kind: _enum(SHARE_VIEW_KINDS),
13022
+ deviceIds: array(number().int().nonnegative()).min(1).max(64),
13023
+ /** Talk-back, opt-in at mint time; absent means off. */
13024
+ talk: boolean().optional()
13025
+ });
13026
+ var ShareViewScopeSchema = object({
13027
+ type: literal("share-view"),
13028
+ grant: ShareViewGrantSchema
13029
+ });
13030
+ var AddonRoutesScopeSchema = object({
13031
+ type: literal("addon-routes"),
13032
+ access: array(MethodAccessSchema).min(1)
13033
+ });
13034
+ var CredentialScopeSchema = union([
13035
+ literal("*"),
13036
+ ShareViewScopeSchema,
13037
+ AddonRoutesScopeSchema,
13038
+ TokenScopeSchema
13039
+ ]);
13040
+ object({
13041
+ kind: literal("credential"),
13042
+ sub: string().min(1),
13043
+ jti: string().min(16),
13044
+ scopes: array(CredentialScopeSchema).min(1)
13045
+ });
12817
13046
  _enum(["new-session", "step-up"]);
12818
13047
  /** The languages the hub's app landing pages are written in (D858). */
12819
13048
  var AppPageLangSchema = _enum(["en", "it"]);
@@ -43951,202 +44180,6 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
43951
44180
  auth: "admin"
43952
44181
  }), method(NcAlarmSensorsInputSchema, NcAlarmSensorsSchema, { auth: "admin" });
43953
44182
  /**
43954
- * Zod schemas for persisted record types.
43955
- *
43956
- * These schemas serve as the single source of truth for types that are
43957
- * stored in the settings-store backend and retrieved via tRPC. They
43958
- * replace the manual TypeScript interfaces with schema-derived types,
43959
- * enabling runtime validation when reading from the backend.
43960
- *
43961
- * Convention: each schema is named `<Type>Schema`, the inferred TS type
43962
- * keeps the original name for backward compatibility.
43963
- */
43964
- /**
43965
- * Auth model (v2 — 2026-05-12 caps-only):
43966
- *
43967
- * Two principals:
43968
- * - `isAdmin: true` — full bypass. Admin scope set is ignored.
43969
- * - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
43970
- *
43971
- * No role enum. There used to be `viewer / agent / scoped` and earlier
43972
- * `super_admin` — collapsed onto the single boolean because every
43973
- * non-admin caller is governed by the same scope-check rules; the
43974
- * distinction was only ever used to route role-specific procedures
43975
- * which we no longer have. CLI / webhook integrations are `cst_*` tokens
43976
- * with `isAdmin: false` + an explicit scope list.
43977
- */
43978
- /** Per-method access flavour. Materialised on every cap method via codegen. */
43979
- var MethodAccessSchema = _enum([
43980
- "view",
43981
- "create",
43982
- "delete"
43983
- ]);
43984
- /**
43985
- * A single scope grant. Three forms — checked in matcher with OR
43986
- * semantics; the first match decides whether the call passes:
43987
- *
43988
- * - `category` — broadest. `target: 'device' | 'system'` selects every
43989
- * cap with the matching `scope` declaration. New caps inherit grants
43990
- * automatically — adding a device cap to the codebase makes it
43991
- * reachable to every user with `category:device [...]` without a
43992
- * re-grant. Use this for "family viewer" / "operator" tiers.
43993
- *
43994
- * - `addon` — every cap that ships inside a given addon id. Niche;
43995
- * useful when an integration needs to interact with one specific
43996
- * addon's surface regardless of how many caps it exports.
43997
- *
43998
- * - `capability` — the most precise form. Single cap name. Use for
43999
- * integration tokens where you want to grant `addons.installPackage`
44000
- * and nothing else.
44001
- *
44002
- * `access` is REQUIRED with min 1 element. `admin` bypass is the
44003
- * "give me everything" escape; there is no wildcard scope.
44004
- */
44005
- var AllowedProviderSchema = union([literal("*"), array(string())]);
44006
- var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
44007
- var CapScopeSchema = _enum(["device", "system"]);
44008
- /**
44009
- * DeviceSelector (scope model v3 — 2026-08-12).
44010
- *
44011
- * A `device` grant no longer carries a frozen list of deviceIds. It carries
44012
- * a SELECTOR the matcher resolves against the live fleet, so the grant can be
44013
- * DYNAMIC: a `types:['camera']` selector automatically covers a camera added
44014
- * AFTER the grant was minted — no re-grant, no re-login.
44015
- *
44016
- * - `all` — every device in the deployment. The broad viewer/operator
44017
- * lever without a `category` grant (a `category` grant also covers device
44018
- * caps that carry no deviceId; `all` is specifically the device set).
44019
- * - `ids` — an explicit deviceId list. This is what a v2 `device:[…]`
44020
- * grant migrates to (see {@link TokenScopeSchema}); STATIC — a new camera
44021
- * is NOT covered until the grant is edited.
44022
- * - `types` — every device of a `DeviceType` (e.g. every `camera`).
44023
- * DYNAMIC. A device that changes type, or a new device of the type,
44024
- * re-resolves on the next request.
44025
- * - `locations` — every device whose operator-assigned `location` label is
44026
- * in the set (e.g. "Garden", "Front door"). DYNAMIC. A device with a
44027
- * null/unset location matches NO `locations` selector.
44028
- */
44029
- var DeviceSelectorSchema = discriminatedUnion("kind", [
44030
- object({ kind: literal("all") }),
44031
- object({
44032
- kind: literal("ids"),
44033
- ids: array(number().int()).min(1)
44034
- }),
44035
- object({
44036
- kind: literal("types"),
44037
- types: array(_enum(DeviceType)).min(1)
44038
- }),
44039
- object({
44040
- kind: literal("locations"),
44041
- locations: array(string().min(1)).min(1)
44042
- })
44043
- ]);
44044
- var DeviceTokenScopeSchema = object({
44045
- type: literal("device"),
44046
- /** The device SET this grant covers — resolved against the live fleet. */
44047
- selector: DeviceSelectorSchema,
44048
- access: array(MethodAccessSchema).min(1),
44049
- /**
44050
- * Whether a grant on a PARENT device transparently covers its accessory
44051
- * CHILDREN (siren / floodlight / PIR) via the persisted-parentage walk.
44052
- * Direction is parent → children ONLY.
44053
- *
44054
- * Absent → the matcher DERIVES it from the access flavour: `view`
44055
- * inherits (a camera viewer sees the camera's accessories), `create` /
44056
- * `delete` do NOT (actuating/removing a child is an explicit act the
44057
- * operator must grant on the child, not inherit from the parent). Set it
44058
- * explicitly to override that default per grant.
44059
- */
44060
- includeLinked: boolean().optional()
44061
- });
44062
- /**
44063
- * v2 → v3 lazy migration. A pre-v3 `device` grant carried
44064
- * `targets: string[]` (stringified deviceIds); it rewrites to the equivalent
44065
- * `selector: {kind:'ids', ids}`. Applied as a `preprocess` so it runs on
44066
- * EVERY parse path — stored records AND the JWT-carried scope arrays
44067
- * normalised at the request boundary ({@link normalizeTokenScopes} in
44068
- * `device-selector.ts`). Chosen over a one-time DB migration because a
44069
- * migration cannot reach a JWT already in a client's hands; parse-time
44070
- * migration covers both without a flag day. No cast — the raw object is read
44071
- * through `Reflect.get` (its static type is `unknown`).
44072
- */
44073
- function migrateLegacyTokenScope(raw) {
44074
- if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return raw;
44075
- if (Reflect.get(raw, "type") !== "device") return raw;
44076
- if (Reflect.get(raw, "selector") !== void 0) return raw;
44077
- const targets = Reflect.get(raw, "targets");
44078
- if (!Array.isArray(targets)) return raw;
44079
- return {
44080
- type: "device",
44081
- selector: {
44082
- kind: "ids",
44083
- ids: targets.map((t) => typeof t === "string" ? Number(t) : t).filter((n) => typeof n === "number" && Number.isInteger(n))
44084
- },
44085
- access: Reflect.get(raw, "access")
44086
- };
44087
- }
44088
- var TokenScopeSchema = preprocess(migrateLegacyTokenScope, discriminatedUnion("type", [
44089
- object({
44090
- type: literal("category"),
44091
- target: CapScopeSchema,
44092
- access: array(MethodAccessSchema).min(1)
44093
- }),
44094
- object({
44095
- type: literal("capability"),
44096
- target: string(),
44097
- access: array(MethodAccessSchema).min(1)
44098
- }),
44099
- object({
44100
- type: literal("addon"),
44101
- target: string(),
44102
- access: array(MethodAccessSchema).min(1)
44103
- }),
44104
- DeviceTokenScopeSchema
44105
- ]));
44106
- object({
44107
- id: string(),
44108
- username: string(),
44109
- passwordHash: string(),
44110
- /**
44111
- * Admin bypass. When true, the middleware skips the scope-access
44112
- * check entirely. There is no other axis of privilege; the legacy
44113
- * role enum collapsed onto this boolean in v2.
44114
- */
44115
- isAdmin: boolean().default(false),
44116
- allowedProviders: AllowedProviderSchema,
44117
- allowedDevices: AllowedDevicesSchema,
44118
- /**
44119
- * Scopes granted to this user. Admins bypass; their `scopes` is
44120
- * ignored. Non-admins without scopes are locked out of every
44121
- * protected call.
44122
- */
44123
- scopes: array(TokenScopeSchema).default([]),
44124
- createdAt: number(),
44125
- updatedAt: number()
44126
- });
44127
- object({
44128
- id: string(),
44129
- label: string(),
44130
- isAdmin: boolean().default(false),
44131
- allowedProviders: AllowedProviderSchema,
44132
- allowedDevices: AllowedDevicesSchema,
44133
- tokenHash: string(),
44134
- tokenPrefix: string(),
44135
- createdAt: number(),
44136
- lastUsedAt: number().optional()
44137
- });
44138
- object({
44139
- id: string(),
44140
- userId: string(),
44141
- name: string(),
44142
- tokenHash: string(),
44143
- tokenPrefix: string(),
44144
- scopes: array(TokenScopeSchema),
44145
- expiresAt: number().nullish(),
44146
- lastUsedAt: number().nullish(),
44147
- createdAt: number()
44148
- });
44149
- /**
44150
44183
  * `oauth-integration` — a collection cap by which an addon declares
44151
44184
  * itself a linkable OAuth client of the hub's core `/oauth2/*` server.
44152
44185
  * 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-waste-collections",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Waste collection schedules via the vendored HACS waste_collection_schedule Python core — full source catalog as calendar-source.",
5
5
  "keywords": [
6
6
  "camstack",