Skip to content

Decompose webSessionSetup into reusable parts (Phase 3)#788

Merged
kraenhansen merged 3 commits into
kh/client-dom-api-isolationfrom
kh/client-dom-api-isolation-p3
May 19, 2026
Merged

Decompose webSessionSetup into reusable parts (Phase 3)#788
kraenhansen merged 3 commits into
kh/client-dom-api-isolationfrom
kh/client-dom-api-isolation-p3

Conversation

@kraenhansen

@kraenhansen kraenhansen commented May 16, 2026

Copy link
Copy Markdown
Member

Summary

  • Extract setupWebRTCSession() into common platform/VoiceSessionSetup.ts — platform-agnostic WebRTC session setup that extracts input/output from WebRTCConnection
  • Replace web setupInputOutput (handled both connection types) with setupWebSocketIO (WebSocket-only), delegating WebRTC to the common function
  • Update internal.ts exports: remove webSessionSetup (which imported web-only code), add setupWebRTCSession + createConnection
  • Update React Native package to call createConnection() + setupWebRTCSession() directly instead of webSessionSetup()

This fixes all TS6307 errors where internal.ts was dragging platform/web/ files into the common tsconfig build. React Native no longer transitively imports web-only MediaDeviceInput/MediaDeviceOutput.

Stacked on #784 (Phase 2).

Breaking changes

webSessionSetup removed from @elevenlabs/client/internal

Before:

import { webSessionSetup } from "@elevenlabs/client/internal";

const result = await webSessionSetup(options);

After: Use createConnection + setupWebRTCSession for WebRTC, or the registered platform strategy for full setup:

import { createConnection, setupWebRTCSession } from "@elevenlabs/client/internal";

const connection = await createConnection(options);
const result = setupWebRTCSession(connection);

Why: webSessionSetup imported web-only code (MediaDeviceInput/Output, AudioContext), which pulled DOM dependencies into any consumer — including React Native. Splitting into createConnection (platform-agnostic) and setupWebRTCSession (also platform-agnostic) lets non-web platforms set up voice sessions without importing web APIs.


setupInputOutput removed from platform/web/VoiceSessionSetup.ts exports

Before:

import { setupInputOutput } from "@elevenlabs/client/platform/web/VoiceSessionSetup";

After: This function is no longer exported. WebRTC setup is handled by setupWebRTCSession from platform/VoiceSessionSetup.ts. WebSocket I/O setup is internal to the web strategy.

Why: setupInputOutput mixed WebRTC and WebSocket concerns in a single function, making it impossible to use either path independently. WebRTC setup is now platform-agnostic, and WebSocket setup is an internal detail of the web platform strategy.

Changes

  • packages/client/src/internal.ts — Remove webSessionSetup export, add setupWebRTCSession and createConnection exports
  • packages/client/src/platform/VoiceSessionSetup.ts — Add setupWebRTCSession() function for platform-agnostic WebRTC session setup
  • packages/client/src/platform/web/VoiceSessionSetup.ts — Replace exported setupInputOutput with private setupWebSocketIO; webSessionSetup now delegates WebRTC to setupWebRTCSession
  • packages/react-native/src/index.react-native.ts — Use createConnection() + setupWebRTCSession() instead of webSessionSetup()

Test plan

  • TS6307 errors eliminated (verified: 0 occurrences)
  • React Native package passes tsc --noEmit
  • 65/65 unit tests passing
  • Manual test: web voice conversation (WebRTC path)
  • Manual test: web voice conversation (WebSocket path)
  • Manual test: React Native voice conversation

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk due to a breaking change in @elevenlabs/client/internal exports and refactoring of session setup branching across web and React Native, which could affect voice initialization paths if edge cases were missed.

Overview
WebRTC session setup is extracted into a reusable, platform-agnostic helper. platform/VoiceSessionSetup.ts now exposes setupWebRTCSession() (with type-checking and clearer errors) and adds unit tests for expected/invalid inputs.

Web and React Native setup paths are refactored to use the new split. Web webSessionSetup now only builds MediaDevice I/O for WebSocketConnection and delegates WebRTC to setupWebRTCSession, while React Native stops importing the web strategy, explicitly creates a connection via createConnection, uses setupWebRTCSession, and throws early if WebSocket/signedUrl options are provided. internal.ts updates exports to drop webSessionSetup and instead export setupWebRTCSession plus createConnection.

Reviewed by Cursor Bugbot for commit b4f4512. Bugbot is set up for automated code reviews on this repo. Configure here.

