Skip to content

Commit a2bcc7b

Browse files
committed
Merge branch 'main' into audio-sdk-impl
2 parents b2b878f + 2fe1f9f commit a2bcc7b

File tree

81 files changed

+2027
-378
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+2027
-378
lines changed

AGENTS.md

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
# Guidance for AI coding agents
2+
3+
File purpose: operational rules for automated or assisted code changes. Human-facing conceptual docs belong in `README.md` or the docs site.
4+
5+
## Repository purpose
6+
7+
Stream Video SDKs for:
8+
9+
- React
10+
- React Native
11+
- Plain JavaScript (core client)
12+
13+
Goals: API stability, backward compatibility, predictable releases, strong test coverage, accessibility, and performance discipline.
14+
15+
## Tech & toolchain
16+
17+
- Languages: TypeScript, React (web + native)
18+
- Runtime: Node (use `nvm use` with `.nvmrc`)
19+
- Package manager: Yarn (workspaces)
20+
- Testing: Vitest (unit/integration), Playwright (E2E)
21+
- Lint/Format: ESLint + Prettier
22+
- Build: Package-local build scripts (composed via root)
23+
- Release: Conventional Commits -> automated versioning/publishing
24+
- Platforms:
25+
- React: Web
26+
- React Native: iOS and Android
27+
28+
## Environment setup
29+
30+
1. `nvm use`
31+
2. `yarn install`
32+
3. (Optional) Verify: `node -v` matches `.nvmrc`
33+
4. Run initial full build: `yarn build:all`
34+
5. Run tests: `yarn test:ci:all`
35+
36+
## Project layout (high-level)
37+
38+
- `packages/`
39+
- `react-sdk/`
40+
- `react-native-sdk/`
41+
- `client/` (core, no UI)
42+
- `sample-apps/`
43+
- `react/`
44+
- `react-native/`
45+
- `client/`
46+
- Config roots: linting, tsconfig, playwright, babel
47+
- Do not edit generated output (`dist/`, build artifacts)
48+
49+
## Core commands (Runbook)
50+
51+
| Action | Command |
52+
| ----------------------------------- | ------------------------------------------ |
53+
| Install deps | `yarn install` |
54+
| Full build | `yarn build:all` |
55+
| Watch (if available) | `yarn dev` or `yarn start` (add if absent) |
56+
| Lint | `yarn lint:all` |
57+
| Fix lint (if separate) | `yarn lint:all --fix` |
58+
| Unit/Integration tests (CI profile) | `yarn test:ci:all` |
59+
| Local fast tests | `yarn test` |
60+
| E2E (Playwright) | `yarn test:e2e` |
61+
| Coverage | `yarn test:coverage` |
62+
| Clean | `yarn clean:all` |
63+
64+
## API design principles
65+
66+
- Semantic versioning
67+
- Use `@deprecated` JSDoc with replacement guidance
68+
- Provide migration docs for breaking changes
69+
- Avoid breaking changes; prefer additive evolution
70+
- Public surfaces: explicit TypeScript types/interfaces
71+
- Consistent naming: `camelCase` for functions/properties, `PascalCase` for components/types
72+
73+
### Deprecation lifecycle
74+
75+
1. Mark with `@deprecated` + rationale + alternative.
76+
2. Maintain for at least one minor release unless security-critical.
77+
3. Add to migration documentation.
78+
4. Remove only in next major.
79+
80+
## Performance guidelines
81+
82+
- Minimize re-renders (memoization, stable refs)
83+
- Use `React.memo` / `useCallback` / `useMemo` when profiling justifies
84+
- Clean up side effects (`AbortController` for network calls)
85+
- Monitor bundle size; justify increases > 2% per package
86+
- Prefer lazy loading for optional heavy modules
87+
- Avoid unnecessary large dependency additions
88+
89+
## Accessibility (a11y)
90+
91+
- All interactive elements keyboard accessible
92+
- Provide ARIA roles/labels where semantic tags insufficient
93+
- Maintain color contrast (WCAG AA)
94+
- Do not convey state by color alone
95+
- Announce dynamic content changes (ARIA live regions if needed)
96+
97+
## Error & logging policy
98+
99+
- Public API: throw descriptive errors or return typed error results (consistent with existing patterns)
100+
- No console noise in production builds
101+
- Internal debug logging gated behind env flag (if present)
102+
- Never leak credentials/user data in errors
103+
104+
## Concurrency & async
105+
106+
- Cancel stale async operations (media, network) when components unmount
107+
- Use `AbortController` for fetch-like APIs
108+
- Avoid race conditions: check instance IDs / timestamps before state updates
109+
110+
## Testing strategy
111+
112+
- Unit: pure functions, small components
113+
- Integration: component-tree interactions, state flows
114+
- React Native: target minimal smoke + platform logic (avoid flakiness)
115+
- E2E: critical user journeys (Playwright)
116+
- Mocks/fakes: prefer shared test helpers
117+
- Coverage target: maintain or improve existing percentage (fail PR if global coverage drops)
118+
- File naming: `*.test.ts` / `*.spec.ts(x)`
119+
- Add tests for: new public API, bug fixes (regression test), performance-sensitive utilities
120+
121+
## CI expectations
122+
123+
- Mandatory: build, lint, type check, unit/integration tests, (optionally) E2E smoke
124+
- Node versions: those listed in matrix (see workflow YAML)
125+
- Failing or flaky tests: fix or quarantine with justification PR comment (temporary)
126+
- Zero new warnings
127+
128+
## Release workflow (high-level)
129+
130+
1. Conventional Commit messages on PR merge
131+
2. Release automation aggregates commits
132+
3. Version bump + changelog + tag
133+
4. Publish to registry
134+
5. Deprecations noted in CHANGELOG
135+
6. Ensure docs updated prior to publishing breaking changes
136+
137+
## Dependency policy
138+
139+
- Avoid adding large deps without justification (size, maintenance)
140+
- Prefer existing utility packages
141+
- Run `yarn audit` (or equivalent) if adding security-impacting deps
142+
- Keep upgrades separate from feature changes when possible
143+
144+
## Samples & docs
145+
146+
- New public feature: update at least one sample app
147+
- Breaking changes: provide migration snippet
148+
- Keep code snippets compilable
149+
- Use placeholder keys (`YOUR_STREAM_KEY`)
150+
151+
## React Native specifics
152+
153+
- Clear Metro cache if module resolution issues: `yarn react-native start --reset-cache`
154+
- Test on iOS + Android for native module or platform-specific UI changes
155+
- Avoid unguarded web-only APIs in shared code
156+
157+
## Linting & formatting
158+
159+
- Run `yarn lint:all` before commit
160+
- Narrowly scope `eslint-disable` with inline comments and rationale
161+
- No broad rule disabling
162+
163+
## Commit / PR conventions
164+
165+
- Small, focused PRs
166+
- Include tests for changes
167+
- Screenshot or video for UI changes (before/after)
168+
- Label breaking changes clearly in description
169+
- Document public API changes
170+
171+
## Security
172+
173+
- No credentials or real user data
174+
- Use placeholders in examples
175+
- Scripts must error on missing critical env vars
176+
- Avoid introducing unmaintained dependencies
177+
178+
## Prohibited edits
179+
180+
- Do not edit build artifacts (`dist/`, generated types)
181+
- Do not bypass lint/type errors with force merges
182+
183+
## Quick agent checklist (per commit)
184+
185+
- Build succeeds
186+
- Lint clean
187+
- Type check clean
188+
- Tests (unit/integration) green
189+
- Coverage not reduced
190+
- Public API docs updated if changed
191+
- Samples updated if feature surfaced
192+
- No new warnings
193+
- No generated files modified
194+
195+
---
196+
197+
Refine this file iteratively for agent clarity; keep human-facing explanations in docs site / `README.md`.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
"eslint": "^9.26.0",
7171
"eslint-plugin-import": "^2.31.0",
7272
"eslint-plugin-react": "^7.37.5",
73-
"eslint-plugin-react-hooks": "^5.2.0",
73+
"eslint-plugin-react-hooks": "^6.1.0",
7474
"globals": "^16.1.0",
7575
"husky": "^9.1.7",
7676
"lint-staged": "^15.5.2",

