@xorgate/react-native 0.2.0 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -1,16 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0
4
+
5
+ Tracks `@xorgate/react` 0.3.0 (live scope) and `@xorgate/sdk` 0.7.0 (device
6
+ transfer). No native change: the same `dist`, plus re-exports.
7
+
8
+ ### Added
9
+
10
+ - **`useLiveScope()` and `useDeviceScope(deviceId)`**, re-exported unchanged
11
+ from `@xorgate/react`. A device can be transferred to another workspace or
12
+ organization while a viewer is open; the vended live credential is cached for
13
+ up to ~50 minutes, and neither plane fails loudly when it goes stale —
14
+ telemetry simply stops arriving and video keeps flowing until the next
15
+ reconnect. `useDeviceScope` turns that into a typed `DEVICE_OUT_OF_SCOPE`,
16
+ re-vending once first. `useLiveScope().invalidate()` makes every open live
17
+ consumer re-resolve after you transfer a device yourself.
18
+
19
+ **This matters more on a phone than on the web.** A backgrounded app resumes
20
+ onto the same cached credential, and `appStateWake` nudges the live planes
21
+ back up — straight onto the stale scope. The scope guard is what makes the
22
+ resume honest.
23
+
24
+ - The transfer vocabulary from `@xorgate/sdk` (`TransferSummary`,
25
+ `TransferPreview`, `TransferOffer`, `TransferOfferPreview`,
26
+ `TransferAdoption`, `ScopeAttributeResult` and the rest), so a screen and a
27
+ server route pass the same objects around.
28
+
29
+ ### Unchanged, and checked
30
+
31
+ - **Replay carries no tenancy assumption.** Segment playback runs on presigned
32
+ URLs minted per manifest and refreshed from `manifest.urlExpiresAt`; nothing
33
+ in `replay-engine.ts` caches an organization or workspace id, so a transfer
34
+ cannot strand a replay. (Replay does become unreadable to the OLD owner once
35
+ the device moves — media is device-keyed and travels with it — but that shows
36
+ up as a plain 404 from the manifest route, not as a stale URL.)
37
+
38
+ ## 0.2.1
39
+
40
+ - **Fix: a replay lane froze for a whole segment at a time on Android.** The
41
+ seam is an opacity flip between two overlapping views, and `expo-video` draws
42
+ into a `SurfaceView` by default — a layer the system composites rather than
43
+ one the view hierarchy draws, so `opacity: 0` does not hide it and `zIndex`
44
+ does not order it against its sibling. The hidden PRELOAD therefore won: it
45
+ parked on the first frame of the next segment, that still frame covered the
46
+ player that was actually running, and the picture held for the whole segment
47
+ while the clock, the map and the readouts carried on. It looked like the
48
+ engine had stalled; the engine was never involved. `ReplayVideo` now renders
49
+ into a `TextureView` on Android (`surfaceType`), which is an ordinary view,
50
+ so alpha and z-order mean what they say. iOS is untouched — `AVPlayerLayer`
51
+ is a real layer and the prop is Android's alone.
52
+
53
+ Measured on an API 36 emulator against a six-segment recording, sampling the
54
+ lane every ~1.5 s for three minutes: **11 distinct frames out of 80 before,
55
+ 79 out of 80 after**, over three segment boundaries either way. Each freeze
56
+ began within ~1.5 s of the hidden player reporting ready, and the LAST
57
+ segment of a replay always played correctly — by then there is nothing left
58
+ to preload on top of it. The engine's own trace was clean throughout, every
59
+ boundary a 0-131 ms seam.
60
+
3
61
  ## 0.2.0
4
62
 
5
63
  Replay: the recorded-media player, over `expo-video`.
6
64
 
7
- - **Needs `@xorgate/react` 0.2.1 or later**, which is where `useReplayTelemetry`
8
- takes the player CORE rather than the browser wrapper — the change that lets a
9
- React Native player drive replay telemetry at all. The declared range stays
10
- `^0.2.0-next.1` because it already admits 0.2.1 and a tighter one cannot be
11
- installed until 0.2.1 is on npm; the only way to get the wrong types is a
12
- lockfile pinned at the pre-release, where `useReplayTelemetry(deviceId,
13
- player)` will not typecheck. Publish `@xorgate/react` 0.2.1 first.
65
+ - **`@xorgate/react` `^0.2.1`**, up from `^0.2.0-next.1`. 0.2.1 is where
66
+ `useReplayTelemetry` takes the player CORE rather than the browser wrapper —
67
+ the change that lets a React Native player drive replay telemetry at all. The
68
+ 0.2.0 tarball shipped with the looser `^0.2.0-next.1`, which resolves to 0.2.1
69
+ on any fresh install and is wrong only for a lockfile pinned at the
70
+ pre-release; the range was tightened as soon as 0.2.1 existed to be named.
14
71
  - **`expo-video` is a new required peer.** It is the native player; there is no
15
72
  way to play a recorded segment without it, and an optional peer whose absence
16
73
  fails deep inside a lane is worse than one line of install (the same call
package/dist/index.d.ts CHANGED
@@ -27,5 +27,5 @@ export { LiveVideo, type LiveVideoProps } from "./live-video.js";
27
27
  export { ExpoVideoReplayEngine, type ExpoVideoReplayEngineOptions, } from "./replay-engine.js";
28
28
  export { useReplayPlayer, type UseReplayPlayer, type UseReplayPlayerNativeOptions, type ReplayLaneMedia, type ReplayLaneState, type ReplayLaneStatus, type UseReplayPlayerOptions, } from "./use-replay-player.js";
29
29
  export { ReplayVideo, type ReplayVideoProps } from "./replay-video.js";
30
- export { XorgateProvider, useXorgate, useXorgateTenancy, DEFAULT_BASE_URL, type XorgateProviderProps, type XorgateConfig, type XorgateAuth, type FirstPartyAuth, type SessionTokenAuth, type ApiKeyAuth, type LiveCredentialsAuth, type LiveCredentials, type LiveUnavailableReason, type Awaitable, type XorgatePlatform, XorgateError, isXorgateError, type Device, type DeviceModel, type LatestByMetric, type LatestReading, type MediaSession, type Me, type MetricName, type Organization, type PageMeta, type RecordingRun, type ReplayGap, type ReplayManifest, type ReplaySegment, type ReplaySession, type StreamKey, type TelemetryHistory, type TelemetryReading, type VideoChannel, type Workspace, type XorgateClient, useMe, useOrganizations, useWorkspaces, useDevices, useDevice, useDeviceModel, useTelemetryLatest, useTelemetryHistory, useVideoChannels, useRecordingRuns, useSessions, type QueryResult, type QueryOptions, type UseDevicesParams, type UseDevicesResult, type UseTelemetryHistoryParams, type UseSessionsParams, type UseRunsResult, type UseSessionsResult, useLiveTelemetry, useLivePlane, useLiveCredentials, parseTelemetryPayload, presignIotWssUrl, type UseLiveTelemetry, type UseLiveTelemetryOptions, type LiveStatus, type TelemetryPoint, type TelemetrySnapshot, type TelemetryFeedDeps, type MqttLikeClient, type MqttConnectionSpec, type ResolvedLiveCredentials, type ParsedTelemetryPayload, type TelemetryRecordingStatus, type TelemetryMediaStatus, type TelemetryStreamStatus, type LiveBlockPayload, useLiveBlock, resolveLiveBlock, liveBlockElapsedMs, LIVE_BLOCK_STALE_MS, type LiveBlockReason, type LiveBlockOpenReason, type LiveBlockState, type ResolveLiveBlockInput, useLiveVideoSession, type UseLiveVideoSession, type UseLiveVideoSessionOptions, type LiveVideoStatus, type LiveVideoStats, type WebRtcPlatform, type PeerConnectionLike, type SignalingLike, type TimerHost, useReplayManifest, useReplayPlayerCore, useReplayTelemetry, buildTimeline, segmentEnd, segmentAt, hasCoverageAt, gapAt, nextSegmentAfter, prevBoundary, nextBoundary, clampTs, buildLanes, buildClockTimeline, segmentUrlKey, ReplayClock, REPLAY_RATES, parseSegmentMediaInfo, seriesFromReadings, floorIndex, latestWithin, metricGroup, stalenessMs, overviewStalenessMs, chooseIntervalSeconds, windowBoundsFor, windowNeedsRefetch, buildTrace, traceLines, traveledLines, positionAt, GROUP_STALENESS_MS, DEFAULT_STALENESS_MS, WINDOW_SPAN_MS, WINDOW_MIN_SPAN_MS, type UseReplayManifestParams, type UseReplayManifestResult, type UseReplayPlayerCore, type UseReplayTelemetry, type UseReplayTelemetryOptions, type ReplayEngine, type ReplayEngineOptions, type ReplayEngineFactory, type ReplayTimeline, type ReplayLane, type LaneSegment, type ReplayClockState, type ReplayRate, type PacerCandidate, type GapSkipEvent, type SegmentMediaInfo, type MetricSeries, type GpsTrace, type WindowBounds, type PositionResolution, } from "@xorgate/react";
30
+ export { XorgateProvider, useXorgate, useXorgateTenancy, DEFAULT_BASE_URL, type XorgateProviderProps, type XorgateConfig, type XorgateAuth, type FirstPartyAuth, type SessionTokenAuth, type ApiKeyAuth, type LiveCredentialsAuth, type LiveCredentials, type LiveUnavailableReason, type Awaitable, type XorgatePlatform, XorgateError, isXorgateError, type AcceptedTransferOffer, type BulkTransferItem, type CreatedTransferOffer, type Device, type DeviceModel, type LatestByMetric, type LatestReading, type MediaSession, type Me, type MetricName, type Organization, type PageMeta, type RecordingRun, type ReplayGap, type ReplayManifest, type ReplaySegment, type ReplaySession, type StreamKey, type TelemetryHistory, type TelemetryReading, type TransferAdoption, type TransferBlocker, type TransferBlockerCode, type TransferDeviceInput, type TransferOffer, type TransferOfferPreview, type TransferOfferStatus, type TransferPreview, type TransferResult, type TransferSummary, type TransferTenancy, type ScopeAttributeResult, type VideoChannel, type Workspace, type XorgateClient, useMe, useOrganizations, useWorkspaces, useDevices, useDevice, useDeviceModel, useTelemetryLatest, useTelemetryHistory, useVideoChannels, useRecordingRuns, useSessions, type QueryResult, type QueryOptions, type UseDevicesParams, type UseDevicesResult, type UseTelemetryHistoryParams, type UseSessionsParams, type UseRunsResult, type UseSessionsResult, useLiveTelemetry, useLivePlane, useLiveCredentials, useLiveScope, useDeviceScope, type UseLiveScope, type UseDeviceScope, type UseDeviceScopeOptions, type DeviceScopeStatus, type LiveScope, parseTelemetryPayload, presignIotWssUrl, type UseLiveTelemetry, type UseLiveTelemetryOptions, type LiveStatus, type TelemetryPoint, type TelemetrySnapshot, type TelemetryFeedDeps, type MqttLikeClient, type MqttConnectionSpec, type ResolvedLiveCredentials, type ParsedTelemetryPayload, type TelemetryRecordingStatus, type TelemetryMediaStatus, type TelemetryStreamStatus, type LiveBlockPayload, useLiveBlock, resolveLiveBlock, liveBlockElapsedMs, LIVE_BLOCK_STALE_MS, type LiveBlockReason, type LiveBlockOpenReason, type LiveBlockState, type ResolveLiveBlockInput, useLiveVideoSession, type UseLiveVideoSession, type UseLiveVideoSessionOptions, type LiveVideoStatus, type LiveVideoStats, type WebRtcPlatform, type PeerConnectionLike, type SignalingLike, type TimerHost, useReplayManifest, useReplayPlayerCore, useReplayTelemetry, buildTimeline, segmentEnd, segmentAt, hasCoverageAt, gapAt, nextSegmentAfter, prevBoundary, nextBoundary, clampTs, buildLanes, buildClockTimeline, segmentUrlKey, ReplayClock, REPLAY_RATES, parseSegmentMediaInfo, seriesFromReadings, floorIndex, latestWithin, metricGroup, stalenessMs, overviewStalenessMs, chooseIntervalSeconds, windowBoundsFor, windowNeedsRefetch, buildTrace, traceLines, traveledLines, positionAt, GROUP_STALENESS_MS, DEFAULT_STALENESS_MS, WINDOW_SPAN_MS, WINDOW_MIN_SPAN_MS, type UseReplayManifestParams, type UseReplayManifestResult, type UseReplayPlayerCore, type UseReplayTelemetry, type UseReplayTelemetryOptions, type ReplayEngine, type ReplayEngineOptions, type ReplayEngineFactory, type ReplayTimeline, type ReplayLane, type LaneSegment, type ReplayClockState, type ReplayRate, type PacerCandidate, type GapSkipEvent, type SegmentMediaInfo, type MetricSeries, type GpsTrace, type WindowBounds, type PositionResolution, } from "@xorgate/react";
31
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,eAAe,CAAC;AAMvB,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMjE,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAMvE,OAAO,EAEL,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,eAAe,EAGpB,YAAY,EACZ,cAAc,EACd,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,EAAE,EACP,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,EAGlB,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAGtB,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAGrB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAG1B,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,SAAS,EAGd,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,eAAe,CAAC;AAMvB,OAAO,EACL,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMjE,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAMvE,OAAO,EAEL,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,SAAS,EACd,KAAK,eAAe,EAGpB,YAAY,EACZ,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,EAAE,EACP,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,aAAa,EAGlB,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,iBAAiB,EAGtB,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAOlB,YAAY,EACZ,cAAc,EACd,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAGrB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAG1B,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,SAAS,EAGd,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,yBAAyB,EAC9B,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -47,7 +47,14 @@ XorgateError, isXorgateError,
47
47
  // Data hooks
48
48
  useMe, useOrganizations, useWorkspaces, useDevices, useDevice, useDeviceModel, useTelemetryLatest, useTelemetryHistory, useVideoChannels, useRecordingRuns, useSessions,
49
49
  // Live telemetry and the live plane
50
- useLiveTelemetry, useLivePlane, useLiveCredentials, parseTelemetryPayload, presignIotWssUrl,
50
+ useLiveTelemetry, useLivePlane, useLiveCredentials,
51
+ // Live SCOPE: a device can be transferred to another workspace or
52
+ // organization while a viewer is open, and a vended credential is cached for
53
+ // up to ~50 minutes. On a phone that window is longer in practice, because a
54
+ // backgrounded app resumes onto the same cached credential. Neither plane
55
+ // fails loudly: telemetry simply stops arriving and video keeps flowing until
56
+ // the next reconnect. `useDeviceScope` is how that becomes a typed error.
57
+ useLiveScope, useDeviceScope, parseTelemetryPayload, presignIotWssUrl,
51
58
  // Live-block policy
52
59
  useLiveBlock, resolveLiveBlock, liveBlockElapsedMs, LIVE_BLOCK_STALE_MS,
53
60
  // The video session core and its platform boundary
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,OAAO,EAAE,cAAc,EAA8B,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAmB,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,qBAAqB,GAEtB,MAAM,eAAe,CAAC;AAEvB,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,OAAO,EACL,YAAY,GAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAC;AAEjE,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,OAAO,EACL,qBAAqB,GAEtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,GAOhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAyB,MAAM,mBAAmB,CAAC;AAEvE,8EAA8E;AAC9E,8CAA8C;AAC9C,8EAA8E;AAE9E,OAAO;AACL,uBAAuB;AACvB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB;AAahB,oCAAoC;AACpC,YAAY,EACZ,cAAc;AAsBd,aAAa;AACb,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW;AAUX,oCAAoC;AACpC,gBAAgB,EAChB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB;AAgBhB,oBAAoB;AACpB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB;AAMnB,mDAAmD;AACnD,mBAAmB;AAUnB,4EAA4E;AAC5E,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,GAqBnB,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E,OAAO,EAAE,cAAc,EAA8B,MAAM,eAAe,CAAC;AAC3E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAmB,MAAM,WAAW,CAAC;AACvE,OAAO,EACL,qBAAqB,GAEtB,MAAM,eAAe,CAAC;AAEvB,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,OAAO,EACL,YAAY,GAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAC;AAEjE,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,OAAO,EACL,qBAAqB,GAEtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,GAOhB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,WAAW,EAAyB,MAAM,mBAAmB,CAAC;AAEvE,8EAA8E;AAC9E,8CAA8C;AAC9C,8EAA8E;AAE9E,OAAO;AACL,uBAAuB;AACvB,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,gBAAgB;AAahB,oCAAoC;AACpC,YAAY,EACZ,cAAc;AAqCd,aAAa;AACb,KAAK,EACL,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAChB,WAAW;AAUX,oCAAoC;AACpC,gBAAgB,EAChB,YAAY,EACZ,kBAAkB;AAClB,kEAAkE;AAClE,6EAA6E;AAC7E,6EAA6E;AAC7E,0EAA0E;AAC1E,8EAA8E;AAC9E,0EAA0E;AAC1E,YAAY,EACZ,cAAc,EAMd,qBAAqB,EACrB,gBAAgB;AAgBhB,oBAAoB;AACpB,YAAY,EACZ,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB;AAMnB,mDAAmD;AACnD,mBAAmB;AAUnB,4EAA4E;AAC5E,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,KAAK,EACL,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,WAAW,EACX,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,EACd,kBAAkB,GAqBnB,MAAM,gBAAgB,CAAC"}
@@ -9,19 +9,5 @@ export interface ReplayVideoProps extends Omit<ViewProps, "children"> {
9
9
  /** Defaults to "contain", matching the web component's object-fit. */
10
10
  contentFit?: VideoContentFit;
11
11
  }
12
- /**
13
- * One replay lane's picture, and nothing else: no transport bar, no scrubber,
14
- * no gap overlay, no loading state. Same minimalism as `LiveVideo` and as the
15
- * web `ReplayVideo` — read `player.laneState(streamKey)` and render those
16
- * yourself.
17
- *
18
- * What it does that a single `VideoView` cannot is hold BOTH of the lane's
19
- * players mounted, the hidden one already loaded with the next segment. The
20
- * boundary is then an opacity flip between two live views rather than a
21
- * source change on one, which is the whole of why the seam is a picture
22
- * change instead of a reload. `nativeControls` is off: the clock owns the
23
- * transport, and a native scrubber would drive one segment's file rather than
24
- * the replay.
25
- */
26
12
  export declare function ReplayVideo(props: ReplayVideoProps): ReactNode;
27
13
  //# sourceMappingURL=replay-video.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"replay-video.d.ts","sourceRoot":"","sources":["../src/replay-video.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACnE,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CAsB9D"}
1
+ {"version":3,"file":"replay-video.d.ts","sourceRoot":"","sources":["../src/replay-video.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IACnE,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,sEAAsE;IACtE,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AA6CD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,SAAS,CA0B9D"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { StyleSheet, View } from "react-native";
2
+ import { Platform, StyleSheet, View } from "react-native";
3
3
  import { VideoView } from "expo-video";
4
4
  /**
5
5
  * One replay lane's picture, and nothing else: no transport bar, no scrubber,
@@ -14,17 +14,48 @@ import { VideoView } from "expo-video";
14
14
  * change instead of a reload. `nativeControls` is off: the clock owns the
15
15
  * transport, and a native scrubber would drive one segment's file rather than
16
16
  * the replay.
17
+ *
18
+ * On Android the two views are `TextureView`s rather than the default
19
+ * `SurfaceView`s, and that is load-bearing — see {@link ANDROID_SURFACE}.
20
+ */
21
+ /**
22
+ * What the lane's two views are rendered INTO on Android, and the one thing
23
+ * about this component that is not cosmetic.
24
+ *
25
+ * The seam is an opacity flip between two overlapping views, and `expo-video`
26
+ * draws into a `SurfaceView` by default. A `SurfaceView`'s picture is not
27
+ * drawn by the view hierarchy at all — it is a separate layer the system
28
+ * composites — so `opacity: 0` does not hide it and `zIndex` does not order it
29
+ * against a sibling surface. Two stacked lanes then fight over the same
30
+ * pixels, and the PRELOAD is the one that wins: the hidden player parks on the
31
+ * first frame of the NEXT segment, that still frame covers the player that is
32
+ * actually running, and the lane freezes for a whole segment while the clock,
33
+ * the map and the readouts carry on. Measured on an API 36 emulator before
34
+ * this line existed: 11 distinct frames in three minutes, each freeze starting
35
+ * within a second and a half of the hidden player reporting ready — and the
36
+ * LAST segment of a replay playing normally, because by then there is nothing
37
+ * left to preload on top of it.
38
+ *
39
+ * A `TextureView` is an ordinary view in the hierarchy, so alpha and z-order
40
+ * mean what they say. It costs a GPU copy per frame and cannot show
41
+ * DRM-protected media; a recorded lane is neither hot nor protected. iOS has
42
+ * no such split — `AVPlayerLayer` is a real layer — and the prop is Android's
43
+ * alone, so it is spread in rather than passed at all elsewhere.
17
44
  */
45
+ const ANDROID_SURFACE = { surfaceType: "textureView" };
18
46
  export function ReplayVideo(props) {
19
47
  const { player, streamKey, contentFit = "contain", style, ...viewProps } = props;
20
48
  const media = player.laneMedia(streamKey);
49
+ // Read per render rather than once at module load: it costs nothing, and a
50
+ // constant frozen at import time cannot be asserted from the other platform.
51
+ const surface = Platform.OS === "android" ? ANDROID_SURFACE : undefined;
21
52
  return (_jsx(View, { style: style, ...viewProps, children: media?.players.map((lanePlayer, index) => (_jsx(VideoView
22
53
  // Positional and stable: the pair is the same two objects for the
23
54
  // life of the lane, and only their order on screen changes.
24
55
  , { player: lanePlayer, style: [
25
56
  StyleSheet.absoluteFill,
26
57
  index === media.activeIndex ? styles.front : styles.behind,
27
- ], contentFit: contentFit, nativeControls: false }, index))) }));
58
+ ], contentFit: contentFit, nativeControls: false, ...surface }, index))) }));
28
59
  }