@kraenhansen kraenhansen self-assigned this May 16, 2026
@kraenhansen kraenhansen force-pushed the kh/client-dom-api-isolation-p2 branch 3 times, most recently from ae4a718 to 9b8b335 Compare May 19, 2026 07:18
Base automatically changed from kh/client-dom-api-isolation-p2 to kh/client-dom-api-isolation May 19, 2026 09:07
@kraenhansen kraenhansen force-pushed the kh/client-dom-api-isolation-p3 branch from da8c219 to 7db4e16 Compare May 19, 2026 09:10
Extract setupWebRTCSession into common platform/VoiceSessionSetup.ts
so React Native no longer calls the web-only webSessionSetup function.

- Add setupWebRTCSession() to common code (platform-agnostic WebRTC
  session setup that extracts input/output from WebRTCConnection)
- Replace web setupInputOutput with setupWebSocketIO (WebSocket-only)
- Update internal.ts exports: remove webSessionSetup, add
  setupWebRTCSession + createConnection
- Update React Native to use createConnection + setupWebRTCSession
  directly instead of webSessionSetup

This fixes all TS6307 errors where internal.ts was dragging web files
into the common tsconfig build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kraenhansen kraenhansen force-pushed the kh/client-dom-api-isolation-p3 branch from 7db4e16 to abb1cd4 Compare May 19, 2026 09:28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kraenhansen kraenhansen marked this pull request as ready for review May 19, 2026 10:20
@kraenhansen kraenhansen requested a review from Copilot May 19, 2026 10:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the platform-isolation work by decomposing the web-only webSessionSetup flow into reusable, platform-agnostic pieces so React Native can set up WebRTC voice sessions without importing any platform/web/* DOM-dependent modules.

Changes:

  • Introduces a common setupWebRTCSession() helper that extracts input/output controllers directly from WebRTCConnection.
  • Refactors the web setup strategy to keep WebSocket media-device I/O web-only while delegating WebRTC setup to the common helper.
  • Updates @elevenlabs/client/internal exports and switches the React Native package to createConnection() + setupWebRTCSession().

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/react-native/src/index.react-native.ts Switch RN setup strategy from webSessionSetup to createConnection + setupWebRTCSession.
packages/client/src/platform/web/VoiceSessionSetup.ts Splits WebSocket-only I/O setup from WebRTC setup; delegates WebRTC path to common helper.
packages/client/src/platform/VoiceSessionSetup.ts Adds platform-agnostic setupWebRTCSession() helper.
packages/client/src/platform/VoiceSessionSetup.test.ts Adds unit coverage for setupWebRTCSession() behavior and validation.
packages/client/src/internal.ts Removes webSessionSetup export; adds setupWebRTCSession and createConnection exports.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -2,6 +2,7 @@ import type { Options } from "../../BaseConversation.js";
import type { BaseConnection } from "../../utils/BaseConnection.js";

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in Phase 4 (b4f451288eaf14): the web setup file was rewritten and BaseConnection is no longer imported. WebRTCConnection is still used for the instanceof check in the WebRTC branch.

Comment on lines +44 to +46
throw new Error(
"setupWebRTCSession requires a WebRTCConnection. " +
`Received: ${connection.constructor.name}`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b4f4512: changed to connection?.constructor?.name ?? typeof connection so null/undefined inputs produce "Received: object" or "Received: undefined" instead of throwing a TypeError. Added tests for both edge cases.

Comment on lines +40 to 42
const connection = await createConnection(options);
const result = attachNativeVolume(setupWebRTCSession(connection));

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b4f4512: added an early guard that checks for connectionType: "websocket" or signedUrl and throws a clear RN-specific error: "WebSocket connections are not supported on React Native." Also updated the doc comment to document the limitation.

import type { BaseConnection } from "../../utils/BaseConnection.js";
import {
setSetupStrategy,
setupWebRTCSession,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused WebRTCConnection import after refactoring

Low Severity

WebRTCConnection is imported as a value on line 11 but is never referenced in the file after the instanceof WebRTCConnection check was moved into setupWebRTCSession. The project has @typescript-eslint/no-unused-vars disabled and noUnusedLocals is not set, so no tooling catches this. The type-only BaseConnection import on line 2 is also unused.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d51ad5f. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in Phase 4 (88eaf14): the web setup file was rewritten — WebRTCConnection is used again (for the instanceof check in the WebRTC branch) and BaseConnection is no longer imported.

- Use defensive access in setupWebRTCSession error message to handle
  null/undefined inputs without masking the validation error
- Add early guard in React Native strategy for unsupported WebSocket
  connections with a clear, RN-specific error message
- Update doc comment to reflect actual RN strategy behavior
- Add tests for null/undefined edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b4f4512. Configure here.

"Only WebRTC connections are available. " +
"Remove the connectionType/signedUrl option or use connectionType: 'webrtc'."
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Native guard misses textOnly WebSocket path

Medium Severity

The early guard intended to catch unsupported WebSocket connections on React Native only checks for options.connectionType === "websocket" and options.signedUrl, but createConnection also infers "websocket" when textOnly: true is set (via determineConnectionType in ConnectionFactory.ts). Passing { agentId: "...", textOnly: true } bypasses the guard, starts the AudioSession, then fails inside setupWebRTCSession with a confusing "requires a WebRTCConnection" error instead of the friendly "WebSocket connections are not supported on React Native" message.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b4f4512. Configure here.

@kraenhansen kraenhansen merged commit 9f4b356 into kh/client-dom-api-isolation May 19, 2026
2 of 5 checks passed
@kraenhansen kraenhansen deleted the kh/client-dom-api-isolation-p3 branch May 19, 2026 10:56
kraenhansen added a commit that referenced this pull request May 20, 2026
* Isolate DOM APIs from common client code

Split tsconfig.build.json into tsconfig.common.json (no DOM lib) and
tsconfig.web.json (with DOM), add runtime.d.ts for cross-platform
globals, and introduce platform/web/ entry point.

This is the landing pad for migrating DOM usage out of common code
and eventually injecting React Native alternatives.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Expand runtime.ts with cross-platform type declarations

Rename runtime.d.ts → runtime.ts and add declarations for fetch,
WebSocket, URL, encoding, and event types needed by tsconfig.common.json
(lib: ["ES2022"], no DOM). Also adds assertRuntimeCompatibility() for
early environment checks.

Fixes window.btoa/window.atob → btoa/atob in audio.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Move web-only files to platform/web/ (Phase 1) (#786)

* Move web-only files to platform/web/ (Phase 1)

Move files whose consumers are entirely within web code to
src/platform/web/: compatibility.ts, addLibsamplerateModule.ts,
input.ts (MediaDeviceInput), output.ts (MediaDeviceOutput).

Split VoiceSessionSetup into common (types + injectable strategy
variable) and web (implementation + side-effect registration).

Extract shared types (InputConfig, InputEventTarget, OutputConfig,
PlaybackEventTarget, etc.) into common InputController.ts and
OutputController.ts so they remain accessible without DOM.

Refactor applyDelay to accept a resolved delay value directly,
removing the dependency on web-only compatibility.ts.

Files still depending on common code (rawAudioProcessor,
scribeAudioProcessor, createWorkletModuleLoader,
createAnalyserVolumeProvider) stay in src/utils/ until their
injection refactoring in Phases 5-6.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove unused type re-exports from web input/output

Types are defined in common InputController.ts / OutputController.ts
and imported from there by all consumers. The re-exports from the
web files were dead code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix IIFE bundle entry point to include web strategy

The IIFE build (for CDN/script-tag users) used src/index.ts as entry,
which no longer imports the web session setup strategy after the
platform split. Change entry to src/platform/web/index.ts which
re-exports everything plus the side-effect registration.

Also adds a TODO for the lost Android delay default in resolveDelay.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Call assertRuntimeCompatibility() in Conversation.startSession

Validates that required cross-platform globals (fetch, WebSocket,
TextEncoder, etc.) are available before starting a session, giving
users a clear error message instead of cryptic failures.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Replace DOM event constructors with platform-agnostic types (Phase 2) (#787)

* Replace DOM event constructors with platform-agnostic types

- Rename runtime.d.ts → runtime.ts with expanded cross-platform global
  declarations (WebSocket, fetch, URL, encoding, events) and a runtime
  assertion function
- Replace Event/CloseEvent constructors in DisconnectionDetails with
  plain DisconnectionContext objects ({ type, reason?, code? })
- Fix window.btoa/window.atob → btoa/atob in audio.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changeset: remove runtime.ts reference (now in p1)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add disconnection context tests for WebSocket, BaseConversation, and WebRTC

Cover the new platform-agnostic DisconnectionContext shape across all
disconnect paths: WebSocket close/error events, end_call and
max_duration_exceeded in BaseConversation, and LiveKit room disconnect
events in WebRTCConnection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Decompose webSessionSetup into reusable parts (Phase 3) (#788)

* Decompose webSessionSetup into reusable parts (Phase 3)

Extract setupWebRTCSession into common platform/VoiceSessionSetup.ts
so React Native no longer calls the web-only webSessionSetup function.

- Add setupWebRTCSession() to common code (platform-agnostic WebRTC
  session setup that extracts input/output from WebRTCConnection)
- Replace web setupInputOutput with setupWebSocketIO (WebSocket-only)
- Update internal.ts exports: remove webSessionSetup, add
  setupWebRTCSession + createConnection
- Update React Native to use createConnection + setupWebRTCSession
  directly instead of webSessionSetup

This fixes all TS6307 errors where internal.ts was dragging web files
into the common tsconfig build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add tests for setupWebRTCSession

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address PR review feedback

- Use defensive access in setupWebRTCSession error message to handle
  null/undefined inputs without masking the validation error
- Add early guard in React Native strategy for unsupported WebSocket
  connections with a clear, RN-specific error message
- Update doc comment to reflect actual RN strategy behavior
- Add tests for null/undefined edge cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Make VoiceConversation platform-agnostic (Phase 4) (#792)

* Make VoiceConversation platform-agnostic (Phase 4)

Move wake lock management, preliminary mic permission, platform-specific
delay, and visibility change handling from VoiceConversation into the web
setup strategy. VoiceConversation now has zero DOM references. The detach
callback becomes async to support wake lock release on cleanup. Also
restores the previously-lost Android platform delay by detecting the
platform in the web strategy via compatibility.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address review comments: cleanup ordering and error handling

- VoiceConversation: detach IO before closing connection (cleanUp/close
  before super.handleEndSession) to avoid listeners firing during teardown
- VoiceSessionSetup: close connection if IO setup throws to prevent
  leaking network resources
- React Native: use try/finally so AudioSession.stopAudioSession() runs
  even if upstream detach fails; await the stop call
- nativeVolume: make detach async and await originalDetach to honor the
  Promise<void> contract

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Inject platform audio via WebRTCAudioAdapter (Phase 5) (#794)

* Inject platform audio behavior into WebRTCConnection via adapter (Phase 5)

Introduce WebRTCAudioAdapter interface so WebRTCConnection delegates all
DOM-dependent audio operations (AudioContext, HTMLAudioElement, AudioWorklet)
to a platform-specific adapter instead of calling web APIs directly.

- Add WebRTCAudioAdapter interface + module-level factory registration
- Create WebAudioAdapter (web implementation) in platform/web/
- Move generated worklet files (raw/concat) to platform/web/
- Move createAnalyserVolumeProvider to platform/web/volumeProvider.ts
- Add MediaStream/MediaStreamTrack declarations to runtime.ts
- Register web adapter as side-effect in platform/web/index.ts
- Address Phase 4 review comments: IO error cleanup, detach ordering,
  async detach in nativeVolume, connection leak fix in webSessionSetup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address review comments: stale refs, debug event, dead code

- Reset inputAnalyser/outputAnalyser/volumeProviders in close() after
  adapter cleanup so consumers get undefined/zero instead of stale refs
- Move audio_element_ready debug event inside the adapter guard so it
  only fires when playback was actually set up
- Remove write-only outputDeviceId field from WebAudioAdapter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Make Scribe module platform-agnostic (Phase 6) (#800)

* Make Scribe module platform-agnostic (Phase 6)

Move createWorkletModuleLoader and scribeAudioProcessor to platform/web/
where they belong (both use web-only APIs like Blob, URL.createObjectURL,
and AudioWorklet). Extract microphone streaming from ScribeRealtime into
an injectable ScribeMicrophoneSetup strategy, with the web implementation
in platform/web/scribeMicrophone.ts. The scribe module now has zero DOM
references — microphone mode uses the same dependency injection pattern
as VoiceConversation and WebRTCAudioAdapter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Vendor ConstrainDOMString as MediaDeviceConstraint in runtime.ts

Declare MediaDeviceConstraint (and MediaDeviceConstraintParameters) in
runtime.ts as a semantic replacement for ConstrainDOMString. Uses a
unique name to avoid conflicts with DOM lib's ConstrainDOMString when
consumers compile with lib: ["DOM"].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Fix remaining tsconfig.common.json type errors (Phase 7) (#801)

* Fix remaining tsconfig.common.json type errors (Phase 7)

Resolve the last 5 type errors in tsconfig.common.json (no DOM lib),
completing the platform isolation effort:

- Declare minimal AnalyserNode interface for deprecated getAnalyser()
- Add type assertion for response.json() in BaseConversation and errors
- Fix Promise<void> typing in applyDelay
- Fix Function type lint error in WebRTCConnection.test.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add isJsonObject type guard for response.json() calls

Replace unsafe type assertions on response.json() with a proper
type guard that validates the value is a non-null, non-array object
before accessing properties.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Move isJsonObject to shared utils/assert.ts

Consolidate the duplicate isPlainObject from mergeOptions.ts and
isJsonObject from errors.ts into a single shared type guard in
utils/assert.ts. Add assertJsonObject assertion function and use
it in BaseConversation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Fix browser test import to use web platform entry point

The test was importing from the common index.ts which no longer
auto-registers the web setup strategy after the Phase 3-7 refactoring.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Restore Android 3-second default delay when connectionDelay is undefined

The refactoring of applyDelay into resolveDelay dropped the default
DelayConfig that provided a 3-second delay on Android (for AudioManager
mode switching). When connectionDelay was undefined, resolveDelay
returned 0, removing the delay Android web users relied on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Restore original shutdown ordering: close connection before input/output

The refactoring moved super.handleEndSession() (which closes the
connection) to after input.close()/output.close(). Restore the original
order: detach listeners, close connection, then close input/output.
This prevents the connection from delivering events to already-closing
controllers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove dead WebRTC volume control tests

These tests set audioElements on WebRTCConnection directly, but that
field was moved to WebAudioAdapter during Phase 5. The tests were
silently passing (wrapped in try/catch) with zero coverage. The
volume-setting and cleanup behavior is trivially correct and best
tested via browser integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Clean up VoiceSessionSetup: fix redundant spread and stale comment

Restructure the web caller to build VoiceSessionSetupResult directly
per branch — WebSocket constructs from parts, WebRTC gets the complete
result from setupWebRTCSession. This avoids the redundant spread of
connection from both the explicit property and the io object.

Also update stale comment that referenced the old webSessionSetup name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix double audio connection in WebAudioAdapter.setupOutputAnalysis

The audio graph had source connected to both analyser and worklet
directly (source→analyser→worklet AND source→worklet), causing the
worklet to sum two copies of the signal at double amplitude.

Remove the duplicate connection and reorder so the message listener
and format configuration are set up before connecting the graph.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Handle rejected promises from endSessionWithDetails

Three call sites invoked the async endSessionWithDetails without
awaiting or catching — rejections from handleEndSession were lost.
Forward errors through onError callback. Also extract duplicated
end_call DisconnectionDetails literal into a module-level constant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove unnecessary async from RoomEvent.Connected handler

The handler body is synchronous (just sets this.isConnected = true),
so the async keyword is unnecessary and causes the event emitter to
receive a promise it silently ignores.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Move volumeThreshold to module-level constant

Avoids recreating the constant on every audio callback invocation
in the hot path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify streamFromMicrophone config passing

Pass options.microphone directly instead of copying each property
individually — the types are identical and the callee only reads
from the config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Remove dead-store inputAnalyser and outputAnalyser fields

These fields were written but never read. The analyser nodes are
already returned to the caller via the result objects; storing them
on the instance only to null them during cleanup served no purpose.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use element.remove() instead of parentNode.removeChild

Simpler modern DOM API — remove() is a no-op when the element
isn't attached, so the parentNode null check is unnecessary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Merge InputAnalysisResult and OutputAnalysisResult into AnalysisResult

The two types had identical shapes. The method names on
WebRTCAudioAdapter already distinguish input vs output context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Replace forEach with for...of on media stream tracks

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Extract closeCode/closeReason variables in WebSocket close handler

Removes triple-repeated event.code and event.reason || undefined
expressions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Use shared arrayBufferToBase64 in scribe microphone handler

Replaces hand-rolled base64 encoding loop with the existing
utility from utils/audio.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Void audioContext.close() in scribe microphone cleanup

Explicitly marks the discarded promise to avoid silent unhandled
rejections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Deduplicate getAudioTracks() call in scribe microphone setup

Extract audioTrack once at the top instead of calling
getAudioTracks() twice.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Consolidate duplicate re-exports from InputController and OutputController

Merge separate export statements from the same module into single
import blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix synchronous throw in startSession and AudioContext leak in setupOutputAnalysis

Make startSession async so assertRuntimeCompatibility() errors are
caught by .catch() callers. Add cleanup of previous audioCaptureContext
in setupOutputAnalysis, matching the existing pattern in setupInputAnalysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add React Native DOM API leak changeset

Co-authored-by: Kræn Hansen <mail@kraenhansen.dk>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants