Conversation
The same src/telemetry/index.ts that client-sdk-js runs in Chromium, copied with no edits into a bare RN app: Metro resolves the OTLP encoder and Hermes executes both serializers, which is the whole claim behind "one implementation for web and React Native". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
livekit-client carries the pipeline; React Native supplies the two things it cannot know — which platform this is, and that an app going to the background is the last chance to upload, since there is no visibilitychange here. registerGlobals calls it, and it is imported after the DOMException and TextEncoder polyfills: livekit-client evaluates both at module scope and Hermes has neither. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Thermal state, low power mode and memory pressure: the three signals SPEC's cadence policy needs and no browser API exposes. LKDeviceState.swift watches ProcessInfo's two notifications and a DispatchSource memory-pressure source; DeviceStateMonitor.kt watches PowerManager's thermal listener, the power-save broadcast and onTrimMemory. Both map the platform's levels onto SPEC's names, so a record from iOS and one from Android say the same thing. The first state comes back on startDeviceStateUpdates' promise rather than as an event — an event sent from inside that call arrives before JS has registered its listener, and RCTEventEmitter drops it without a word JS can see. Verified on the simulator: thermal nominal and low power false reach the collector alongside app_state. Memory pressure has no simulator trigger. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
React Native follows the phones: thermal, low power and memory pressure belong to the Rust core, which this package will bind over UniFFI, so livekit-client never has to know a phone gets hot. The native monitors stay — they are the device half of that design and they work — and the JS bridge that was carrying their values is gone. What crosses from JavaScript is what a JavaScript runtime knows: the platform's name and the app lifecycle. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…adence LKBatchStore.swift and BatchStore.kt are a directory of batch files mirroring the Rust core's FileCache: written before the network is tried, renamed into place so a crash never leaves half a batch readable, pruned oldest-first above the byte, count and age budgets, and one batch always survives. livekit-client stores through it and never learns what it is. Proved on the simulator: 36 batches cached with the collector down, app killed, and all 45 records delivered on relaunch. Device state comes back the same way — through two verbs that name no platform. The RN package maps thermal, low power and memory pressure onto SPEC's events and onto a cadence factor itself, so the mobile vocabulary stays here. One gotcha worth the comment it got: a blocking synchronous method must return an object. Two of these returned Bool, and the TurboModule interop retained it as a pointer and segfaulted the app with no word JavaScript could see. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: d63adbe The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
registerGlobalsnow wireslivekit-client's telemetry pipeline to the platform. The instrumentation — when a connect span starts, what a stats window holds, how a subscribe ends at first media — stays inlivekit-clientand is reused unchanged; this PR adds the two things a phone knows and a browser does not.LKBatchStore.swiftandBatchStore.ktare a directory of batch files mirroring the Rust core'sFileCache: written before the network is tried, renamed into place so a crash never leaves half a batch readable, pruned oldest-first above 4 MiB / 512 batches / 24 h, and one batch always survives. A session that ends with the app being killed, or an hour spent offline, replays at the next launch instead of being lost.LKDeviceState.swift(ProcessInfo.thermalStateDidChangeNotification,NSProcessInfoPowerStateDidChange, aDispatchSourcememory-pressure source) andDeviceStateMonitor.kt(PowerManager.addThermalStatusListener,ACTION_POWER_SAVE_MODE_CHANGED,onTrimMemory), each mapping the platform's levels onto SPEC's names so an iOS record and an Android record say the same thing.livekit-client. It receives aTelemetryStorageit never inspects, an event name it never interprets, and a cadence factor without a reason attached. The thermal/low-power/memory mapping lives in this package.awaitin it — which is also why this is native code here rather thanexpo-file-systemor the maintainedreact-native-fsfork, all of which are async.Depends on
livekit/client-sdk-js#2109. This uses
Telemetry.configure({ storage }),Telemetry.emitandTelemetry.setCadenceFactor, none of which exist in a releasedlivekit-client. Local verification was done against that branch.There is also a pre-existing blocker unrelated to telemetry:
src/e2ee/RNE2EEManager.tsdoes not type-check againstlivekit-client2.22 (threeUint8Array<ArrayBufferLike>vsUint8Array<ArrayBuffer>errors from the typed-array variance that package introduced). That breaksbob build, sonpm iof this package fails against a currentlivekit-client.Testing
Library/Caches/livekit-telemetry; killed fromsimctl; relaunched with the collector back; all 45 records delivered, including the 35 per-tick self-reports that reconstruct the offline period after the fact. Zero rejected.lk.device.thermal.changed=nominal,lk.device.low_power.changed=false,lk.device.app_state.changed=foreground, withservice.name=livekit-client-react-native os.name=ios os.version=18.6.yarn typescriptandyarn lintclean apart from the pre-existingRNE2EEManagererrors above.Notes for review
telemetry-poc/is a bare RN 0.82 app used as the harness — it links this package's pod and proves the native store on a simulator without dragging in WebRTC. Happy to drop it from the PR or move it underexample/if it does not belong here.Bool, and the TurboModule interop retained it as a pointer and segfaulted the app with nothing JavaScript could see); and an event sent from inside a native method races the listener JS registers around it, so the first device state comes back on a promise.Before undrafting
RNE2EEManagertype errors fixed againstlivekit-client≥ 2.22lk.device.memory.changedexercised (the simulator gives no way to driveDispatchSource's levels)telemetry-poc/stays