packages/audio-filters-web/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
44

5+
## [0.5.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/audio-filters-web-0.4.3...@stream-io/audio-filters-web-0.5.0) (2025-09-30)
6+
7+
### Features
8+
9+
- Audio profiles and Hi-Fi stereo audio ([#1887](https://github.com/GetStream/stream-video-js/issues/1887)) ([3b60c89](https://github.com/GetStream/stream-video-js/commit/3b60c89b8c0dbc40544fe13be79c10e93bbddd3d))
10+
511
## [0.4.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/audio-filters-web-0.4.2...@stream-io/audio-filters-web-0.4.3) (2025-07-25)
612

713
### Bug Fixes

packages/audio-filters-web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stream-io/audio-filters-web",
3-
"version": "0.4.3",
3+
"version": "0.5.0",
44
"main": "./dist/index.cjs.js",
55
"module": "./dist/index.es.js",
66
"types": "./dist/index.d.ts",

packages/audio-filters-web/src/NoiseCancellation.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,13 @@ export class NoiseCancellation implements INoiseCancellation {
269269
if (!this.filterNode || !this.audioContext) {
270270
throw new Error('NoiseCancellation is not initialized');
271271
}
272+
273+
const [audioTrack] = mediaStream.getAudioTracks();
274+
if (!audioTrack) throw new Error('No audio track found in the stream');
275+
272276
const source = this.audioContext.createMediaStreamSource(mediaStream);
273277
const destination = this.audioContext.createMediaStreamDestination();
278+
destination.channelCount = audioTrack.getSettings().channelCount ?? 1;
274279

275280
source.connect(this.filterNode).connect(destination);
276281
// When filter is started, user's microphone media stream is active.

packages/client/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
44

5+
## [1.33.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.33.0...@stream-io/video-client-1.33.1) (2025-10-02)
6+
7+
### Bug Fixes
8+
9+
- ensure ingress participants are prioritized ([#1943](https://github.com/GetStream/stream-video-js/issues/1943)) ([a51a119](https://github.com/GetStream/stream-video-js/commit/a51a119cfb9f13736395b4afb3d3947ef994a6d9))
10+
11+
## [1.33.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.32.0...@stream-io/video-client-1.33.0) (2025-09-30)
12+
13+
### Features
14+
15+
- Audio profiles and Hi-Fi stereo audio ([#1887](https://github.com/GetStream/stream-video-js/issues/1887)) ([3b60c89](https://github.com/GetStream/stream-video-js/commit/3b60c89b8c0dbc40544fe13be79c10e93bbddd3d))
16+
17+
### Bug Fixes
18+
19+
- **client:** server side pinning ([#1936](https://github.com/GetStream/stream-video-js/issues/1936)) ([cd33b9e](https://github.com/GetStream/stream-video-js/commit/cd33b9e4417e8fdc452b6d4a192e10183ddfa31b))
20+
521
## [1.32.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.31.0...@stream-io/video-client-1.32.0) (2025-09-29)
622

723
### Features

packages/client/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@stream-io/video-client",
3-
"version": "1.32.0",
3+
"version": "1.33.1",
44
"main": "dist/index.cjs.js",
55
"module": "dist/index.es.js",
66
"browser": "dist/index.browser.es.js",
@@ -42,6 +42,7 @@
4242
"@rollup/plugin-typescript": "^12.1.2",
4343
"@stream-io/audio-filters-web": "workspace:^",
4444
"@stream-io/node-sdk": "^0.4.24",
45+
"@total-typescript/shoehorn": "^0.1.2",
4546
"@types/sdp-transform": "^2.4.9",
4647
"@types/ua-parser-js": "^0.7.39",
4748
"@vitest/coverage-v8": "^3.1.3",

packages/client/src/Call.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
Publisher,
99
Subscriber,
1010
toRtcConfiguration,
11+
TrackPublishOptions,
1112
trackTypeToParticipantStreamKey,
1213
} from './rtc';
1314
import {
@@ -1287,6 +1288,7 @@ export class Call {
12871288
}
12881289

12891290
this.tracer.setEnabled(enableTracing);
1291+
this.sfuStatsReporter?.flush();
12901292
this.sfuStatsReporter?.stop();
12911293
if (statsOptions?.reporting_interval_ms > 0) {
12921294
this.sfuStatsReporter = new SfuStatsReporter(sfuClient, {
@@ -1770,9 +1772,14 @@ export class Call {
17701772
*
17711773
* @param mediaStream the media stream to publish.
17721774
* @param trackType the type of the track to announce.
1775+
* @param options the publish options.
17731776
*/
1774-
publish = async (mediaStream: MediaStream, trackType: TrackType) => {
1775-
if (!this.sfuClient) throw new Error(`Call not joined yet.`);
1777+
publish = async (
1778+
mediaStream: MediaStream,
1779+
trackType: TrackType,
1780+
options?: TrackPublishOptions,
1781+
) => {
1782+
if (!this.sfuClient) throw new Error(`Call is not joined yet`);
17761783
// joining is in progress, and we should wait until the client is ready
17771784
await this.sfuClient.joinTask;
17781785

@@ -1797,15 +1804,16 @@ export class Call {
17971804
}
17981805

17991806
pushToIfMissing(this.trackPublishOrder, trackType);
1800-
await this.publisher.publish(track, trackType);
1807+
await this.publisher.publish(track, trackType, options);
18011808

18021809
const trackTypes = [trackType];
18031810
if (trackType === TrackType.SCREEN_SHARE) {
18041811
const [audioTrack] = mediaStream.getAudioTracks();
18051812
if (audioTrack) {
1806-
pushToIfMissing(this.trackPublishOrder, TrackType.SCREEN_SHARE_AUDIO);
1807-
await this.publisher.publish(audioTrack, TrackType.SCREEN_SHARE_AUDIO);
1808-
trackTypes.push(TrackType.SCREEN_SHARE_AUDIO);
1813+
const screenShareAudio = TrackType.SCREEN_SHARE_AUDIO;
1814+
pushToIfMissing(this.trackPublishOrder, screenShareAudio);
1815+
await this.publisher.publish(audioTrack, screenShareAudio, options);
1816+
trackTypes.push(screenShareAudio);
18091817
}
18101818
}
18111819

packages/client/src/__tests__/Call.publishing.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ describe('Publishing and Unpublishing tracks', () => {
3535
describe('Validations', () => {
3636
it('publishing is not allowed only when call is not joined', async () => {
3737
const ms = new MediaStream();
38-
const err = 'Call not joined yet.';
38+
const err = 'Call is not joined yet';
3939
await expect(call.publish(ms, TrackType.VIDEO)).rejects.toThrowError(err);
4040
await expect(call.publish(ms, TrackType.AUDIO)).rejects.toThrowError(err);
4141
await expect(
@@ -140,7 +140,11 @@ describe('Publishing and Unpublishing tracks', () => {
140140
vi.spyOn(mediaStream, 'getVideoTracks').mockReturnValue([track]);
141141

142142
await call.publish(mediaStream, TrackType.VIDEO);
143-
expect(publisher.publish).toHaveBeenCalledWith(track, TrackType.VIDEO);
143+
expect(publisher.publish).toHaveBeenCalledWith(
144+
track,
145+
TrackType.VIDEO,
146+
undefined,
147+
);
144148
expect(call['trackPublishOrder']).toEqual([TrackType.VIDEO]);
145149

146150
expect(sfuClient.updateMuteStates).toHaveBeenCalledWith([
@@ -159,7 +163,11 @@ describe('Publishing and Unpublishing tracks', () => {
159163
vi.spyOn(mediaStream, 'getAudioTracks').mockReturnValue([track]);
160164

161165
await call.publish(mediaStream, TrackType.AUDIO);
162-
expect(publisher.publish).toHaveBeenCalledWith(track, TrackType.AUDIO);
166+
expect(publisher.publish).toHaveBeenCalledWith(
167+
track,
168+
TrackType.AUDIO,
169+
undefined,
170+
);
163171
expect(call['trackPublishOrder']).toEqual([TrackType.AUDIO]);
164172

165173
expect(sfuClient.updateMuteStates).toHaveBeenCalledWith([
@@ -181,6 +189,7 @@ describe('Publishing and Unpublishing tracks', () => {
181189
expect(publisher.publish).toHaveBeenCalledWith(
182190
track,
183191
TrackType.SCREEN_SHARE,
192+
undefined,
184193
);
185194
expect(call['trackPublishOrder']).toEqual([TrackType.SCREEN_SHARE]);
186195

@@ -205,10 +214,12 @@ describe('Publishing and Unpublishing tracks', () => {
205214
expect(publisher.publish).toHaveBeenCalledWith(
206215
videoTrack,
207216
TrackType.SCREEN_SHARE,
217+
undefined,
208218
);
209219
expect(publisher.publish).toHaveBeenCalledWith(
210220
audioTrack,
211221
TrackType.SCREEN_SHARE_AUDIO,
222+
undefined,
212223
);
213224
expect(call['trackPublishOrder']).toEqual([
214225
TrackType.SCREEN_SHARE,

packages/client/src/__tests__/StreamVideoClient.api.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const tokenProvider = (userId: string) => {
2525
};
2626
};
2727

28-
describe.skip('StreamVideoClient - coordinator API', () => {
28+
describe('StreamVideoClient - coordinator API', () => {
2929
let client: StreamVideoClient;
3030
const user = {
3131
id: 'sara',

0 commit comments

Comments
 (0)