29
60
  const styles = StyleSheet.create({
30
61
  front: { opacity: 1, zIndex: 2 },
@@ -1 +1 @@
1
- {"version":3,"file":"replay-video.js","sourceRoot":"","sources":["../src/replay-video.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAYvC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAE1C,OAAO,CACL,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,KAAM,SAAS,YAC9B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CACzC,KAAC,SAAS;QACR,kEAAkE;QAClE,4DAA4D;YAE5D,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE;gBACL,UAAU,CAAC,YAAY;gBACvB,KAAK,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM;aAC3D,EACD,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,KAAK,IAPhB,KAAK,CAQV,CACH,CAAC,GACG,CACR,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IAChC,yEAAyE;IACzE,0CAA0C;IAC1C,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;CAClC,CAAC,CAAC"}
1
+ {"version":3,"file":"replay-video.js","sourceRoot":"","sources":["../src/replay-video.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAYvC;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,eAAe,GAAG,EAAE,WAAW,EAAE,aAAa,EAAW,CAAC;AAEhE,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,GAAG,SAAS,EAAE,KAAK,EAAE,GAAG,SAAS,EAAE,GAAG,KAAK,CAAC;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC;IAExE,OAAO,CACL,KAAC,IAAI,IAAC,KAAK,EAAE,KAAK,KAAM,SAAS,YAC9B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,CACzC,KAAC,SAAS;QACR,kEAAkE;QAClE,4DAA4D;YAE5D,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE;gBACL,UAAU,CAAC,YAAY;gBACvB,KAAK,KAAK,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM;aAC3D,EACD,UAAU,EAAE,UAAU,EACtB,cAAc,EAAE,KAAK,KACjB,OAAO,IARN,KAAK,CASV,CACH,CAAC,GACG,CACR,CAAC;AACJ,CAAC;AAED,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;IAChC,yEAAyE;IACzE,0CAA0C;IAC1C,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;CAClC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xorgate/react-native",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "React Native adapters for @xorgate/react: live telemetry over MQTT, live video over WebRTC (react-native-webrtc / RTCView), and the recorded-media replay player over expo-video.",
5
5
  "license": "MIT",
6
6
  "author": "Epye Labs",
@@ -77,7 +77,7 @@
77
77
  },
78
78
  "dependencies": {
79
79
  "@aws-crypto/sha256-js": "^5.2.0",
80
- "@xorgate/react": "^0.2.0-next.1"
80
+ "@xorgate/react": "^0.3.0"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@testing-library/react": "^16.3.3",
package/readme.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @xorgate/react-native
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@xorgate/react-native.svg)](https://www.npmjs.com/package/@xorgate/react-native)
4
+ [![CI](https://github.com/epyelabs/xorgate-react-native/actions/workflows/ci.yml/badge.svg)](https://github.com/epyelabs/xorgate-react-native/actions/workflows/ci.yml)
5
+ [![license](https://img.shields.io/npm/l/@xorgate/react-native.svg)](./LICENSE)
6
+
3
7
  React Native adapters for [`@xorgate/react`](https://www.npmjs.com/package/@xorgate/react).
4
8
  Live telemetry over MQTT-over-WebSocket, live video over WebRTC rendered by
5
9
  `react-native-webrtc`'s `RTCView`, and recorded-media replay over `expo-video`
@@ -209,6 +213,27 @@ Unchanged from the web package — `mqtt.js` 5 runs on React Native's global
209
213
  const { latest, history, status, receivedAt } = useLiveTelemetry(deviceId)
210
214
  ```
211
215
 
216
+ ### Live scope
217
+
218
+ A device can be transferred to another workspace or organization while a screen
219
+ is open. The vended live credential encodes the old tenancy and is cached for up
220
+ to ~50 minutes, and **nothing fails**: telemetry simply stops arriving and video
221
+ keeps flowing until the next reconnect. On a phone that window is effectively
222
+ longer, because a backgrounded app resumes onto the same cached credential and
223
+ `appStateWake` nudges the live planes straight back onto the stale scope.
224
+
225
+ ```tsx
226
+ const scope = useDeviceScope(deviceId)
227
+ const live = useLiveTelemetry(scope.outOfScope ? null : deviceId)
228
+
229
+ if (scope.error) return <Text>{scope.error.message}</Text> // DEVICE_OUT_OF_SCOPE
230
+ ```
231
+
232
+ `useDeviceScope` re-vends once before complaining, since the ordinary cause is
233
+ just a stale cached credential. If your app moves devices itself, call
234
+ `useLiveScope().invalidate()` afterwards and every open live consumer
235
+ re-resolves immediately.
236
+
212
237
  Read freshness off `receivedAt`, not `status`. A phone that loses signal keeps
213
238
  a socket that reads `connected` until the OS surfaces the close, which can take
214
239
  20 s or more; the age of the last frame is the honest signal, and the web app
@@ -266,8 +291,9 @@ The transport (`play`, `pause`, `toggle`, `seek`, `setRate`, `setSkipGaps`,
266
291
  `stepBoundary`), the clock, the lanes and the per-lane `laneState` are the same
267
292
  ones the web package exposes — `useReplayPlayer` here is `useReplayPlayerCore`
268
293
  with native media bound to it. Everything is driven by wall-clock epoch ms, so
269
- `useReplayTelemetry(manifest, player.clock)` tracks the video without either
270
- side knowing about the other.
294
+ `useReplayTelemetry(deviceId, player)` tracks the video without either side
295
+ knowing about the other: it takes the player CORE, reads a timeline and a
296
+ playhead, and has no idea what is playing the video.
271
297
 
272
298
  The manifest comes from `useReplayManifest(deviceId, …)`, or from your own API
273
299
  on the vended-direct auth mode — same rule as `useVideoChannels` above.