@camstack/types 1.2.309 → 1.2.310

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-C3gDpn7G.js");
2
+ const require_sleep = require("./sleep-DyF95STh.js");
3
3
  const require_event_category = require("./event-category-DmS_tLsU.js");
4
4
  const require_composition = require("./composition-BLWAivB2.js");
5
5
  const require_err_msg = require("./err-msg-Dx2Kor0g.js");
@@ -16116,7 +16116,143 @@ var filesystemBrowseCapability = {
16116
16116
  }
16117
16117
  };
16118
16118
  //#endregion
16119
+ //#region src/capabilities/viewer-link-url.ts
16120
+ /** The central link domain the viewer app claims (Universal Links / App Links). */
16121
+ var CAMSTACK_APP_LINK_ORIGIN = "https://camstack.zentik.app";
16122
+ /** Where the hub serves the viewer SPA. */
16123
+ var HUB_VIEWER_MOUNT = "/viewer/camstack";
16124
+ /** True when `baseUrl` names the CamStack app link rather than a hub address. */
16125
+ function isAppLinkBase(baseUrl) {
16126
+ if (baseUrl === null || baseUrl === void 0 || baseUrl === "") return false;
16127
+ try {
16128
+ return new URL(baseUrl).origin === CAMSTACK_APP_LINK_ORIGIN;
16129
+ } catch {
16130
+ return false;
16131
+ }
16132
+ }
16133
+ /**
16134
+ * The base a viewer link is built on, from the flagged notification address:
16135
+ * the effective address is the explicit choice, else AUTO's `resolved`. The
16136
+ * one reading of that answer — the NC's tap and the Network page's preview
16137
+ * both go through it.
16138
+ */
16139
+ function viewerLinkBaseOf(endpoint) {
16140
+ const flagged = endpoint.baseUrl ?? endpoint.resolved;
16141
+ return isAppLinkBase(flagged) ? {
16142
+ form: "app",
16143
+ hubBaseUrl: endpoint.appLinkHub ?? null
16144
+ } : {
16145
+ form: "hub",
16146
+ hubBaseUrl: flagged
16147
+ };
16148
+ }
16149
+ /**
16150
+ * Reduce a base URL to the bare origin the central page accepts as `hub=`,
16151
+ * or `null` when it cannot be one: not http(s), carries credentials, or does
16152
+ * not parse. A path/query/fragment is dropped — the central page mounts the
16153
+ * viewer under the origin itself.
16154
+ */
16155
+ function toAppLinkHubOrigin(baseUrl) {
16156
+ let parsed;
16157
+ try {
16158
+ parsed = new URL(baseUrl);
16159
+ } catch {
16160
+ return null;
16161
+ }
16162
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
16163
+ if (parsed.username !== "" || parsed.password !== "") return null;
16164
+ if (parsed.hostname === "") return null;
16165
+ return parsed.origin;
16166
+ }
16167
+ /**
16168
+ * The hub address an app link carries as `hub=`, chosen from the hub's OWN
16169
+ * advertised palette so it always agrees with what the Network page lists:
16170
+ * the public tunnel first, then the mesh (Tailscale MagicDNS) — HTTPS only,
16171
+ * because the app and the hosted viewer reach it from off the LAN. `null`
16172
+ * when the hub advertises neither.
16173
+ */
16174
+ function pickAppLinkHub(endpoints) {
16175
+ const rank = (e) => {
16176
+ if (e.kind !== "public" || !e.baseUrl.startsWith("https://")) return null;
16177
+ if (e.interfaceKind === "public") return 0;
16178
+ if (e.interfaceKind === "vpn") return 1;
16179
+ return null;
16180
+ };
16181
+ return endpoints.flatMap((e) => {
16182
+ const r = rank(e);
16183
+ const origin = toAppLinkHubOrigin(e.baseUrl);
16184
+ return r === null || origin === null ? [] : [{
16185
+ r,
16186
+ p: e.priority,
16187
+ origin
16188
+ }];
16189
+ }).toSorted((a, b) => a.r - b.r || a.p - b.p)[0]?.origin ?? null;
16190
+ }
16191
+ /** The viewer route for a destination, or null when it names nothing. */
16192
+ function viewerRoute(destination) {
16193
+ switch (destination.kind) {
16194
+ case "camera": {
16195
+ if (!Number.isInteger(destination.deviceId) || destination.deviceId <= 0) return null;
16196
+ const at = destination.atMs;
16197
+ return {
16198
+ path: `/camera/${String(destination.deviceId)}`,
16199
+ query: at !== void 0 && Number.isFinite(at) ? [["at", String(Math.trunc(at))]] : []
16200
+ };
16201
+ }
16202
+ case "events": return {
16203
+ path: "/events",
16204
+ query: []
16205
+ };
16206
+ case "notifications": return {
16207
+ path: "/notifications",
16208
+ query: []
16209
+ };
16210
+ }
16211
+ }
16212
+ function encodeQuery(query) {
16213
+ if (query.length === 0) return "";
16214
+ return `?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
16215
+ }
16216
+ /**
16217
+ * The link for a viewer destination, or `null` when none can be minted: an
16218
+ * unknown destination, or the `hub` form with no reachable base (a dead link
16219
+ * is worse than none).
16220
+ */
16221
+ function buildViewerLink(input) {
16222
+ const route = viewerRoute(input.destination);
16223
+ if (route === null) return null;
16224
+ const raw = input.base.hubBaseUrl;
16225
+ const base = raw === null ? "" : raw.replace(/\/+$/, "");
16226
+ if (input.base.form === "app") {
16227
+ const origin = base === "" ? null : toAppLinkHubOrigin(base);
16228
+ const query = origin === null ? route.query : [...route.query, ["hub", origin]];
16229
+ return `${CAMSTACK_APP_LINK_ORIGIN}${route.path}${encodeQuery(query)}`;
16230
+ }
16231
+ if (base === "" || isAppLinkBase(base)) return null;
16232
+ return `${base}${HUB_VIEWER_MOUNT}${route.path}${encodeQuery(route.query)}`;
16233
+ }
16234
+ //#endregion
16119
16235
  //#region src/capabilities/hub-address.ts
16236
+ var HUB_ADDRESS_ROLES = [
16237
+ "viewer",
16238
+ "notifications",
16239
+ "agents",
16240
+ "signIn"
16241
+ ];
16242
+ var SINGLE_HUB_ADDRESS_ROLES = [
16243
+ "notifications",
16244
+ "agents",
16245
+ "signIn"
16246
+ ];
16247
+ function isSingleHubAddressRole(role) {
16248
+ return role !== "viewer";
16249
+ }
16250
+ var AUTOMATIC_HUB_ADDRESS_FLAGS = {
16251
+ viewer: [],
16252
+ notifications: null,
16253
+ agents: null,
16254
+ signIn: null
16255
+ };
16120
16256
  var IPV4_LITERAL = /^\d{1,3}(?:\.\d{1,3}){3}$/;
16121
16257
  function hostnameOf(baseUrl) {
16122
16258
  try {
@@ -16126,6 +16262,9 @@ function hostnameOf(baseUrl) {
16126
16262
  return null;
16127
16263
  }
16128
16264
  }
16265
+ function trimSlash(baseUrl) {
16266
+ return baseUrl.replace(/\/+$/, "");
16267
+ }
16129
16268
  function isMesh(endpoint) {
16130
16269
  return endpoint.kind === "public" && endpoint.interfaceKind === "vpn";
16131
16270
  }
@@ -16140,11 +16279,11 @@ function isLanIpv4Literal(endpoint) {
16140
16279
  const host = hostnameOf(endpoint.baseUrl);
16141
16280
  return host !== null && IPV4_LITERAL.test(host);
16142
16281
  }
16143
- /** Lower = better; `null` = never this audience's answer. */
16144
- function rankFor(audience, endpoint) {
16145
- if (endpoint.kind === "loopback" || endpoint.kind === "app-link") return null;
16282
+ /** Lower = better; `null` = never this role's AUTOMATIC answer. */
16283
+ function autoRankFor(role, endpoint) {
16284
+ if (endpoint.kind === "loopback" || endpoint.kind === "app-link" || endpoint.kind === "custom") return null;
16146
16285
  if (hostnameOf(endpoint.baseUrl) === null) return null;
16147
- if (audience === "cluster") {
16286
+ if (role === "agents") {
16148
16287
  if (isLanIpv4Literal(endpoint)) return 0;
16149
16288
  if (isPlausibleLan(endpoint)) return 1;
16150
16289
  if (isMesh(endpoint)) return 2;
@@ -16170,11 +16309,11 @@ function isLoopbackHost(hostname) {
16170
16309
  return /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(h);
16171
16310
  }
16172
16311
  /**
16173
- * Validate an operator-typed override: an http(s) URL with a host, no
16312
+ * Validate an operator-typed address: an http(s) URL with a host, no
16174
16313
  * credentials, no path, and never loopback (it would name whoever dials it).
16175
16314
  * Answers the normalised origin (`scheme://host[:port]`).
16176
16315
  */
16177
- function validateHubAddressOverride(raw) {
16316
+ function validateCustomAddress(raw) {
16178
16317
  const trimmed = raw.trim();
16179
16318
  if (trimmed === "") return {
16180
16319
  ok: false,
@@ -16214,47 +16353,125 @@ function validateHubAddressOverride(raw) {
16214
16353
  baseUrl: parsed.origin
16215
16354
  };
16216
16355
  }
16356
+ /** The reason a plain-http sign-in address is refused unless accepted. */
16357
+ var SIGN_IN_HTTP_REASON = "Sign-in needs an https:// address: passkeys and most identity providers refuse a plain-http origin. Accept http only for a sign-in that never leaves your own network.";
16217
16358
  /**
16218
- * The hub's base URL for `audience`, without a trailing slash, or `null` when
16219
- * the palette holds nothing that audience can reach. A valid operator
16220
- * `override` wins outright; an empty one means automatic. Ties inside a rank
16221
- * go to the palette's own `priority` (its preferred interface first).
16359
+ * May `baseUrl` carry `role`? The address check above, plus two role rules:
16360
+ * the app link carries `notifications` only (it is not a hub address), and a
16361
+ * plain-http address carries `signIn` only when the operator explicitly
16362
+ * accepted it (`acceptInsecureHttp`) — a LAN-only password or magic-link
16363
+ * sign-in works over http, a passkey or an OIDC callback does not.
16222
16364
  */
16223
- function pickHubBaseUrl(endpoints, audience, override = null) {
16224
- return resolveHubAddress(endpoints, audience, override).resolved;
16225
- }
16226
- /** {@link pickHubBaseUrl}, saying where the answer came from. */
16227
- function resolveHubAddress(endpoints, audience, override) {
16228
- if (override !== null && override !== "") {
16229
- const check = validateHubAddressOverride(override);
16230
- if (check.ok) return {
16231
- override,
16232
- resolved: check.baseUrl,
16233
- source: "override"
16234
- };
16235
- }
16236
- const auto = pickFromPalette(endpoints, audience);
16237
- return {
16238
- override: override === null || override === "" ? null : override,
16239
- resolved: auto,
16240
- source: auto === null ? "none" : "auto"
16365
+ function checkRoleAddress(role, baseUrl, options = {}) {
16366
+ if (isAppLinkBase(baseUrl)) return role === "notifications" ? {
16367
+ ok: true,
16368
+ baseUrl: CAMSTACK_APP_LINK_ORIGIN
16369
+ } : {
16370
+ ok: false,
16371
+ reason: "The CamStack app link is not a hub address — only notifications can use it."
16241
16372
  };
16373
+ const check = validateCustomAddress(baseUrl);
16374
+ if (!check.ok) return check;
16375
+ if (role === "signIn" && check.baseUrl.startsWith("http:") && options.acceptInsecureHttp !== true) return {
16376
+ ok: false,
16377
+ reason: SIGN_IN_HTTP_REASON
16378
+ };
16379
+ return check;
16380
+ }
16381
+ /** A stored flag that still validates (stored values are external data). */
16382
+ function validFlag(role, flag) {
16383
+ if (flag === null || flag === "") return null;
16384
+ const check = checkRoleAddress(role, flag, { acceptInsecureHttp: true });
16385
+ return check.ok ? check.baseUrl : null;
16242
16386
  }
16243
- function pickFromPalette(endpoints, audience) {
16387
+ function pickAuto(endpoints, role) {
16244
16388
  const best = endpoints.flatMap((endpoint) => {
16245
- const rank = rankFor(audience, endpoint);
16389
+ const rank = autoRankFor(role, endpoint);
16246
16390
  return rank === null ? [] : [{
16247
16391
  rank,
16248
16392
  priority: endpoint.priority,
16249
16393
  endpoint
16250
16394
  }];
16251
16395
  }).toSorted((a, b) => a.rank - b.rank || a.priority - b.priority)[0];
16252
- return best === void 0 ? null : best.endpoint.baseUrl.replace(/\/+$/, "");
16396
+ return best === void 0 ? null : trimSlash(best.endpoint.baseUrl);
16397
+ }
16398
+ function resolveHubRole(endpoints, role, flag) {
16399
+ const flagged = validFlag(role, flag);
16400
+ if (flagged !== null) return {
16401
+ flagged,
16402
+ resolved: flagged,
16403
+ source: "flagged"
16404
+ };
16405
+ const auto = pickAuto(endpoints, role);
16406
+ return {
16407
+ flagged: null,
16408
+ resolved: auto,
16409
+ source: auto === null ? "none" : "auto"
16410
+ };
16253
16411
  }
16254
- /** The bare hostname of {@link pickHubBaseUrl}'s answer, or `null`. */
16255
- function pickHubHostname(endpoints, audience, override = null) {
16256
- const baseUrl = pickHubBaseUrl(endpoints, audience, override);
16257
- return baseUrl === null ? null : hostnameOf(baseUrl);
16412
+ /** `agents`: what agents and LAN devices pull from. */
16413
+ function resolveAgentsAddress(endpoints, flags) {
16414
+ return resolveHubRole(endpoints, "agents", flags.agents);
16415
+ }
16416
+ /** `signIn`: sign-in links, OIDC callbacks, the relying party, OAuth2. */
16417
+ function resolveSignInAddress(endpoints, flags) {
16418
+ return resolveHubRole(endpoints, "signIn", flags.signIn);
16419
+ }
16420
+ /** `notifications`: the flagged address, else the CamStack app link (D709). */
16421
+ function resolveNotificationsAddress(flags) {
16422
+ const flagged = validFlag("notifications", flags.notifications);
16423
+ if (flagged !== null && !isAppLinkBase(flagged)) return {
16424
+ flagged,
16425
+ resolved: flagged,
16426
+ source: "flagged"
16427
+ };
16428
+ return {
16429
+ flagged: null,
16430
+ resolved: CAMSTACK_APP_LINK_ORIGIN,
16431
+ source: "auto"
16432
+ };
16433
+ }
16434
+ /** AUTO viewer race: LAN IPv4 + the public tunnel. Never IPv6, mesh, loopback or custom. */
16435
+ function isDefaultViewerEndpoint(endpoint) {
16436
+ if (endpoint.kind === "lan-ipv4") return true;
16437
+ return endpoint.kind === "public" && endpoint.interfaceKind === "public";
16438
+ }
16439
+ /**
16440
+ * The endpoints the apps race: the flagged ones that are in `endpoints`
16441
+ * (which carries the custom addresses flagged `viewer`), else AUTO. A flagged
16442
+ * set none of which is currently up falls back to AUTO rather than racing
16443
+ * nothing.
16444
+ */
16445
+ function selectViewerEndpoints(endpoints, flags) {
16446
+ const auto = endpoints.filter(isDefaultViewerEndpoint);
16447
+ if (flags.viewer.length === 0) return auto;
16448
+ const wanted = new Set(flags.viewer);
16449
+ const explicit = endpoints.filter((e) => wanted.has(e.baseUrl));
16450
+ return explicit.length > 0 ? explicit : auto;
16451
+ }
16452
+ /** `viewer`: what the apps race and connect to. */
16453
+ function resolveViewerAddresses(endpoints, flags) {
16454
+ const wanted = new Set(flags.viewer);
16455
+ const selected = selectViewerEndpoints(endpoints, flags);
16456
+ const usedFlags = selected.some((e) => wanted.has(e.baseUrl));
16457
+ return {
16458
+ flagged: flags.viewer,
16459
+ resolved: selected.map((e) => e.baseUrl),
16460
+ source: selected.length === 0 ? "none" : usedFlags ? "flagged" : "auto"
16461
+ };
16462
+ }
16463
+ /**
16464
+ * Every role at once, each through its own resolver. `viewerPalette` is what
16465
+ * the apps race (it carries the custom addresses flagged `viewer`);
16466
+ * `hubPalette` is the detected HTTPS palette agents and sign-in resolve over.
16467
+ */
16468
+ function resolveHubAddressRoles(viewerPalette, hubPalette, flags) {
16469
+ return {
16470
+ viewer: resolveViewerAddresses(viewerPalette, flags),
16471
+ notifications: resolveNotificationsAddress(flags),
16472
+ agents: resolveAgentsAddress(hubPalette, flags),
16473
+ signIn: resolveSignInAddress(hubPalette, flags)
16474
+ };
16258
16475
  }
16259
16476
  /** The bare hostname of a base URL, or `null` when it does not parse. */
16260
16477
  function hubAddressHostname(baseUrl) {
@@ -32796,122 +33013,6 @@ var CLIP_PLAYBACK_OPTIONS = {
32796
33013
  }
32797
33014
  };
32798
33015
  //#endregion
32799
- //#region src/capabilities/viewer-link-url.ts
32800
- /** The central link domain the viewer app claims (Universal Links / App Links). */
32801
- var CAMSTACK_APP_LINK_ORIGIN = "https://camstack.zentik.app";
32802
- /** Where the hub serves the viewer SPA. */
32803
- var HUB_VIEWER_MOUNT = "/viewer/camstack";
32804
- /** True when `baseUrl` names the CamStack app link rather than a hub address. */
32805
- function isAppLinkBase(baseUrl) {
32806
- if (baseUrl === null || baseUrl === void 0 || baseUrl === "") return false;
32807
- try {
32808
- return new URL(baseUrl).origin === CAMSTACK_APP_LINK_ORIGIN;
32809
- } catch {
32810
- return false;
32811
- }
32812
- }
32813
- /**
32814
- * The base a viewer link is built on, from the flagged notification address:
32815
- * the effective address is the explicit choice, else AUTO's `resolved`. The
32816
- * one reading of that answer — the NC's tap and the Network page's preview
32817
- * both go through it.
32818
- */
32819
- function viewerLinkBaseOf(endpoint) {
32820
- const flagged = endpoint.baseUrl ?? endpoint.resolved;
32821
- return isAppLinkBase(flagged) ? {
32822
- form: "app",
32823
- hubBaseUrl: endpoint.appLinkHub ?? null
32824
- } : {
32825
- form: "hub",
32826
- hubBaseUrl: flagged
32827
- };
32828
- }
32829
- /**
32830
- * Reduce a base URL to the bare origin the central page accepts as `hub=`,
32831
- * or `null` when it cannot be one: not http(s), carries credentials, or does
32832
- * not parse. A path/query/fragment is dropped — the central page mounts the
32833
- * viewer under the origin itself.
32834
- */
32835
- function toAppLinkHubOrigin(baseUrl) {
32836
- let parsed;
32837
- try {
32838
- parsed = new URL(baseUrl);
32839
- } catch {
32840
- return null;
32841
- }
32842
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
32843
- if (parsed.username !== "" || parsed.password !== "") return null;
32844
- if (parsed.hostname === "") return null;
32845
- return parsed.origin;
32846
- }
32847
- /**
32848
- * The hub address an app link carries as `hub=`, chosen from the hub's OWN
32849
- * advertised palette so it always agrees with what the Network page lists:
32850
- * the public tunnel first, then the mesh (Tailscale MagicDNS) — HTTPS only,
32851
- * because the app and the hosted viewer reach it from off the LAN. `null`
32852
- * when the hub advertises neither.
32853
- */
32854
- function pickAppLinkHub(endpoints) {
32855
- const rank = (e) => {
32856
- if (e.kind !== "public" || !e.baseUrl.startsWith("https://")) return null;
32857
- if (e.interfaceKind === "public") return 0;
32858
- if (e.interfaceKind === "vpn") return 1;
32859
- return null;
32860
- };
32861
- return endpoints.flatMap((e) => {
32862
- const r = rank(e);
32863
- const origin = toAppLinkHubOrigin(e.baseUrl);
32864
- return r === null || origin === null ? [] : [{
32865
- r,
32866
- p: e.priority,
32867
- origin
32868
- }];
32869
- }).toSorted((a, b) => a.r - b.r || a.p - b.p)[0]?.origin ?? null;
32870
- }
32871
- /** The viewer route for a destination, or null when it names nothing. */
32872
- function viewerRoute(destination) {
32873
- switch (destination.kind) {
32874
- case "camera": {
32875
- if (!Number.isInteger(destination.deviceId) || destination.deviceId <= 0) return null;
32876
- const at = destination.atMs;
32877
- return {
32878
- path: `/camera/${String(destination.deviceId)}`,
32879
- query: at !== void 0 && Number.isFinite(at) ? [["at", String(Math.trunc(at))]] : []
32880
- };
32881
- }
32882
- case "events": return {
32883
- path: "/events",
32884
- query: []
32885
- };
32886
- case "notifications": return {
32887
- path: "/notifications",
32888
- query: []
32889
- };
32890
- }
32891
- }
32892
- function encodeQuery(query) {
32893
- if (query.length === 0) return "";
32894
- return `?${query.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&")}`;
32895
- }
32896
- /**
32897
- * The link for a viewer destination, or `null` when none can be minted: an
32898
- * unknown destination, or the `hub` form with no reachable base (a dead link
32899
- * is worse than none).
32900
- */
32901
- function buildViewerLink(input) {
32902
- const route = viewerRoute(input.destination);
32903
- if (route === null) return null;
32904
- const raw = input.base.hubBaseUrl;
32905
- const base = raw === null ? "" : raw.replace(/\/+$/, "");
32906
- if (input.base.form === "app") {
32907
- const origin = base === "" ? null : toAppLinkHubOrigin(base);
32908
- const query = origin === null ? route.query : [...route.query, ["hub", origin]];
32909
- return `${CAMSTACK_APP_LINK_ORIGIN}${route.path}${encodeQuery(query)}`;
32910
- }
32911
- if (base === "" || isAppLinkBase(base)) return null;
32912
- return `${base}${HUB_VIEWER_MOUNT}${route.path}${encodeQuery(route.query)}`;
32913
- }
32914
- //#endregion
32915
33016
  //#region src/capabilities/webrtc-session.cap.ts
32916
33017
  /**
32917
33018
  * Optional client-side hints sent at session creation to help the provider
@@ -33419,6 +33520,35 @@ var webrtcSessionCapability = {
33419
33520
  alive: zod.z.boolean()
33420
33521
  })),
33421
33522
  /**
33523
+ * The viewer's downlink probe, for ONE live session (D733).
33524
+ *
33525
+ * The player measures `/adaptive-probe` after the session's first decoded
33526
+ * frame (D635). It used to act on the reading itself, by opening a SECOND
33527
+ * session with `hints.downlinkMbps` — every adaptive live open became two
33528
+ * sessions, two ICE + DTLS handshakes and two server pipelines. It now
33529
+ * hands the measurement to the session it already has; the provider picks
33530
+ * the rung exactly as it would from an offer's hints (same viewport
33531
+ * ceiling) and moves the session there — in place when the negotiated
33532
+ * codec carries the new rendition, by the existing re-offer
33533
+ * (`getSessionState.pendingRenegotiation`) when it cannot.
33534
+ *
33535
+ * A MEASUREMENT, never a tier: the client does not choose. A probe only
33536
+ * raises the rung; lowering it stays the ladder's call on delivery.
33537
+ * `held` answers why nothing moved (`not-adaptive`, `pinned`,
33538
+ * `already-there`, `recorded`, …) — a client that gets ANY answer must not
33539
+ * fall back to a reconnect; only a provider that does not know the method
33540
+ * (an older hub) leaves the client its legacy second session.
33541
+ */
33542
+ applyDownlinkProbe: require_sleep.method(zod.z.object({
33543
+ deviceId: zod.z.number().int().nonnegative(),
33544
+ sessionId: zod.z.string(),
33545
+ downlinkMbps: zod.z.number().nonnegative()
33546
+ }), zod.z.object({
33547
+ outcome: zod.z.enum(["switching", "held"]),
33548
+ tier: require_sleep.CamProfileSchema.nullable(),
33549
+ reason: zod.z.string()
33550
+ }), { kind: "mutation" }),
33551
+ /**
33422
33552
  * The SERVER's half of the stream-debug overlay, for one live session.
33423
33553
  *
33424
33554
  * The client can measure its own transport (`getStats()`); it cannot see
@@ -37840,7 +37970,8 @@ var ConnectionEndpointSchema = zod.z.object({
37840
37970
  "lan-ipv6",
37841
37971
  "public",
37842
37972
  "loopback",
37843
- "app-link"
37973
+ "app-link",
37974
+ "custom"
37844
37975
  ]),
37845
37976
  /**
37846
37977
  * Originating-interface kind — `lan`/`wifi`/`docker`/`vpn`/`other`/
@@ -37943,13 +38074,12 @@ var ViewerEndpointsSchema = zod.z.object({
37943
38074
  resolved: zod.z.array(zod.z.string()).readonly()
37944
38075
  });
37945
38076
  /**
37946
- * One audience's hub address (see `hub-address.ts`): the operator's stored
37947
- * override, the address in use, and where it came from.
38077
+ * DEPRECATED compatibility shape of `getHubAddresses` (D729 amendment, kept
38078
+ * one release for addons built against it): `cluster` is the `agents` role,
38079
+ * `browser` the `signIn` role; `override` is the flagged address.
37948
38080
  */
37949
38081
  var HubAddressSnapshotSchema = zod.z.object({
37950
- /** The operator's override, or null for automatic. */
37951
38082
  override: zod.z.string().nullable(),
37952
- /** The address in use, or null when nothing qualifies (never loopback). */
37953
38083
  resolved: zod.z.string().nullable(),
37954
38084
  source: zod.z.enum([
37955
38085
  "override",
@@ -37958,11 +38088,67 @@ var HubAddressSnapshotSchema = zod.z.object({
37958
38088
  ])
37959
38089
  });
37960
38090
  var HubAddressesSchema = zod.z.object({
37961
- /** "Hub address for agents" — agents' bundle/model pulls, LAN devices. */
37962
38091
  cluster: HubAddressSnapshotSchema,
37963
- /** "Public address for sign-in links" — e-mail links, OIDC, WebAuthn, OAuth2. */
37964
38092
  browser: HubAddressSnapshotSchema
37965
38093
  });
38094
+ /**
38095
+ * What an address is used for (`hub-address.ts`). `viewer` is many-valued;
38096
+ * `notifications`, `agents` and `signIn` are carried by at most one address.
38097
+ */
38098
+ var HubAddressRoleSchema = zod.z.enum([
38099
+ "viewer",
38100
+ "notifications",
38101
+ "agents",
38102
+ "signIn"
38103
+ ]);
38104
+ var RoleSourceSchema = zod.z.enum([
38105
+ "flagged",
38106
+ "auto",
38107
+ "none"
38108
+ ]);
38109
+ /** One single-valued role: the flagged address, the one in use, and why. */
38110
+ var HubAddressRoleResolutionSchema = zod.z.object({
38111
+ /** The address carrying the flag, or null = automatic. */
38112
+ flagged: zod.z.string().nullable(),
38113
+ /** The address in use, or null when nothing qualifies (never loopback). */
38114
+ resolved: zod.z.string().nullable(),
38115
+ source: RoleSourceSchema
38116
+ });
38117
+ var HubViewerResolutionSchema = zod.z.object({
38118
+ /** The addresses flagged `viewer`, or empty = automatic. */
38119
+ flagged: zod.z.array(zod.z.string()).readonly(),
38120
+ /** What the apps race right now. */
38121
+ resolved: zod.z.array(zod.z.string()).readonly(),
38122
+ source: RoleSourceSchema
38123
+ });
38124
+ /**
38125
+ * One row of the Network page's Addresses table: a detected endpoint, an
38126
+ * operator-entered custom address, or the CamStack app link — flagged the
38127
+ * same way. A URL that is both detected and entered is ONE row.
38128
+ */
38129
+ var HubAddressRowSchema = ConnectionEndpointSchema.extend({
38130
+ /** In the hub's detected palette right now. */
38131
+ detected: zod.z.boolean(),
38132
+ /** Entered by the operator (removable). */
38133
+ custom: zod.z.boolean(),
38134
+ /** The roles explicitly flagged on this address. */
38135
+ flags: zod.z.array(HubAddressRoleSchema).readonly(),
38136
+ /** The roles this address serves right now — flagged or automatic. */
38137
+ inUse: zod.z.array(HubAddressRoleSchema).readonly(),
38138
+ /** The roles this address may be flagged with. */
38139
+ eligible: zod.z.array(HubAddressRoleSchema).readonly()
38140
+ });
38141
+ var HubAddressBookSchema = zod.z.object({
38142
+ rows: zod.z.array(HubAddressRowSchema).readonly(),
38143
+ roles: zod.z.object({
38144
+ viewer: HubViewerResolutionSchema,
38145
+ notifications: HubAddressRoleResolutionSchema,
38146
+ agents: HubAddressRoleResolutionSchema,
38147
+ signIn: HubAddressRoleResolutionSchema
38148
+ }),
38149
+ /** The `hub=` an app link carries (D709), or null. */
38150
+ appLinkHub: zod.z.string().nullable()
38151
+ });
37966
38152
  var AllowedAddressesSchema = zod.z.object({
37967
38153
  /**
37968
38154
  * Allowlist of interface addresses operators have explicitly opted
@@ -38075,46 +38261,78 @@ var localNetworkCapability = {
38075
38261
  */
38076
38262
  getNotificationEndpoint: require_sleep.method(zod.z.void(), NotificationEndpointSchema),
38077
38263
  /**
38078
- * Mark which endpoint notification artifacts use. `baseUrl: null` restores
38079
- * AUTO. The value is stored verbatim (not an index into the candidate
38080
- * list): interfaces come and go, and an index would silently re-point at a
38081
- * different address after a reboot.
38264
+ * @deprecated Use `setAddressRole({ role: 'notifications', … })`. Kept so
38265
+ * an older admin UI keeps working: it writes the same `notifications`
38266
+ * flag, with the same validation. `baseUrl: null` restores automatic; an
38267
+ * address that is not detected becomes a custom address.
38082
38268
  */
38083
38269
  setNotificationEndpoint: require_sleep.method(zod.z.object({ baseUrl: zod.z.string().nullable() }), NotificationEndpointSchema, { kind: "mutation" }),
38084
38270
  /**
38085
- * The endpoints the SDK / viewer races for API access. Empty `baseUrls`
38086
- * means AUTO: every LAN IPv4 address plus the public tunnel, never IPv6,
38087
- * never mesh, never loopback. `resolved` is that set (or the operator's
38088
- * explicit subset) as it stands right now.
38271
+ * The `viewer` role as shipped apps read it: the flagged addresses
38272
+ * (detected or custom), or empty for AUTO — every LAN IPv4 address plus
38273
+ * the public tunnel, never IPv6, mesh, loopback or a custom address.
38274
+ * `resolved` is the set as it stands right now; the custom ones in it are
38275
+ * also in `getConnectionEndpoints`, which the apps filter by it.
38089
38276
  */
38090
38277
  getViewerEndpoints: require_sleep.method(zod.z.void(), ViewerEndpointsSchema),
38091
38278
  /**
38092
- * Replace the viewer race set. Empty `baseUrls` restores AUTO. Stored
38093
- * verbatim (not indices) so a temporarily-down tunnel is not silently
38094
- * dropped from the operator's choice.
38279
+ * @deprecated Use `setAddressRole({ role: 'viewer', … })`. Kept so an
38280
+ * older admin UI keeps working: it replaces the same `viewer` flags.
38281
+ * Empty `baseUrls` restores automatic.
38095
38282
  */
38096
38283
  setViewerEndpoints: require_sleep.method(zod.z.object({ baseUrls: zod.z.array(zod.z.string()).readonly() }), ViewerEndpointsSchema, { kind: "mutation" }),
38097
38284
  /**
38098
- * The hub's address for each audience — agents (`cluster`) and sign-in
38099
- * links (`browser`) — as every consumer must read it: the operator's
38100
- * override when set, else the automatic pick from this cap's palette,
38101
- * never loopback. The ONE answer to "at which address is this hub
38102
- * reached?"; there is no environment variable for it.
38285
+ * The Network page's Addresses table and every role's answer — the ONE
38286
+ * reading of "which address is this hub reached at, for what": detected
38287
+ * endpoints and custom addresses with their role flags, and per role the
38288
+ * flagged address, the one in use and whether it is automatic. Agent
38289
+ * deliveries, sign-in and LAN devices read `roles.agents` / `roles.signIn`
38290
+ * here; nothing ranks the palette itself. `scheme` is the one the caller
38291
+ * shows LAN rows in (as `getConnectionEndpoints`); agents and sign-in are
38292
+ * always resolved over the HTTPS palette.
38103
38293
  */
38104
- getHubAddresses: require_sleep.method(zod.z.void(), HubAddressesSchema),
38294
+ getAddresses: require_sleep.method(zod.z.object({ scheme: zod.z.enum(["http", "https"]).optional() }).optional(), HubAddressBookSchema),
38105
38295
  /**
38106
- * Set or clear (`baseUrl: null`) one audience's override. Validated: an
38107
- * http(s) origin with a host, no credentials, no path, never loopback —
38108
- * an invalid value is refused with the reason, nothing is stored.
38296
+ * Add a custom address. Validated: an http(s) origin with a host, no
38297
+ * credentials, no path, never loopback — an invalid one is refused with
38298
+ * the reason and nothing is stored. Adding an address that already exists
38299
+ * only updates its label.
38109
38300
  */
38110
- setHubAddress: require_sleep.method(zod.z.object({
38111
- audience: zod.z.enum(["cluster", "browser"]),
38112
- baseUrl: zod.z.string().nullable()
38113
- }), HubAddressesSchema, {
38301
+ addCustomAddress: require_sleep.method(zod.z.object({
38302
+ baseUrl: zod.z.string(),
38303
+ label: zod.z.string().max(80)
38304
+ }), HubAddressBookSchema, {
38305
+ kind: "mutation",
38306
+ auth: "admin"
38307
+ }),
38308
+ /** Remove a custom address. Its role flags go with it (those roles turn automatic). */
38309
+ removeCustomAddress: require_sleep.method(zod.z.object({ baseUrl: zod.z.string() }), HubAddressBookSchema, {
38114
38310
  kind: "mutation",
38115
38311
  auth: "admin"
38116
38312
  }),
38117
38313
  /**
38314
+ * Flag (`enabled: true`) or unflag an address for a role. For a
38315
+ * single-valued role, flagging moves the flag off every other address.
38316
+ * `baseUrl: null` returns the role to automatic. The address must be a row
38317
+ * of the table (detected, custom, or the app link for notifications);
38318
+ * `signIn` on a plain-http address is refused unless `acceptInsecureHttp`.
38319
+ */
38320
+ setAddressRole: require_sleep.method(zod.z.object({
38321
+ role: HubAddressRoleSchema,
38322
+ baseUrl: zod.z.string().nullable(),
38323
+ enabled: zod.z.boolean(),
38324
+ acceptInsecureHttp: zod.z.boolean().optional()
38325
+ }), HubAddressBookSchema, {
38326
+ kind: "mutation",
38327
+ auth: "admin"
38328
+ }),
38329
+ /**
38330
+ * @deprecated Read `getAddresses().roles.agents` / `.signIn`. Kept one
38331
+ * release so an addon built against the D729 amendment keeps resolving:
38332
+ * `cluster` = the `agents` role, `browser` = the `signIn` role.
38333
+ */
38334
+ getHubAddresses: require_sleep.method(zod.z.void(), HubAddressesSchema),
38335
+ /**
38118
38336
  * Read the operator's allowlist. Empty = "auto" (no filter). Used
38119
38337
  * by the admin UI's address selector to seed its checkbox state.
38120
38338
  */
@@ -53757,12 +53975,24 @@ var METHOD_ACCESS_MAP = Object.freeze({
53757
53975
  addonId: null,
53758
53976
  access: "view"
53759
53977
  },
53978
+ "localNetwork.addCustomAddress": {
53979
+ capName: "local-network",
53980
+ capScope: "system",
53981
+ addonId: null,
53982
+ access: "create"
53983
+ },
53760
53984
  "localNetwork.downloadCa": {
53761
53985
  capName: "local-network",
53762
53986
  capScope: "system",
53763
53987
  addonId: null,
53764
53988
  access: "view"
53765
53989
  },
53990
+ "localNetwork.getAddresses": {
53991
+ capName: "local-network",
53992
+ capScope: "system",
53993
+ addonId: null,
53994
+ access: "view"
53995
+ },
53766
53996
  "localNetwork.getAllowedAddresses": {
53767
53997
  capName: "local-network",
53768
53998
  capScope: "system",
@@ -53817,6 +54047,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
53817
54047
  addonId: null,
53818
54048
  access: "create"
53819
54049
  },
54050
+ "localNetwork.removeCustomAddress": {
54051
+ capName: "local-network",
54052
+ capScope: "system",
54053
+ addonId: null,
54054
+ access: "delete"
54055
+ },
53820
54056
  "localNetwork.resetAllowlistToBestMatch": {
53821
54057
  capName: "local-network",
53822
54058
  capScope: "system",
@@ -53829,13 +54065,13 @@ var METHOD_ACCESS_MAP = Object.freeze({
53829
54065
  addonId: null,
53830
54066
  access: "create"
53831
54067
  },
53832
- "localNetwork.setAllowedAddresses": {
54068
+ "localNetwork.setAddressRole": {
53833
54069
  capName: "local-network",
53834
54070
  capScope: "system",
53835
54071
  addonId: null,
53836
54072
  access: "create"
53837
54073
  },
53838
- "localNetwork.setHubAddress": {
54074
+ "localNetwork.setAllowedAddresses": {
53839
54075
  capName: "local-network",
53840
54076
  capScope: "system",
53841
54077
  addonId: null,
@@ -58089,6 +58325,12 @@ var METHOD_ACCESS_MAP = Object.freeze({
58089
58325
  addonId: null,
58090
58326
  access: "create"
58091
58327
  },
58328
+ "webrtcSession.applyDownlinkProbe": {
58329
+ capName: "webrtc-session",
58330
+ capScope: "device",
58331
+ addonId: null,
58332
+ access: "create"
58333
+ },
58092
58334
  "webrtcSession.closeSession": {
58093
58335
  capName: "webrtc-session",
58094
58336
  capScope: "device",
@@ -65363,6 +65605,11 @@ var METHOD_DEVICE_SELECTORS = Object.freeze({
65363
65605
  form: "single",
65364
65606
  optional: false
65365
65607
  }],
65608
+ "webrtcSession.applyDownlinkProbe": [{
65609
+ name: "deviceId",
65610
+ form: "single",
65611
+ optional: false
65612
+ }],
65366
65613
  "webrtcSession.closeSession": [{
65367
65614
  name: "deviceId",
65368
65615
  form: "single",
@@ -66403,8 +66650,11 @@ function createSystemProxy(api) {
66403
66650
  setNotificationEndpoint: (input) => dispatch("localNetwork", "setNotificationEndpoint", "mutation", input),
66404
66651
  getViewerEndpoints: (input) => dispatch("localNetwork", "getViewerEndpoints", "query", input),
66405
66652
  setViewerEndpoints: (input) => dispatch("localNetwork", "setViewerEndpoints", "mutation", input),
66653
+ getAddresses: (input) => dispatch("localNetwork", "getAddresses", "query", input),
66654
+ addCustomAddress: (input) => dispatch("localNetwork", "addCustomAddress", "mutation", input),
66655
+ removeCustomAddress: (input) => dispatch("localNetwork", "removeCustomAddress", "mutation", input),
66656
+ setAddressRole: (input) => dispatch("localNetwork", "setAddressRole", "mutation", input),
66406
66657
  getHubAddresses: (input) => dispatch("localNetwork", "getHubAddresses", "query", input),
66407
- setHubAddress: (input) => dispatch("localNetwork", "setHubAddress", "mutation", input),
66408
66658
  getAllowedAddresses: (input) => dispatch("localNetwork", "getAllowedAddresses", "query", input),
66409
66659
  setAllowedAddresses: (input) => dispatch("localNetwork", "setAllowedAddresses", "mutation", input),
66410
66660
  resetAllowlistToBestMatch: (input) => dispatch("localNetwork", "resetAllowlistToBestMatch", "mutation", input),
@@ -73591,6 +73841,7 @@ exports.AUDIO_NOT_CLASSIFIED_REASONS = AUDIO_NOT_CLASSIFIED_REASONS;
73591
73841
  exports.AUDIO_PRESETS = require_composition.AUDIO_PRESETS;
73592
73842
  exports.AUDIO_SCALE_EPOCH_ISO = AUDIO_SCALE_EPOCH_ISO;
73593
73843
  exports.AUDIO_SCALE_EPOCH_MS = AUDIO_SCALE_EPOCH_MS;
73844
+ exports.AUTOMATIC_HUB_ADDRESS_FLAGS = AUTOMATIC_HUB_ADDRESS_FLAGS;
73594
73845
  exports.AccessoriesStatusSchema = AccessoriesStatusSchema;
73595
73846
  exports.AccessoryKind = AccessoryKind;
73596
73847
  exports.AddBrokerInputSchema = AddBrokerInputSchema;
@@ -74158,12 +74409,14 @@ exports.HAP_KEYFRAME_INTERVAL_SEC = HAP_KEYFRAME_INTERVAL_SEC;
74158
74409
  exports.HF_BASE_URL = HF_BASE_URL;
74159
74410
  exports.HF_REPO = HF_REPO;
74160
74411
  exports.HOURS_PER_DAY = HOURS_PER_DAY;
74412
+ exports.HUB_ADDRESS_ROLES = HUB_ADDRESS_ROLES;
74161
74413
  exports.HUB_VIEWER_MOUNT = HUB_VIEWER_MOUNT;
74162
74414
  exports.HWACCEL_OPTIONS = HWACCEL_OPTIONS;
74163
74415
  exports.HealthStatusSchema = HealthStatusSchema;
74164
74416
  exports.HfModelResolutionSchema = HfModelResolutionSchema;
74165
74417
  exports.HistoryPointSchema = HistoryPointSchema;
74166
74418
  exports.HistoryResolutionEnum = HistoryResolutionEnum;
74419
+ exports.HubAddressRoleSchema = HubAddressRoleSchema;
74167
74420
  exports.HumidifierStatusSchema = HumidifierStatusSchema;
74168
74421
  exports.HumiditySensorStatusSchema = HumiditySensorStatusSchema;
74169
74422
  exports.HvacModeSchema = HvacModeSchema;
@@ -74823,6 +75076,8 @@ exports.SCOPE_PRESETS = SCOPE_PRESETS;
74823
75076
  exports.SENSOR_FEATURES = SENSOR_FEATURES;
74824
75077
  exports.SENSOR_MAP = SENSOR_MAP;
74825
75078
  exports.SHARE_VIEW_KINDS = require_sleep.SHARE_VIEW_KINDS;
75079
+ exports.SIGN_IN_HTTP_REASON = SIGN_IN_HTTP_REASON;
75080
+ exports.SINGLE_HUB_ADDRESS_ROLES = SINGLE_HUB_ADDRESS_ROLES;
74826
75081
  exports.SOURCE_CAPS = require_composition.SOURCE_CAPS;
74827
75082
  exports.SOURCE_CAP_ACTIVE_FIELD = require_composition.SOURCE_CAP_ACTIVE_FIELD;
74828
75083
  exports.SOURCE_CAP_CHANGED_AT_FIELD = require_composition.SOURCE_CAP_CHANGED_AT_FIELD;
@@ -75170,6 +75425,7 @@ exports.canConvertUnit = canConvertUnit;
75170
75425
  exports.canonicalEgressPlan = canonicalEgressPlan;
75171
75426
  exports.carbonMonoxideCapability = carbonMonoxideCapability;
75172
75427
  exports.cellsToRects = cellsToRects;
75428
+ exports.checkRoleAddress = checkRoleAddress;
75173
75429
  exports.classifyBearerPrincipal = classifyBearerPrincipal;
75174
75430
  exports.classifyStream = classifyStream;
75175
75431
  exports.classifyStreams = classifyStreams;
@@ -75404,6 +75660,7 @@ exports.isBatteryPresenceFault = isBatteryPresenceFault;
75404
75660
  exports.isClusterScopedStep = isClusterScopedStep;
75405
75661
  exports.isCollectionArrayMethod = isCollectionArrayMethod;
75406
75662
  exports.isDefaultAlarmProfileId = isDefaultAlarmProfileId;
75663
+ exports.isDefaultViewerEndpoint = isDefaultViewerEndpoint;
75407
75664
  exports.isDeployableToAgent = isDeployableToAgent;
75408
75665
  exports.isDetectionMacroClass = isDetectionMacroClass;
75409
75666
  exports.isDeviceConfigCap = require_sleep.isDeviceConfigCap;
@@ -75433,6 +75690,7 @@ exports.isRootSuppliableStepId = isRootSuppliableStepId;
75433
75690
  exports.isSameAddonId = isSameAddonId;
75434
75691
  exports.isScheduleActive = isScheduleActive;
75435
75692
  exports.isSecretConfigField = isSecretConfigField;
75693
+ exports.isSingleHubAddressRole = isSingleHubAddressRole;
75436
75694
  exports.isSoftwareDecode = require_composition.isSoftwareDecode;
75437
75695
  exports.isSourceCap = require_composition.isSourceCap;
75438
75696
  exports.isStateLabelKey = isStateLabelKey;
@@ -75549,8 +75807,6 @@ exports.pickAppLinkHub = pickAppLinkHub;
75549
75807
  exports.pickClusterStepModels = pickClusterStepModels;
75550
75808
  exports.pickClusterStepSettings = pickClusterStepSettings;
75551
75809
  exports.pickDetailCropConvention = pickDetailCropConvention;
75552
- exports.pickHubBaseUrl = pickHubBaseUrl;
75553
- exports.pickHubHostname = pickHubHostname;
75554
75810
  exports.pickInvalidClusterStepModels = pickInvalidClusterStepModels;
75555
75811
  exports.pickNativeLeaseOverride = pickNativeLeaseOverride;
75556
75812
  exports.pickPreferredRtspEntry = pickPreferredRtspEntry;
@@ -75611,6 +75867,7 @@ exports.resetPoolBaseline = resetPoolBaseline;
75611
75867
  exports.resolveAddonExecution = resolveAddonExecution;
75612
75868
  exports.resolveAddonGroup = resolveAddonGroup;
75613
75869
  exports.resolveAddonPlacement = resolveAddonPlacement;
75870
+ exports.resolveAgentsAddress = resolveAgentsAddress;
75614
75871
  exports.resolveBucketMs = resolveBucketMs;
75615
75872
  exports.resolveCapMount = require_sleep.resolveCapMount;
75616
75873
  exports.resolveClipSearchMinScore = resolveClipSearchMinScore;
@@ -75624,7 +75881,7 @@ exports.resolveDeviceProfile = resolveDeviceProfile;
75624
75881
  exports.resolveEgressDecodeHwAccel = resolveEgressDecodeHwAccel;
75625
75882
  exports.resolveExportProfiles = resolveExportProfiles;
75626
75883
  exports.resolveFormat = resolveFormat;
75627
- exports.resolveHubAddress = resolveHubAddress;
75884
+ exports.resolveHubAddressRoles = resolveHubAddressRoles;
75628
75885
  exports.resolveHydratedFieldValue = require_sleep.resolveHydratedFieldValue;
75629
75886
  exports.resolveLocationIcon = resolveLocationIcon;
75630
75887
  exports.resolveLocationMode = resolveLocationMode;
@@ -75632,13 +75889,16 @@ exports.resolveMethodAuth = resolveMethodAuth;
75632
75889
  exports.resolveModeDelays = resolveModeDelays;
75633
75890
  exports.resolveModeNotifyPolicy = resolveModeNotifyPolicy;
75634
75891
  exports.resolveMutate = resolveMutate;
75892
+ exports.resolveNotificationsAddress = resolveNotificationsAddress;
75635
75893
  exports.resolvePoolMemoryPolicy = resolvePoolMemoryPolicy;
75636
75894
  exports.resolveRecordingProfiles = resolveRecordingProfiles;
75637
75895
  exports.resolveRootSupply = resolveRootSupply;
75638
75896
  exports.resolveRunnerId = resolveRunnerId;
75897
+ exports.resolveSignInAddress = resolveSignInAddress;
75639
75898
  exports.resolveStreamMaps = require_composition.resolveStreamMaps;
75640
75899
  exports.resolveVariantModelId = resolveVariantModelId;
75641
75900
  exports.resolveViewableDeviceIds = resolveViewableDeviceIds;
75901
+ exports.resolveViewerAddresses = resolveViewerAddresses;
75642
75902
  exports.roleSpec = roleSpec;
75643
75903
  exports.roomCompositionExample = roomCompositionExample;
75644
75904
  exports.rootSuppliesOf = rootSuppliesOf;
@@ -75672,6 +75932,7 @@ exports.scriptRunnerCapability = scriptRunnerCapability;
75672
75932
  exports.searchDisplayIcons = searchDisplayIcons;
75673
75933
  exports.secureActuationFor = secureActuationFor;
75674
75934
  exports.selectAssignedProfileSlots = require_sleep.selectAssignedProfileSlots;
75935
+ exports.selectViewerEndpoints = selectViewerEndpoints;
75675
75936
  exports.sendTimesForEditor = sendTimesForEditor;
75676
75937
  exports.sendTimesOfSchedule = sendTimesOfSchedule;
75677
75938
  exports.sensorRaisedOf = sensorRaisedOf;
@@ -75763,8 +76024,8 @@ exports.userManagementCapability = userManagementCapability;
75763
76024
  exports.userPasskeysCapability = userPasskeysCapability;
75764
76025
  exports.vacuumControlCapability = vacuumControlCapability;
75765
76026
  exports.validateComposition = validateComposition;
76027
+ exports.validateCustomAddress = validateCustomAddress;
75766
76028
  exports.validateExpressionSource = require_composition.validateExpressionSource;
75767
- exports.validateHubAddressOverride = validateHubAddressOverride;
75768
76029
  exports.validateRecipeBounds = validateRecipeBounds;
75769
76030
  exports.valveCapability = valveCapability;
75770
76031
  exports.vectorDimFromBase64 = vectorDimFromBase64;