@camstack/types 1.2.241 → 1.2.243

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/addon.js CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_sleep = require("./sleep-vrBqRLsb.js");
2
+ const require_sleep = require("./sleep-mVRRyKVq.js");
3
3
  const require_event_category = require("./event-category-BVfsrBYA.js");
4
4
  const require_err_msg = require("./err-msg-COpsHMw2.js");
5
5
  //#region src/generated/cap-input-defaults.ts
package/dist/addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { A as DeviceType, Dt as DisposerChain, G as readNodePin, J as ReadinessTimeoutError, L as expandCapMethods, Mt as emitReadiness, Ot as BaseAddon, S as DEVICE_CHILDREN_BATCH_MAX, T as describeCustomActions, Tt as DATAPLANE_SECRET_HEADER, W as nodePin, Z as scopeKey, a as asJsonObject, b as deviceOpsCapability, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, j as adminUiCapability, kt as normalizeAddonInitResult, m as createDeviceProxy, p as isDeviceScopedCap, q as ReadinessRegistry, s as asString, t as sleep, u as parseJsonUnknown, x as viewerUiCapability } from "./sleep-CUmLTud7.mjs";
1
+ import { A as DeviceType, Dt as DisposerChain, G as readNodePin, J as ReadinessTimeoutError, L as expandCapMethods, Mt as emitReadiness, Ot as BaseAddon, S as DEVICE_CHILDREN_BATCH_MAX, T as describeCustomActions, Tt as DATAPLANE_SECRET_HEADER, W as nodePin, Z as scopeKey, a as asJsonObject, b as deviceOpsCapability, d as BOOT_RECOVERY_BACKOFF_MS, f as DEVICE_SCOPED_CAPS, h as createEventBusSliceSource, j as adminUiCapability, kt as normalizeAddonInitResult, m as createDeviceProxy, p as isDeviceScopedCap, q as ReadinessRegistry, s as asString, t as sleep, u as parseJsonUnknown, x as viewerUiCapability } from "./sleep-De1BvmWo.mjs";
2
2
  import { t as EventCategory } from "./event-category-BVDXG4tB.mjs";
3
3
  import { t as errMsg } from "./err-msg-IQTHeDzc.mjs";
4
4
  //#region src/generated/cap-input-defaults.ts
@@ -116,8 +116,8 @@ export type { IUserPasskeysProvider, PasskeySummary } from './user-passkeys.cap.
116
116
  export { PasskeySummarySchema, userPasskeysCapability } from './user-passkeys.cap.js';
117
117
  export type { IVectorStoreProvider } from './vector-store.cap.js';
118
118
  export { VectorDeclareIndexInputSchema, VectorDeleteByFilterInputSchema, VectorDeleteInputSchema, VectorDeleteResultSchema, type VectorFilter, VectorFilterSchema, VectorGetInputSchema, VectorGetResultSchema, type VectorItem, VectorItemSchema, type VectorMatch, VectorMatchSchema, type VectorMetadata, VectorMetadataSchema, type VectorMetric, VectorMetricSchema, VectorQueryInputSchema, VectorQueryResultSchema, VectorStatsInputSchema, VectorStatsResultSchema, VectorUpsertInputSchema, VectorUpsertResultSchema, vectorStoreCapability, } from './vector-store.cap.js';
119
- export type { Clip, ClipBytes, ClipPlayback, ClipSource, ClipSourceAvailability, ClipStillDisposition, ClipStillRefusalCode, ClipWake, IVideoclipsProvider, } from './videoclips.cap.js';
120
- export { CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, ClipBytesSchema, ClipPlaybackSchema, ClipSchema, ClipSourceAvailabilitySchema, ClipSourceSchema, ClipStillDispositionSchema, ClipStillRefusalCodeSchema, ClipWakeSchema, MAX_CLIP_EVENT_IDS, MAX_CLIP_LABELS, VIDEOCLIPS_MAX_READ_BYTES, videoclipsCapability, } from './videoclips.cap.js';
119
+ export type { Clip, ClipBytes, ClipPlayback, ClipSource, ClipSourceAvailability, ClipStillDisposition, ClipStillRefusalCode, ClipStreamAudio, ClipStreamAudioReason, ClipStreamDial, ClipWake, IVideoclipsProvider, } from './videoclips.cap.js';
120
+ export { CLIP_STILL_DISPOSITION_HEADER, CLIP_STILL_REASON_CODE_HEADER, CLIP_STILL_REASON_HEADER, CLIP_STREAM_TRANSPORT_HEADER, ClipBytesSchema, ClipPlaybackSchema, ClipSchema, ClipSourceAvailabilitySchema, ClipSourceSchema, ClipStillDispositionSchema, ClipStillRefusalCodeSchema, ClipStreamAudioReasonSchema, ClipStreamAudioSchema, ClipStreamDialSchema, ClipWakeSchema, MAX_CLIP_EVENT_IDS, MAX_CLIP_LABELS, VIDEOCLIPS_MAX_READ_BYTES, videoclipsCapability, } from './videoclips.cap.js';
121
121
  export type { IViewerUiProvider } from './viewer-ui.cap.js';
122
122
  export { viewerUiCapability } from './viewer-ui.cap.js';
123
123
  export { type IWebrtcSessionProvider, type WebrtcClientHints, type WebrtcLadderDecision, WebrtcLadderDecisionSchema, type WebrtcSessionDebug, WebrtcSessionDebugSchema, type WebrtcStreamChoice, WebrtcStreamChoiceSchema, type WebrtcStreamTarget, WebrtcStreamTargetSchema, type WebrtcTierFailure, WebrtcTierFailureSchema, webrtcClientHintsSchema, webrtcSessionCapability, } from './webrtc-session.cap.js';
@@ -1273,9 +1273,11 @@ export declare const pipelineOrchestratorCapability: {
1273
1273
  unavailableReason: z.ZodOptional<z.ZodEnum<{
1274
1274
  "no-provider": "no-provider";
1275
1275
  "source-unreachable": "source-unreachable";
1276
- "not-configured": "not-configured";
1277
1276
  "device-disabled": "device-disabled";
1278
1277
  }>>;
1278
+ warning: z.ZodOptional<z.ZodEnum<{
1279
+ "no-privacy-zones": "no-privacy-zones";
1280
+ }>>;
1279
1281
  enabled: z.ZodBoolean;
1280
1282
  authority: z.ZodDiscriminatedUnion<[z.ZodObject<{
1281
1283
  kind: z.ZodLiteral<"device-disabled">;
@@ -1349,9 +1351,11 @@ export declare const pipelineOrchestratorCapability: {
1349
1351
  unavailableReason: z.ZodOptional<z.ZodEnum<{
1350
1352
  "no-provider": "no-provider";
1351
1353
  "source-unreachable": "source-unreachable";
1352
- "not-configured": "not-configured";
1353
1354
  "device-disabled": "device-disabled";
1354
1355
  }>>;
1356
+ warning: z.ZodOptional<z.ZodEnum<{
1357
+ "no-privacy-zones": "no-privacy-zones";
1358
+ }>>;
1355
1359
  enabled: z.ZodBoolean;
1356
1360
  authority: z.ZodDiscriminatedUnion<[z.ZodObject<{
1357
1361
  kind: z.ZodLiteral<"device-disabled">;
@@ -95,6 +95,15 @@ export declare const CLIP_STILL_REASON_HEADER = "x-camstack-reason";
95
95
  export declare const CLIP_STILL_REASON_CODE_HEADER = "x-camstack-reason-code";
96
96
  /** One {@link ClipStillDispositionSchema} token, alone. */
97
97
  export declare const CLIP_STILL_DISPOSITION_HEADER = "x-camstack-disposition";
98
+ /**
99
+ * The clip STREAM route's own statement of what it is (D597): the value is
100
+ * `forward-only`. A producer writes it on the 200; a consumer that dials a
101
+ * URL and does not read it back refuses to play, because a progressive file
102
+ * through a forward-only feeder looks like it worked until the first seek.
103
+ * Named here because BOTH the provider and the broker spell it, and two
104
+ * copies of a header name is how one of them stops being checked.
105
+ */
106
+ export declare const CLIP_STREAM_TRANSPORT_HEADER = "x-camstack-clip-transport";
98
107
  export declare const ClipSchema: z.ZodObject<{
99
108
  id: z.ZodString;
100
109
  source: z.ZodString;
@@ -239,6 +248,71 @@ export declare const ClipBytesSchema: z.ZodObject<{
239
248
  durationMs: z.ZodOptional<z.ZodNumber>;
240
249
  }, z.core.$strip>;
241
250
  export type ClipBytes = z.infer<typeof ClipBytesSchema>;
251
+ /**
252
+ * Where a clip's STREAM can be dialled (D597) — the answer to
253
+ * {@link videoclipsCapability.methods.dialClipStream}.
254
+ *
255
+ * `url` is plain `http://` on the provider's own token-gated listener, one
256
+ * shot, short-lived. It is NOT the hub-proxied `/addon/<id>/clip-stream/…`
257
+ * path: that plane takes only a user credential, which an addon does not
258
+ * hold, and the framework offers no addon→addon byte transport — the one
259
+ * precedent is the broker's own restream URLs (`getStreamWithCodec`), which
260
+ * a consumer dials directly, and this is that precedent one addon over.
261
+ */
262
+ /**
263
+ * The audio a clip stream can be asked for, and the audio it carries.
264
+ *
265
+ * The camera stores AAC and no WebRTC session speaks it, so a stream that
266
+ * carries sound carries it TRANSCODED, muxed as a second track beside the
267
+ * video by the one ffmpeg the provider already runs (D600). `opus` is
268
+ * 48 kHz mono, `pcmu` is 8 kHz µ-law — the two codecs a broker session
269
+ * negotiates — and `none` is a stream asked for, or served, without sound.
270
+ */
271
+ export declare const ClipStreamAudioSchema: z.ZodEnum<{
272
+ pcmu: "pcmu";
273
+ none: "none";
274
+ opus: "opus";
275
+ }>;
276
+ export type ClipStreamAudio = z.infer<typeof ClipStreamAudioSchema>;
277
+ /**
278
+ * Why a stream that was ASKED for sound is not carrying it (D600).
279
+ *
280
+ * Only ever set beside `servedAudio: 'none'` after a non-`none` ask. Absent
281
+ * means the ask was honoured, or there was no ask.
282
+ *
283
+ * - `not-requested` — the caller asked for `none`.
284
+ * - `unsupported-ask` — the provider cannot produce that codec.
285
+ *
286
+ * Whether the RECORDING has sound at all is not knowable here: the camera
287
+ * declares no audio track before its first frame arrives. That answer is in
288
+ * the stream itself — an fMP4 with no audio track — and the broker reads it
289
+ * there. A dial that says `opus` promises the codec, never the content.
290
+ */
291
+ export declare const ClipStreamAudioReasonSchema: z.ZodEnum<{
292
+ "not-requested": "not-requested";
293
+ "unsupported-ask": "unsupported-ask";
294
+ }>;
295
+ export type ClipStreamAudioReason = z.infer<typeof ClipStreamAudioReasonSchema>;
296
+ export declare const ClipStreamDialSchema: z.ZodObject<{
297
+ url: z.ZodString;
298
+ expiresAtMs: z.ZodNumber;
299
+ transport: z.ZodLiteral<"forward-only">;
300
+ served: z.ZodEnum<{
301
+ high: "high";
302
+ mid: "mid";
303
+ low: "low";
304
+ }>;
305
+ servedAudio: z.ZodEnum<{
306
+ pcmu: "pcmu";
307
+ none: "none";
308
+ opus: "opus";
309
+ }>;
310
+ audioReason: z.ZodOptional<z.ZodEnum<{
311
+ "not-requested": "not-requested";
312
+ "unsupported-ask": "unsupported-ask";
313
+ }>>;
314
+ }, z.core.$strip>;
315
+ export type ClipStreamDial = z.infer<typeof ClipStreamDialSchema>;
242
316
  export declare const ClipSourceAvailabilitySchema: z.ZodObject<{
243
317
  state: z.ZodEnum<{
244
318
  ok: "ok";
@@ -489,6 +563,57 @@ export declare const videoclipsCapability: {
489
563
  }, z.core.$strip>, "query"> & {
490
564
  readonly providerOptional: true;
491
565
  };
566
+ /**
567
+ * Where this clip's STREAM can be dialled (D597) — the forward-only
568
+ * fMP4 the provider writes from the first muxed byte, for the broker to
569
+ * play through the same WebRTC session as recorded footage, with the
570
+ * first frame at the first fragment instead of after the last byte.
571
+ *
572
+ * Nothing is fetched here: the answer is a one-shot URL on the
573
+ * provider's own listener, and the camera is touched when it is dialled.
574
+ * The gates the file path runs (`readClipBytes`) run on the dial too —
575
+ * the id, the row, a clip still being written — and refuse in the same
576
+ * vocabulary, THROWN with `clipExportFailure(<token>, <prose>)`. Every
577
+ * profile is served (D599) and the answer states which twin it carries;
578
+ * `high` is not refused. Optional on the provider for the same reason
579
+ * `readClipBytes` is: a source with no camera socket behind it (the
580
+ * recorder's analytics window, a HomeKit tee) has no stream.
581
+ */
582
+ readonly dialClipStream: import("./capability-definition.js").CapabilityMethodSchema<z.ZodObject<{
583
+ deviceId: z.ZodNumber;
584
+ clipId: z.ZodString;
585
+ provider: z.ZodString;
586
+ profile: z.ZodOptional<z.ZodEnum<{
587
+ high: "high";
588
+ mid: "mid";
589
+ low: "low";
590
+ }>>;
591
+ audio: z.ZodOptional<z.ZodEnum<{
592
+ pcmu: "pcmu";
593
+ none: "none";
594
+ opus: "opus";
595
+ }>>;
596
+ }, z.core.$strip>, z.ZodObject<{
597
+ url: z.ZodString;
598
+ expiresAtMs: z.ZodNumber;
599
+ transport: z.ZodLiteral<"forward-only">;
600
+ served: z.ZodEnum<{
601
+ high: "high";
602
+ mid: "mid";
603
+ low: "low";
604
+ }>;
605
+ servedAudio: z.ZodEnum<{
606
+ pcmu: "pcmu";
607
+ none: "none";
608
+ opus: "opus";
609
+ }>;
610
+ audioReason: z.ZodOptional<z.ZodEnum<{
611
+ "not-requested": "not-requested";
612
+ "unsupported-ask": "unsupported-ask";
613
+ }>>;
614
+ }, z.core.$strip>, "query"> & {
615
+ readonly providerOptional: true;
616
+ };
492
617
  };
493
618
  };
494
619
  export type IVideoclipsProvider = InferProvider<typeof videoclipsCapability>;
@@ -90,10 +90,4 @@ export interface ContainerChildRef {
90
90
  * they are deliberately absent and fall through to type priority.
91
91
  */
92
92
  export declare const CONTAINER_CHILD_ROLE_PRIORITY: readonly string[];
93
- /**
94
- * The child a container stands for: the operator's pick when it still exists,
95
- * else the highest-priority type. `null` for a childless container — a caller
96
- * must decide what an empty container means for it, rather than being handed a
97
- * child that is not there.
98
- */
99
93
  export declare function resolveContainerPrimaryChild(children: readonly ContainerChildRef[], overrideEntityId: string | null | undefined, containerName?: string): ContainerChildRef | null;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * display-icon-vocabulary — the icon KEYS both UIs share, and the search that
3
+ * finds one.
4
+ *
5
+ * The reason this is here and not in a UI package is the one
6
+ * `location-icon.ts` already states for the location vocabulary: two surfaces
7
+ * draw these icons and they do NOT share an icon module. The admin UI imports
8
+ * `lucide-react`; the viewer imports a hand-maintained per-icon allowlist
9
+ * (`src/lib/icons.ts`, re-exporting `lucide-react-native/icons/*` because the
10
+ * barrel is 47 % of that app's modules). Anything shared between them must
11
+ * therefore be STRINGS, and this module contains no React, no lucide, and no
12
+ * glyph.
13
+ *
14
+ * So the layering is:
15
+ *
16
+ * | layer | lives in | holds |
17
+ * | --- | --- | --- |
18
+ * | vocabulary + search | here | keys, groups, synonyms, scoring |
19
+ * | glyph table (web) | `ui-library`'s `DISPLAY_ICON_REGISTRY` | key → lucide-react |
20
+ * | glyph table (phone) | viewer's accessory icon map | key → lucide-react-native |
21
+ *
22
+ * Each glyph table must cover exactly these keys, and a test on each side
23
+ * pins that. A key offered on one surface and missing on the other falls
24
+ * through to the next precedence level there, which the operator reads as "my
25
+ * change did nothing" — the failure this vocabulary exists to make impossible.
26
+ *
27
+ * {@link LOCATION_ICON_IDS} is a SUBSET of these keys, pinned by a test. Two
28
+ * icon vocabularies on the same two surfaces with different members is the
29
+ * same bug wearing a different name — `briefcase` and `route` are here only
30
+ * because locations already draw them.
31
+ *
32
+ * ## Synonyms, and why they are bilingual
33
+ *
34
+ * The keys are English lucide ids and always will be: they name the DRAWING,
35
+ * mechanically checkable against a package, not the room. But the operator
36
+ * searching this list types Italian — "tapparella", not "blinds". A picker
37
+ * whose search only matches the key would make three quarters of the
38
+ * vocabulary unreachable to them.
39
+ *
40
+ * So every entry carries the words it answers to, in both languages. They are
41
+ * deliberately not translations of a label: they are the words somebody would
42
+ * actually type looking for that picture, including the wrong-but-obvious
43
+ * ones (`blinds` answers to `persiana` and `serranda` too).
44
+ */
45
+ /** What an operator is naming when they pick from this group. */
46
+ export type DisplayIconGroup = 'device' | 'lighting' | 'sensing' | 'place' | 'life';
47
+ /** The order the groups are offered in, and the only members there are. */
48
+ export declare const DISPLAY_ICON_GROUPS: readonly DisplayIconGroup[];
49
+ /** One icon in the shared vocabulary. */
50
+ export interface DisplayIconEntry {
51
+ /** The kebab-case lucide id. This is the value that is PERSISTED. */
52
+ readonly key: string;
53
+ readonly group: DisplayIconGroup;
54
+ /**
55
+ * Words this icon answers to in search, English and Italian. Never
56
+ * rendered — a caller that wants a label supplies its own translation.
57
+ */
58
+ readonly synonyms: readonly string[];
59
+ /**
60
+ * The key this one is a deprecated spelling OF, drawing the same picture.
61
+ *
62
+ * An alias must keep RESOLVING — rows have it persisted — but it must not
63
+ * be OFFERED: a second, identical tile lets an operator newly choose the
64
+ * spelling we are trying to retire, and gives them no way to tell the two
65
+ * apart. So `searchDisplayIcons` skips aliases and `displayIconEntry` does
66
+ * not.
67
+ */
68
+ readonly aliasOf?: string;
69
+ }
70
+ /**
71
+ * The vocabulary, in the order a picker offers it.
72
+ *
73
+ * Adding a key here is not enough to make it pickable: both glyph tables must
74
+ * gain it too, and their tests fail until they do. That is the intended
75
+ * friction — it is the only thing that keeps the two surfaces honest.
76
+ */
77
+ export declare const DISPLAY_ICON_VOCABULARY: readonly DisplayIconEntry[];
78
+ /** Every key, in vocabulary order — aliases included, because they resolve. */
79
+ export declare const DISPLAY_ICON_KEYS: readonly string[];
80
+ /** The keys a picker OFFERS: everything that is not a deprecated spelling. */
81
+ export declare const DISPLAY_ICON_OFFERED_KEYS: readonly string[];
82
+ /** Is this a key of the shared vocabulary? */
83
+ export declare function isDisplayIconKey(value: unknown): boolean;
84
+ /** The entry for a key, or `undefined` — never a guessed one. */
85
+ export declare function displayIconEntry(key: string): DisplayIconEntry | undefined;
86
+ /** The entries of one group, in vocabulary order. */
87
+ export declare function displayIconsInGroup(group: DisplayIconGroup): readonly DisplayIconEntry[];
88
+ /** One search result: the entry, and how strongly it matched. */
89
+ export interface DisplayIconHit {
90
+ readonly entry: DisplayIconEntry;
91
+ readonly score: number;
92
+ }
93
+ /**
94
+ * Rank the vocabulary against a free-text query.
95
+ *
96
+ * An empty query returns the WHOLE vocabulary in its declared order, because
97
+ * a picker's resting state is the full list, not an empty one.
98
+ *
99
+ * `extraWords` lets a caller add the words IT can translate — a localised
100
+ * device-role label, say — without this module knowing what a locale is.
101
+ * They score exactly like a synonym.
102
+ *
103
+ * Ties keep vocabulary order: a search that reshuffles equally-good results
104
+ * between two keystrokes makes the list unusable.
105
+ */
106
+ export declare function searchDisplayIcons(query: string, extraWords?: ReadonlyMap<string, readonly string[]>): readonly DisplayIconHit[];
@@ -9087,6 +9087,13 @@ export type AppRouter = TrpcCoreRouter<{
9087
9087
  output: z.infer<typeof videoclipsCapability.methods.readClipBytes.output>;
9088
9088
  meta: object;
9089
9089
  }>;
9090
+ dialClipStream: TRPCQueryProcedure<{
9091
+ input: {
9092
+ [x: string]: unknown;
9093
+ } & z.input<typeof videoclipsCapability.methods.dialClipStream.input>;
9094
+ output: z.infer<typeof videoclipsCapability.methods.dialClipStream.output>;
9095
+ meta: object;
9096
+ }>;
9090
9097
  }>>;
9091
9098
  viewerUi: TRPCBuiltRouter<{
9092
9099
  ctx: TrpcContext;
@@ -6,7 +6,7 @@
6
6
  * scope+access check inside `protectedProcedure` (see
7
7
  * `server/backend/src/api/trpc/trpc.middleware.ts`).
8
8
  *
9
- * Coverage: 1164 method paths across 158 capabilities.
9
+ * Coverage: 1165 method paths across 158 capabilities.
10
10
  */
11
11
  import type { CapabilityMethodAccess } from '../capabilities/capability-definition.js';
12
12
  export interface MethodAccessRecord {
@@ -6,7 +6,7 @@
6
6
  * system-scope cap that takes a deviceId was previously never device-filtered
7
7
  * — see the generator header).
8
8
  *
9
- * Coverage: 391 methods carry a device reference, of which
9
+ * Coverage: 392 methods carry a device reference, of which
10
10
  * 141 are on SYSTEM-scope caps.
11
11
  *
12
12
  * Top-level fields, number arrays, and one-level arrays of objects carrying
package/dist/index.d.ts CHANGED
@@ -176,6 +176,8 @@ export { ChargingStatus, DeviceFeature, DeviceRole, DeviceType } from './device/
176
176
  export type { IBatteryOperated, IDoorbellButton, INativeSnapshot, IPanTiltZoom, IRebootable, ITwoWayAudio, } from './device/features.js';
177
177
  export type { LocationDescriptionAssignment, LocationIconAssignment, LocationIconId, } from './device/location-icon.js';
178
178
  export { LOCATION_ICON_FALLBACK, LOCATION_ICON_IDS, LOCATION_DESCRIPTION_MAX, LocationDescriptionAssignmentSchema, LocationIconAssignmentSchema, LocationIconIdSchema, locationIconKey, resolveLocationIcon, } from './device/location-icon.js';
179
+ export type { DisplayIconEntry, DisplayIconGroup, DisplayIconHit, } from './device/display-icon-vocabulary.js';
180
+ export { DISPLAY_ICON_GROUPS, DISPLAY_ICON_KEYS, DISPLAY_ICON_OFFERED_KEYS, DISPLAY_ICON_VOCABULARY, displayIconEntry, displayIconsInGroup, isDisplayIconKey, searchDisplayIcons, } from './device/display-icon-vocabulary.js';
179
181
  export { getByPath, setByPath } from './device/path-util.js';
180
182
  export type { ReachabilityPollHandle, ReachabilityPollLogger, ReachabilityPollOptions, } from './device/reachability-poll.js';
181
183
  export { REACHABILITY_FAILURES_TO_OFFLINE, REACHABILITY_POLL_INTERVAL_MS, REACHABILITY_PROBE_TIMEOUT_MS, startReachabilityPoll, } from './device/reachability-poll.js';